diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 369040983858..44443560fe15 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -20,6 +20,8 @@ body: - **Remote OS**: Ubuntu - **Remote Architecture**: amd64 - **`code-server --version`**: 4.0.1 + + Please do not just put "latest" for the version. value: | - Web Browser: - Local OS: @@ -84,6 +86,18 @@ body: validations: required: true + - type: dropdown + attributes: + label: Does this bug reproduce in VS Code web? + description: If the bug reproduces in VS Code web, submit the issue upstream instead (https://github.com/microsoft/vscode). You can run VS Code web with `code serve-web`. + options: + - Yes, this is also broken in VS Code web + - No, this works as expected in VS Code web + - This cannot be tested in VS Code web + - I did not test VS Code web + validations: + required: true + - type: dropdown attributes: label: Does this bug reproduce in GitHub Codespaces? diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 337b55ba6e73..29bf6393ef4d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -22,223 +22,178 @@ concurrency: # will skip running `npm install` if it successfully fetched from cache jobs: + changes: + runs-on: ubuntu-latest + outputs: + ci: ${{ steps.filter.outputs.ci }} + code: ${{ steps.filter.outputs.code }} + deps: ${{ steps.filter.outputs.deps }} + docs: ${{ steps.filter.outputs.docs }} + helm: ${{ steps.filter.outputs.helm }} + steps: + - name: Checkout repo + uses: actions/checkout@v6 + - name: Check changed files + uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + ci: + - ".github/**" + - "ci/**" + docs: + - "docs/**" + - "README.md" + - "CHANGELOG.md" + helm: + - "ci/helm-chart/**" + code: + - "src/**" + - "test/**" + deps: + - "lib/**" + - "patches/**" + - "package-lock.json" + - "test/package-lock.json" + - id: debug + run: | + echo "${{ toJSON(steps.filter )}}" + prettier: name: Run prettier check runs-on: ubuntu-22.04 timeout-minutes: 5 steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Install Node.js - uses: actions/setup-node@v4 + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 with: node-version-file: .node-version cache: npm cache-dependency-path: | package-lock.json test/package-lock.json - - run: SKIP_SUBMODULE_DEPS=1 npm ci - - run: npx prettier --check . doctoc: name: Doctoc markdown files runs-on: ubuntu-22.04 timeout-minutes: 5 + needs: changes + if: needs.changes.outputs.docs == 'true' steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@v45 - with: - files: | - docs/** - - - name: Install Node.js - if: steps.changed-files.outputs.any_changed == 'true' - uses: actions/setup-node@v4 + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 with: node-version-file: .node-version cache: npm cache-dependency-path: | package-lock.json test/package-lock.json - - - if: steps.changed-files.outputs.any_changed == 'true' - run: SKIP_SUBMODULE_DEPS=1 npm ci - - - if: steps.changed-files.outputs.any_changed == 'true' - run: npm run doctoc + - run: SKIP_SUBMODULE_DEPS=1 npm ci + - run: npm run doctoc lint-helm: name: Lint Helm chart runs-on: ubuntu-22.04 timeout-minutes: 5 + needs: changes + if: needs.changes.outputs.helm == 'true' steps: - - name: Checkout repo - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@v45 - with: - files: | - ci/helm-chart/** - - - name: Install helm - if: steps.changed-files.outputs.any_changed == 'true' - uses: azure/setup-helm@v4 + - uses: actions/checkout@v6 + - uses: azure/setup-helm@v4 with: token: ${{ secrets.GITHUB_TOKEN }} - - - if: steps.changed-files.outputs.any_changed == 'true' - run: helm plugin install https://github.com/instrumenta/helm-kubeval - - - if: steps.changed-files.outputs.any_changed == 'true' - run: helm kubeval ci/helm-chart + - run: helm plugin install https://github.com/instrumenta/helm-kubeval + - run: helm kubeval ci/helm-chart lint-ts: name: Lint TypeScript files runs-on: ubuntu-22.04 timeout-minutes: 5 + needs: changes + if: needs.changes.outputs.code == 'true' steps: - - name: Checkout repo - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@v45 - with: - files: | - **/*.ts - **/*.js - files_ignore: | - lib/vscode/** - - - name: Install Node.js - if: steps.changed-files.outputs.any_changed == 'true' - uses: actions/setup-node@v4 + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 with: node-version-file: .node-version cache: npm cache-dependency-path: | package-lock.json test/package-lock.json - - - if: steps.changed-files.outputs.any_changed == 'true' - run: SKIP_SUBMODULE_DEPS=1 npm ci - - - name: Lint TypeScript files - if: steps.changed-files.outputs.any_changed == 'true' - run: npm run lint:ts + - run: SKIP_SUBMODULE_DEPS=1 npm ci + - run: npm run lint:ts lint-actions: name: Lint GitHub Actions runs-on: ubuntu-latest + needs: changes + if: needs.changes.outputs.ci == 'true' steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Check workflow files run: | - bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 1.7.1 - ./actionlint -color -shellcheck= -ignore "set-output" + bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 1.7.9 + ./actionlint -color -shellcheck= -ignore "softprops/action-gh-release" shell: bash test-unit: name: Run unit tests runs-on: ubuntu-22.04 timeout-minutes: 5 + needs: changes + if: needs.changes.outputs.code == 'true' steps: - - name: Checkout repo - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@v45 - with: - files: | - **/*.ts - files_ignore: | - lib/vscode/** - - - name: Install Node.js - if: steps.changed-files.outputs.any_changed == 'true' - uses: actions/setup-node@v4 + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 with: node-version-file: .node-version cache: npm cache-dependency-path: | package-lock.json test/package-lock.json - - - if: steps.changed-files.outputs.any_changed == 'true' - run: SKIP_SUBMODULE_DEPS=1 npm ci - - - if: steps.changed-files.outputs.any_changed == 'true' - run: npm run test:unit - - - name: Upload coverage report to Codecov - uses: codecov/codecov-action@v4 + - run: SKIP_SUBMODULE_DEPS=1 npm ci + - run: npm run test:unit + - uses: codecov/codecov-action@v5 + if: success() with: token: ${{ secrets.CODECOV_TOKEN }} - if: success() build: name: Build code-server runs-on: ubuntu-22.04 - timeout-minutes: 60 + timeout-minutes: 70 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} DISABLE_V8_COMPILE_CACHE: 1 steps: - - name: Checkout repo - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - - - name: Install system dependencies - run: sudo apt update && sudo apt install -y libkrb5-dev - - - name: Install quilt - uses: awalsh128/cache-apt-pkgs-action@latest + - run: sudo apt update && sudo apt install -y libkrb5-dev + - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: quilt version: 1.0 - - - name: Patch Code - run: quilt push -a - - - name: Install Node.js - uses: actions/setup-node@v4 + - run: quilt push -a + - uses: actions/setup-node@v6 with: node-version-file: .node-version cache: npm cache-dependency-path: | package-lock.json test/package-lock.json - - run: SKIP_SUBMODULE_DEPS=1 npm ci - - - env: + - run: npm run build + env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: npm run build - # Get Code's git hash. When this changes it means the content is # different and we need to rebuild. - name: Get latest lib/vscode rev id: vscode-rev run: echo "rev=$(git rev-parse HEAD:./lib/vscode)" >> $GITHUB_OUTPUT - # We need to rebuild when we have a new version of Code, when any of # the patches changed, or when the code-server version changes (since # it gets embedded into the code). Use VSCODE_CACHE_VERSION to @@ -249,7 +204,6 @@ jobs: with: path: lib/vscode-reh-web-* key: vscode-reh-package-${{ secrets.VSCODE_CACHE_VERSION }}-${{ steps.vscode-rev.outputs.rev }}-${{ hashFiles('patches/*.diff', 'ci/build/build-vscode.sh') }} - - name: Build vscode env: VERSION: "0.0.0" @@ -259,107 +213,77 @@ jobs: npm ci popd npm run build:vscode - # The release package does not contain any native modules # and is neutral to architecture/os/libc version. - run: npm run release if: success() - # https://github.com/actions/upload-artifact/issues/38 - run: tar -czf package.tar.gz release - - - name: Upload npm package artifact - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v4 with: name: npm-package path: ./package.tar.gz test-e2e: name: Run e2e tests - needs: build runs-on: ubuntu-22.04 timeout-minutes: 25 + needs: [changes, build] + if: needs.changes.outputs.code == 'true' || needs.changes.outputs.deps == 'true' steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Install system dependencies - run: sudo apt update && sudo apt install -y libkrb5-dev - - - name: Install Node.js - uses: actions/setup-node@v4 + - uses: actions/checkout@v6 + - run: sudo apt update && sudo apt install -y libkrb5-dev + - uses: actions/setup-node@v6 with: node-version-file: .node-version cache: npm cache-dependency-path: | package-lock.json test/package-lock.json - - run: SKIP_SUBMODULE_DEPS=1 npm ci - - - name: Download npm package - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: name: npm-package - - run: tar -xzf package.tar.gz - - run: cd release && npm install --unsafe-perm --omit=dev - - name: Install Playwright OS dependencies run: | ./test/node_modules/.bin/playwright install-deps ./test/node_modules/.bin/playwright install - - run: CODE_SERVER_TEST_ENTRY=./release npm run test:e2e - - - name: Upload test artifacts + - uses: actions/upload-artifact@v4 if: always() - uses: actions/upload-artifact@v4 with: name: failed-test-videos path: ./test/test-results - - - name: Remove release packages and test artifacts - run: rm -rf ./release ./test/test-results + - run: rm -rf ./release ./test/test-results test-e2e-proxy: name: Run e2e tests behind proxy - needs: build runs-on: ubuntu-22.04 timeout-minutes: 25 + needs: [changes, build] + if: needs.changes.outputs.code == 'true' || needs.changes.outputs.deps == 'true' steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Install system dependencies - run: sudo apt update && sudo apt install -y libkrb5-dev - - - name: Install Node.js - uses: actions/setup-node@v4 + - uses: actions/checkout@v6 + - run: sudo apt update && sudo apt install -y libkrb5-dev + - uses: actions/setup-node@v6 with: node-version-file: .node-version cache: npm cache-dependency-path: | package-lock.json test/package-lock.json - - run: SKIP_SUBMODULE_DEPS=1 npm ci - - - name: Download npm package - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: name: npm-package - - run: tar -xzf package.tar.gz - - run: cd release && npm install --unsafe-perm --omit=dev - - name: Install Playwright OS dependencies run: | ./test/node_modules/.bin/playwright install-deps ./test/node_modules/.bin/playwright install - - name: Cache Caddy uses: actions/cache@v4 id: caddy-cache @@ -367,7 +291,6 @@ jobs: path: | ~/.cache/caddy key: cache-caddy-2.5.2 - - name: Install Caddy env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -376,16 +299,13 @@ jobs: gh release download v2.5.2 --repo caddyserver/caddy --pattern "caddy_2.5.2_linux_amd64.tar.gz" mkdir -p ~/.cache/caddy tar -xzf caddy_2.5.2_linux_amd64.tar.gz --directory ~/.cache/caddy - - - run: sudo ~/.cache/caddy/caddy start --config ./ci/Caddyfile - + - run: ~/.cache/caddy/caddy start --config ./ci/Caddyfile - run: CODE_SERVER_TEST_ENTRY=./release npm run test:e2e:proxy + - run: ~/.cache/caddy/caddy stop --config ./ci/Caddyfile + if: always() - - if: always() - run: sudo ~/.cache/caddy/caddy stop --config ./ci/Caddyfile - - - if: always() - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v4 + if: always() with: name: failed-test-videos-proxy path: ./test/test-results diff --git a/.github/workflows/installer.yaml b/.github/workflows/installer.yaml index e8b04453dcfc..f89c5b31de1b 100644 --- a/.github/workflows/installer.yaml +++ b/.github/workflows/installer.yaml @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install code-server run: ./install.sh @@ -44,7 +44,7 @@ jobs: container: "alpine:3.17" steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install curl run: apk add curl @@ -67,7 +67,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install code-server run: ./install.sh diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 7c22d7f9d154..49e1c6013be7 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -25,15 +25,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code-server - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: .node-version - name: Download npm package from release artifacts - uses: robinraju/release-downloader@v1.11 + uses: robinraju/release-downloader@v1.12 with: repository: "coder/code-server" tag: ${{ github.event.inputs.version || github.ref_name }} @@ -53,38 +53,6 @@ jobs: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_ENVIRONMENT: "production" - homebrew: - needs: npm - runs-on: ubuntu-latest - steps: - # Ensure things are up to date - # Suggested by homebrew maintainers - # https://github.com/Homebrew/discussions/discussions/1532#discussioncomment-782633 - - name: Set up Homebrew - id: set-up-homebrew - uses: Homebrew/actions/setup-homebrew@master - - - name: Checkout code-server - uses: actions/checkout@v4 - - - name: Configure git - run: | - git config --global user.name cdrci - git config --global user.email opensource@coder.com - - # Strip out the v (v4.9.1 -> 4.9.1). - - name: Get and set VERSION - run: | - TAG="${{ github.event.inputs.version || github.ref_name }}" - echo "VERSION=${TAG#v}" >> $GITHUB_ENV - - - name: Bump code-server homebrew version - env: - VERSION: ${{ env.VERSION }} - HOMEBREW_GITHUB_API_TOKEN: ${{secrets.HOMEBREW_GITHUB_API_TOKEN}} - - run: ./ci/steps/brew-bump.sh - aur: runs-on: ubuntu-latest timeout-minutes: 10 @@ -94,13 +62,13 @@ jobs: steps: # We need to checkout code-server so we can get the version - name: Checkout code-server - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 path: "./code-server" - name: Checkout code-server-aur repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: "cdrci/code-server-aur" token: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }} @@ -124,7 +92,7 @@ jobs: echo "VERSION=${TAG#v}" >> $GITHUB_ENV - name: Validate package - uses: heyhusen/archlinux-package-action@v2.2.1 + uses: heyhusen/archlinux-package-action@v2.4.0 env: VERSION: ${{ env.VERSION }} with: @@ -145,10 +113,10 @@ jobs: gh pr create --repo coder/code-server-aur --title "chore: bump version to ${{ env.VERSION }}" --body "PR opened by @$GITHUB_ACTOR" --assignee $GITHUB_ACTOR docker: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Checkout code-server - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -176,7 +144,7 @@ jobs: echo "VERSION=${TAG#v}" >> $GITHUB_ENV - name: Download deb artifacts - uses: robinraju/release-downloader@v1.11 + uses: robinraju/release-downloader@v1.12 with: repository: "coder/code-server" tag: v${{ env.VERSION }} @@ -184,7 +152,7 @@ jobs: out-file-path: "release-packages" - name: Download rpm artifacts - uses: robinraju/release-downloader@v1.11 + uses: robinraju/release-downloader@v1.12 with: repository: "coder/code-server" tag: v${{ env.VERSION }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 1a09c6dda41b..8f49c908083c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -19,95 +19,27 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: - package-linux-amd64: - name: x86-64 Linux build - runs-on: ubuntu-latest - timeout-minutes: 15 - needs: npm-version - container: "centos:8" - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version-file: .node-version - cache: npm - cache-dependency-path: | - package-lock.json - test/package-lock.json - - - run: SKIP_SUBMODULE_DEPS=1 npm ci - - - name: Install development tools - run: | - cd /etc/yum.repos.d/ - sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* - sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* - yum install -y gcc-c++ make jq rsync python3 libsecret-devel krb5-devel - - - name: Install nfpm and envsubst - run: | - mkdir -p ~/.local/bin - curl -sSfL https://github.com/goreleaser/nfpm/releases/download/v2.22.2/nfpm_2.22.2_`uname -s`_`uname -m`.tar.gz | tar -C ~/.local/bin -zxv nfpm - curl -sSfL https://github.com/a8m/envsubst/releases/download/v1.1.0/envsubst-`uname -s`-`uname -m` -o envsubst - chmod +x envsubst - mv envsubst ~/.local/bin - echo "$HOME/.local/bin" >> $GITHUB_PATH - - - name: Download npm package - uses: actions/download-artifact@v4 - with: - name: npm-release-package - - - run: tar -xzf package.tar.gz - - - run: npm run release:standalone - - - run: npm run test:integration - - - name: Upload coverage report to Codecov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - if: success() - continue-on-error: true - - # Strip out the v (v4.9.1 -> 4.9.1). - - name: Get and set VERSION - run: | - TAG="${{ inputs.version || github.ref_name }}" - echo "VERSION=${TAG#v}" >> $GITHUB_ENV - - - env: - VERSION: ${{ env.VERSION }} - run: npm run package - - - uses: softprops/action-gh-release@v1 - with: - draft: true - discussion_category_name: "📣 Announcements" - files: ./release-packages/* - package-linux-cross: - name: Linux cross-compile builds + name: ${{ matrix.prefix }} runs-on: ubuntu-latest timeout-minutes: 15 needs: npm-version - container: "debian:buster" + container: "python:3.8-slim-buster" strategy: matrix: include: + - prefix: x86_64-linux-gnu + npm_arch: x64 + apt_arch: amd64 + package_arch: amd64 - prefix: aarch64-linux-gnu npm_arch: arm64 apt_arch: arm64 + package_arch: arm64 - prefix: arm-linux-gnueabihf npm_arch: armv7l apt_arch: armhf + package_arch: armv7l env: AR: ${{ format('{0}-ar', matrix.prefix) }} @@ -121,16 +53,17 @@ jobs: PKG_CONFIG_PATH: ${{ format('/usr/lib/{0}/pkgconfig', matrix.prefix) }} TARGET_ARCH: ${{ matrix.apt_arch }} npm_config_arch: ${{ matrix.npm_arch }} + PKG_ARCH: ${{ matrix.package_arch }} # Not building from source results in an x86_64 argon2, as if # npm_config_arch is being ignored. npm_config_build_from_source: true steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: .node-version cache: npm @@ -140,8 +73,9 @@ jobs: - name: Install cross-compiler and system dependencies run: | + sed -i 's/deb\.debian\.org/archive.debian.org/g' /etc/apt/sources.list dpkg --add-architecture $TARGET_ARCH - apt-get update && apt-get install -y --no-install-recommends \ + apt update && apt install -y --no-install-recommends \ crossbuild-essential-$TARGET_ARCH \ libx11-dev:$TARGET_ARCH \ libx11-xcb-dev:$TARGET_ARCH \ @@ -149,8 +83,7 @@ jobs: libsecret-1-dev:$TARGET_ARCH \ libkrb5-dev:$TARGET_ARCH \ ca-certificates \ - curl wget rsync gettext-base \ - python3 + curl wget rsync gettext-base - run: SKIP_SUBMODULE_DEPS=1 npm ci @@ -161,7 +94,7 @@ jobs: echo "$HOME/.local/bin" >> $GITHUB_PATH - name: Download npm package - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: npm-release-package @@ -183,7 +116,7 @@ jobs: - env: VERSION: ${{ env.VERSION }} - run: npm run package ${npm_config_arch} + run: npm run package $PKG_ARCH - uses: softprops/action-gh-release@v1 with: @@ -193,6 +126,67 @@ jobs: package-macos-amd64: name: x86-64 macOS build + runs-on: macos-15-intel + timeout-minutes: 15 + needs: npm-version + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + steps: + - name: Checkout repo + uses: actions/checkout@v6 + + - name: Install Node.js + uses: actions/setup-node@v6 + with: + node-version-file: .node-version + cache: npm + cache-dependency-path: | + package-lock.json + test/package-lock.json + + - run: SKIP_SUBMODULE_DEPS=1 npm ci + + - name: Install nfpm + run: | + mkdir -p ~/.local/bin + curl -sSfL https://github.com/goreleaser/nfpm/releases/download/v2.3.1/nfpm_2.3.1_`uname -s`_`uname -m`.tar.gz | tar -C ~/.local/bin -zxv nfpm + echo "$HOME/.local/bin" >> $GITHUB_PATH + + # The version of node-gyp we use depends on distutils but it was removed + # in Python 3.12. It seems to be fixed in the latest node-gyp so when we + # next update Node we can probably remove this. For now, install + # setuptools since it contains distutils. + - run: brew install python-setuptools + + - name: Download npm package + uses: actions/download-artifact@v5 + with: + name: npm-release-package + + - run: tar -xzf package.tar.gz + - run: npm run release:standalone + - run: npm run test:native + + # Strip out the v (v4.9.1 -> 4.9.1). + - name: Get and set VERSION + run: | + TAG="${{ inputs.version || github.ref_name }}" + echo "VERSION=${TAG#v}" >> $GITHUB_ENV + + - name: Build packages with nfpm + env: + VERSION: ${{ env.VERSION }} + run: npm run package + + - uses: softprops/action-gh-release@v1 + with: + draft: true + discussion_category_name: "📣 Announcements" + files: ./release-packages/* + + package-macos-arm64: + name: arm64 macOS build runs-on: macos-latest timeout-minutes: 15 needs: npm-version @@ -201,10 +195,10 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: .node-version cache: npm @@ -227,7 +221,7 @@ jobs: - run: brew install python-setuptools - name: Download npm package - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: npm-release-package @@ -259,7 +253,7 @@ jobs: needs: npm-version steps: - name: Download npm package - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: npm-release-package @@ -275,7 +269,7 @@ jobs: timeout-minutes: 15 steps: - name: Download artifacts - uses: dawidd6/action-download-artifact@v6 + uses: dawidd6/action-download-artifact@v11 id: download with: branch: ${{ github.ref }} diff --git a/.github/workflows/scripts.yaml b/.github/workflows/scripts.yaml index d3ca65cbc548..d6b2728ad66f 100644 --- a/.github/workflows/scripts.yaml +++ b/.github/workflows/scripts.yaml @@ -41,7 +41,7 @@ jobs: container: "alpine:3.17" steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install test utilities run: apk add bats checkbashisms @@ -58,7 +58,7 @@ jobs: timeout-minutes: 5 steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install lint utilities run: sudo apt install shellcheck diff --git a/.github/workflows/security.yaml b/.github/workflows/security.yaml index 5f41bec5bade..cb512093d6c2 100644 --- a/.github/workflows/security.yaml +++ b/.github/workflows/security.yaml @@ -25,12 +25,12 @@ jobs: timeout-minutes: 15 steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Install Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: .node-version @@ -43,15 +43,15 @@ jobs: permissions: contents: read # for actions/checkout to fetch code security-events: write # for github/codeql-action/upload-sarif to upload SARIF results - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Run Trivy vulnerability scanner in repo mode - uses: aquasecurity/trivy-action@915b19bbe73b92a6cf82a1bc12b087c9a19a5fe2 + uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 with: scan-type: "fs" scan-ref: "." @@ -72,11 +72,11 @@ jobs: contents: read # for actions/checkout to fetch code security-events: write # for github/codeql-action/autobuild to send a status report name: Analyze with CodeQL - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/trivy-docker.yaml b/.github/workflows/trivy-docker.yaml index 30c6efda575d..e8c303e9d9c0 100644 --- a/.github/workflows/trivy-docker.yaml +++ b/.github/workflows/trivy-docker.yaml @@ -44,14 +44,14 @@ concurrency: jobs: trivy-scan-image: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Run Trivy vulnerability scanner in image mode - uses: aquasecurity/trivy-action@915b19bbe73b92a6cf82a1bc12b087c9a19a5fe2 + uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 with: image-ref: "docker.io/codercom/code-server:latest" ignore-unfixed: true diff --git a/.node-version b/.node-version index d4b7699d36ca..5767036af0e2 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -20.18.1 +22.21.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d6003b33317..637932edc382 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,295 @@ Code v99.99.999 ## Unreleased +Code v1.107.0 + +### Changed + +- Update to Code 1.107.0 + +### Added + +- New `--cookie-suffix` flag that can be used to add a suffix to the cookie when + using the built-in password authentication. You can use this to avoid + collisions with other instances of code-server. +- Support a new property `authorizationHeaderToken` on the extension gallery + configuration. This will be added to marketplace requests as a bearer token + using the `Authorization` header. + +## [4.106.3](https://github.com/coder/code-server/releases/tag/v4.106.3) - 2025-12-01 + +Code v1.106.3 + +### Changed + +- Update to Code 1.106.3 + +## [4.106.2](https://github.com/coder/code-server/releases/tag/v4.106.2) - 2025-11-19 + +Code v1.106.2 + +### Changed + +- Update to Code 1.106.2 + +## [4.106.0](https://github.com/coder/code-server/releases/tag/v4.106.0) - 2025-11-19 + +Code v1.106.0 + +### Changed + +- Update to Code 1.106.0 + +## [4.105.1](https://github.com/coder/code-server/releases/tag/v4.105.1) - 2025-10-20 + +Code v1.105.1 + +### Changed + +- Update to Code 1.105.1 + +## [4.105.0](https://github.com/coder/code-server/releases/tag/v4.105.0) - 2025-10-17 + +Code v1.105.0 + +### Changed + +- Update to Code 1.105.0 + +## [4.104.3](https://github.com/coder/code-server/releases/tag/v4.104.3) - 2025-10-07 + +Code v1.104.3 + +### Changed + +- Update to Code 1.104.3. + +## [4.104.2](https://github.com/coder/code-server/releases/tag/v4.104.2) - 2025-09-26 + +Code v1.104.2 + +### Changed + +- Update to Code 1.104.2. + +## [4.104.1](https://github.com/coder/code-server/releases/tag/v4.104.1) - 2025-09-19 + +Code v1.104.1 + +### Changed + +- Update to Code 1.104.1. + +## [4.104.0](https://github.com/coder/code-server/releases/tag/v4.104.0) - 2025-09-15 + +Code v1.104.0 + +### Fixed + +- Fix "extension not found" errors from Open VSX when trying to install the + latest version of an extension. + +### Changed + +- Update to Code 1.104.0. + +## [4.103.2](https://github.com/coder/code-server/releases/tag/v4.103.2) - 2025-08-25 + +Code v1.103.2 + +### Changed + +- Update to Code 1.103.2. + +## [4.103.1](https://github.com/coder/code-server/releases/tag/v4.103.1) - 2025-08-15 + +Code v1.103.1 + +### Changed + +- Update to Code 1.103.1. + +## [4.103.0](https://github.com/coder/code-server/releases/tag/v4.103.0) - 2025-08-12 + +Code v1.103.0 + +### Changed + +- Update to Code 1.103.0. + +## [4.102.2](https://github.com/coder/code-server/releases/tag/v4.102.2) - 2025-07-24 + +Code v1.102.2 + +### Changed + +- Update to Code 1.102.2. + +## [4.102.1](https://github.com/coder/code-server/releases/tag/v4.102.1) - 2025-07-17 + +Code v1.102.1 + +### Changed + +- Update to Code 1.102.1. + +## [4.102.0](https://github.com/coder/code-server/releases/tag/v4.102.0) - 2025-07-16 + +Code v1.102.0 + +### Changed + +- Update to Code 1.102.0. + +### Added + +- Custom strings can be configured using the `--i18n` flag set to a JSON + file. This can be used for either translation (and can be used alongside + `--locale`) or for customizing the strings. See + [./src/node/i18n/locales/en.json](./src/node/i18n/locales/en.json) for the + available keys. + +## [4.101.2](https://github.com/coder/code-server/releases/tag/v4.101.2) - 2025-06-25 + +Code v1.101.2 + +### Changed + +- Update to Code 1.101.2. + +### Fixed + +- Fix web views not loading due to 401 when requesting the service worker. + +## [4.101.1](https://github.com/coder/code-server/releases/tag/v4.101.1) - 2025-06-20 + +Code v1.101.1 + +### Changed + +- Update to Code 1.101.1. + +## [4.101.0](https://github.com/coder/code-server/releases/tag/v4.101.0) - 2025-06-20 + +Code v1.101.0 + +### Changed + +- Update to Code 1.101.0. + +## [4.100.3](https://github.com/coder/code-server/releases/tag/v4.100.3) - 2025-06-03 + +Code v1.100.3 + +### Changed + +- Update to Code 1.100.3. + +## [4.100.2](https://github.com/coder/code-server/releases/tag/v4.100.2) - 2025-05-15 + +Code v1.100.2 + +### Changed + +- Update to Code 1.100.2. + +## [4.100.1](https://github.com/coder/code-server/releases/tag/v4.100.1) - 2025-05-13 + +Code v1.100.1 + +### Changed + +- Update to Code 1.100.1. + +## [4.100.0](https://github.com/coder/code-server/releases/tag/v4.100.0) - 2025-05-12 + +Code v1.100.0 + +### Added + +- Trusted domains for links can now be set at run-time by configuring + `linkProtectionTrustedDomains` in the `lib/vscode/product.json` file or via + the `--link-protection-trusted-domains` flag. + +### Changed + +- Update to Code 1.100.0. +- Disable extension signature verification, which previously was skipped by + default (the package used for verification is not available to OSS builds of + VS Code) but now reportedly throws hard errors making it impossible to install + extensions. + +### Fixed + +- Flags with repeatable options now work via the config file. + +## [4.99.4](https://github.com/coder/code-server/releases/tag/v4.99.4) - 2025-05-02 + +Code v1.99.3 + +### Security + +- Validate that ports in the path proxy are numbers, to prevent proxying to + arbitrary domains. + +## [4.99.3](https://github.com/coder/code-server/releases/tag/v4.99.3) - 2025-04-17 + +Code v1.99.3 + +### Added + +- Added `--skip-auth-preflight` flag to let preflight requests through the + proxy. + +### Changed + +- Update to Code 1.99.3. + +## [4.99.2](https://github.com/coder/code-server/releases/tag/v4.99.2) - 2025-04-10 + +Code v1.99.2 + +### Changed + +- Update to Code 1.99.2. + +## [4.99.1](https://github.com/coder/code-server/releases/tag/v4.99.1) - 2025-04-08 + +Code v1.99.1 + +### Changed + +- Update to Code 1.99.1. + +## [4.99.0](https://github.com/coder/code-server/releases/tag/v4.99.0) - 2025-04-07 + +Code v1.99.0 + +### Changed + +- Update to Code 1.99.0. + +## [4.98.0](https://github.com/coder/code-server/releases/tag/v4.98.0) - 2025-03-07 + +Code v1.98.0 + +### Changed + +- Update to Code 1.98.0. + +## [4.97.2](https://github.com/coder/code-server/releases/tag/v4.96.4) - 2025-02-18 + +Code v1.97.2 + +### Added + +- Added back macOS amd64 builds. + +### Changed + +- Update to Code 1.97.2. +- Softened dark mode login page colors. + ## [4.96.4](https://github.com/coder/code-server/releases/tag/v4.96.4) - 2025-01-20 Code v1.96.4 @@ -637,7 +926,6 @@ Code v1.68.1 would be accessible at `my.domain/proxy/8000/` without any authentication. If all of the following apply to you please update as soon as possible: - - You run code-server with the built-in password authentication. - You run unprotected HTTP services on ports accessible by code-server. diff --git a/ci/build/build-release.sh b/ci/build/build-release.sh index 780e84bf18b5..0d226fd83a25 100755 --- a/ci/build/build-release.sh +++ b/ci/build/build-release.sh @@ -44,10 +44,6 @@ bundle_code_server() { rsync src/browser/pages/*.css "$RELEASE_PATH/src/browser/pages" rsync src/browser/robots.txt "$RELEASE_PATH/src/browser" - # Add typings for plugins - mkdir -p "$RELEASE_PATH/typings" - rsync typings/pluginapi.d.ts "$RELEASE_PATH/typings" - # Adds the commit to package.json jq --slurp '(.[0] | del(.scripts,.jest,.devDependencies)) * .[1]' package.json <( cat << EOF diff --git a/ci/build/build-standalone-release.sh b/ci/build/build-standalone-release.sh index f4078557789b..b0833db810cb 100755 --- a/ci/build/build-standalone-release.sh +++ b/ci/build/build-standalone-release.sh @@ -16,7 +16,7 @@ main() { # Package managers may shim their own "node" wrapper into the PATH, so run # node and ask it for its true path. local node_path - node_path="$(node <<< 'console.info(process.execPath)')" + node_path="$(node -p process.execPath)" mkdir -p "$RELEASE_PATH/bin" mkdir -p "$RELEASE_PATH/lib" diff --git a/ci/build/build-vscode.sh b/ci/build/build-vscode.sh index e4a781a88b40..f037fac3a4c1 100755 --- a/ci/build/build-vscode.sh +++ b/ci/build/build-vscode.sh @@ -112,7 +112,9 @@ EOF # this because we have an NPM package that could be installed on any platform. # The correct platform dependencies and scripts will be installed as part of # the post-install during `npm install` or when building a standalone release. - npm run gulp "vscode-reh-web-linux-x64${MINIFY:+-min}" + node --max-old-space-size=16384 --optimize-for-size \ + ./node_modules/gulp/bin/gulp.js \ + "vscode-reh-web-linux-x64${MINIFY:+-min}" # Reset so if you develop after building you will not be stuck with the wrong # commit (the dev client will use `oss-dev` but the dev server will still use diff --git a/ci/build/npm-postinstall.sh b/ci/build/npm-postinstall.sh index fc82b2ffbe47..ee4c92237023 100755 --- a/ci/build/npm-postinstall.sh +++ b/ci/build/npm-postinstall.sh @@ -76,8 +76,8 @@ main() { echo "USE AT YOUR OWN RISK!" fi - if [ "$major_node_version" -ne "${FORCE_NODE_VERSION:-20}" ]; then - echo "ERROR: code-server currently requires node v20." + if [ "$major_node_version" -ne "${FORCE_NODE_VERSION:-22}" ]; then + echo "ERROR: code-server currently requires node v22." if [ -n "$FORCE_NODE_VERSION" ]; then echo "However, you have overrided the version check to use v$FORCE_NODE_VERSION." fi diff --git a/ci/dev/gen_icons.sh b/ci/dev/gen_icons.sh index 9d27486dcc57..39d509bc4f4b 100755 --- a/ci/dev/gen_icons.sh +++ b/ci/dev/gen_icons.sh @@ -1,44 +1,50 @@ #!/bin/sh set -eu +# Generate icons from a single favicon.svg. favicon.svg should have no fill +# colors set. main() { cd src/browser/media - # We need .ico for backwards compatibility. - # The other two are the only icon sizes required by Chrome and - # we use them for stuff like apple-touch-icon as well. - # https://web.dev/add-manifest/ + # We need .ico for backwards compatibility. The other two are the only icon + # sizes required by Chrome and we use them for stuff like apple-touch-icon as + # well. https://web.dev/add-manifest/ # # This should be enough and we can always add more if there are problems. - + # + # -quiet to avoid https://github.com/ImageMagick/ImageMagick/issues/884 # -background defaults to white but we want it transparent. + # -density somehow makes the image both sharper and smaller in file size. + # # https://imagemagick.org/script/command-line-options.php#background - convert -quiet -background transparent -resize 256x256 favicon.svg favicon.ico - # We do not generate the pwa-icon from the favicon as they are slightly different - # designs and sizes. - # See favicon.afdesign and #2401 for details on the differences. - convert -quiet -background transparent -resize 192x192 pwa-icon.png pwa-icon-192.png - convert -quiet -background transparent -resize 512x512 pwa-icon.png pwa-icon-512.png + convert -quiet -background transparent \ + -resize 256x256 -density 256x256 \ + favicon.svg favicon.ico - # We use -quiet above to avoid https://github.com/ImageMagick/ImageMagick/issues/884 + # Generate PWA icons. There should be enough padding to support masking. + convert -quiet -border 60x60 -bordercolor white -background white \ + -resize 192x192 -density 192x192 \ + favicon.svg pwa-icon-maskable-192.png + convert -quiet -border 160x160 -bordercolor white -background white \ + -resize 512x512 -density 512x512 \ + favicon.svg pwa-icon-maskable-512.png - # The following adds dark mode support for the favicon as favicon-dark-support.svg - # There is no similar capability for pwas or .ico so we can only add support to the svg. - favicon_dark_style="" - # See https://stackoverflow.com/a/22901380/4283659 - # This escapes all newlines so that sed will accept them. - favicon_dark_style="$(printf "%s\n" "$favicon_dark_style" | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/\\n/g')" - sed "$( - cat -n << EOF -s% favicon-dark-support.svg + # Generate non-maskable PWA icons. + magick pwa-icon-maskable-192.png \ + \( +clone -threshold 101% -fill white -draw "roundRectangle 0,0 %[fx:int(w)],%[fx:int(h)] 50,50" \) \ + -channel-fx "| gray=>alpha" \ + pwa-icon-192.png + magick pwa-icon-maskable-512.png \ + \( +clone -threshold 101% -fill white -draw "roundRectangle 0,0 %[fx:int(w)],%[fx:int(h)] 100,100" \) \ + -channel-fx "| gray=>alpha" \ + pwa-icon-512.png + + # The following adds dark mode support for the favicon as + # favicon-dark-support.svg There is no similar capability for pwas or .ico so + # we can only add support to the svg. + favicon_dark_style="" + cp favicon.svg favicon-dark-support.svg + sed "s% favicon-dark-support.svg } main "$@" diff --git a/ci/dev/test-integration.sh b/ci/dev/test-integration.sh index 7f8fd5b3e826..380502c230ba 100755 --- a/ci/dev/test-integration.sh +++ b/ci/dev/test-integration.sh @@ -33,7 +33,7 @@ main() { exit 1 fi - CODE_SERVER_PATH="$path" CS_DISABLE_PLUGINS=true ./test/node_modules/.bin/jest "$@" --coverage=false --testRegex "./test/integration" --testPathIgnorePatterns "./test/integration/fixtures" + CODE_SERVER_PATH="$path" ./test/node_modules/.bin/jest "$@" --coverage=false --testRegex "./test/integration" --testPathIgnorePatterns "./test/integration/fixtures" } main "$@" diff --git a/ci/dev/test-unit.sh b/ci/dev/test-unit.sh index e312c073e4ef..15fd2030ea59 100755 --- a/ci/dev/test-unit.sh +++ b/ci/dev/test-unit.sh @@ -6,15 +6,10 @@ main() { source ./ci/lib.sh - echo "Building test plugin" - pushd test/unit/node/test-plugin - make -s out/index.js - popd - # We must keep jest in a sub-directory. See ../../test/package.json for more # information. We must also run it from the root otherwise coverage will not # include our source files. - CS_DISABLE_PLUGINS=true ./test/node_modules/.bin/jest "$@" --testRegex "./test/unit/.*ts" --testPathIgnorePatterns "./test/unit/node/test-plugin" + ./test/node_modules/.bin/jest "$@" --testRegex "./test/unit/.*ts" } main "$@" diff --git a/ci/helm-chart/Chart.yaml b/ci/helm-chart/Chart.yaml index 6a4123a9aa72..00e7221ee109 100644 --- a/ci/helm-chart/Chart.yaml +++ b/ci/helm-chart/Chart.yaml @@ -15,9 +15,9 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 3.25.2 +version: 3.31.2 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. -appVersion: 4.96.4 +appVersion: 4.104.2 diff --git a/ci/helm-chart/templates/deployment.yaml b/ci/helm-chart/templates/deployment.yaml index 474653c33aac..8f6fd89a5800 100644 --- a/ci/helm-chart/templates/deployment.yaml +++ b/ci/helm-chart/templates/deployment.yaml @@ -7,8 +7,11 @@ metadata: helm.sh/chart: {{ include "code-server.chart" . }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- if .Values.annotations }} + annotations: {{- toYaml .Values.annotations | nindent 4 }} + {{- end }} spec: - replicas: 1 + replicas: {{ .Values.replicaCount | default 1 }} strategy: type: Recreate selector: diff --git a/ci/helm-chart/values.yaml b/ci/helm-chart/values.yaml index 91c258929fe6..a414de0da0ec 100644 --- a/ci/helm-chart/values.yaml +++ b/ci/helm-chart/values.yaml @@ -6,7 +6,7 @@ replicaCount: 1 image: repository: codercom/code-server - tag: '4.96.4' + tag: '4.104.2' pullPolicy: Always # Specifies one or more secrets to be used when pulling images from a @@ -19,6 +19,9 @@ nameOverride: "" fullnameOverride: "" hostnameOverride: "" +# The existing secret to use for code-server authentication in the frontend. the password is stored in the secret under the key `password` +# existingSecret: "" + serviceAccount: # Specifies whether a service account should be created create: true @@ -28,6 +31,9 @@ serviceAccount: # If not set and create is true, a name is generated using the fullname template name: "" +# Specifies annotations for deployment +annotations: {} + podAnnotations: {} podSecurityContext: {} @@ -72,8 +78,9 @@ extraArgs: [] extraVars: [] # - name: DISABLE_TELEMETRY # value: "true" +# if dind is desired: # - name: DOCKER_HOST -# value: "tcp://localhost:2375" +# value: "tcp://localhost:2376" ## ## Init containers parameters: @@ -136,25 +143,39 @@ lifecycle: # - -c # - curl -s -L SOME_SCRIPT | bash + # for dind, the following may be helpful + # postStart: + # exec: + # command: + # - /bin/sh + # - -c + # - | + # sudo apt-get update \ + # && sudo apt-get install -y docker.io + ## Enable an Specify container in extraContainers. ## This is meant to allow adding code-server dependencies, like docker-dind. extraContainers: | # If docker-dind is used, DOCKER_HOST env is mandatory to set in "extraVars" -#- name: docker-dind -# image: docker:19.03-dind -# imagePullPolicy: IfNotPresent -# resources: -# requests: -# cpu: 250m -# memory: 256M -# securityContext: -# privileged: true -# procMount: Default -# env: -# - name: DOCKER_TLS_CERTDIR -# value: "" -# - name: DOCKER_DRIVER -# value: "overlay2" +# - name: docker-dind +# image: docker:28.3.2-dind +# imagePullPolicy: IfNotPresent +# resources: +# requests: +# cpu: 1 +# ephemeral-storage: "50Gi" +# memory: 10Gi +# securityContext: +# privileged: true +# procMount: Default +# env: +# - name: DOCKER_TLS_CERTDIR +# value: "" # disable TLS setup +# command: +# - dockerd +# - --host=unix:///var/run/docker.sock +# - --host=tcp://0.0.0.0:2376 + extraInitContainers: | # - name: customization diff --git a/ci/release-image/Dockerfile b/ci/release-image/Dockerfile index afdd1b851601..fba7189e6999 100644 --- a/ci/release-image/Dockerfile +++ b/ci/release-image/Dockerfile @@ -31,7 +31,10 @@ RUN sed -i "s/# en_US.UTF-8/en_US.UTF-8/" /etc/locale.gen \ && locale-gen ENV LANG=en_US.UTF-8 -RUN adduser --gecos '' --disabled-password coder \ +RUN if grep -q 1000 /etc/passwd; then \ + userdel -r "$(id -un 1000)"; \ + fi \ + && adduser --gecos '' --disabled-password coder \ && echo "coder ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd RUN ARCH="$(dpkg --print-architecture)" \ diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 9d6c413c4ff4..3a89005e32f6 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -32,7 +32,7 @@ The prerequisites for contributing to code-server are almost the same as those for [VS Code](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#prerequisites). Here is what is needed: -- `node` v20.x +- `node` v22.x - `git` v2.x or greater - [`git-lfs`](https://git-lfs.github.com) - [`npm`](https://www.npmjs.com/) diff --git a/docs/FAQ.md b/docs/FAQ.md index c46c003b8800..c9c4f7c3a6ef 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -31,6 +31,7 @@ - [What's the difference between code-server and Theia?](#whats-the-difference-between-code-server-and-theia) - [What's the difference between code-server and OpenVSCode-Server?](#whats-the-difference-between-code-server-and-openvscode-server) - [What's the difference between code-server and GitHub Codespaces?](#whats-the-difference-between-code-server-and-github-codespaces) +- [What's the difference between code-server and VS Code web?](#whats-the-difference-between-code-server-and-vs-code-web) - [Does code-server have any security login validation?](#does-code-server-have-any-security-login-validation) - [Are there community projects involving code-server?](#are-there-community-projects-involving-code-server) - [How do I change the port?](#how-do-i-change-the-port) @@ -322,12 +323,8 @@ As long as there is an active browser connection, code-server touches `~/.local/share/code-server/heartbeat` once a minute. If you want to shutdown code-server if there hasn't been an active connection -after a predetermined amount of time, you can do so by checking continuously for -the last modified time on the heartbeat file. If it is older than X minutes (or -whatever amount of time you'd like), you can kill code-server. - -Eventually, [#1636](https://github.com/coder/code-server/issues/1636) will make -this process better. +after a predetermined amount of time, you can use the --idle-timeout-seconds flag +or set an `CODE_SERVER_IDLE_TIMEOUT_SECONDS` environment variable. ## How do I change the password? @@ -383,6 +380,9 @@ mount into `/home/coder/myproject` from inside the `code-server` container. You need to make sure the Docker daemon's `/home/coder/myproject` is the same as the one mounted inside the `code-server` container, and the mount will work. +If you want Docker enabled when deploying on Kubernetes, look at the `values.yaml` +file for the 3 fields: `extraVars`, `lifecycle.postStart`, and `extraContainers`. + ## How do I disable telemetry? Use the `--disable-telemetry` flag to disable telemetry. @@ -440,6 +440,8 @@ Specific changes include: - The ability to use your own marketplace and collect your own telemetry - Built-in proxy for accessing ports on the remote machine integrated into VS Code's ports panel +- Settings are stored on disk like desktop VS Code, instead of in browser + storage (note that state is still stored in browser storage). - Wrapper process that spawns VS Code on-demand and has a separate CLI - Notification when updates are available - [Some other things](https://github.com/coder/code-server/tree/main/patches) @@ -448,6 +450,12 @@ Some of these changes appear very unlikely to ever be adopted by Microsoft. Some may make their way upstream, further closing the gap, but at the moment it looks like there will always be some subtle differences. +## What's the difference between code-server and VS Code web? + +VS Code web (which can be ran using `code serve-web`) has the same differences +as the Codespaces section above. VS Code web can be a better choice if you need +access to the official Microsoft marketplace. + ## Does code-server have any security login validation? code-server supports setting a single password and limits logins to two per diff --git a/docs/README.md b/docs/README.md index b56f267e4d3c..470095071afd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # code-server -[!["GitHub Discussions"](https://img.shields.io/badge/%20GitHub-%20Discussions-gray.svg?longCache=true&logo=github&colorB=purple)](https://github.com/coder/code-server/discussions) [!["Join us on Slack"](https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen)](https://coder.com/community) [![Twitter Follow](https://img.shields.io/twitter/follow/CoderHQ?label=%40CoderHQ&style=social)](https://twitter.com/coderhq) [![codecov](https://codecov.io/gh/coder/code-server/branch/main/graph/badge.svg?token=5iM9farjnC)](https://codecov.io/gh/coder/code-server) [![See latest](https://img.shields.io/static/v1?label=Docs&message=see%20latest&color=blue)](https://coder.com/docs/code-server/latest) +[!["GitHub Discussions"](https://img.shields.io/badge/%20GitHub-%20Discussions-gray.svg?longCache=true&logo=github&colorB=purple)](https://github.com/coder/code-server/discussions) [!["Join us on Slack"](https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen)](https://coder.com/community) [![Twitter Follow](https://img.shields.io/twitter/follow/CoderHQ?label=%40CoderHQ&style=social)](https://twitter.com/coderhq) [![Discord](https://img.shields.io/discord/747933592273027093)](https://discord.com/invite/coder) [![codecov](https://codecov.io/gh/coder/code-server/branch/main/graph/badge.svg?token=5iM9farjnC)](https://codecov.io/gh/coder/code-server) [![See latest](https://img.shields.io/static/v1?label=Docs&message=see%20latest&color=blue)](https://coder.com/docs/code-server/latest) Run [VS Code](https://github.com/Microsoft/vscode) on any machine anywhere and access it in the browser. @@ -24,7 +24,7 @@ on how to set up a Google VM on which you can install code-server. ## Getting started -There are four ways to get started: +There are five ways to get started: 1. Using the [install script](https://github.com/coder/code-server/blob/main/install.sh), which @@ -35,6 +35,9 @@ There are four ways to get started: 3. Deploy code-server to your team with [coder/coder](https://cdr.co/coder-github) 4. Using our one-click buttons and guides to [deploy code-server to a cloud provider](https://github.com/coder/deploy-code-server) ⚡ +5. Using the [code-server feature for + devcontainers](https://github.com/coder/devcontainer-features/blob/main/src/code-server/README.md), + if you already use devcontainers in your project. If you use the install script, you can preview what occurs during the install process: diff --git a/docs/android.md b/docs/android.md index 00909f975415..0b1dc24abf6e 100644 --- a/docs/android.md +++ b/docs/android.md @@ -11,11 +11,11 @@ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash ``` 6. Exit the terminal using `exit` and then reopen the terminal -7. Install and use Node.js 20: +7. Install and use Node.js 22: ```shell -nvm install 18 -nvm use 18 +nvm install 22 +nvm use 22 ``` 8. Install code-server globally on device with: `npm install --global code-server` diff --git a/docs/guide.md b/docs/guide.md index 60098ca7e679..0aa8901c36a1 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -21,6 +21,10 @@ - [Proxying to an Angular app](#proxying-to-an-angular-app) - [Proxying to a Svelte app](#proxying-to-a-svelte-app) - [Prefixing `/absproxy/` with a path](#prefixing-absproxyport-with-a-path) + - [Preflight requests](#preflight-requests) +- [Internationalization and customization](#internationalization-and-customization) + - [Available keys and placeholders](#available-keys-and-placeholders) + - [Legacy flag](#legacy-flag) @@ -119,22 +123,22 @@ access code-server on an iPad or do not want to use SSH port forwarding. 1. This option requires that the remote machine be exposed to the internet. Make sure that your instance allows HTTP/HTTPS traffic. -1. You'll need a domain name (if you don't have one, you can purchase one from +2. You'll need a domain name (if you don't have one, you can purchase one from [Google Domains](https://domains.google.com) or the domain service of your - choice)). Once you have a domain name, add an A record to your domain that contains your + choice). Once you have a domain name, add an A record to your domain that contains your instance's IP address. -1. Install [Caddy](https://caddyserver.com/docs/download#debian-ubuntu-raspbian): +3. Install [Caddy](https://caddyserver.com/docs/download#debian-ubuntu-raspbian): -```console -sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https -curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg -curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list -sudo apt update -sudo apt install caddy -``` + ```console + sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https + curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg + curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list + sudo apt update + sudo apt install caddy + ``` -1. Replace `/etc/caddy/Caddyfile` using `sudo` so that the file looks like this: +4. Replace `/etc/caddy/Caddyfile` using `sudo` so that the file looks like this: ```text mydomain.com { @@ -153,7 +157,7 @@ sudo apt install caddy Remember to replace `mydomain.com` with your domain name! -1. Reload Caddy: +5. Reload Caddy: ```console sudo systemctl reload caddy @@ -164,21 +168,22 @@ At this point, you should be able to access code-server via ### Using Let's Encrypt with NGINX -1. This option requires that the remote machine be exposed to the internet. Make sure that your instance allows HTTP/HTTPS traffic. +1. This option requires that the remote machine be exposed to the internet. Make + sure that your instance allows HTTP/HTTPS traffic. -1. You'll need a domain name (if you don't have one, you can purchase one from +2. You'll need a domain name (if you don't have one, you can purchase one from [Google Domains](https://domains.google.com) or the domain service of your - choice)). Once you have a domain name, add an A record to your domain that contains your + choice). Once you have a domain name, add an A record to your domain that contains your instance's IP address. -1. Install NGINX: +3. Install NGINX: ```bash sudo apt update sudo apt install -y nginx certbot python3-certbot-nginx ``` -1. Update `/etc/nginx/sites-available/code-server` using sudo with the following +4. Update `/etc/nginx/sites-available/code-server` using sudo with the following configuration: ```text @@ -199,13 +204,11 @@ At this point, you should be able to access code-server via Be sure to replace `mydomain.com` with your domain name! -1. Enable the config: - +5. Enable the config: ```console sudo ln -s ../sites-available/code-server /etc/nginx/sites-enabled/code-server sudo certbot --non-interactive --redirect --agree-tos --nginx -d mydomain.com -m me@example.com ``` - Be sure to replace `me@example.com` with your actual email. At this point, you should be able to access code-server via @@ -271,9 +274,9 @@ should see OSSStatus: 9836 in the browser console. If you want to use external authentication mechanism (e.g., Sign in with Google), you can do this with a reverse proxy such as: -- [Pomerium](https://www.pomerium.io/guides/code-server.html) -- [oauth2_proxy](https://github.com/pusher/oauth2_proxy) -- [Cloudflare Access](https://teams.cloudflare.com/access) +- [Pomerium](https://www.pomerium.com/docs/guides/code-server.html) +- [oauth2-proxy](https://oauth2-proxy.github.io/oauth2-proxy/) +- [Cloudflare Access](https://www.cloudflare.com/zero-trust/products/access/) ## HTTPS and self-signed certificates @@ -292,7 +295,9 @@ redirect all HTTP requests to HTTPS. > You can use [Let's Encrypt](https://letsencrypt.org/) to get a TLS certificate > for free. -Note: if you set `proxy_set_header Host $host;` in your reverse proxy config, it will change the address displayed in the green section of code-server in the bottom left to show the correct address. +Note: if you set `proxy_set_header Host $host;` in your reverse proxy config, it +will change the address displayed in the green section of code-server in the +bottom left to show the correct address. ## Accessing web services @@ -378,14 +383,16 @@ PUBLIC_URL=/absproxy/3000 \ BROWSER=none yarn start ``` -You should then be able to visit `https://my-code-server-address.io/absproxy/3000` to see your app exposed through -code-server! +You should then be able to visit +`https://my-code-server-address.io/absproxy/3000` to see your app exposed +through code-server. > We highly recommend using the subdomain approach instead to avoid this class of issue. ### Proxying to a Vue app -Similar to the situation with React apps, you have to make a few modifications to proxy a Vue app. +Similar to the situation with React apps, you have to make a few modifications +to proxy a Vue app. 1. add `vue.config.js` 2. update the values to match this (you can use any free port): @@ -406,7 +413,8 @@ Read more about `publicPath` in the [Vue.js docs](https://cli.vuejs.org/config/# ### Proxying to an Angular app -In order to use code-server's built-in proxy with Angular, you need to make the following changes in your app: +In order to use code-server's built-in proxy with Angular, you need to make the +following changes in your app: 1. use `` in `src/index.html` 2. add `--serve-path /absproxy/4200` to `ng serve` in your `package.json` @@ -415,7 +423,8 @@ For additional context, see [this GitHub Discussion](https://github.com/coder/co ### Proxying to a Svelte app -In order to use code-server's built-in proxy with Svelte, you need to make the following changes in your app: +In order to use code-server's built-in proxy with Svelte, you need to make the +following changes in your app: 1. Add `svelte.config.js` if you don't already have one 2. Update the values to match this (you can use any free port): @@ -436,9 +445,68 @@ For additional context, see [this Github Issue](https://github.com/sveltejs/kit/ ### Prefixing `/absproxy/` with a path -This is a case where you need to serve an application via `absproxy` as explained above while serving `codeserver` itself from a path other than the root in your domain. +This is a case where you need to serve an application via `absproxy` as +explained above while serving code-server itself from a path other than the root +in your domain. -For example: `http://my-code-server.com/user/123/workspace/my-app`. To achieve this result: +For example: `http://my-code-server.com/user/123/workspace/my-app`. To achieve +this result: -1. Start code server with the switch `--abs-proxy-base-path=/user/123/workspace` +1. Start code-server with the switch `--abs-proxy-base-path=/user/123/workspace` 2. Follow one of the instructions above for your framework. + +### Preflight requests + +By default, if you have auth enabled, code-server will authenticate all proxied +requests including preflight requests. This can cause issues because preflight +requests do not typically include credentials. To allow all preflight requests +through the proxy without authentication, use `--skip-auth-preflight`. + +## Internationalization and customization + +code-server allows you to provide a JSON file to configure certain strings. This +can be used for both internationalization and customization. + +Create a JSON file with your custom strings: + +```json +{ + "WELCOME": "Welcome to {{app}}", + "LOGIN_TITLE": "{{app}} Access Portal", + "LOGIN_BELOW": "Please log in to continue", + "PASSWORD_PLACEHOLDER": "Enter Password" +} +``` + +Then reference the file: + +```shell +code-server --i18n /path/to/custom-strings.json +``` + +Or this can be done in the config file: + +```yaml +i18n: /path/to/custom-strings.json +``` + +You can combine this with the `--locale` flag to configure language support for +both code-server and VS Code in cases where code-server has no support but VS +Code does. If you are using this for internationalization, please consider +sending us a pull request to contribute it to `src/node/i18n/locales`. + +### Available keys and placeholders + +Refer to [../src/node/i18n/locales/en.json](../src/node/i18n/locales/en.json) +for a full list of the available keys for translations. Note that the only +placeholders supported for each key are the ones used in the default string. + +The `--app-name` flag controls the `{{app}}` placeholder in templates. If you +want to change the name, you can either: + +1. Set `--app-name` (potentially alongside `--i18n`) +2. Use `--i18n` and hardcode the name in your strings + +### Legacy flag + +The `--welcome-text` flag is now deprecated. Use the `WELCOME` key instead. diff --git a/docs/install.md b/docs/install.md index e2dd905f9401..30da4d415e77 100644 --- a/docs/install.md +++ b/docs/install.md @@ -82,13 +82,11 @@ _exact_ same commands presented in the rest of this document. - For Arch Linux, code-server will install the AUR package. - For any unrecognized Linux operating system, code-server will install the latest standalone release into `~/.local`. - - Ensure that you add `~/.local/bin` to your `$PATH` to run code-server. - For macOS, code-server will install the Homebrew package (if you don't have Homebrew installed, code-server will install the latest standalone release into `~/.local`). - - Ensure that you add `~/.local/bin` to your `$PATH` to run code-server. - For FreeBSD, code-server will install the [npm package](#npm) with `npm` diff --git a/docs/npm.md b/docs/npm.md index d300cfa5bd55..8d3afd0be06a 100644 --- a/docs/npm.md +++ b/docs/npm.md @@ -30,7 +30,7 @@ includes installing instructions based on your operating system. ## Node.js version We use the same major version of Node.js shipped with Code's remote, which is -currently `20.x`. VS Code also [lists Node.js +currently `22.x`. VS Code also [lists Node.js requirements](https://github.com/microsoft/vscode/wiki/How-to-Contribute#prerequisites). Using other versions of Node.js [may lead to unexpected @@ -78,7 +78,7 @@ Proceed to [installing](#installing) ## FreeBSD ```sh -pkg install -y git python npm-node20 pkgconf +pkg install -y git python npm-node22 pkgconf pkg install -y libinotify ``` diff --git a/docs/termux.md b/docs/termux.md index 84dcec0765e4..db81cb57d1e1 100644 --- a/docs/termux.md +++ b/docs/termux.md @@ -57,7 +57,7 @@ npm config set python python3 node -v ``` -you will get Node version `v20` +you will get Node version `v22` 5. Now install code-server following our guide on [installing with npm](./npm.md) diff --git a/flake.lock b/flake.lock index a877450fff10..948f73585131 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1726560853, - "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1730359060, - "narHash": "sha256-Hkk0mf4pgvX9Ut0YA397nsFqMLhzFVBdFHc4PhBrxYE=", + "lastModified": 1739303263, + "narHash": "sha256-c/Z/6gZLN8BIpYh1B3qMzEn0TArjf4F2lmy59lDLVBM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "e19cfce6f3f08d07653157d8826f5c920c770d7b", + "rev": "6cc4213488e886db863878a1e3dc26cc932d38b8", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 2fee8f28b6fd..3a1f7efbc122 100644 --- a/flake.nix +++ b/flake.nix @@ -10,10 +10,11 @@ flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; - nodejs = pkgs.nodejs_20; + nodejs = pkgs.nodejs_22; in { devShells.default = pkgs.mkShell { nativeBuildInputs = with pkgs; [ + imagemagick nodejs python3 pkg-config diff --git a/lib/vscode b/lib/vscode index cd4ee3b1c348..302ff6a2e235 160000 --- a/lib/vscode +++ b/lib/vscode @@ -1 +1 @@ -Subproject commit cd4ee3b1c348a13bafd8f9ad8060705f6d4b9cba +Subproject commit 302ff6a2e235c95b88e587d4a4b6eeb1b6613297 diff --git a/package-lock.json b/package-lock.json index 3779af901516..8bd50af3431d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,17 +15,16 @@ "compression": "^1.7.4", "cookie-parser": "^1.4.6", "env-paths": "^2.2.1", - "express": "5.0.0-beta.3", + "express": "^5.0.1", "http-proxy": "^1.18.1", "httpolyglot": "^0.1.2", - "i18next": "^23.5.1", + "i18next": "^25.3.0", "js-yaml": "^4.1.0", "limiter": "^2.1.0", "pem": "^1.14.8", "proxy-agent": "^6.3.1", - "qs": "6.13.0", + "qs": "6.14.0", "rotating-file-stream": "^3.1.1", - "safe-buffer": "^5.2.1", "safe-compare": "^1.1.4", "semver": "^7.5.4", "ws": "^8.14.2", @@ -42,10 +41,10 @@ "@types/compression": "^1.7.3", "@types/cookie-parser": "^1.4.4", "@types/eslint__js": "^8.42.3", - "@types/express": "^4.17.17", + "@types/express": "^5.0.0", "@types/http-proxy": "1.17.7", "@types/js-yaml": "^4.0.6", - "@types/node": "20.x", + "@types/node": "22.x", "@types/pem": "^1.14.1", "@types/proxy-from-env": "^1.0.1", "@types/safe-compare": "^1.1.0", @@ -55,27 +54,24 @@ "doctoc": "^2.2.1", "eslint": "^9.12.0", "eslint-config-prettier": "^9.0.0", - "eslint-import-resolver-typescript": "^3.6.0", + "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.28.1", "eslint-plugin-prettier": "^5.0.0", - "globals": "^15.10.0", - "prettier": "3.4.2", + "globals": "^16.1.0", + "prettier": "3.6.2", "prettier-plugin-sh": "^0.14.0", "ts-node": "^10.9.1", "typescript": "^5.6.2", "typescript-eslint": "^8.8.0" }, "engines": { - "node": "20" + "node": "22" } }, "node_modules/@babel/runtime": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", - "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", "engines": { "node": ">=6.9.0" } @@ -97,26 +93,65 @@ "node": ">=12" } }, + "node_modules/@emnapi/core": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.4.tgz", + "integrity": "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz", + "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.3.tgz", + "integrity": "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -140,13 +175,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -154,19 +189,33 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", - "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", "dependencies": { @@ -201,18 +250,22 @@ } }, "node_modules/@eslint/js": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz", - "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==", + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -220,12 +273,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", - "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -233,9 +287,9 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", - "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -243,19 +297,33 @@ } }, "node_modules/@humanfs/node": { - "version": "0.16.5", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", - "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.0", + "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -270,9 +338,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -327,11 +395,25 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -345,6 +427,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -354,6 +437,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -362,15 +446,6 @@ "node": ">= 8" } }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", - "dev": true, - "engines": { - "node": ">=12.4.0" - } - }, "node_modules/@phc/format": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", @@ -429,7 +504,8 @@ "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT" }, "node_modules/@tsconfig/node10": { "version": "1.0.11", @@ -455,6 +531,17 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", + "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -466,12 +553,14 @@ } }, "node_modules/@types/compression": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.5.tgz", - "integrity": "sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.0.tgz", + "integrity": "sha512-g4vmPIwbTii9dX1HVioHbOolubEaf4re4vDxuzpKrzz9uI7uarBExi9begX0cXyIB85jXZ5X2A/v8rsHZxSAPw==", "dev": true, + "license": "MIT", "dependencies": { - "@types/express": "*" + "@types/express": "*", + "@types/node": "*" } }, "node_modules/@types/connect": { @@ -521,22 +610,23 @@ "license": "MIT" }, "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", + "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", "dev": true, + "license": "MIT", "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", + "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "*" } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -594,12 +684,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.16.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.10.tgz", - "integrity": "sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==", + "version": "22.15.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.31.tgz", + "integrity": "sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.21.0" } }, "node_modules/@types/pem": { @@ -621,16 +712,18 @@ } }, "node_modules/@types/qs": { - "version": "6.9.16", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", - "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", - "dev": true + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/safe-compare": { "version": "1.1.2", @@ -639,10 +732,11 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/send": { "version": "0.17.4", @@ -687,20 +781,21 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz", - "integrity": "sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==", + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.0.tgz", + "integrity": "sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.12.2", - "@typescript-eslint/type-utils": "8.12.2", - "@typescript-eslint/utils": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2", + "@typescript-eslint/scope-manager": "8.33.0", + "@typescript-eslint/type-utils": "8.33.0", + "@typescript-eslint/utils": "8.33.0", + "@typescript-eslint/visitor-keys": "8.33.0", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -710,25 +805,32 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@typescript-eslint/parser": "^8.33.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.12.2.tgz", - "integrity": "sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==", + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.0.tgz", + "integrity": "sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.12.2", - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/typescript-estree": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2", + "@typescript-eslint/scope-manager": "8.33.0", + "@typescript-eslint/types": "8.33.0", + "@typescript-eslint/typescript-estree": "8.33.0", + "@typescript-eslint/visitor-keys": "8.33.0", "debug": "^4.3.4" }, "engines": { @@ -739,41 +841,75 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.0.tgz", + "integrity": "sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.33.0", + "@typescript-eslint/types": "^8.33.0", + "debug": "^4.3.4" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz", - "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==", + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.0.tgz", + "integrity": "sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2" + "@typescript-eslint/types": "8.33.0", + "@typescript-eslint/visitor-keys": "8.33.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.0.tgz", + "integrity": "sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==", + "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz", - "integrity": "sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==", + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.0.tgz", + "integrity": "sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.12.2", - "@typescript-eslint/utils": "8.12.2", + "@typescript-eslint/typescript-estree": "8.33.0", + "@typescript-eslint/utils": "8.33.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -782,17 +918,17 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz", - "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==", + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.0.tgz", + "integrity": "sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -802,19 +938,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz", - "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==", + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.0.tgz", + "integrity": "sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2", + "@typescript-eslint/project-service": "8.33.0", + "@typescript-eslint/tsconfig-utils": "8.33.0", + "@typescript-eslint/types": "8.33.0", + "@typescript-eslint/visitor-keys": "8.33.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -823,17 +962,16 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -843,6 +981,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -854,15 +993,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.12.2.tgz", - "integrity": "sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==", + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.0.tgz", + "integrity": "sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.12.2", - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/typescript-estree": "8.12.2" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.33.0", + "@typescript-eslint/types": "8.33.0", + "@typescript-eslint/typescript-estree": "8.33.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -872,17 +1012,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz", - "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==", + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.0.tgz", + "integrity": "sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.12.2", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.33.0", + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -892,28 +1034,312 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -1062,11 +1488,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-flatten": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz", - "integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==" - }, "node_modules/array-includes": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", @@ -1169,6 +1590,7 @@ "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", "dependencies": { "tslib": "^2.0.1" }, @@ -1211,70 +1633,35 @@ "version": "5.0.5", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "license": "MIT", "engines": { "node": ">=10.0.0" } }, "node_modules/body-parser": { - "version": "2.0.0-beta.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.0.0-beta.2.tgz", - "integrity": "sha512-oxdqeGYQcO5ovwwkC1A89R0Mf0v3+7smTVh0chGfzDeiK37bg5bYNtXDy3Nmzn6CShoIYk5+nHTyBoSZIWwnCA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "3.1.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.5.2", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "3.0.0-beta.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", "dependencies": { - "side-channel": "^1.0.4" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" }, "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -1286,6 +1673,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -1313,9 +1701,10 @@ "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" }, "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1324,6 +1713,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -1338,6 +1728,35 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1458,16 +1877,17 @@ } }, "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "license": "MIT", "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "engines": { @@ -1487,10 +1907,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, "node_modules/concat-map": { "version": "0.0.1", @@ -1504,9 +1928,10 @@ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" }, @@ -1518,24 +1943,27 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", "dependencies": { - "cookie": "0.4.1", + "cookie": "0.7.2", "cookie-signature": "1.0.6" }, "engines": { @@ -1554,10 +1982,11 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1579,6 +2008,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", "engines": { "node": ">= 14" } @@ -1635,9 +2065,10 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -1660,6 +2091,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -1693,6 +2125,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", @@ -1711,19 +2144,11 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/detect-libc": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", @@ -1822,10 +2247,25 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/emoji-regex": { "version": "10.1.0", @@ -1834,26 +2274,14 @@ "dev": true }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/entities": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", @@ -1935,12 +2363,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -1954,10 +2380,10 @@ } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -2016,7 +2442,8 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, "node_modules/escape-string-regexp": { "version": "4.0.0", @@ -2034,6 +2461,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -2051,31 +2479,33 @@ } }, "node_modules/eslint": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz", - "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==", + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.18.0", - "@eslint/core": "^0.7.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.13.0", - "@eslint/plugin-kit": "^0.2.0", - "@humanfs/node": "^0.16.5", + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.36.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.1", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.1.0", - "eslint-visitor-keys": "^4.1.0", - "espree": "^10.2.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2089,8 +2519,7 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -2122,6 +2551,31 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-import-context": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz", + "integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-tsconfig": "^4.10.1", + "stable-hash-x": "^0.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-context" + }, + "peerDependencies": { + "unrs-resolver": "^1.0.0" + }, + "peerDependenciesMeta": { + "unrs-resolver": { + "optional": true + } + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -2143,25 +2597,25 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz", - "integrity": "sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz", + "integrity": "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==", "dev": true, + "license": "ISC", "dependencies": { - "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.3.5", - "enhanced-resolve": "^5.15.0", - "eslint-module-utils": "^2.8.1", - "fast-glob": "^3.3.2", - "get-tsconfig": "^4.7.5", - "is-bun-module": "^1.0.2", - "is-glob": "^4.0.3" + "debug": "^4.4.1", + "eslint-import-context": "^0.1.8", + "get-tsconfig": "^4.10.1", + "is-bun-module": "^2.0.0", + "stable-hash-x": "^0.2.0", + "tinyglobby": "^0.2.14", + "unrs-resolver": "^1.7.11" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^16.17.0 || >=18.6.0" }, "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + "url": "https://opencollective.com/eslint-import-resolver-typescript" }, "peerDependencies": { "eslint": "*", @@ -2297,9 +2751,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", - "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2326,9 +2780,9 @@ } }, "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2339,15 +2793,15 @@ } }, "node_modules/espree": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", - "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.12.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.1.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2357,9 +2811,9 @@ } }, "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2373,6 +2827,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -2426,6 +2881,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -2436,81 +2892,54 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "node_modules/express": { - "version": "5.0.0-beta.3", - "resolved": "https://registry.npmjs.org/express/-/express-5.0.0-beta.3.tgz", - "integrity": "sha512-e7Qizw4gMBVe1Ky2oNi5C1h6oS8aWDcY2yYxvRMy5aMc6t2aqobuHpQRfR3LRC9NAW/c6081SeGWMGBorLXePg==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "3.0.0", - "body-parser": "2.0.0-beta.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "3.1.0", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "mime-types": "~2.1.34", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-is-absolute": "1.0.1", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "router": "2.0.0-beta.2", - "safe-buffer": "5.2.1", - "send": "1.0.0-beta.2", - "serve-static": "2.0.0-beta.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", "dependencies": { - "ms": "2.0.0" + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/express/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, + "node_modules/express/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.6.0" } }, "node_modules/extend": { @@ -2533,16 +2962,17 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -2553,6 +2983,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -2574,10 +3005,11 @@ "dev": true }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -2613,6 +3045,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2621,35 +3054,22 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { "node": ">= 0.8" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2733,24 +3153,12 @@ } }, "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", "engines": { - "node": ">=14.14" + "node": ">= 0.8" } }, "node_modules/fs-minipass": { @@ -2836,15 +3244,21 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -2853,6 +3267,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-symbol-description": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", @@ -2871,10 +3298,11 @@ } }, "node_modules/get-tsconfig": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", - "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", "dev": true, + "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -2883,14 +3311,14 @@ } }, "node_modules/get-uri": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", - "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", + "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", + "license": "MIT", "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4", - "fs-extra": "^11.2.0" + "debug": "^4.3.4" }, "engines": { "node": ">= 14" @@ -2929,9 +3357,9 @@ } }, "node_modules/globals": { - "version": "15.10.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.10.0.tgz", - "integrity": "sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, "license": "MIT", "engines": { @@ -2958,26 +3386,23 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/has-bigints": { "version": "1.0.2", @@ -3001,6 +3426,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -3012,6 +3438,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -3020,9 +3447,10 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3084,6 +3512,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -3112,6 +3541,7 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -3121,12 +3551,10 @@ } }, "node_modules/http-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dependencies": { - "debug": "^4.3.4" - }, + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", "engines": { "node": ">= 14" } @@ -3152,9 +3580,9 @@ } }, "node_modules/i18next": { - "version": "23.16.4", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.4.tgz", - "integrity": "sha512-9NIYBVy9cs4wIqzurf7nLXPyf3R78xYbxExVqHLK9od3038rjpyOEzW+XB130kZ1N4PZ9inTtJ471CRJ4Ituyg==", + "version": "25.3.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.0.tgz", + "integrity": "sha512-ZSQIiNGfqSG6yoLHaCvrkPp16UejHI8PCDxFYaNG/1qxtmqNmqEg4JlWKlxkrUmrin2sEjsy+Mjy1TRozBhOgw==", "funding": [ { "type": "individual", @@ -3170,15 +3598,24 @@ } ], "dependencies": { - "@babel/runtime": "^7.23.2" + "@babel/runtime": "^7.27.6" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/iconv-lite": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", - "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" @@ -3253,6 +3690,7 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" @@ -3343,12 +3781,13 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "node_modules/is-bun-module": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.2.1.tgz", - "integrity": "sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "dev": true, + "license": "MIT", "dependencies": { - "semver": "^7.6.3" + "semver": "^7.7.1" } }, "node_modules/is-callable": { @@ -3474,6 +3913,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -3505,7 +3945,8 @@ "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" }, "node_modules/is-regex": { "version": "1.1.4", @@ -3620,7 +4061,8 @@ "node_modules/jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT" }, "node_modules/json-buffer": { "version": "3.0.1", @@ -3654,17 +4096,6 @@ "json5": "lib/cli.js" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/just-performance": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/just-performance/-/just-performance-4.3.0.tgz", @@ -3781,6 +4212,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -3951,35 +4391,36 @@ } }, "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/micromark": { "version": "2.11.4", "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", @@ -4111,6 +4552,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -4120,32 +4562,26 @@ } }, "node_modules/mime-db": { - "version": "1.53.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", - "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { "node": ">= 0.6" } }, - "node_modules/mime-types/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4220,6 +4656,22 @@ "integrity": "sha512-kMbrH0EObaKmK3nVRKUIIya1dpASHIEusM13S4V1ViHFuxuNxCo+arxoa6j/dbV22YBGjl7UKJm9QQKJ2Crzhg==", "dev": true }, + "node_modules/napi-postinstall": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.0.tgz", + "integrity": "sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4227,9 +4679,10 @@ "dev": true }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -4238,6 +4691,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -4301,9 +4755,10 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4391,6 +4846,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -4399,9 +4855,10 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -4470,40 +4927,40 @@ } }, "node_modules/pac-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", - "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.5", + "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.4" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" } }, "node_modules/pac-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dependencies": { - "debug": "^4.3.4" - }, + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", "engines": { "node": ">= 14" } }, "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -4514,6 +4971,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" @@ -4557,6 +5015,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -4594,9 +5053,14 @@ "dev": true }, "node_modules/path-to-regexp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz", - "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==" + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, "node_modules/pem": { "version": "1.14.8", @@ -4617,6 +5081,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -4643,9 +5108,9 @@ } }, "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "bin": { @@ -4702,40 +5167,40 @@ } }, "node_modules/proxy-agent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", - "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.3", + "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", + "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" } }, "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dependencies": { - "debug": "^4.3.4" - }, + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", "engines": { "node": ">= 14" } }, "node_modules/proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -4758,11 +5223,12 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -4789,36 +5255,47 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { - "version": "3.0.0-beta.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0-beta.1.tgz", - "integrity": "sha512-XlSTHr67bCjSo5aOfAnN3x507zGvi3unF65BW57limYkc2ws/XB0mLUtJvvP7JGFeSPsYrlCv1ZrPGh0cwDxPQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", - "iconv-lite": "0.5.2", + "iconv-lite": "0.7.0", "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" } }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/readable-stream": { @@ -4834,11 +5311,6 @@ "node": ">= 6" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, "node_modules/regexp.prototype.flags": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", @@ -4958,15 +5430,17 @@ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -4999,20 +5473,19 @@ } }, "node_modules/router": { - "version": "2.0.0-beta.2", - "resolved": "https://registry.npmjs.org/router/-/router-2.0.0-beta.2.tgz", - "integrity": "sha512-ascmzrv4IAB64SpWzFwYOA+jz6PaUbrzHLPsQrPjQ3uQTL2qlhwY9S2sRvvBMgUISQptQG457jcWWcWqtwrbag==", - "dependencies": { - "array-flatten": "3.0.0", - "is-promise": "4.0.0", - "methods": "~1.1.2", - "parseurl": "~1.3.3", - "path-to-regexp": "3.2.0", - "setprototypeof": "1.2.0", - "utils-merge": "1.0.1" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 18" } }, "node_modules/run-parallel": { @@ -5034,6 +5507,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -5103,12 +5577,14 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -5117,52 +5593,40 @@ } }, "node_modules/send": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/send/-/send-1.0.0-beta.2.tgz", - "integrity": "sha512-k1yHu/FNK745PULKdsGpQ+bVSXYNwSk+bWnYzbxGZbt5obZc0JKDVANsCRuJD1X/EG15JtP9eZpwxkhUxIYEcg==", - "dependencies": { - "debug": "3.1.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime-types": "~2.1.34", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.10" - } - }, - "node_modules/send/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dependencies": { - "ms": "2.0.0" + "node": ">= 18" } }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/serve-static": { - "version": "2.0.0-beta.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.0.0-beta.2.tgz", - "integrity": "sha512-Ge718g4UJjzYoXFEGLY/VLSuTHp0kQcUV65QA98J8d3XREsVIHu53GBh9NWjDy4u2xwsSwRzu9nu7Q+b4o6Xyw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "^1.0.0-beta.2" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 18" } }, "node_modules/set-blocking": { @@ -5174,6 +5638,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -5204,7 +5669,8 @@ "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, "node_modules/sh-syntax": { "version": "0.4.2", @@ -5243,14 +5709,69 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -5268,15 +5789,17 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", + "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", + "license": "MIT", "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" @@ -5287,11 +5810,12 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", - "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", "dependencies": { - "agent-base": "^7.1.1", + "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" }, @@ -5300,12 +5824,10 @@ } }, "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dependencies": { - "debug": "^4.3.4" - }, + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", "engines": { "node": ">= 14" } @@ -5314,6 +5836,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", "optional": true, "engines": { "node": ">=0.10.0" @@ -5322,12 +5845,24 @@ "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, + "node_modules/stable-hash-x": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz", + "integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -5480,15 +6015,6 @@ "url": "https://opencollective.com/unts" } }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", @@ -5505,17 +6031,57 @@ "node": ">=10" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -5527,6 +6093,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } @@ -5564,15 +6131,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", - "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-node": { @@ -5648,12 +6216,14 @@ } }, "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { "node": ">= 0.6" @@ -5753,10 +6323,10 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", - "dev": true, + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -5767,14 +6337,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.12.2.tgz", - "integrity": "sha512-UbuVUWSrHVR03q9CWx+JDHeO6B/Hr9p4U5lRH++5tq/EbFq1faYZe50ZSBePptgfIKLEti0aPQ3hFgnPVcd8ZQ==", + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.33.0.tgz", + "integrity": "sha512-5YmNhF24ylCsvdNW2oJwMzTbaeO4bg90KeGtMjUw0AGtHksgEPLRTUil+coHwCfiu4QjVJFnjp94DmU6zV7DhQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.12.2", - "@typescript-eslint/parser": "8.12.2", - "@typescript-eslint/utils": "8.12.2" + "@typescript-eslint/eslint-plugin": "8.33.0", + "@typescript-eslint/parser": "8.33.0", + "@typescript-eslint/utils": "8.33.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5783,10 +6354,9 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/unbox-primitive": { @@ -5811,10 +6381,11 @@ "dev": true }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" }, "node_modules/unified": { "version": "9.2.2", @@ -5894,22 +6465,50 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, "node_modules/update-section": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/update-section/-/update-section-0.3.3.tgz", @@ -5931,14 +6530,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", diff --git a/package.json b/package.json index e91feacc035e..8bd1c71b7caa 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "publish:docker": "./ci/steps/docker-buildx-push.sh", "fmt": "npm run prettier && ./ci/dev/doctoc.sh", "lint:scripts": "./ci/dev/lint-scripts.sh", - "lint:ts": "eslint --max-warnings=0 --fix $(git ls-files '*.ts' '*.js' | grep -v 'lib/vscode' | grep -v test-plugin)", + "lint:ts": "eslint --max-warnings=0 --fix $(git ls-files '*.ts' '*.js' | grep -v 'lib/vscode')", "test": "echo 'Run npm run test:unit or npm run test:e2e' && exit 1", "watch": "VSCODE_DEV=1 VSCODE_IPC_HOOK_CLI= NODE_OPTIONS='--max_old_space_size=32384 --trace-warnings' ts-node ./ci/dev/watch.ts", "icons": "./ci/dev/gen_icons.sh" @@ -44,10 +44,10 @@ "@types/compression": "^1.7.3", "@types/cookie-parser": "^1.4.4", "@types/eslint__js": "^8.42.3", - "@types/express": "^4.17.17", + "@types/express": "^5.0.0", "@types/http-proxy": "1.17.7", "@types/js-yaml": "^4.0.6", - "@types/node": "20.x", + "@types/node": "22.x", "@types/pem": "^1.14.1", "@types/proxy-from-env": "^1.0.1", "@types/safe-compare": "^1.1.0", @@ -57,11 +57,11 @@ "doctoc": "^2.2.1", "eslint": "^9.12.0", "eslint-config-prettier": "^9.0.0", - "eslint-import-resolver-typescript": "^3.6.0", + "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.28.1", "eslint-plugin-prettier": "^5.0.0", - "globals": "^15.10.0", - "prettier": "3.4.2", + "globals": "^16.1.0", + "prettier": "3.6.2", "prettier-plugin-sh": "^0.14.0", "ts-node": "^10.9.1", "typescript": "^5.6.2", @@ -73,24 +73,23 @@ "compression": "^1.7.4", "cookie-parser": "^1.4.6", "env-paths": "^2.2.1", - "express": "5.0.0-beta.3", + "express": "^5.0.1", "http-proxy": "^1.18.1", "httpolyglot": "^0.1.2", - "i18next": "^23.5.1", + "i18next": "^25.3.0", "js-yaml": "^4.1.0", "limiter": "^2.1.0", "pem": "^1.14.8", "proxy-agent": "^6.3.1", - "qs": "6.13.0", + "qs": "6.14.0", "rotating-file-stream": "^3.1.1", - "safe-buffer": "^5.2.1", "safe-compare": "^1.1.4", "semver": "^7.5.4", "ws": "^8.14.2", "xdg-basedir": "^4.0.0" }, "resolutions": { - "@types/node": "20.x" + "@types/node": "22.x" }, "bin": { "code-server": "out/node/entry.js" @@ -105,7 +104,7 @@ "remote-development" ], "engines": { - "node": "20" + "node": "22" }, "jest": { "transform": { diff --git a/patches/base-path.diff b/patches/base-path.diff index ba6e054a8fe5..0d4d5e0504bf 100644 --- a/patches/base-path.diff +++ b/patches/base-path.diff @@ -10,7 +10,7 @@ Index: code-server/lib/vscode/src/vs/base/common/network.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/base/common/network.ts +++ code-server/lib/vscode/src/vs/base/common/network.ts -@@ -220,7 +220,9 @@ class RemoteAuthoritiesImpl { +@@ -237,7 +237,9 @@ class RemoteAuthoritiesImpl { return URI.from({ scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource, authority: `${host}:${port}`, @@ -99,33 +99,38 @@ Index: code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactor =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts +++ code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts -@@ -281,6 +281,7 @@ export class BrowserSocketFactory implem +@@ -282,6 +282,7 @@ export class BrowserSocketFactory implem connect({ host, port }: WebSocketRemoteConnection, path: string, query: string, debugLabel: string): Promise { return new Promise((resolve, reject) => { const webSocketSchema = (/^https:/.test(mainWindow.location.href) ? 'wss' : 'ws'); + path = (mainWindow.location.pathname + "/" + path).replace(/\/\/+/g, "/") const socket = this._webSocketFactory.create(`${webSocketSchema}://${(/:/.test(host) && !/\[/.test(host)) ? `[${host}]` : host}:${port}${path}?${query}&skipWebSocketFrames=false`, debugLabel); - const errorListener = socket.onError(reject); - socket.onOpen(() => { + const disposables = new DisposableStore(); + disposables.add(socket.onError(reject)); Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -271,16 +271,15 @@ export class WebClientServer { - return void res.end(); - } +@@ -245,7 +245,9 @@ export class WebClientServer { + }; + + // Prefix routes with basePath for clients +- const basePath = getFirstHeader('x-forwarded-prefix') || this._basePath; ++ const rootBase = relativeRoot(getOriginalUrl(req)) ++ const vscodeBase = relativePath(getOriginalUrl(req)) ++ const basePath = vscodeBase || getFirstHeader('x-forwarded-prefix') || this._basePath; + + const queryConnectionToken = parsedUrl.query[connectionTokenQueryName]; + if (typeof queryConnectionToken === 'string') { +@@ -284,10 +286,14 @@ export class WebClientServer { + }; -- const getFirstHeader = (headerName: string) => { -- const val = req.headers[headerName]; -- return Array.isArray(val) ? val[0] : val; -- }; -- const useTestResolver = (!this._environmentService.isBuilt && this._environmentService.args['use-test-resolver']); + // For now we are getting the remote authority from the client to avoid + // needing specific configuration for reverse proxies to work. Set this to + // something invalid to make sure we catch code that is using this value + // from the backend when it should not. - const remoteAuthority = ( + let remoteAuthority = ( useTestResolver ? 'test+test' - : (getFirstHeader('x-original-host') || getFirstHeader('x-forwarded-host') || req.headers.host) @@ -133,43 +138,26 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts ); if (!remoteAuthority) { return serveError(req, res, 400, `Bad request.`); -@@ -307,8 +306,12 @@ export class WebClientServer { - scopes: [['user:email'], ['repo']] - } : undefined; +@@ -334,6 +340,7 @@ export class WebClientServer { -+ const base = relativeRoot(getOriginalUrl(req)) -+ const vscodeBase = relativePath(getOriginalUrl(req)) -+ - const productConfiguration = { + const productConfiguration: Partial> = { codeServerVersion: this._productService.codeServerVersion, -+ rootEndpoint: base, ++ rootEndpoint: rootBase, embedderIdentifier: 'server-distro', extensionsGallery: this._webExtensionResourceUrlTemplate && this._productService.extensionsGallery ? { ...this._productService.extensionsGallery, -@@ -337,7 +340,7 @@ export class WebClientServer { - folderUri: resolveWorkspaceURI(this._environmentService.args['default-folder']), - workspaceUri: resolveWorkspaceURI(this._environmentService.args['default-workspace']), - productConfiguration, -- callbackRoute: this._callbackRoute -+ callbackRoute: vscodeBase + this._callbackRoute - }; - - const cookies = cookie.parse(req.headers.cookie || ''); -@@ -354,9 +357,11 @@ export class WebClientServer { - const values: { [key: string]: string } = { - WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration), +@@ -387,7 +394,9 @@ export class WebClientServer { WORKBENCH_AUTH_SESSION: authSessionInfo ? asJSON(authSessionInfo) : '', -- WORKBENCH_WEB_BASE_URL: this._staticRoute, -+ WORKBENCH_WEB_BASE_URL: vscodeBase + this._staticRoute, + WORKBENCH_WEB_BASE_URL: staticRoute, WORKBENCH_NLS_URL, -- WORKBENCH_NLS_FALLBACK_URL: `${this._staticRoute}/out/nls.messages.js` -+ WORKBENCH_NLS_FALLBACK_URL: `${vscodeBase}${this._staticRoute}/out/nls.messages.js`, -+ BASE: base, -+ VS_BASE: vscodeBase, +- WORKBENCH_NLS_FALLBACK_URL: `${staticRoute}/out/nls.messages.js` ++ WORKBENCH_NLS_FALLBACK_URL: `${staticRoute}/out/nls.messages.js`, ++ BASE: rootBase, ++ VS_BASE: basePath, }; // DEV --------------------------------------------------------------------------------------- -@@ -393,7 +398,7 @@ export class WebClientServer { +@@ -424,7 +433,7 @@ export class WebClientServer { 'default-src \'self\';', 'img-src \'self\' https: data: blob:;', 'media-src \'self\';', @@ -178,7 +166,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts 'child-src \'self\';', `frame-src 'self' https://*.vscode-cdn.net data:;`, 'worker-src \'self\' data: blob:;', -@@ -466,3 +471,70 @@ export class WebClientServer { +@@ -497,3 +506,70 @@ export class WebClientServer { return void res.end(data); } } @@ -253,7 +241,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts -@@ -56,6 +56,7 @@ export type ExtensionVirtualWorkspaceSup +@@ -66,6 +66,7 @@ export type ExtensionVirtualWorkspaceSup export interface IProductConfiguration { readonly codeServerVersion?: string @@ -265,7 +253,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.ts +++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts -@@ -317,7 +317,8 @@ class LocalStorageURLCallbackProvider ex +@@ -339,7 +339,8 @@ class LocalStorageURLCallbackProvider ex this.startListening(); } @@ -275,7 +263,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts } private startListening(): void { -@@ -563,17 +564,6 @@ class WorkspaceProvider implements IWork +@@ -584,17 +585,6 @@ class WorkspaceProvider implements IWork } } @@ -293,7 +281,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts (function () { // Find config by checking for DOM -@@ -582,8 +572,8 @@ function readCookie(name: string): strin +@@ -604,8 +594,8 @@ function readCookie(name: string): strin if (!configElement || !configElementAttribute) { throw new Error('Missing web configuration element'); } @@ -308,7 +296,7 @@ Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/ext =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts +++ code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts -@@ -98,7 +98,7 @@ export abstract class AbstractExtensionR +@@ -120,7 +120,7 @@ export abstract class AbstractExtensionR : version, path: 'extension' })); diff --git a/patches/cli-window-open.diff b/patches/cli-window-open.diff index 8da64b2a5d70..3bc21fa3b4bc 100644 --- a/patches/cli-window-open.diff +++ b/patches/cli-window-open.diff @@ -17,7 +17,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTe =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts -@@ -106,10 +106,14 @@ class RemoteTerminalBackend extends Base +@@ -107,10 +107,14 @@ class RemoteTerminalBackend extends Base } const reqId = e.reqId; const commandId = e.commandId; diff --git a/patches/clipboard.diff b/patches/clipboard.diff index 84d6c8f84ebc..c6f2fe86ae4b 100644 --- a/patches/clipboard.diff +++ b/patches/clipboard.diff @@ -26,7 +26,7 @@ Index: code-server/lib/vscode/src/vs/workbench/api/node/extHostCLIServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/api/node/extHostCLIServer.ts +++ code-server/lib/vscode/src/vs/workbench/api/node/extHostCLIServer.ts -@@ -43,7 +43,12 @@ export interface ExtensionManagementPipe +@@ -44,7 +44,12 @@ export interface ExtensionManagementPipe force?: boolean; } @@ -40,7 +40,7 @@ Index: code-server/lib/vscode/src/vs/workbench/api/node/extHostCLIServer.ts export interface ICommandsExecuter { executeCommand(id: string, ...args: any[]): Promise; -@@ -105,6 +110,9 @@ export class CLIServerBase { +@@ -106,6 +111,9 @@ export class CLIServerBase { case 'extensionManagement': returnObj = await this.manageExtensions(data); break; @@ -50,7 +50,7 @@ Index: code-server/lib/vscode/src/vs/workbench/api/node/extHostCLIServer.ts default: sendResponse(404, `Unknown message type: ${data.type}`); break; -@@ -172,6 +180,10 @@ export class CLIServerBase { +@@ -173,6 +181,10 @@ export class CLIServerBase { return await this._commands.executeCommand('_remoteCLI.getSystemStatus'); } @@ -65,7 +65,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTe =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts -@@ -97,7 +97,7 @@ class RemoteTerminalBackend extends Base +@@ -98,7 +98,7 @@ class RemoteTerminalBackend extends Base } }); @@ -78,19 +78,19 @@ Index: code-server/lib/vscode/src/vs/platform/environment/common/argv.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/environment/common/argv.ts +++ code-server/lib/vscode/src/vs/platform/environment/common/argv.ts -@@ -119,6 +119,7 @@ export interface NativeParsedArgs { +@@ -137,6 +137,7 @@ export interface NativeParsedArgs { + 'disable-chromium-sandbox'?: boolean; sandbox?: boolean; - 'enable-coi'?: boolean; + 'stdin-to-clipboard'?: boolean; - - // chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches - 'no-proxy-server'?: boolean; + 'unresponsive-sample-interval'?: string; + 'unresponsive-sample-period'?: string; + 'enable-rdp-display-tracking'?: boolean; Index: code-server/lib/vscode/src/vs/platform/environment/node/argv.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/environment/node/argv.ts +++ code-server/lib/vscode/src/vs/platform/environment/node/argv.ts -@@ -90,6 +90,7 @@ export const OPTIONS: OptionDescriptions +@@ -105,6 +105,7 @@ export const OPTIONS: OptionDescriptions 'user-data-dir': { type: 'string', cat: 'o', args: 'dir', description: localize('userDataDir', "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.") }, 'profile': { type: 'string', 'cat': 'o', args: 'profileName', description: localize('profileName', "Opens the provided folder or workspace with the given profile and associates the profile with the workspace. If the profile does not exist, a new empty one is created.") }, 'help': { type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") }, @@ -102,7 +102,7 @@ Index: code-server/lib/vscode/src/vs/server/node/server.cli.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/server.cli.ts +++ code-server/lib/vscode/src/vs/server/node/server.cli.ts -@@ -76,6 +76,7 @@ const isSupportedForPipe = (optionId: ke +@@ -77,6 +77,7 @@ const isSupportedForPipe = (optionId: ke case 'verbose': case 'remote': case 'locate-shell-integration-path': @@ -110,7 +110,7 @@ Index: code-server/lib/vscode/src/vs/server/node/server.cli.ts return true; default: return false; -@@ -293,6 +294,22 @@ export async function main(desc: Product +@@ -300,6 +301,22 @@ export async function main(desc: Product } } } else { @@ -131,5 +131,5 @@ Index: code-server/lib/vscode/src/vs/server/node/server.cli.ts + } + if (parsedArgs.status) { - sendToPipe({ + await sendToPipe({ type: 'status' diff --git a/patches/disable-builtin-ext-update.diff b/patches/disable-builtin-ext-update.diff index 842b54534747..5e71ed6770e7 100644 --- a/patches/disable-builtin-ext-update.diff +++ b/patches/disable-builtin-ext-update.diff @@ -7,7 +7,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts -@@ -320,6 +320,10 @@ export class Extension implements IExten +@@ -341,6 +341,10 @@ export class Extension implements IExten if (this.type === ExtensionType.System && this.productService.quality === 'stable') { return false; } diff --git a/patches/display-language.diff b/patches/display-language.diff index 9cfc21ca5083..f464be65f94c 100644 --- a/patches/display-language.diff +++ b/patches/display-language.diff @@ -11,16 +11,16 @@ Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts +++ code-server/lib/vscode/src/vs/server/node/serverServices.ts @@ -12,7 +12,7 @@ import * as path from '../../base/common import { IURITransformer } from '../../base/common/uriIpc.js'; - import { getMachineId, getSqmMachineId, getdevDeviceId } from '../../base/node/id.js'; + import { getMachineId, getSqmMachineId, getDevDeviceId } from '../../base/node/id.js'; import { Promises } from '../../base/node/pfs.js'; -import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer, StaticRouter } from '../../base/parts/ipc/common/ipc.js'; +import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer, ProxyChannel, StaticRouter } from '../../base/parts/ipc/common/ipc.js'; import { ProtocolConstants } from '../../base/parts/ipc/common/ipc.net.js'; import { IConfigurationService } from '../../platform/configuration/common/configuration.js'; import { ConfigurationService } from '../../platform/configuration/common/configurationService.js'; -@@ -245,6 +245,9 @@ export async function setupServerService - const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority)); - socketServer.registerChannel('extensions', channel); +@@ -272,6 +272,9 @@ export async function setupServerService + + socketServer.registerChannel('mcpManagement', new McpManagementChannel(mcpManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority))); + const languagePackChannel = ProxyChannel.fromService(accessor.get(ILanguagePackService), disposables); + socketServer.registerChannel('languagePacks', languagePackChannel); @@ -32,7 +32,7 @@ Index: code-server/lib/vscode/src/vs/platform/environment/common/environmentServ =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/environment/common/environmentService.ts +++ code-server/lib/vscode/src/vs/platform/environment/common/environmentService.ts -@@ -101,7 +101,7 @@ export abstract class AbstractNativeEnvi +@@ -98,7 +98,7 @@ export abstract class AbstractNativeEnvi return URI.file(join(vscodePortable, 'argv.json')); } @@ -153,31 +153,15 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -26,6 +26,7 @@ import { URI } from '../../base/common/u +@@ -25,6 +25,7 @@ import { URI } from '../../base/common/u import { streamToBuffer } from '../../base/common/buffer.js'; import { IProductConfiguration } from '../../base/common/product.js'; - import { isString } from '../../base/common/types.js'; + import { isString, Mutable } from '../../base/common/types.js'; +import { getLocaleFromConfig, getBrowserNLSConfiguration } from './remoteLanguagePacks.js'; import { CharCode } from '../../base/common/charCode.js'; import { IExtensionManifest } from '../../platform/extensions/common/extensions.js'; import { ICSSDevelopmentService } from '../../platform/cssDev/node/cssDevService.js'; -@@ -98,6 +99,7 @@ export class WebClientServer { - private readonly _webExtensionResourceUrlTemplate: URI | undefined; - - private readonly _staticRoute: string; -+ private readonly _serverRoot: string; - private readonly _callbackRoute: string; - private readonly _webExtensionRoute: string; - -@@ -113,6 +115,7 @@ export class WebClientServer { - ) { - this._webExtensionResourceUrlTemplate = this._productService.extensionsGallery?.resourceUrlTemplate ? URI.parse(this._productService.extensionsGallery.resourceUrlTemplate) : undefined; - -+ this._serverRoot = serverRootPath; - this._staticRoute = `${serverRootPath}/static`; - this._callbackRoute = `${serverRootPath}/callback`; - this._webExtensionRoute = `/web-extension-resource`; -@@ -351,14 +354,22 @@ export class WebClientServer { +@@ -385,14 +386,22 @@ export class WebClientServer { }; const cookies = cookie.parse(req.headers.cookie || ''); @@ -193,7 +177,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts + try { + const nlsFile = await getBrowserNLSConfiguration(locale, this._environmentService.userDataPath); + WORKBENCH_NLS_URL = nlsFile -+ ? `${vscodeBase}${this._serverRoot}/vscode-remote-resource?path=${encodeURIComponent(nlsFile)}` ++ ? `${vscodeBase}/vscode-remote-resource?path=${encodeURIComponent(nlsFile)}` + : ''; + } catch (error) { + console.error("Failed to generate translations", error); @@ -206,7 +190,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts +++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts -@@ -19,6 +19,7 @@ export const serverOptions: OptionDescri +@@ -22,6 +22,7 @@ export const serverOptions: OptionDescri 'disable-file-downloads': { type: 'boolean' }, 'disable-file-uploads': { type: 'boolean' }, 'disable-getting-started-override': { type: 'boolean' }, @@ -214,7 +198,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts /* ----- server setup ----- */ -@@ -105,6 +106,7 @@ export interface ServerParsedArgs { +@@ -113,6 +114,7 @@ export interface ServerParsedArgs { 'disable-file-downloads'?: boolean; 'disable-file-uploads'?: boolean; 'disable-getting-started-override'?: boolean, @@ -260,10 +244,10 @@ Index: code-server/lib/vscode/src/vs/platform/languagePacks/browser/languagePack + return this.languagePackService.getInstalledLanguages() } } -Index: code-server/lib/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts +Index: code-server/lib/vscode/src/vs/workbench/services/localization/electron-browser/localeService.ts =================================================================== ---- code-server.orig/lib/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts -+++ code-server/lib/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts +--- code-server.orig/lib/vscode/src/vs/workbench/services/localization/electron-browser/localeService.ts ++++ code-server/lib/vscode/src/vs/workbench/services/localization/electron-browser/localeService.ts @@ -51,7 +51,8 @@ class NativeLocaleService implements ILo @IProductService private readonly productService: IProductService ) { } @@ -288,7 +272,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts -@@ -435,9 +435,6 @@ export class InstallAction extends Exten +@@ -475,9 +475,6 @@ export class InstallAction extends Exten if (this.extension.isBuiltin) { return; } @@ -298,7 +282,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens if (this.extension.state !== ExtensionState.Uninstalled) { return; } -@@ -742,7 +739,7 @@ export abstract class InstallInOtherServ +@@ -782,7 +779,7 @@ export abstract class InstallInOtherServ } if (isLanguagePackExtension(this.extension.local.manifest)) { @@ -307,7 +291,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens } // Prefers to run on UI -@@ -2028,17 +2025,6 @@ export class SetLanguageAction extends E +@@ -2073,17 +2070,6 @@ export class SetLanguageAction extends E update(): void { this.enabled = false; this.class = SetLanguageAction.DisabledClass; @@ -325,7 +309,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens } override async run(): Promise { -@@ -2055,7 +2041,6 @@ export class ClearLanguageAction extends +@@ -2100,7 +2086,6 @@ export class ClearLanguageAction extends private static readonly DisabledClass = `${this.EnabledClass} disabled`; constructor( @@ -333,7 +317,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens @ILocaleService private readonly localeService: ILocaleService, ) { super(ClearLanguageAction.ID, ClearLanguageAction.TITLE.value, ClearLanguageAction.DisabledClass, false); -@@ -2065,17 +2050,6 @@ export class ClearLanguageAction extends +@@ -2110,17 +2095,6 @@ export class ClearLanguageAction extends update(): void { this.enabled = false; this.class = ClearLanguageAction.DisabledClass; @@ -351,28 +335,16 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens } override async run(): Promise { -Index: code-server/lib/vscode/build/gulpfile.reh.js -=================================================================== ---- code-server.orig/lib/vscode/build/gulpfile.reh.js -+++ code-server/lib/vscode/build/gulpfile.reh.js -@@ -58,6 +58,7 @@ const serverResourceIncludes = [ - - // NLS - 'out-build/nls.messages.json', -+ 'out-build/nls.keys.json', // Required to generate translations. - - // Process monitor - 'out-build/vs/base/node/cpuUsage.sh', Index: code-server/lib/vscode/src/vs/workbench/workbench.web.main.internal.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/workbench.web.main.internal.ts +++ code-server/lib/vscode/src/vs/workbench/workbench.web.main.internal.ts -@@ -52,7 +52,7 @@ import './services/dialogs/browser/fileD +@@ -54,7 +54,7 @@ import './services/dialogs/browser/fileD import './services/host/browser/browserHostService.js'; import './services/lifecycle/browser/lifecycleService.js'; import './services/clipboard/browser/clipboardService.js'; -import './services/localization/browser/localeService.js'; -+import './services/localization/electron-sandbox/localeService.js'; ++import './services/localization/electron-browser/localeService.js'; import './services/path/browser/pathService.js'; import './services/themes/browser/browserHostColorSchemeService.js'; import './services/encryption/browser/encryptionService.js'; diff --git a/patches/external-file-actions.diff b/patches/external-file-actions.diff index dd89f4882f41..fe6d448f22a9 100644 --- a/patches/external-file-actions.diff +++ b/patches/external-file-actions.diff @@ -90,7 +90,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts +++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts -@@ -16,6 +16,8 @@ export const serverOptions: OptionDescri +@@ -19,6 +19,8 @@ export const serverOptions: OptionDescri /* ----- code-server ----- */ 'disable-update-check': { type: 'boolean' }, 'auth': { type: 'string' }, @@ -99,7 +99,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts /* ----- server setup ----- */ -@@ -99,6 +101,8 @@ export interface ServerParsedArgs { +@@ -107,6 +109,8 @@ export interface ServerParsedArgs { /* ----- code-server ----- */ 'disable-update-check'?: boolean; 'auth'?: string; @@ -112,9 +112,9 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -335,6 +335,8 @@ export class WebClientServer { - serverBasePath: this._basePath, - webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre', +@@ -369,6 +369,8 @@ export class WebClientServer { + serverBasePath: basePath, + webviewEndpoint: staticRoute + '/out/vs/workbench/contrib/webview/browser/pre', userDataPath: this._environmentService.userDataPath, + isEnabledFileDownloads: !this._environmentService.args['disable-file-downloads'], + isEnabledFileUploads: !this._environmentService.args['disable-file-uploads'], @@ -125,13 +125,12 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/browser/contextkeys.ts +++ code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts -@@ -7,11 +7,11 @@ import { Event } from '../../base/common +@@ -6,10 +6,10 @@ import { Disposable } from '../../base/common/lifecycle.js'; import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from '../../platform/contextkey/common/contextkey.js'; - import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from '../../platform/contextkey/common/contextkeys.js'; --import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext } from '../common/contextkeys.js'; -+import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, IsEnabledFileDownloads, IsEnabledFileUploads } from '../common/contextkeys.js'; - import { trackFocus, addDisposableListener, EventType, onDidRegisterWindow, getActiveWindow, isEditableElement } from '../../base/browser/dom.js'; + import { IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from '../../platform/contextkey/common/contextkeys.js'; +-import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, AuxiliaryBarMaximizedContext, InAutomationContext } from '../common/contextkeys.js'; ++import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, AuxiliaryBarMaximizedContext, InAutomationContext, IsEnabledFileDownloads, IsEnabledFileUploads } from '../common/contextkeys.js'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from '../services/editor/common/editorGroupsService.js'; import { IConfigurationService } from '../../platform/configuration/common/configuration.js'; -import { IWorkbenchEnvironmentService } from '../services/environment/common/environmentService.js'; @@ -139,7 +138,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts import { WorkbenchState, IWorkspaceContextService, isTemporaryWorkspace } from '../../platform/workspace/common/workspace.js'; import { IWorkbenchLayoutService, Parts, positionToString } from '../services/layout/browser/layoutService.js'; import { getRemoteName } from '../../platform/remote/common/remoteHosts.js'; -@@ -70,7 +70,7 @@ export class WorkbenchContextKeysHandler +@@ -69,7 +69,7 @@ export class WorkbenchContextKeysHandler @IContextKeyService private readonly contextKeyService: IContextKeyService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -148,9 +147,9 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts @IProductService private readonly productService: IProductService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IEditorService private readonly editorService: IEditorService, -@@ -197,6 +197,10 @@ export class WorkbenchContextKeysHandler - this.auxiliaryBarVisibleContext = AuxiliaryBarVisibleContext.bindTo(this.contextKeyService); - this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART)); +@@ -199,6 +199,10 @@ export class WorkbenchContextKeysHandler + this.auxiliaryBarMaximizedContext = AuxiliaryBarMaximizedContext.bindTo(this.contextKeyService); + this.auxiliaryBarMaximizedContext.set(this.layoutService.isAuxiliaryBarMaximized()); + // code-server + IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads ?? true) @@ -208,16 +207,16 @@ Index: code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/common/contextkeys.ts +++ code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts -@@ -41,6 +41,9 @@ export const HasWebFileSystemAccess = ne +@@ -39,6 +39,9 @@ export const EmbedderIdentifierContext = - export const EmbedderIdentifierContext = new RawContextKey('embedderIdentifier', undefined, localize('embedderIdentifier', 'The identifier of the embedder according to the product service, if one is defined')); + export const InAutomationContext = new RawContextKey('inAutomation', false, localize('inAutomation', "Whether VS Code is running under automation/smoke test")); +export const IsEnabledFileDownloads = new RawContextKey('isEnabledFileDownloads', true, true); +export const IsEnabledFileUploads = new RawContextKey('isEnabledFileUploads', true, true); + //#endregion - + //#region < --- Window --- > Index: code-server/lib/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -231,7 +230,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/dialogs/browser/simpleFi import { IRemoteAgentService } from '../../remote/common/remoteAgentService.js'; import { IContextKeyService, IContextKey, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { equalsIgnoreCase, format, startsWithIgnoreCase } from '../../../../base/common/strings.js'; -@@ -139,7 +139,7 @@ export class SimpleFileDialog extends Di +@@ -144,7 +144,7 @@ export class SimpleFileDialog extends Di @IFileDialogService private readonly fileDialogService: IFileDialogService, @IModelService private readonly modelService: IModelService, @ILanguageService private readonly languageService: ILanguageService, @@ -240,10 +239,10 @@ Index: code-server/lib/vscode/src/vs/workbench/services/dialogs/browser/simpleFi @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, @IPathService protected readonly pathService: IPathService, @IKeybindingService private readonly keybindingService: IKeybindingService, -@@ -283,20 +283,22 @@ export class SimpleFileDialog extends Di - this.filePickBox.sortByLabel = false; - this.filePickBox.ignoreFocusOut = true; +@@ -311,20 +311,22 @@ export class SimpleFileDialog extends Di + this.filePickBox.placeholder = nls.localize('remoteFileDialog.placeholder', "Folder path"); this.filePickBox.ok = true; + this.filePickBox.okLabel = typeof this.options.openLabel === 'string' ? this.options.openLabel : this.options.openLabel?.withoutMnemonic; - if ((this.scheme !== Schemas.file) && this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1) && (this.options.availableFileSystems.indexOf(Schemas.file) > -1)) { - this.filePickBox.customButton = true; - this.filePickBox.customLabel = nls.localize('remoteFileDialog.local', 'Show Local'); @@ -289,7 +288,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/files/browser/views/explo import { WorkbenchCompressibleAsyncDataTree } from '../../../../../platform/list/browser/listService.js'; import { ISearchService, QueryType, getExcludes, ISearchConfiguration, ISearchComplete, IFileQuery } from '../../../../services/search/common/search.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; -@@ -1601,7 +1602,8 @@ export class FileDragAndDrop implements +@@ -1594,7 +1595,8 @@ export class FileDragAndDrop implements @IConfigurationService private configurationService: IConfigurationService, @IInstantiationService private instantiationService: IInstantiationService, @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, @@ -299,7 +298,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/files/browser/views/explo ) { const updateDropEnablement = (e: IConfigurationChangeEvent | undefined) => { if (!e || e.affectsConfiguration('explorer.enableDragAndDrop')) { -@@ -1826,15 +1828,17 @@ export class FileDragAndDrop implements +@@ -1819,15 +1821,17 @@ export class FileDragAndDrop implements // External file DND (Import/Upload file) if (data instanceof NativeDragAndDropData) { @@ -330,7 +329,7 @@ Index: code-server/lib/vscode/src/vs/platform/files/node/diskFileSystemProviderS =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/files/node/diskFileSystemProviderServer.ts +++ code-server/lib/vscode/src/vs/platform/files/node/diskFileSystemProviderServer.ts -@@ -92,6 +92,7 @@ export abstract class AbstractDiskFileSy +@@ -99,6 +99,7 @@ export abstract class AbstractDiskFileSy private async readFile(uriTransformer: IURITransformer, _resource: UriComponents, opts?: IFileAtomicReadOptions): Promise { const resource = this.transformIncoming(uriTransformer, _resource, true); @@ -338,7 +337,7 @@ Index: code-server/lib/vscode/src/vs/platform/files/node/diskFileSystemProviderS const buffer = await this.provider.readFile(resource, opts); return VSBuffer.wrap(buffer); -@@ -110,6 +111,7 @@ export abstract class AbstractDiskFileSy +@@ -117,6 +118,7 @@ export abstract class AbstractDiskFileSy } }); @@ -346,7 +345,7 @@ Index: code-server/lib/vscode/src/vs/platform/files/node/diskFileSystemProviderS const fileStream = this.provider.readFileStream(resource, opts, cts.token); listenStream(fileStream, { onData: chunk => emitter.fire(VSBuffer.wrap(chunk)), -@@ -130,7 +132,7 @@ export abstract class AbstractDiskFileSy +@@ -137,7 +139,7 @@ export abstract class AbstractDiskFileSy private writeFile(uriTransformer: IURITransformer, _resource: UriComponents, content: VSBuffer, opts: IFileWriteOptions): Promise { const resource = this.transformIncoming(uriTransformer, _resource); diff --git a/patches/getting-started.diff b/patches/getting-started.diff index cf06077f8a0a..5affbfa828d3 100644 --- a/patches/getting-started.diff +++ b/patches/getting-started.diff @@ -19,7 +19,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro import { renderFormattedText } from '../../../../base/browser/formattedTextRenderer.js'; import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; import { Button } from '../../../../base/browser/ui/button/button.js'; -@@ -54,7 +54,7 @@ import { IRecentFolder, IRecentWorkspace +@@ -53,7 +53,7 @@ import { IRecentFolder, IRecentWorkspace import { OpenRecentAction } from '../../../browser/actions/windowActions.js'; import { OpenFileFolderAction, OpenFolderAction, OpenFolderViaWorkspaceAction } from '../../../browser/actions/workspaceActions.js'; import { EditorPane } from '../../../browser/parts/editor/editorPane.js'; @@ -28,7 +28,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro import { IEditorOpenContext, IEditorSerializer } from '../../../common/editor.js'; import { IWebviewElement, IWebviewService } from '../../webview/browser/webview.js'; import './gettingStartedColors.js'; -@@ -834,6 +834,72 @@ export class GettingStartedPage extends +@@ -916,6 +916,72 @@ export class GettingStartedPage extends $('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved")) ); @@ -101,7 +101,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro const leftColumn = $('.categories-column.categories-column-left', {},); const rightColumn = $('.categories-column.categories-column-right', {},); -@@ -869,6 +935,9 @@ export class GettingStartedPage extends +@@ -951,6 +1017,9 @@ export class GettingStartedPage extends recentList.setLimit(5); reset(leftColumn, startList.getDomElement(), recentList.getDomElement()); } @@ -181,7 +181,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts +++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts -@@ -18,6 +18,7 @@ export const serverOptions: OptionDescri +@@ -21,6 +21,7 @@ export const serverOptions: OptionDescri 'auth': { type: 'string' }, 'disable-file-downloads': { type: 'boolean' }, 'disable-file-uploads': { type: 'boolean' }, @@ -189,7 +189,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts /* ----- server setup ----- */ -@@ -103,6 +104,7 @@ export interface ServerParsedArgs { +@@ -111,6 +112,7 @@ export interface ServerParsedArgs { 'auth'?: string; 'disable-file-downloads'?: boolean; 'disable-file-uploads'?: boolean; @@ -201,7 +201,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -339,6 +339,7 @@ export class WebClientServer { +@@ -373,6 +373,7 @@ export class WebClientServer { userDataPath: this._environmentService.userDataPath, isEnabledFileDownloads: !this._environmentService.args['disable-file-downloads'], isEnabledFileUploads: !this._environmentService.args['disable-file-uploads'], @@ -213,16 +213,16 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/browser/contextkeys.ts +++ code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts -@@ -7,7 +7,7 @@ import { Event } from '../../base/common +@@ -6,7 +6,7 @@ import { Disposable } from '../../base/common/lifecycle.js'; import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from '../../platform/contextkey/common/contextkey.js'; - import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from '../../platform/contextkey/common/contextkeys.js'; --import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, IsEnabledFileDownloads, IsEnabledFileUploads } from '../common/contextkeys.js'; -+import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, IsEnabledFileDownloads, IsEnabledFileUploads, IsEnabledCoderGettingStarted, } from '../common/contextkeys.js'; - import { trackFocus, addDisposableListener, EventType, onDidRegisterWindow, getActiveWindow, isEditableElement } from '../../base/browser/dom.js'; + import { IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from '../../platform/contextkey/common/contextkeys.js'; +-import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, AuxiliaryBarMaximizedContext, InAutomationContext, IsEnabledFileDownloads, IsEnabledFileUploads } from '../common/contextkeys.js'; ++import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, AuxiliaryBarMaximizedContext, InAutomationContext, IsEnabledFileDownloads, IsEnabledFileUploads, IsEnabledCoderGettingStarted, } from '../common/contextkeys.js'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from '../services/editor/common/editorGroupsService.js'; import { IConfigurationService } from '../../platform/configuration/common/configuration.js'; -@@ -200,6 +200,7 @@ export class WorkbenchContextKeysHandler + import { IBrowserWorkbenchEnvironmentService } from '../services/environment/browser/environmentService.js'; +@@ -202,6 +202,7 @@ export class WorkbenchContextKeysHandler // code-server IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads ?? true) IsEnabledFileUploads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileUploads ?? true) @@ -234,7 +234,7 @@ Index: code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/common/contextkeys.ts +++ code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts -@@ -43,6 +43,7 @@ export const EmbedderIdentifierContext = +@@ -41,6 +41,7 @@ export const InAutomationContext = new R export const IsEnabledFileDownloads = new RawContextKey('isEnabledFileDownloads', true, true); export const IsEnabledFileUploads = new RawContextKey('isEnabledFileUploads', true, true); diff --git a/patches/integration.diff b/patches/integration.diff index 09a072e53c83..f17b3c95f2ea 100644 --- a/patches/integration.diff +++ b/patches/integration.diff @@ -109,24 +109,6 @@ Index: code-server/lib/vscode/src/vs/base/common/processes.ts ]; const envKeys = Object.keys(env); envKeys -Index: code-server/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts -=================================================================== ---- code-server.orig/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts -+++ code-server/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts -@@ -78,8 +78,11 @@ export class BrowserDialogHandler extend - - async about(): Promise { - const detailString = (useAgo: boolean): string => { -- return localize('aboutDetail', -- "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", -+ return localize('aboutCodeServerDetail', -+ "code-server: {0}", -+ this.productService.codeServerVersion ? `v${this.productService.codeServerVersion}` : 'Unknown' -+ ) + '\n' + localize('aboutDetail', -+ "Code: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", - this.productService.version || 'Unknown', - this.productService.commit || 'Unknown', - this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown', Index: code-server/lib/vscode/src/vs/workbench/browser/client.ts =================================================================== --- /dev/null @@ -187,10 +169,10 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.main.ts import { IndexedDB } from '../../base/browser/indexedDB.js'; import { WebFileSystemAccess } from '../../platform/files/browser/webFileSystemAccess.js'; +import { CodeServerClient } from '../../workbench/browser/client.js'; - import { ITelemetryService } from '../../platform/telemetry/common/telemetry.js'; import { IProgressService } from '../../platform/progress/common/progress.js'; import { DelayedLogChannel } from '../services/output/common/delayedLogChannel.js'; -@@ -131,6 +132,9 @@ export class BrowserMain extends Disposa + import { dirname, joinPath } from '../../base/common/resources.js'; +@@ -133,6 +134,9 @@ export class BrowserMain extends Disposa // Startup const instantiationService = workbench.startup(); @@ -204,7 +186,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts -@@ -55,6 +55,8 @@ export type ExtensionVirtualWorkspaceSup +@@ -65,6 +65,8 @@ export type ExtensionVirtualWorkspaceSup }; export interface IProductConfiguration { @@ -269,10 +251,10 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -308,6 +308,7 @@ export class WebClientServer { +@@ -333,6 +333,7 @@ export class WebClientServer { } : undefined; - const productConfiguration = { + const productConfiguration: Partial> = { + codeServerVersion: this._productService.codeServerVersion, embedderIdentifier: 'server-distro', extensionsGallery: this._webExtensionResourceUrlTemplate && this._productService.extensionsGallery ? { @@ -281,9 +263,9 @@ Index: code-server/lib/vscode/src/server-main.ts =================================================================== --- code-server.orig/lib/vscode/src/server-main.ts +++ code-server/lib/vscode/src/server-main.ts -@@ -25,6 +25,9 @@ const __dirname = path.dirname(fileURLTo +@@ -22,6 +22,9 @@ import { IServerAPI } from './vs/server/ perf.mark('code/server/start'); - (globalThis as any).vscodeServerStartTime = performance.now(); + (globalThis as { vscodeServerStartTime?: number }).vscodeServerStartTime = performance.now(); +// This is not indented to make the diff less noisy. We need to move this out +// of the top-level so it will not run immediately and we can control the start. @@ -291,13 +273,13 @@ Index: code-server/lib/vscode/src/server-main.ts // Do a quick parse to determine if a server or the cli needs to be started const parsedArgs = minimist(process.argv.slice(2), { boolean: ['start-server', 'list-extensions', 'print-ip-address', 'help', 'version', 'accept-server-license-terms', 'update-extensions'], -@@ -153,6 +156,7 @@ if (shouldSpawnCli) { +@@ -150,6 +153,7 @@ if (shouldSpawnCli) { } }); } +} - function sanitizeStringArg(val: any): string | undefined { + function sanitizeStringArg(val: unknown): string | undefined { if (Array.isArray(val)) { // if an argument is passed multiple times, minimist creates an array @@ -283,3 +287,22 @@ function prompt(question: string): Promi }); @@ -310,7 +292,7 @@ Index: code-server/lib/vscode/src/server-main.ts + osLocale: 'en', + commit: product.commit, + userDataPath: '', -+ nlsMetadataPath: __dirname, ++ nlsMetadataPath: import.meta.dirname, + }); + return loadCode(nlsConfiguration); +} @@ -322,3 +304,21 @@ Index: code-server/lib/vscode/src/server-main.ts +if (!process.env.CODE_SERVER_PARENT_PID) { + start(); +} +Index: code-server/lib/vscode/src/vs/platform/dialogs/browser/dialog.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/platform/dialogs/browser/dialog.ts ++++ code-server/lib/vscode/src/vs/platform/dialogs/browser/dialog.ts +@@ -45,8 +45,11 @@ export function createWorkbenchDialogOpt + + export function createBrowserAboutDialogDetails(productService: IProductService): { title: string; details: string; detailsToCopy: string } { + const detailString = (useAgo: boolean): string => { +- return localize('aboutDetail', +- "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", ++ return localize('aboutCodeServerDetail', ++ "code-server: {0}", ++ productService.codeServerVersion ? `v${productService.codeServerVersion}` : 'Unknown' ++ ) + '\n' + localize('aboutDetail', ++ "Code: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", + productService.version || 'Unknown', + productService.commit || 'Unknown', + productService.date ? `${productService.date}${useAgo ? ' (' + fromNow(new Date(productService.date), true) + ')' : ''}` : 'Unknown', diff --git a/patches/local-storage.diff b/patches/local-storage.diff index 3e187b5ffb94..097be4d692d6 100644 --- a/patches/local-storage.diff +++ b/patches/local-storage.diff @@ -18,10 +18,10 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -330,6 +330,7 @@ export class WebClientServer { +@@ -364,6 +364,7 @@ export class WebClientServer { remoteAuthority, - serverBasePath: this._basePath, - webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre', + serverBasePath: basePath, + webviewEndpoint: staticRoute + '/out/vs/workbench/contrib/webview/browser/pre', + userDataPath: this._environmentService.userDataPath, _wrapWebWorkerExtHostInIframe, developmentOptions: { enableSmokeTestDriver: this._environmentService.args['enable-smoke-test-driver'] ? true : undefined, logLevel: this._logService.getLevel() }, @@ -32,7 +32,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts +++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts @@ -298,6 +298,11 @@ export interface IWorkbenchConstructionO */ - readonly configurationDefaults?: Record; + readonly configurationDefaults?: Record; + /** + * Path to the user data directory. @@ -66,7 +66,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/configuration/browser/co =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/services/configuration/browser/configurationService.ts +++ code-server/lib/vscode/src/vs/workbench/services/configuration/browser/configurationService.ts -@@ -145,8 +145,10 @@ export class WorkspaceService extends Di +@@ -147,8 +147,10 @@ export class WorkspaceService extends Di this.workspaceConfiguration = this._register(new WorkspaceConfiguration(configurationCache, fileService, uriIdentityService, logService)); this._register(this.workspaceConfiguration.onDidUpdateConfiguration(fromCache => { this.onWorkspaceConfigurationChanged(fromCache).then(() => { @@ -79,7 +79,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/configuration/browser/co }); })); -@@ -552,6 +554,12 @@ export class WorkspaceService extends Di +@@ -556,6 +558,12 @@ export class WorkspaceService extends Di previousFolders = this.workspace.folders; this.workspace.update(workspace); } else { diff --git a/patches/logout.diff b/patches/logout.diff index 32f4ecaf5de0..b326b49485fb 100644 --- a/patches/logout.diff +++ b/patches/logout.diff @@ -8,7 +8,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts -@@ -58,6 +58,7 @@ export interface IProductConfiguration { +@@ -68,6 +68,7 @@ export interface IProductConfiguration { readonly codeServerVersion?: string readonly rootEndpoint?: string readonly updateEndpoint?: string @@ -20,7 +20,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts +++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts -@@ -15,6 +15,7 @@ import { URI } from '../../base/common/u +@@ -18,6 +18,7 @@ import { ProtocolConstants } from '../.. export const serverOptions: OptionDescriptions> = { /* ----- code-server ----- */ 'disable-update-check': { type: 'boolean' }, @@ -28,7 +28,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts /* ----- server setup ----- */ -@@ -97,6 +98,7 @@ export const serverOptions: OptionDescri +@@ -105,6 +106,7 @@ export const serverOptions: OptionDescri export interface ServerParsedArgs { /* ----- code-server ----- */ 'disable-update-check'?: boolean; @@ -40,14 +40,14 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -313,6 +313,7 @@ export class WebClientServer { +@@ -341,6 +341,7 @@ export class WebClientServer { codeServerVersion: this._productService.codeServerVersion, - rootEndpoint: base, - updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined, -+ logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined, + rootEndpoint: rootBase, + updateEndpoint: !this._environmentService.args['disable-update-check'] ? rootBase + '/update/check' : undefined, ++ logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? rootBase + '/logout' : undefined, embedderIdentifier: 'server-distro', extensionsGallery: this._productService.extensionsGallery, - } satisfies Partial; + }; Index: code-server/lib/vscode/src/vs/workbench/browser/client.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/browser/client.ts diff --git a/patches/marketplace.diff b/patches/marketplace.diff index ccc809dbdc5d..5024eda28fc9 100644 --- a/patches/marketplace.diff +++ b/patches/marketplace.diff @@ -19,7 +19,7 @@ Index: code-server/lib/vscode/src/vs/platform/product/common/product.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/product/common/product.ts +++ code-server/lib/vscode/src/vs/platform/product/common/product.ts -@@ -47,6 +47,16 @@ else if (globalThis._VSCODE_PRODUCT_JSON +@@ -49,6 +49,17 @@ else if (globalThis._VSCODE_PRODUCT_JSON version: pkg.version }); } @@ -28,6 +28,7 @@ Index: code-server/lib/vscode/src/vs/platform/product/common/product.ts + extensionsGallery: env.EXTENSIONS_GALLERY ? JSON.parse(env.EXTENSIONS_GALLERY) : (product.extensionsGallery || { + serviceUrl: "https://open-vsx.org/vscode/gallery", + itemUrl: "https://open-vsx.org/vscode/item", ++ extensionUrlTemplate: "https://open-vsx.org/vscode/gallery/{publisher}/{name}/latest", + resourceUrlTemplate: "https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}", + controlUrl: "", + recommendationsUrl: "", @@ -40,31 +41,30 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -115,7 +115,7 @@ export class WebClientServer { +@@ -326,7 +326,6 @@ export class WebClientServer { - this._staticRoute = `${serverRootPath}/static`; - this._callbackRoute = `${serverRootPath}/callback`; -- this._webExtensionRoute = `${serverRootPath}/web-extension-resource`; -+ this._webExtensionRoute = `/web-extension-resource`; - } + const staticRoute = posix.join(basePath, this._productPath, STATIC_PATH); + const callbackRoute = posix.join(basePath, this._productPath, CALLBACK_PATH); +- const webExtensionRoute = posix.join(basePath, this._productPath, WEB_EXTENSION_PATH); + + const resolveWorkspaceURI = (defaultLocation?: string) => defaultLocation && URI.file(resolve(defaultLocation)).with({ scheme: Schemas.vscodeRemote, authority: remoteAuthority }); - /** -@@ -313,14 +313,7 @@ export class WebClientServer { +@@ -342,14 +341,7 @@ export class WebClientServer { codeServerVersion: this._productService.codeServerVersion, - rootEndpoint: base, + rootEndpoint: rootBase, embedderIdentifier: 'server-distro', - extensionsGallery: this._webExtensionResourceUrlTemplate && this._productService.extensionsGallery ? { - ...this._productService.extensionsGallery, - resourceUrlTemplate: this._webExtensionResourceUrlTemplate.with({ - scheme: 'http', - authority: remoteAuthority, -- path: `${this._webExtensionRoute}/${this._webExtensionResourceUrlTemplate.authority}${this._webExtensionResourceUrlTemplate.path}` +- path: `${webExtensionRoute}/${this._webExtensionResourceUrlTemplate.authority}${this._webExtensionResourceUrlTemplate.path}` - }).toString(true) - } : undefined + extensionsGallery: this._productService.extensionsGallery, - } satisfies Partial; + }; - if (!this._environmentService.isBuilt) { + const proposedApi = this._environmentService.args['enable-proposed-api']; Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts @@ -75,9 +75,9 @@ Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/ext import { getTelemetryLevel, supportsTelemetry } from '../../telemetry/common/telemetryUtils.js'; -import { RemoteAuthorities } from '../../../base/common/network.js'; import { TargetPlatform } from '../../extensions/common/extensions.js'; - - const WEB_EXTENSION_RESOURCE_END_POINT_SEGMENT = '/web-extension-resource/'; -@@ -140,9 +139,9 @@ export abstract class AbstractExtensionR + import { ExtensionGalleryResourceType, getExtensionGalleryManifestResourceUri, IExtensionGalleryManifest, IExtensionGalleryManifestService } from '../../extensionManagement/common/extensionGalleryManifest.js'; + import { ILogService } from '../../log/common/log.js'; +@@ -163,9 +162,9 @@ export abstract class AbstractExtensionR } protected _isWebExtensionResourceEndPoint(uri: URI): boolean { @@ -90,3 +90,18 @@ Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/ext } } +Index: code-server/lib/vscode/src/vs/platform/externalServices/common/marketplace.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/platform/externalServices/common/marketplace.ts ++++ code-server/lib/vscode/src/vs/platform/externalServices/common/marketplace.ts +@@ -26,6 +26,10 @@ export async function resolveMarketplace + 'User-Agent': `VSCode ${version} (${productService.nameShort})` + }; + ++ if (productService.extensionsGallery?.authorizationHeaderToken) { ++ headers['Authorization'] = `Bearer ${productService.extensionsGallery.authorizationHeaderToken}`; ++ } ++ + if (supportsTelemetry(productService, environmentService) && getTelemetryLevel(configurationService) === TelemetryLevel.USAGE) { + const serviceMachineId = await getServiceMachineId(environmentService, fileService, storageService); + headers['X-Market-User-Id'] = serviceMachineId; diff --git a/patches/proposed-api.diff b/patches/proposed-api.diff index dd9f25badfc7..ccd274634617 100644 --- a/patches/proposed-api.diff +++ b/patches/proposed-api.diff @@ -10,7 +10,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/extensions/common/extens =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts +++ code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts -@@ -314,10 +314,7 @@ function extensionDescriptionArrayToMap( +@@ -321,10 +321,7 @@ function extensionDescriptionArrayToMap( } export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean { diff --git a/patches/proxy-uri.diff b/patches/proxy-uri.diff index 2d2e014e3cf8..710ed71072ec 100644 --- a/patches/proxy-uri.diff +++ b/patches/proxy-uri.diff @@ -30,7 +30,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts -@@ -59,6 +59,7 @@ export interface IProductConfiguration { +@@ -69,6 +69,7 @@ export interface IProductConfiguration { readonly rootEndpoint?: string readonly updateEndpoint?: string readonly logoutEndpoint?: string @@ -71,19 +71,19 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -314,6 +314,7 @@ export class WebClientServer { - rootEndpoint: base, - updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined, - logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined, -+ proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/', +@@ -342,6 +342,7 @@ export class WebClientServer { + rootEndpoint: rootBase, + updateEndpoint: !this._environmentService.args['disable-update-check'] ? rootBase + '/update/check' : undefined, + logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? rootBase + '/logout' : undefined, ++ proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? rootBase + '/proxy/{{port}}/', embedderIdentifier: 'server-distro', extensionsGallery: this._productService.extensionsGallery, - } satisfies Partial; + }; Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts -@@ -291,7 +291,7 @@ export async function createTerminalEnvi +@@ -292,7 +292,7 @@ export async function createTerminalEnvi // Sanitize the environment, removing any undesirable VS Code and Electron environment // variables @@ -96,7 +96,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.ts +++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts -@@ -19,6 +19,7 @@ import { ISecretStorageProvider } from ' +@@ -20,6 +20,7 @@ import { ISecretStorageProvider } from ' import { isFolderToOpen, isWorkspaceToOpen } from '../../../platform/window/common/window.js'; import type { IWorkbenchConstructionOptions, IWorkspace, IWorkspaceProvider } from '../../../workbench/browser/web.api.js'; import { AuthenticationSessionInfo } from '../../../workbench/services/authentication/browser/authenticationService.js'; @@ -104,7 +104,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts import type { IURLCallbackProvider } from '../../../workbench/services/url/browser/urlService.js'; import { create } from '../../../workbench/workbench.web.main.internal.js'; -@@ -584,6 +585,39 @@ class WorkspaceProvider implements IWork +@@ -606,6 +607,39 @@ class WorkspaceProvider implements IWork settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined, workspaceProvider: WorkspaceProvider.create(config), urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute), @@ -148,7 +148,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExpl =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts -@@ -77,8 +77,8 @@ export class ForwardedPortsView extends +@@ -83,8 +83,8 @@ export class ForwardedPortsView extends private async enableForwardedPortsFeatures() { this.contextKeyListener.clear(); diff --git a/patches/series b/patches/series index 61c801ae9357..ffc15fdd9d75 100644 --- a/patches/series +++ b/patches/series @@ -20,3 +20,5 @@ getting-started.diff keepalive.diff clipboard.diff display-language.diff +trusted-domains.diff +signature-verification.diff diff --git a/patches/service-worker.diff b/patches/service-worker.diff index 3adf246fbf30..6245aeecdafe 100644 --- a/patches/service-worker.diff +++ b/patches/service-worker.diff @@ -6,7 +6,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts -@@ -60,6 +60,10 @@ export interface IProductConfiguration { +@@ -70,6 +70,10 @@ export interface IProductConfiguration { readonly updateEndpoint?: string readonly logoutEndpoint?: string readonly proxyEndpointTemplate?: string @@ -54,14 +54,14 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -315,6 +315,10 @@ export class WebClientServer { - updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined, - logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined, - proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/', +@@ -343,6 +343,10 @@ export class WebClientServer { + updateEndpoint: !this._environmentService.args['disable-update-check'] ? rootBase + '/update/check' : undefined, + logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? rootBase + '/logout' : undefined, + proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? rootBase + '/proxy/{{port}}/', + serviceWorker: { + scope: vscodeBase + '/', -+ path: base + '/_static/out/browser/serviceWorker.js', ++ path: rootBase + '/_static/out/browser/serviceWorker.js', + }, embedderIdentifier: 'server-distro', extensionsGallery: this._productService.extensionsGallery, - } satisfies Partial; + }; diff --git a/patches/signature-verification.diff b/patches/signature-verification.diff new file mode 100644 index 000000000000..8c05f4a3f760 --- /dev/null +++ b/patches/signature-verification.diff @@ -0,0 +1,34 @@ +Disable signature verification. + +Extension signature verification is now mandatory for all platforms and needs to be disabled. + +Index: code-server/lib/vscode/src/vs/platform/extensionManagement/node/extensionManagementService.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/platform/extensionManagement/node/extensionManagementService.ts ++++ code-server/lib/vscode/src/vs/platform/extensionManagement/node/extensionManagementService.ts +@@ -34,6 +34,7 @@ import { + ExtensionSignatureVerificationCode, + computeSize, + IAllowedExtensionsService, ++ // @ts-expect-error no-unused-variable + VerifyExtensionSignatureConfigKey, + shouldRequireRepositorySignatureFor, + } from '../common/extensionManagement.js'; +@@ -87,6 +88,7 @@ export class ExtensionManagementService + @IDownloadService private downloadService: IDownloadService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IFileService private readonly fileService: IFileService, ++ // @ts-expect-error no-unused-variable + @IConfigurationService private readonly configurationService: IConfigurationService, + @IExtensionGalleryManifestService protected readonly extensionGalleryManifestService: IExtensionGalleryManifestService, + @IProductService productService: IProductService, +@@ -339,8 +341,7 @@ export class ExtensionManagementService + + private async downloadExtension(extension: IGalleryExtension, operation: InstallOperation, verifySignature: boolean, clientTargetPlatform?: TargetPlatform): Promise<{ readonly location: URI; readonly verificationStatus: ExtensionSignatureVerificationCode | undefined }> { + if (verifySignature) { +- const value = this.configurationService.getValue(VerifyExtensionSignatureConfigKey); +- verifySignature = isBoolean(value) ? value : true; ++ verifySignature = false; + } + const { location, verificationStatus } = await this.extensionsDownloader.download(extension, operation, verifySignature, clientTargetPlatform); + const shouldRequireSignature = shouldRequireRepositorySignatureFor(extension.private, await this.extensionGalleryManifestService.getExtensionGalleryManifest()); diff --git a/patches/sourcemaps.diff b/patches/sourcemaps.diff index 18792a317e51..62f3197ef5f9 100644 --- a/patches/sourcemaps.diff +++ b/patches/sourcemaps.diff @@ -6,33 +6,33 @@ not host our source maps there and want them to be self-hosted even if we could. To test try debugging/browsing the source of a build in a browser. -Index: code-server/lib/vscode/build/gulpfile.reh.js +Index: code-server/lib/vscode/build/gulpfile.reh.ts =================================================================== ---- code-server.orig/lib/vscode/build/gulpfile.reh.js -+++ code-server/lib/vscode/build/gulpfile.reh.js -@@ -253,8 +253,7 @@ function packageTask(type, platform, arc - +--- code-server.orig/lib/vscode/build/gulpfile.reh.ts ++++ code-server/lib/vscode/build/gulpfile.reh.ts +@@ -257,8 +257,7 @@ function packageTask(type: string, platf + return () => { const src = gulp.src(sourceFolderName + '/**', { base: '.' }) - .pipe(rename(function (path) { path.dirname = path.dirname.replace(new RegExp('^' + sourceFolderName), 'out'); })) + .pipe(rename(function (path) { path.dirname = path.dirname!.replace(new RegExp('^' + sourceFolderName), 'out'); })) - .pipe(util.setExecutableBit(['**/*.sh'])) -- .pipe(filter(['**', '!**/*.js.map'])); +- .pipe(filter(['**', '!**/*.{js,css}.map'])); + .pipe(util.setExecutableBit(['**/*.sh'])); const workspaceExtensionPoints = ['debuggers', 'jsonValidation']; - const isUIExtension = (manifest) => { -@@ -293,9 +292,9 @@ function packageTask(type, platform, arc + const isUIExtension = (manifest: { extensionKind?: string; main?: string; contributes?: Record }) => { +@@ -298,9 +297,9 @@ function packageTask(type: string, platf .map(name => `.build/extensions/${name}/**`); const extensions = gulp.src(extensionPaths, { base: '.build', dot: true }); - const extensionsCommonDependencies = gulp.src('.build/extensions/node_modules/**', { base: '.build', dot: true }); - const sources = es.merge(src, extensions, extensionsCommonDependencies) + const extensionsCommonDependencies = gulp.src('.build/extensions/node_modules/**', { base: '.build', dot: true }) - .pipe(filter(['**', '!**/*.js.map'], { dot: true })); + .pipe(filter(['**', '!**/*.{js,css}.map'], { dot: true })); + const sources = es.merge(src, extensions, extensionsCommonDependencies); let version = packageJson.version; - const quality = product.quality; -@@ -454,7 +453,7 @@ function tweakProductForServerWeb(produc + const quality = (product as typeof product & { quality?: string }).quality; +@@ -453,7 +452,7 @@ function tweakProductForServerWeb(produc const minifyTask = task.define(`minify-vscode-${type}`, task.series( bundleTask, util.rimraf(`out-vscode-${type}-min`), diff --git a/patches/store-socket.diff b/patches/store-socket.diff index ce173be3283e..a49f53fcb657 100644 --- a/patches/store-socket.diff +++ b/patches/store-socket.diff @@ -21,18 +21,18 @@ Index: code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService. +import * as _http from 'http'; import * as performance from '../../../base/common/performance.js'; + import type * as vscode from 'vscode'; import { createApiFactoryAndRegisterActors } from '../common/extHost.api.impl.js'; - import { RequireInterceptor } from '../common/extHostRequireInterceptor.js'; -@@ -17,6 +18,7 @@ import { ExtensionRuntime } from '../com +@@ -18,6 +19,7 @@ import { ExtensionRuntime } from '../com import { CLIServer } from './extHostCLIServer.js'; - import { realpathSync } from '../../../base/node/extpath.js'; + import { realpathSync } from '../../../base/node/pfs.js'; import { ExtHostConsoleForwarder } from './extHostConsoleForwarder.js'; +import { IExtHostWorkspace } from '../common/extHostWorkspace.js'; import { ExtHostDiskFileSystemProvider } from './extHostDiskFileSystemProvider.js'; - import { createRequire } from 'node:module'; - const require = createRequire(import.meta.url); -@@ -97,6 +99,52 @@ export class ExtHostExtensionService ext - await interceptor.install(); + import nodeModule from 'node:module'; + import { assertType } from '../../../base/common/types.js'; +@@ -226,6 +228,52 @@ export class ExtHostExtensionService ext + performance.mark('code/extHost/didInitAPI'); + (async () => { @@ -96,7 +96,7 @@ Index: code-server/lib/vscode/src/vs/workbench/api/node/extensionHostProcess.ts import minimist from 'minimist'; import * as nativeWatchdog from 'native-watchdog'; import * as net from 'net'; -@@ -422,7 +423,28 @@ async function startExtensionHostProcess +@@ -469,7 +470,28 @@ async function startExtensionHostProcess ); // rewrite onTerminate-function to be a proper shutdown diff --git a/patches/telemetry.diff b/patches/telemetry.diff index 850a3f3d78ff..8dc9a04d8199 100644 --- a/patches/telemetry.diff +++ b/patches/telemetry.diff @@ -28,8 +28,8 @@ Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts import { NullPolicyService } from '../../platform/policy/common/policy.js'; import { OneDataSystemAppender } from '../../platform/telemetry/node/1dsAppender.js'; import { LoggerService } from '../../platform/log/node/loggerService.js'; -@@ -152,11 +154,23 @@ export async function setupServerService - const requestService = new RequestService(configurationService, environmentService, logService); +@@ -166,11 +168,23 @@ export async function setupServerService + const requestService = new RequestService('remote', configurationService, environmentService, logService); services.set(IRequestService, requestService); + let isContainer = undefined; @@ -134,20 +134,20 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -319,6 +319,8 @@ export class WebClientServer { +@@ -347,6 +347,8 @@ export class WebClientServer { scope: vscodeBase + '/', - path: base + '/_static/out/browser/serviceWorker.js', + path: rootBase + '/_static/out/browser/serviceWorker.js', }, + enableTelemetry: this._productService.enableTelemetry, + telemetryEndpoint: this._productService.telemetryEndpoint, embedderIdentifier: 'server-distro', extensionsGallery: this._productService.extensionsGallery, - } satisfies Partial; + }; Index: code-server/lib/vscode/src/vs/base/common/product.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts -@@ -64,6 +64,7 @@ export interface IProductConfiguration { +@@ -74,6 +74,7 @@ export interface IProductConfiguration { readonly path: string; readonly scope: string; } @@ -159,7 +159,7 @@ Index: code-server/lib/vscode/src/vs/platform/product/common/product.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/product/common/product.ts +++ code-server/lib/vscode/src/vs/platform/product/common/product.ts -@@ -55,7 +55,8 @@ else if (globalThis._VSCODE_PRODUCT_JSON +@@ -58,7 +58,8 @@ else if (globalThis._VSCODE_PRODUCT_JSON resourceUrlTemplate: "https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}", controlUrl: "", recommendationsUrl: "", diff --git a/patches/trusted-domains.diff b/patches/trusted-domains.diff new file mode 100644 index 000000000000..7664e65e3572 --- /dev/null +++ b/patches/trusted-domains.diff @@ -0,0 +1,49 @@ +Allow configuring trusted domains via product.json or flag. + +Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts ++++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts +@@ -23,6 +23,7 @@ export const serverOptions: OptionDescri + 'disable-file-uploads': { type: 'boolean' }, + 'disable-getting-started-override': { type: 'boolean' }, + 'locale': { type: 'string' }, ++ 'link-protection-trusted-domains': { type: 'string[]' }, + + /* ----- server setup ----- */ + +@@ -115,6 +116,7 @@ export interface ServerParsedArgs { + 'disable-file-uploads'?: boolean; + 'disable-getting-started-override'?: boolean, + 'locale'?: string ++ 'link-protection-trusted-domains'?: string[], + + /* ----- server setup ----- */ + +Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts ++++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts +@@ -338,6 +338,14 @@ export class WebClientServer { + scopes: [['user:email'], ['repo']] + } : undefined; + ++ const linkProtectionTrustedDomains: string[] = []; ++ if (this._environmentService.args['link-protection-trusted-domains']) { ++ linkProtectionTrustedDomains.push(...this._environmentService.args['link-protection-trusted-domains']); ++ } ++ if (this._productService.linkProtectionTrustedDomains) { ++ linkProtectionTrustedDomains.push(...this._productService.linkProtectionTrustedDomains); ++ } ++ + const productConfiguration: Partial> = { + codeServerVersion: this._productService.codeServerVersion, + rootEndpoint: rootBase, +@@ -352,6 +360,7 @@ export class WebClientServer { + telemetryEndpoint: this._productService.telemetryEndpoint, + embedderIdentifier: 'server-distro', + extensionsGallery: this._productService.extensionsGallery, ++ linkProtectionTrustedDomains, + }; + + const proposedApi = this._environmentService.args['enable-proposed-api']; diff --git a/patches/unique-db.diff b/patches/unique-db.diff index 8d46a0b7aea0..abe17ad75a95 100644 --- a/patches/unique-db.diff +++ b/patches/unique-db.diff @@ -21,7 +21,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/storage/browser/storageS export class BrowserStorageService extends AbstractStorageService { -@@ -298,7 +299,11 @@ export class IndexedDBStorageDatabase ex +@@ -300,7 +301,11 @@ export class IndexedDBStorageDatabase ex } static async createWorkspaceStorage(workspaceId: string, logService: ILogService): Promise { diff --git a/patches/update-check.diff b/patches/update-check.diff index 91dbca114738..bc3519e6e037 100644 --- a/patches/update-check.diff +++ b/patches/update-check.diff @@ -93,7 +93,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts -@@ -57,6 +57,7 @@ export type ExtensionVirtualWorkspaceSup +@@ -67,6 +67,7 @@ export type ExtensionVirtualWorkspaceSup export interface IProductConfiguration { readonly codeServerVersion?: string readonly rootEndpoint?: string @@ -101,24 +101,32 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts readonly version: string; readonly date?: string; +@@ -112,6 +113,7 @@ export interface IProductConfiguration { + readonly resourceUrlTemplate: string; + readonly nlsBaseUrl: string; + readonly accessSKUs?: string[]; ++ readonly authorizationHeaderToken?: string; + }; + + readonly mcpGallery?: { Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -312,6 +312,7 @@ export class WebClientServer { - const productConfiguration = { +@@ -340,6 +340,7 @@ export class WebClientServer { + const productConfiguration: Partial> = { codeServerVersion: this._productService.codeServerVersion, - rootEndpoint: base, -+ updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined, + rootEndpoint: rootBase, ++ updateEndpoint: !this._environmentService.args['disable-update-check'] ? rootBase + '/update/check' : undefined, embedderIdentifier: 'server-distro', extensionsGallery: this._productService.extensionsGallery, - } satisfies Partial; + }; Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts +++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts -@@ -13,6 +13,8 @@ import { memoize } from '../../base/comm - import { URI } from '../../base/common/uri.js'; +@@ -16,6 +16,8 @@ import { join } from '../../base/common/ + import { ProtocolConstants } from '../../base/parts/ipc/common/ipc.net.js'; export const serverOptions: OptionDescriptions> = { + /* ----- code-server ----- */ @@ -126,7 +134,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts /* ----- server setup ----- */ -@@ -93,6 +95,8 @@ export const serverOptions: OptionDescri +@@ -101,6 +103,8 @@ export const serverOptions: OptionDescri }; export interface ServerParsedArgs { diff --git a/patches/webview.diff b/patches/webview.diff index aab7aa9309f8..eba190652651 100644 --- a/patches/webview.diff +++ b/patches/webview.diff @@ -41,7 +41,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/envi =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts +++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts -@@ -225,7 +225,7 @@ export class BrowserWorkbenchEnvironment +@@ -220,7 +220,7 @@ export class BrowserWorkbenchEnvironment @memoize get webviewExternalEndpoint(): string { @@ -54,11 +54,11 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts -@@ -326,6 +326,7 @@ export class WebClientServer { +@@ -360,6 +360,7 @@ export class WebClientServer { const workbenchWebConfiguration = { remoteAuthority, - serverBasePath: this._basePath, -+ webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre', + serverBasePath: basePath, ++ webviewEndpoint: staticRoute + '/out/vs/workbench/contrib/webview/browser/pre', _wrapWebWorkerExtHostInIframe, developmentOptions: { enableSmokeTestDriver: this._environmentService.args['enable-smoke-test-driver'] ? true : undefined, logLevel: this._logService.getLevel() }, settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined, @@ -70,29 +70,21 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index -+ content="default-src 'none'; script-src 'sha256-z5v/0xE4zQPgV1fazhHB/UlsTzdm39hRMk3V8Av0HI4=' 'self'; frame-src 'self'; style-src 'unsafe-inline';"> - +- content="default-src 'none'; script-src 'sha256-TaWGDzV7c9rUH2q/5ygOyYUHSyHIqBMYfucPh3lnKvU=' 'self'; frame-src 'self'; style-src 'unsafe-inline';"> ++ content="default-src 'none'; script-src 'sha256-nQZh+9dHKZP2cHbhYlCbWDtqxxJtGjRGBx57zNP2DZM=' 'self'; frame-src 'self'; style-src 'unsafe-inline';"> -@@ -349,6 +349,12 @@ - - const hostname = location.hostname; + { + /** + * @param {MessageEvent} event +@@ -370,6 +370,12 @@ const hostname = location.hostname; @@ -113,12 +105,12 @@ Index: code-server/lib/vscode/src/vs/workbench/services/extensions/worker/webWor -@@ -23,6 +23,13 @@ +@@ -25,6 +25,13 @@ // validation not requested return start(); } diff --git a/src/browser/favicon.afdesign b/src/browser/favicon.afdesign deleted file mode 100644 index b654f32e82b1..000000000000 --- a/src/browser/favicon.afdesign +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:625d2049c38ae27df0613fa533020e889fa98affd603050f46d3748be7b90d0b -size 38675 diff --git a/src/browser/media/favicon-dark-support.svg b/src/browser/media/favicon-dark-support.svg index 06f1fa00d62c..d64bf32ed96e 100644 --- a/src/browser/media/favicon-dark-support.svg +++ b/src/browser/media/favicon-dark-support.svg @@ -1,7 +1,4 @@ - \ No newline at end of file + + + + diff --git a/src/browser/media/favicon.ico b/src/browser/media/favicon.ico index e721447bd9d1..56078ead6697 100644 Binary files a/src/browser/media/favicon.ico and b/src/browser/media/favicon.ico differ diff --git a/src/browser/media/favicon.svg b/src/browser/media/favicon.svg index 45388729b6b4..01a01541ec75 100644 --- a/src/browser/media/favicon.svg +++ b/src/browser/media/favicon.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/src/browser/media/pwa-icon-192.png b/src/browser/media/pwa-icon-192.png index a6ee503115d8..9f46dd830361 100644 Binary files a/src/browser/media/pwa-icon-192.png and b/src/browser/media/pwa-icon-192.png differ diff --git a/src/browser/media/pwa-icon-512.png b/src/browser/media/pwa-icon-512.png index ff42978ce9a5..9b899e8d690e 100644 Binary files a/src/browser/media/pwa-icon-512.png and b/src/browser/media/pwa-icon-512.png differ diff --git a/src/browser/media/pwa-icon-maskable-192.png b/src/browser/media/pwa-icon-maskable-192.png new file mode 100644 index 000000000000..3f28593b3aee Binary files /dev/null and b/src/browser/media/pwa-icon-maskable-192.png differ diff --git a/src/browser/media/pwa-icon-maskable-512.png b/src/browser/media/pwa-icon-maskable-512.png new file mode 100644 index 000000000000..7df85f7665ac Binary files /dev/null and b/src/browser/media/pwa-icon-maskable-512.png differ diff --git a/src/browser/media/pwa-icon.png b/src/browser/media/pwa-icon.png deleted file mode 100644 index d4f5188ffc0c..000000000000 Binary files a/src/browser/media/pwa-icon.png and /dev/null differ diff --git a/src/browser/pages/global.css b/src/browser/pages/global.css index 6ccbdc8367a6..ba79ea6134c6 100644 --- a/src/browser/pages/global.css +++ b/src/browser/pages/global.css @@ -11,11 +11,12 @@ body, body { background: rgb(244, 247, 252); - background: light-dark(rgb(244, 247, 252), #111); + background: light-dark(rgb(244, 247, 252), #111827); color: #111; color: light-dark(#111, #ddd); margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; overflow: hidden; } @@ -29,14 +30,15 @@ button { .-button { background-color: rgb(87, 114, 245); - background-color: light-dark(rgb(87, 114, 245), rgb(50, 85, 250)); + background-color: light-dark(rgb(87, 114, 245), rgb(26, 86, 219)); border-radius: 5px; border: none; box-sizing: border-box; color: white; - color: light-dark(white, #ddd); + color: light-dark(white, white); cursor: pointer; padding: 18px 20px; + font-weight: 500; text-decoration: none; } @@ -53,10 +55,10 @@ button { .card-box { background-color: rgb(250, 253, 258); - background-color: light-dark(rgb(250, 253, 258), #000); + background-color: light-dark(rgb(250, 253, 258), #1f2937); border-radius: 5px; box-shadow: - rgba(60, 66, 87, 0.117647) 0px 7px 14px 0px, + light-dark(rgba(60, 66, 87, 0.117647), rgba(10, 10, 10, 0.617647)) 0px 7px 14px 0px, rgba(0, 0, 0, 0.117647) 0px 3px 6px 0px; max-width: 650px; width: 100%; @@ -64,9 +66,9 @@ button { .card-box > .header { border-bottom: 1px solid #ddd; - border-bottom: 1px solid light-dark(#ddd, #222); + border-bottom: 1px solid light-dark(#ddd, #111827); color: #444; - color: light-dark(#444, #ccc); + color: light-dark(#444, #fff); padding: 30px; } @@ -77,7 +79,7 @@ button { .card-box > .header > .sub { color: #555; - color: light-dark(#555, #aaa); + color: light-dark(#555, #9ca3af); margin-top: 10px; } diff --git a/src/browser/pages/login.css b/src/browser/pages/login.css index 1647c575f954..33b541c9e844 100644 --- a/src/browser/pages/login.css +++ b/src/browser/pages/login.css @@ -30,15 +30,23 @@ body { .login-form > .field > .password { background-color: rgb(244, 247, 252); - background-color: light-dark(rgb(244, 247, 252), #222); + background-color: light-dark(rgb(244, 247, 252), #374151); border-radius: 5px; border: 1px solid #ddd; - border: 1px solid light-dark(#ddd, #333); + border: 1px solid light-dark(#ddd, #4b5563); box-sizing: border-box; flex: 1; padding: 16px; } +.login-form > .field > .password::placeholder { + color: rgb(148 163 184); +} + +.login-form > .field > .password:focus { + outline: 2px solid rgb(63, 131, 248); +} + .login-form > .user { display: none; } diff --git a/src/common/http.ts b/src/common/http.ts index 5f94c2cd0522..f005272bfc53 100644 --- a/src/common/http.ts +++ b/src/common/http.ts @@ -24,6 +24,6 @@ export class HttpError extends Error { } } -export enum CookieKeys { - Session = "code-server-session", +export function getCookieSessionName(suffix?: string): string { + return suffix ? `code-server-session-${suffix.replace(/[^a-zA-Z0-9-]/g, "-")}` : "code-server-session" } diff --git a/src/node/cli.ts b/src/node/cli.ts index 9eb6e5163e8a..7e4674eaa052 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -30,7 +30,7 @@ export enum LogLevel { export class OptionalString extends Optional {} /** - * Code flags provided by the user. + * (VS) Code flags provided by the user. */ export interface UserProvidedCodeArgs { "disable-telemetry"?: boolean @@ -53,7 +53,10 @@ export interface UserProvidedCodeArgs { "disable-getting-started-override"?: boolean "disable-proxy"?: boolean "session-socket"?: string - "abs-proxy-base-path"?: string + "cookie-suffix"?: string + "link-protection-trusted-domains"?: string[] + // locale is used by both VS Code and code-server. + locale?: string } /** @@ -73,7 +76,6 @@ export interface UserProvidedArgs extends UserProvidedCodeArgs { enable?: string[] help?: boolean host?: string - locale?: string port?: number json?: boolean log?: LogLevel @@ -84,12 +86,16 @@ export interface UserProvidedArgs extends UserProvidedCodeArgs { "trusted-origins"?: string[] version?: boolean "proxy-domain"?: string[] + "skip-auth-preflight"?: boolean "reuse-window"?: boolean "new-window"?: boolean "ignore-last-opened"?: boolean verbose?: boolean "app-name"?: string "welcome-text"?: string + "abs-proxy-base-path"?: string + i18n?: string + "idle-timeout-seconds"?: number /* Positional arguments. */ _?: string[] } @@ -167,6 +173,12 @@ export const options: Options> = { "session-socket": { type: "string", }, + "cookie-suffix": { + type: "string", + description: + "Adds a suffix to the cookie. This can prevent a collision of cookies for subdomains, making them explixit. \n" + + "Without this flag, no suffix is used. This can also be set with CODE_SERVER_COOKIE_SUFFIX set to any string.", + }, "disable-file-downloads": { type: "boolean", description: @@ -193,6 +205,10 @@ export const options: Options> = { enable: { type: "string[]" }, help: { type: "boolean", short: "h", description: "Show this output." }, json: { type: "boolean" }, + "link-protection-trusted-domains": { + type: "string[]", + description: "Links matching a trusted domain can be opened without link protection.", + }, locale: { // The preferred way to set the locale is via the UI. type: "string", @@ -252,6 +268,10 @@ export const options: Options> = { description: "GitHub authentication token (can only be passed in via $GITHUB_TOKEN or the config file).", }, "proxy-domain": { type: "string[]", description: "Domain used for proxying ports." }, + "skip-auth-preflight": { + type: "boolean", + description: "Allows preflight requests through proxy without authentication.", + }, "ignore-last-opened": { type: "boolean", short: "e", @@ -273,17 +293,28 @@ export const options: Options> = { "app-name": { type: "string", short: "an", - description: "The name to use in branding. Will be shown in titlebar and welcome message", + description: + "Will replace the {{app}} placeholder in any strings, which by default includes the title bar and welcome message", }, "welcome-text": { type: "string", short: "w", description: "Text to show on login page", + deprecated: true, }, "abs-proxy-base-path": { type: "string", description: "The base path to prefix to all absproxy requests", }, + i18n: { + type: "string", + path: true, + description: "Path to JSON file with custom translations. Merges with default strings and supports all i18n keys.", + }, + "idle-timeout-seconds": { + type: "number", + description: "Timeout in seconds to wait before shutting down when idle.", + }, } export const optionDescriptions = (opts: Partial>> = options): string[] => { @@ -377,6 +408,10 @@ export const parse = ( throw new Error("--github-auth can only be set in the config file or passed in via $GITHUB_TOKEN") } + if (key === "idle-timeout-seconds" && Number(value) <= 60) { + throw new Error("--idle-timeout-seconds must be greater than 60 seconds.") + } + const option = options[key] if (option.type === "boolean") { ;(args[key] as boolean) = true @@ -588,10 +623,24 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config usingEnvPassword = false } + if (process.env.CODE_SERVER_COOKIE_SUFFIX) { + args["cookie-suffix"] = process.env.CODE_SERVER_COOKIE_SUFFIX + } + if (process.env.GITHUB_TOKEN) { args["github-auth"] = process.env.GITHUB_TOKEN } + if (process.env.CODE_SERVER_IDLE_TIMEOUT_SECONDS) { + if (isNaN(Number(process.env.CODE_SERVER_IDLE_TIMEOUT_SECONDS))) { + logger.info("CODE_SERVER_IDLE_TIMEOUT_SECONDS must be a number") + } + if (Number(process.env.CODE_SERVER_IDLE_TIMEOUT_SECONDS) <= 60) { + throw new Error("--idle-timeout-seconds must be greater than 60 seconds.") + } + args["idle-timeout-seconds"] = Number(process.env.CODE_SERVER_IDLE_TIMEOUT_SECONDS) + } + // Ensure they're not readable by child processes. delete process.env.PASSWORD delete process.env.HASHED_PASSWORD @@ -702,12 +751,16 @@ export function parseConfigFile(configFile: string, configPath: string): ConfigA // We convert the config file into a set of flags. // This is a temporary measure until we add a proper CLI library. - const configFileArgv = Object.entries(config).map(([optName, opt]) => { - if (opt === true) { - return `--${optName}` - } - return `--${optName}=${opt}` - }) + const configFileArgv = Object.entries(config) + .map(([optName, opt]) => { + if (opt === true) { + return `--${optName}` + } else if (Array.isArray(opt)) { + return opt.map((o) => `--${optName}=${o}`) + } + return `--${optName}=${opt}` + }) + .flat() const args = parse(configFileArgv, { configFile: configPath, }) diff --git a/src/node/heart.ts b/src/node/heart.ts index aac917257f23..b78f4edb80d2 100644 --- a/src/node/heart.ts +++ b/src/node/heart.ts @@ -1,5 +1,6 @@ import { logger } from "@coder/logger" import { promises as fs } from "fs" +import { Emitter } from "../common/emitter" /** * Provides a heartbeat using a local file to indicate activity. @@ -8,6 +9,9 @@ export class Heart { private heartbeatTimer?: NodeJS.Timeout private heartbeatInterval = 60000 public lastHeartbeat = 0 + private readonly _onChange = new Emitter<"alive" | "expired" | "unknown">() + readonly onChange = this._onChange.event + private state: "alive" | "expired" | "unknown" = "expired" public constructor( private readonly heartbeatPath: string, @@ -17,6 +21,13 @@ export class Heart { this.alive = this.alive.bind(this) } + private setState(state: typeof this.state) { + if (this.state !== state) { + this.state = state + this._onChange.emit(this.state) + } + } + public alive(): boolean { const now = Date.now() return now - this.lastHeartbeat < this.heartbeatInterval @@ -28,6 +39,7 @@ export class Heart { */ public async beat(): Promise { if (this.alive()) { + this.setState("alive") return } @@ -36,7 +48,22 @@ export class Heart { if (typeof this.heartbeatTimer !== "undefined") { clearTimeout(this.heartbeatTimer) } - this.heartbeatTimer = setTimeout(() => heartbeatTimer(this.isActive, this.beat), this.heartbeatInterval) + + this.heartbeatTimer = setTimeout(async () => { + try { + if (await this.isActive()) { + this.beat() + } else { + this.setState("expired") + } + } catch (error: unknown) { + logger.warn((error as Error).message) + this.setState("unknown") + } + }, this.heartbeatInterval) + + this.setState("alive") + try { return await fs.writeFile(this.heartbeatPath, "") } catch (error: any) { @@ -53,20 +80,3 @@ export class Heart { } } } - -/** - * Helper function for the heartbeatTimer. - * - * If heartbeat is active, call beat. Otherwise do nothing. - * - * Extracted to make it easier to test. - */ -export async function heartbeatTimer(isActive: Heart["isActive"], beat: Heart["beat"]) { - try { - if (await isActive()) { - beat() - } - } catch (error: unknown) { - logger.warn((error as Error).message) - } -} diff --git a/src/node/http.ts b/src/node/http.ts index e0fb3a4caf6b..e00edccd0210 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -1,11 +1,10 @@ import { field, logger } from "@coder/logger" import * as express from "express" -import * as expressCore from "express-serve-static-core" import * as http from "http" import * as net from "net" -import * as qs from "qs" +import qs from "qs" import { Disposable } from "../common/emitter" -import { CookieKeys, HttpCode, HttpError } from "../common/http" +import { HttpCode, HttpError } from "../common/http" import { normalize } from "../common/util" import { AuthType, DefaultedArgs } from "./cli" import { version as codeServerVersion } from "./constants" @@ -41,6 +40,7 @@ declare global { heart: Heart settings: SettingsProvider updater: UpdateProvider + cookieSessionName: string } } } @@ -125,7 +125,7 @@ export const authenticated = async (req: express.Request): Promise => { const passwordMethod = getPasswordMethod(hashedPasswordFromArgs) const isCookieValidArgs: IsCookieValidArgs = { passwordMethod, - cookieKey: sanitizeString(req.cookies[CookieKeys.Session]), + cookieKey: sanitizeString(req.cookies[req.cookieSessionName]), passwordFromArgs: req.args.password || "", hashedPasswordFromArgs: req.args["hashed-password"], } @@ -185,12 +185,7 @@ export const constructRedirectPath = (req: express.Request, query: qs.ParsedQs, * preserved. `to` should be a simple path without any query parameters * `override` will merge with the existing query (use `undefined` to unset). */ -export const redirect = ( - req: express.Request, - res: express.Response, - to: string, - override: expressCore.Query = {}, -): void => { +export const redirect = (req: express.Request, res: express.Response, to: string, override: qs.ParsedQs = {}): void => { const query = Object.assign({}, req.query, override) Object.keys(override).forEach((key) => { if (typeof override[key] === "undefined") { diff --git a/src/node/i18n/index.ts b/src/node/i18n/index.ts index 4ee718e13aa2..e8186067ba98 100644 --- a/src/node/i18n/index.ts +++ b/src/node/i18n/index.ts @@ -1,3 +1,4 @@ +import { promises as fs } from "fs" import i18next, { init } from "i18next" import * as en from "./locales/en.json" import * as ja from "./locales/ja.json" @@ -5,29 +6,54 @@ import * as th from "./locales/th.json" import * as ur from "./locales/ur.json" import * as zhCn from "./locales/zh-cn.json" +const defaultResources = { + en: { + translation: en, + }, + "zh-cn": { + translation: zhCn, + }, + th: { + translation: th, + }, + ja: { + translation: ja, + }, + ur: { + translation: ur, + }, +} + +export async function loadCustomStrings(filePath: string): Promise { + try { + // Read custom strings from file path only + const fileContent = await fs.readFile(filePath, "utf8") + const customStringsData = JSON.parse(fileContent) + + // User-provided strings override all languages. + Object.keys(defaultResources).forEach((locale) => { + i18next.addResourceBundle(locale, "translation", customStringsData) + }) + } catch (error) { + if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") { + throw new Error(`Custom strings file not found: ${filePath}\nPlease ensure the file exists and is readable.`) + } else if (error instanceof SyntaxError) { + throw new Error(`Invalid JSON in custom strings file: ${filePath}\n${error.message}`) + } else { + throw new Error( + `Failed to load custom strings from ${filePath}: ${error instanceof Error ? error.message : String(error)}`, + ) + } + } +} + init({ lng: "en", fallbackLng: "en", // language to use if translations in user language are not available. returnNull: false, lowerCaseLng: true, debug: process.env.NODE_ENV === "development", - resources: { - en: { - translation: en, - }, - "zh-cn": { - translation: zhCn, - }, - th: { - translation: th, - }, - ja: { - translation: ja, - }, - ur: { - translation: ur, - }, - }, + resources: defaultResources, }) export default i18next diff --git a/src/node/main.ts b/src/node/main.ts index b3c4e4c14500..c2d3bd57852b 100644 --- a/src/node/main.ts +++ b/src/node/main.ts @@ -1,14 +1,17 @@ import { field, logger } from "@coder/logger" import http from "http" +import * as os from "os" import * as path from "path" import { Disposable } from "../common/emitter" import { plural } from "../common/util" import { createApp, ensureAddress } from "./app" import { AuthType, DefaultedArgs, Feature, toCodeArgs, UserProvidedArgs } from "./cli" import { commit, version, vsRootPath } from "./constants" +import { loadCustomStrings } from "./i18n" import { register } from "./routes" import { VSCodeModule } from "./routes/vscode" import { isDirectory, open } from "./util" +import { wrapper } from "./wrapper" /** * Return true if the user passed an extension-related VS Code flag. @@ -51,7 +54,11 @@ export const runCodeCli = async (args: DefaultedArgs): Promise => { try { // See vscode.loadVSCode for more on this jank. process.env.CODE_SERVER_PARENT_PID = process.pid.toString() - const modPath = path.join(vsRootPath, "out/server-main.js") + let modPath = path.join(vsRootPath, "out/server-main.js") + if (os.platform() === "win32") { + // On Windows, absolute paths of ESM modules must be a valid file URI. + modPath = "file:///" + modPath.replace(/\\/g, "/") + } const mod = (await eval(`import("${modPath}")`)) as VSCodeModule const serverModule = await mod.loadCodeWithNls() await serverModule.spawnCli(await toCodeArgs(args)) @@ -117,6 +124,12 @@ export const runCodeServer = async ( ): Promise<{ dispose: Disposable["dispose"]; server: http.Server }> => { logger.info(`code-server ${version} ${commit}`) + // Load custom strings if provided + if (args.i18n) { + await loadCustomStrings(args.i18n) + logger.info("Loaded custom strings") + } + logger.info(`Using user-data-dir ${args["user-data-dir"]}`) logger.debug(`Using extensions-dir ${args["extensions-dir"]}`) @@ -129,7 +142,7 @@ export const runCodeServer = async ( const app = await createApp(args) const protocol = args.cert ? "https" : "http" const serverAddress = ensureAddress(app.server, protocol) - const disposeRoutes = await register(app, args) + const { disposeRoutes, heart } = await register(app, args) logger.info(`Using config file ${args.config}`) logger.info(`${protocol.toUpperCase()} server listening on ${serverAddress.toString()}`) @@ -139,6 +152,8 @@ export const runCodeServer = async ( logger.info(" - Using password from $PASSWORD") } else if (args.usingEnvHashedPassword) { logger.info(" - Using password from $HASHED_PASSWORD") + } else if (args["hashed-password"]) { + logger.info(` - Using hashed-password from ${args.config}`) } else { logger.info(` - Using password from ${args.config}`) } @@ -152,12 +167,36 @@ export const runCodeServer = async ( logger.info(" - Not serving HTTPS") } + if (args["idle-timeout-seconds"]) { + logger.info(` - Idle timeout set to ${args["idle-timeout-seconds"]} seconds`) + + let idleShutdownTimer: NodeJS.Timeout | undefined + const startIdleShutdownTimer = () => { + idleShutdownTimer = setTimeout(() => { + logger.warn(`Idle timeout of ${args["idle-timeout-seconds"]} seconds exceeded`) + wrapper.exit(0) + }, args["idle-timeout-seconds"]! * 1000) + } + + startIdleShutdownTimer() + + heart.onChange((state) => { + clearTimeout(idleShutdownTimer) + if (state === "expired") { + startIdleShutdownTimer() + } + }) + } + if (args["disable-proxy"]) { logger.info(" - Proxy disabled") } else if (args["proxy-domain"].length > 0) { logger.info(` - ${plural(args["proxy-domain"].length, "Proxying the following domain")}:`) args["proxy-domain"].forEach((domain) => logger.info(` - ${domain}`)) } + if (args["skip-auth-preflight"]) { + logger.info(" - Skipping authentication for preflight requests") + } if (process.env.VSCODE_PROXY_URI) { logger.info(`Using proxy URI in PORTS tab: ${process.env.VSCODE_PROXY_URI}`) } diff --git a/src/node/plugin.ts b/src/node/plugin.ts deleted file mode 100644 index 85c588c9ab7b..000000000000 --- a/src/node/plugin.ts +++ /dev/null @@ -1,302 +0,0 @@ -import { field, Level, Logger } from "@coder/logger" -import * as express from "express" -import * as fs from "fs" -import * as path from "path" -import * as semver from "semver" -import * as pluginapi from "../../typings/pluginapi" -import { HttpCode, HttpError } from "../common/http" -import { version } from "./constants" -import { authenticated, ensureAuthenticated, replaceTemplates } from "./http" -import { proxy } from "./proxy" -import * as util from "./util" -import { Router as WsRouter, WebsocketRouter, wss } from "./wsRouter" -const fsp = fs.promises - -// Represents a required module which could be anything. -type Module = any - -/** - * Inject code-server when `require`d. This is required because the API provides - * more than just types so these need to be provided at run-time. - */ -const originalLoad = require("module")._load -require("module")._load = function (request: string, parent: object, isMain: boolean): Module { - return request === "code-server" ? codeServer : originalLoad.apply(this, [request, parent, isMain]) -} - -/** - * The module you get when importing "code-server". - */ -export const codeServer = { - HttpCode, - HttpError, - Level, - authenticated, - ensureAuthenticated, - express, - field, - proxy, - replaceTemplates, - WsRouter, - wss, -} - -interface Plugin extends pluginapi.Plugin { - /** - * These fields are populated from the plugin's package.json - * and now guaranteed to exist. - */ - name: string - version: string - - /** - * path to the node module on the disk. - */ - modulePath: string -} - -interface Application extends pluginapi.Application { - /* - * Clone of the above without functions. - */ - plugin: Omit -} - -/** - * PluginAPI implements the plugin API described in typings/pluginapi.d.ts - * Please see that file for details. - */ -export class PluginAPI { - private readonly plugins = new Map() - private readonly logger: Logger - - public constructor( - logger: Logger, - /** - * These correspond to $CS_PLUGIN_PATH and $CS_PLUGIN respectively. - */ - private readonly csPlugin = "", - private readonly csPluginPath = `${path.join(util.paths.data, "plugins")}:/usr/share/code-server/plugins`, - private readonly workingDirectory: string | undefined = undefined, - ) { - this.logger = logger.named("pluginapi") - } - - /** - * applications grabs the full list of applications from - * all loaded plugins. - */ - public async applications(): Promise { - const apps = new Array() - for (const [, p] of this.plugins) { - if (!p.applications) { - continue - } - const pluginApps = await p.applications() - - // Add plugin key to each app. - apps.push( - ...pluginApps.map((app) => { - app = { ...app, path: path.join(p.routerPath, app.path || "") } - app = { ...app, iconPath: path.join(app.path || "", app.iconPath) } - return { - ...app, - plugin: { - name: p.name, - version: p.version, - modulePath: p.modulePath, - - displayName: p.displayName, - description: p.description, - routerPath: p.routerPath, - homepageURL: p.homepageURL, - }, - } - }), - ) - } - return apps - } - - /** - * mount mounts all plugin routers onto r and websocket routers onto wr. - */ - public mount(r: express.Router, wr: express.Router): void { - for (const [, p] of this.plugins) { - if (p.router) { - r.use(`${p.routerPath}`, p.router()) - } - if (p.wsRouter) { - wr.use(`${p.routerPath}`, (p.wsRouter() as WebsocketRouter).router) - } - } - } - - /** - * loadPlugins loads all plugins based on this.csPlugin, - * this.csPluginPath and the built in plugins. - */ - public async loadPlugins(loadBuiltin = true): Promise { - for (const dir of this.csPlugin.split(":")) { - if (!dir) { - continue - } - await this.loadPlugin(dir) - } - - for (const dir of this.csPluginPath.split(":")) { - if (!dir) { - continue - } - await this._loadPlugins(dir) - } - - if (loadBuiltin) { - await this._loadPlugins(path.join(__dirname, "../../plugins")) - } - } - - /** - * _loadPlugins is the counterpart to loadPlugins. - * - * It differs in that it loads all plugins in a single - * directory whereas loadPlugins uses all available directories - * as documented. - */ - private async _loadPlugins(dir: string): Promise { - try { - const entries = await fsp.readdir(dir, { withFileTypes: true }) - for (const ent of entries) { - if (!ent.isDirectory()) { - continue - } - await this.loadPlugin(path.join(dir, ent.name)) - } - } catch (error: any) { - if (error.code !== "ENOENT") { - this.logger.warn(`failed to load plugins from ${q(dir)}: ${error.message}`) - } - } - } - - private async loadPlugin(dir: string): Promise { - try { - const str = await fsp.readFile(path.join(dir, "package.json"), { - encoding: "utf8", - }) - const packageJSON: PackageJSON = JSON.parse(str) - for (const [, p] of this.plugins) { - if (p.name === packageJSON.name) { - this.logger.warn( - `ignoring duplicate plugin ${q(p.name)} at ${q(dir)}, using previously loaded ${q(p.modulePath)}`, - ) - return - } - } - const p = this._loadPlugin(dir, packageJSON) - this.plugins.set(p.name, p) - } catch (error: any) { - if (error.code !== "ENOENT") { - this.logger.warn(`failed to load plugin: ${error.stack}`) - } - } - } - - /** - * _loadPlugin is the counterpart to loadPlugin and actually - * loads the plugin now that we know there is no duplicate - * and that the package.json has been read. - */ - private _loadPlugin(dir: string, packageJSON: PackageJSON): Plugin { - dir = path.resolve(dir) - - const logger = this.logger.named(packageJSON.name) - logger.debug("loading plugin", field("plugin_dir", dir), field("package_json", packageJSON)) - - if (!packageJSON.name) { - throw new Error("plugin package.json missing name") - } - if (!packageJSON.version) { - throw new Error("plugin package.json missing version") - } - if (!packageJSON.engines || !packageJSON.engines["code-server"]) { - throw new Error(`plugin package.json missing code-server range like: - "engines": { - "code-server": "^3.7.0" - } -`) - } - if (!semver.satisfies(version, packageJSON.engines["code-server"])) { - this.logger.warn( - `plugin range ${q(packageJSON.engines["code-server"])} incompatible` + ` with code-server version ${version}`, - ) - } - - const pluginModule = require(dir) - if (!pluginModule.plugin) { - throw new Error("plugin module does not export a plugin") - } - - const p = { - name: packageJSON.name, - version: packageJSON.version, - modulePath: dir, - ...pluginModule.plugin, - } as Plugin - - if (!p.displayName) { - throw new Error("plugin missing displayName") - } - if (!p.description) { - throw new Error("plugin missing description") - } - if (!p.routerPath) { - throw new Error("plugin missing router path") - } - if (!p.routerPath.startsWith("/")) { - throw new Error(`plugin router path ${q(p.routerPath)}: invalid`) - } - if (!p.homepageURL) { - throw new Error("plugin missing homepage") - } - - p.init({ - logger: logger, - workingDirectory: this.workingDirectory, - }) - - logger.debug("loaded") - - return p - } - - public async dispose(): Promise { - await Promise.all( - Array.from(this.plugins.values()).map(async (p) => { - if (!p.deinit) { - return - } - try { - await p.deinit() - } catch (error: any) { - this.logger.error("plugin failed to deinit", field("name", p.name), field("error", error.message)) - } - }), - ) - } -} - -interface PackageJSON { - name: string - version: string - engines: { - "code-server": string - } -} - -function q(s: string | undefined): string { - if (s === undefined) { - s = "undefined" - } - return JSON.stringify(s) -} diff --git a/src/node/routes/apps.ts b/src/node/routes/apps.ts deleted file mode 100644 index 5c8541fc9ad4..000000000000 --- a/src/node/routes/apps.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as express from "express" -import { PluginAPI } from "../plugin" - -/** - * Implements the /api/applications endpoint - * - * See typings/pluginapi.d.ts for details. - */ -export function router(papi: PluginAPI): express.Router { - const router = express.Router() - - router.get("/", async (req, res) => { - res.json(await papi.applications()) - }) - - return router -} diff --git a/src/node/routes/domainProxy.ts b/src/node/routes/domainProxy.ts index 0a9bb4a324f7..6ffee67fa002 100644 --- a/src/node/routes/domainProxy.ts +++ b/src/node/routes/domainProxy.ts @@ -61,6 +61,11 @@ router.all(/.*/, async (req, res, next) => { ensureProxyEnabled(req) + if (req.method === "OPTIONS" && req.args["skip-auth-preflight"]) { + // Allow preflight requests with `skip-auth-preflight` flag + return next() + } + // Must be authenticated to use the proxy. const isAuthenticated = await authenticated(req) if (!isAuthenticated) { diff --git a/src/node/routes/errors.ts b/src/node/routes/errors.ts index 743699c847ef..da0ee8d7c07f 100644 --- a/src/node/routes/errors.ts +++ b/src/node/routes/errors.ts @@ -2,11 +2,11 @@ import { logger } from "@coder/logger" import express from "express" import { promises as fs } from "fs" import path from "path" -import { WebsocketRequest } from "../../../typings/pluginapi" import { HttpCode } from "../../common/http" import { rootPath } from "../constants" import { replaceTemplates } from "../http" import { escapeHtml, getMediaMime } from "../util" +import type { WebsocketRequest } from "../wsRouter" interface ErrorWithStatusCode { statusCode: number diff --git a/src/node/routes/index.ts b/src/node/routes/index.ts index e61cbd65795c..78e30566867d 100644 --- a/src/node/routes/index.ts +++ b/src/node/routes/index.ts @@ -4,20 +4,18 @@ import * as express from "express" import { promises as fs } from "fs" import * as path from "path" import * as tls from "tls" -import * as pluginapi from "../../../typings/pluginapi" import { Disposable } from "../../common/emitter" -import { HttpCode, HttpError } from "../../common/http" +import { getCookieSessionName, HttpCode, HttpError } from "../../common/http" import { plural } from "../../common/util" import { App } from "../app" import { AuthType, DefaultedArgs } from "../cli" import { commit, rootPath } from "../constants" import { Heart } from "../heart" -import { ensureAuthenticated, redirect } from "../http" -import { PluginAPI } from "../plugin" +import { redirect } from "../http" import { CoderSettings, SettingsProvider } from "../settings" import { UpdateProvider } from "../update" import { getMediaMime, paths } from "../util" -import * as apps from "./apps" +import type { WebsocketRequest } from "../wsRouter" import * as domainProxy from "./domainProxy" import { errorHandler, wsErrorHandler } from "./errors" import * as health from "./health" @@ -30,7 +28,10 @@ import * as vscode from "./vscode" /** * Register all routes and middleware. */ -export const register = async (app: App, args: DefaultedArgs): Promise => { +export const register = async ( + app: App, + args: DefaultedArgs, +): Promise<{ disposeRoutes: Disposable["dispose"]; heart: Heart }> => { const heart = new Heart(path.join(paths.data, "heartbeat"), async () => { return new Promise((resolve, reject) => { // getConnections appears to not call the callback when there are no more @@ -60,6 +61,8 @@ export const register = async (app: App, args: DefaultedArgs): Promise(path.join(args["user-data-dir"], "coder.json")) const updater = new UpdateProvider("https://api.github.com/repos/coder/code-server/releases/latest", settings) + const cookieSessionName = getCookieSessionName(args["cookie-suffix"]) + const common: express.RequestHandler = (req, _, next) => { // /healthz|/healthz/ needs to be excluded otherwise health checks will make // it look like code-server is always in use. @@ -74,6 +77,7 @@ export const register = async (app: App, args: DefaultedArgs): Promise { + app.router.use(/.*/, async (req, res, next) => { // If we're handling TLS ensure all requests are redirected to HTTPS. // TODO: This does *NOT* work if you have a base path since to specify the // protocol we need to specify the whole path. if (args.cert && !(req.connection as tls.TLSSocket).encrypted) { return res.redirect(`https://${req.headers.host}${req.originalUrl}`) } + next() + }) - // Return security.txt. - if (req.originalUrl === "/security.txt" || req.originalUrl === "/.well-known/security.txt") { - const resourcePath = path.resolve(rootPath, "src/browser/security.txt") - res.set("Content-Type", getMediaMime(resourcePath)) - return res.send(await fs.readFile(resourcePath)) - } - - // Return robots.txt. - if (req.originalUrl === "/robots.txt") { - const resourcePath = path.resolve(rootPath, "src/browser/robots.txt") - res.set("Content-Type", getMediaMime(resourcePath)) - return res.send(await fs.readFile(resourcePath)) - } + app.router.get(["/security.txt", "/.well-known/security.txt"], async (_, res) => { + const resourcePath = path.resolve(rootPath, "src/browser/security.txt") + res.set("Content-Type", getMediaMime(resourcePath)) + res.send(await fs.readFile(resourcePath)) + }) - next() + app.router.get("/robots.txt", async (_, res) => { + const resourcePath = path.resolve(rootPath, "src/browser/robots.txt") + res.set("Content-Type", getMediaMime(resourcePath)) + res.send(await fs.readFile(resourcePath)) }) app.router.use("/", domainProxy.router) app.wsRouter.use("/", domainProxy.wsRouter.router) - app.router.all("/proxy/:port/:path(.*)?", async (req, res) => { + app.router.all("/proxy/:port{/*path}", async (req, res) => { await pathProxy.proxy(req, res) }) - app.wsRouter.get("/proxy/:port/:path(.*)?", async (req) => { - await pathProxy.wsProxy(req as pluginapi.WebsocketRequest) + app.wsRouter.get("/proxy/:port{/*path}", async (req) => { + await pathProxy.wsProxy(req as unknown as WebsocketRequest) }) // These two routes pass through the path directly. // So the proxied app must be aware it is running // under /absproxy// - app.router.all("/absproxy/:port/:path(.*)?", async (req, res) => { + app.router.all("/absproxy/:port{/*path}", async (req, res) => { await pathProxy.proxy(req, res, { passthroughPath: true, proxyBasePath: args["abs-proxy-base-path"], }) }) - app.wsRouter.get("/absproxy/:port/:path(.*)?", async (req) => { - await pathProxy.wsProxy(req as pluginapi.WebsocketRequest, { + app.wsRouter.get("/absproxy/:port{/*path}", async (req) => { + await pathProxy.wsProxy(req as unknown as WebsocketRequest, { passthroughPath: true, proxyBasePath: args["abs-proxy-base-path"], }) }) - let pluginApi: PluginAPI - if (!process.env.CS_DISABLE_PLUGINS) { - const workingDir = args._ && args._.length > 0 ? path.resolve(args._[args._.length - 1]) : undefined - pluginApi = new PluginAPI(logger, process.env.CS_PLUGIN, process.env.CS_PLUGIN_PATH, workingDir) - await pluginApi.loadPlugins() - pluginApi.mount(app.router, app.wsRouter) - app.router.use("/api/applications", ensureAuthenticated, apps.router(pluginApi)) - } - app.router.use(express.json()) app.router.use(express.urlencoded({ extended: true })) @@ -172,7 +164,9 @@ export const register = async (app: App, args: DefaultedArgs): Promise { - heart.dispose() - pluginApi?.dispose() - vscode.dispose() + return { + disposeRoutes: () => { + heart.dispose() + vscode.dispose() + }, + heart, } } diff --git a/src/node/routes/login.ts b/src/node/routes/login.ts index 29d51a59d13b..db4d1c45dbb3 100644 --- a/src/node/routes/login.ts +++ b/src/node/routes/login.ts @@ -2,7 +2,6 @@ import { Router, Request } from "express" import { promises as fs } from "fs" import { RateLimiter as Limiter } from "limiter" import * as path from "path" -import { CookieKeys } from "../../common/http" import { rootPath } from "../constants" import { authenticated, getCookieOptions, redirect, replaceTemplates } from "../http" import i18n from "../i18n" @@ -32,6 +31,8 @@ const getRoot = async (req: Request, error?: Error): Promise => { i18n.changeLanguage(locale) const appName = req.args["app-name"] || "code-server" const welcomeText = req.args["welcome-text"] || (i18n.t("WELCOME", { app: appName }) as string) + + // Determine password message using i18n let passwordMsg = i18n.t("LOGIN_PASSWORD", { configFile: req.args.config }) if (req.args.usingEnvPassword) { passwordMsg = i18n.t("LOGIN_USING_ENV_PASSWORD") @@ -93,7 +94,7 @@ router.post<{}, string, { password?: string; base?: string } | undefined, { to?: if (isPasswordValid) { // The hash does not add any actual security but we do it for // obfuscation purposes (and as a side effect it handles escaping). - res.cookie(CookieKeys.Session, hashedPassword, getCookieOptions(req)) + res.cookie(req.cookieSessionName, hashedPassword, getCookieOptions(req)) const to = (typeof req.query.to === "string" && req.query.to) || "/" return redirect(req, res, to, { to: undefined }) diff --git a/src/node/routes/logout.ts b/src/node/routes/logout.ts index 63d8accbcef9..425b3ce17c8f 100644 --- a/src/node/routes/logout.ts +++ b/src/node/routes/logout.ts @@ -1,5 +1,4 @@ import { Router } from "express" -import { CookieKeys } from "../../common/http" import { getCookieOptions, redirect } from "../http" import { sanitizeString } from "../util" @@ -7,7 +6,7 @@ export const router = Router() router.get<{}, undefined, undefined, { base?: string; to?: string }>("/", async (req, res) => { // Must use the *identical* properties used to set the cookie. - res.clearCookie(CookieKeys.Session, getCookieOptions(req)) + res.clearCookie(req.cookieSessionName, getCookieOptions(req)) const to = sanitizeString(req.query.to) || "/" return redirect(req, res, to, { to: undefined, base: undefined, href: undefined }) diff --git a/src/node/routes/pathProxy.ts b/src/node/routes/pathProxy.ts index ccfb0cc824a0..7d4f286f6519 100644 --- a/src/node/routes/pathProxy.ts +++ b/src/node/routes/pathProxy.ts @@ -1,9 +1,9 @@ import { Request, Response } from "express" import * as path from "path" -import * as pluginapi from "../../../typings/pluginapi" import { HttpCode, HttpError } from "../../common/http" import { ensureProxyEnabled, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http" import { proxy as _proxy } from "../proxy" +import type { WebsocketRequest } from "../wsRouter" const getProxyTarget = ( req: Request, @@ -13,7 +13,11 @@ const getProxyTarget = ( ): string => { // If there is a base path, strip it out. const base = (req as any).base || "" - return `http://0.0.0.0:${req.params.port}${opts?.proxyBasePath || ""}/${req.originalUrl.slice(base.length)}` + const port = parseInt(req.params.port, 10) + if (isNaN(port)) { + throw new HttpError("Invalid port", HttpCode.BadRequest) + } + return `http://0.0.0.0:${port}${opts?.proxyBasePath || ""}/${req.originalUrl.slice(base.length)}` } export async function proxy( @@ -26,7 +30,9 @@ export async function proxy( ): Promise { ensureProxyEnabled(req) - if (!(await authenticated(req))) { + if (req.method === "OPTIONS" && req.args["skip-auth-preflight"]) { + // Allow preflight requests with `skip-auth-preflight` flag + } else if (!(await authenticated(req))) { // If visiting the root (/:port only) redirect to the login page. if (!req.params.path || req.params.path === "/") { const to = self(req) @@ -49,7 +55,7 @@ export async function proxy( } export async function wsProxy( - req: pluginapi.WebsocketRequest, + req: WebsocketRequest, opts?: { passthroughPath?: boolean proxyBasePath?: string diff --git a/src/node/routes/vscode.ts b/src/node/routes/vscode.ts index 7e8f0f3ff4e5..33d1287a7892 100644 --- a/src/node/routes/vscode.ts +++ b/src/node/routes/vscode.ts @@ -4,15 +4,15 @@ import * as express from "express" import { promises as fs } from "fs" import * as http from "http" import * as net from "net" +import * as os from "os" import * as path from "path" -import { WebsocketRequest } from "../../../typings/pluginapi" import { logError } from "../../common/util" import { CodeArgs, toCodeArgs } from "../cli" import { isDevMode, vsRootPath } from "../constants" import { authenticated, ensureAuthenticated, ensureOrigin, redirect, replaceTemplates, self } from "../http" import { SocketProxyProvider } from "../socket" import { isFile } from "../util" -import { Router as WsRouter } from "../wsRouter" +import { type WebsocketRequest, Router as WsRouter } from "../wsRouter" export const router = express.Router() @@ -58,7 +58,11 @@ async function loadVSCode(req: express.Request): Promise { // which will also require that we switch to ESM, since a hybrid approach // breaks importing `rotating-file-stream` for some reason. To work around // this, use `eval` for now, but we should consider switching to ESM. - const modPath = path.join(vsRootPath, "out/server-main.js") + let modPath = path.join(vsRootPath, "out/server-main.js") + if (os.platform() === "win32") { + // On Windows, absolute paths of ESM modules must be a valid file URI. + modPath = "file:///" + modPath.replace(/\\/g, "/") + } const mod = (await eval(`import("${modPath}")`)) as VSCodeModule const serverModule = await mod.loadCodeWithNls() return serverModule.createServer(null, { @@ -171,7 +175,7 @@ router.get("/manifest.json", async (req, res) => { const appName = req.args["app-name"] || "code-server" res.writeHead(200, { "Content-Type": "application/manifest+json" }) - return res.end( + res.end( replaceTemplates( req, JSON.stringify( @@ -182,11 +186,22 @@ router.get("/manifest.json", async (req, res) => { display: "fullscreen", display_override: ["window-controls-overlay"], description: "Run Code on a remote server.", - icons: [192, 512].map((size) => ({ - src: `{{BASE}}/_static/src/browser/media/pwa-icon-${size}.png`, - type: "image/png", - sizes: `${size}x${size}`, - })), + icons: [192, 512] + .map((size) => [ + { + src: `{{BASE}}/_static/src/browser/media/pwa-icon-${size}.png`, + type: "image/png", + sizes: `${size}x${size}`, + purpose: "any", + }, + { + src: `{{BASE}}/_static/src/browser/media/pwa-icon-maskable-${size}.png`, + type: "image/png", + sizes: `${size}x${size}`, + purpose: "maskable", + }, + ]) + .flat(), }, null, 2, diff --git a/src/node/settings.ts b/src/node/settings.ts index 709ce950cb89..cc55dd5f9508 100644 --- a/src/node/settings.ts +++ b/src/node/settings.ts @@ -1,5 +1,5 @@ import { logger } from "@coder/logger" -import { Query } from "express-serve-static-core" +import type { ParsedQs } from "qs" import { promises as fs } from "fs" export type Settings = { [key: string]: Settings | string | boolean | number } @@ -17,7 +17,7 @@ export class SettingsProvider { public async read(): Promise { try { const raw = (await fs.readFile(this.settingsPath, "utf8")).trim() - return raw ? JSON.parse(raw) : {} + return raw ? JSON.parse(raw) : ({} as T) } catch (error: any) { if (error.code !== "ENOENT") { logger.warn(error.message) @@ -52,5 +52,5 @@ export interface UpdateSettings { * Global code-server settings. */ export interface CoderSettings extends UpdateSettings { - query?: Query + query?: ParsedQs } diff --git a/src/node/vscodeSocket.ts b/src/node/vscodeSocket.ts index 4af30ae91a4f..e01d1a15012b 100644 --- a/src/node/vscodeSocket.ts +++ b/src/node/vscodeSocket.ts @@ -2,8 +2,9 @@ import { logger } from "@coder/logger" import express from "express" import * as http from "http" import * as path from "path" -import { HttpCode } from "../common/http" +import { HttpCode, HttpError } from "../common/http" import { listen } from "./app" +import { errorHandler } from "./routes/errors" import { canConnect } from "./util" export interface EditorSessionEntry { @@ -44,24 +45,18 @@ export async function makeEditorSessionManagerServer( async (req, res) => { const filePath = req.query.filePath if (!filePath) { - res.status(HttpCode.BadRequest).send("filePath is required") - return - } - try { - const socketPath = await editorSessionManager.getConnectedSocketPath(filePath) - const response: GetSessionResponse = { socketPath } - res.json(response) - } catch (error: unknown) { - res.status(HttpCode.ServerError).send(error) + throw new HttpError("filePath is required", HttpCode.BadRequest) } + const socketPath = await editorSessionManager.getConnectedSocketPath(filePath) + const response: GetSessionResponse = { socketPath } + res.json(response) }, ) router.post<{}, string, AddSessionRequest | undefined>("/add-session", async (req, res) => { const entry = req.body?.entry if (!entry) { - res.status(400).send("entry is required") - return + throw new HttpError("entry is required", HttpCode.BadRequest) } editorSessionManager.addSession(entry) res.status(200).send("session added") @@ -70,13 +65,14 @@ export async function makeEditorSessionManagerServer( router.post<{}, string, DeleteSessionRequest | undefined>("/delete-session", async (req, res) => { const socketPath = req.body?.socketPath if (!socketPath) { - res.status(400).send("socketPath is required") - return + throw new HttpError("socketPath is required", HttpCode.BadRequest) } editorSessionManager.deleteSession(socketPath) res.status(200).send("session deleted") }) + router.use(errorHandler) + const server = http.createServer(router) try { await listen(server, { socket: codeServerSocketPath }) diff --git a/src/node/wsRouter.ts b/src/node/wsRouter.ts index 3f15ba9928bc..0f901bb8ad7f 100644 --- a/src/node/wsRouter.ts +++ b/src/node/wsRouter.ts @@ -1,8 +1,17 @@ import * as express from "express" import * as expressCore from "express-serve-static-core" import * as http from "http" +import * as stream from "stream" import Websocket from "ws" -import * as pluginapi from "../../typings/pluginapi" + +export interface WebsocketRequest extends express.Request { + ws: stream.Duplex + head: Buffer +} + +interface InternalWebsocketRequest extends WebsocketRequest { + _ws_handled: boolean +} export const handleUpgrade = (app: express.Express, server: http.Server): void => { server.on("upgrade", (req, socket, head) => { @@ -22,9 +31,11 @@ export const handleUpgrade = (app: express.Express, server: http.Server): void = }) } -interface InternalWebsocketRequest extends pluginapi.WebsocketRequest { - _ws_handled: boolean -} +export type WebSocketHandler = ( + req: WebsocketRequest, + res: express.Response, + next: express.NextFunction, +) => void | Promise export class WebsocketRouter { public readonly router = express.Router() @@ -36,13 +47,13 @@ export class WebsocketRouter { * If the origin header exists it must match the host or the connection will * be prevented. */ - public ws(route: expressCore.PathParams, ...handlers: pluginapi.WebSocketHandler[]): void { + public ws(route: expressCore.PathParams, ...handlers: WebSocketHandler[]): void { this.router.get( route, ...handlers.map((handler) => { const wrapped: express.Handler = (req, res, next) => { ;(req as InternalWebsocketRequest)._ws_handled = true - return handler(req as pluginapi.WebsocketRequest, res, next) + return handler(req as WebsocketRequest, res, next) } return wrapped }), diff --git a/test/e2e/routes.test.ts b/test/e2e/routes.test.ts index 2ac87d0026a9..39c19697e9b9 100644 --- a/test/e2e/routes.test.ts +++ b/test/e2e/routes.test.ts @@ -1,7 +1,26 @@ import { clean, getMaybeProxiedPathname } from "../utils/helpers" import { describe, test, expect } from "./baseFixture" -const routes = ["/", "/vscode", "/vscode/"] +const routes = { + "/": [ + /\.\/manifest.json/, + /\.\/_static\//, + /[a-z]+-[0-9a-z]+\/static\//, + /http:\/\/localhost:[0-9]+\/[a-z]+-[0-9a-z]+\/static\//, + ], + "/vscode": [ + /\.\/vscode\/manifest.json/, + /\.\/_static\//, + /vscode\/[a-z]+-[0-9a-z]+\/static\//, + /http:\/\/localhost:[0-9]+\/vscode\/[a-z]+-[0-9a-z]+\/static\//, + ], + "/vscode/": [ + /\.\/manifest.json/, + /\.\/\.\.\/_static\//, + /[a-z]+-[0-9a-z]+\/static\//, + /http:\/\/localhost:[0-9]+\/vscode\/[a-z]+-[0-9a-z]+\/static\//, + ], +} describe("VS Code Routes", ["--disable-workspace-trust"], {}, async () => { const testName = "vscode-routes-default" @@ -10,7 +29,7 @@ describe("VS Code Routes", ["--disable-workspace-trust"], {}, async () => { }) test("should load all route variations", async ({ codeServerPage }) => { - for (const route of routes) { + for (const [route, matchers] of Object.entries(routes)) { await codeServerPage.navigate(route) // Check there were no redirections @@ -18,21 +37,16 @@ describe("VS Code Routes", ["--disable-workspace-trust"], {}, async () => { const pathname = getMaybeProxiedPathname(url) expect(pathname).toBe(route) - // TODO@jsjoeio - // now that we are in a proper browser instead of scraping the HTML we - // could possibly intercept requests to make sure assets are loading from - // the right spot. - // - // Check that page loaded from correct route - const html = await codeServerPage.page.innerHTML("html") - switch (route) { - case "/": - case "/vscode/": - expect(html).toMatch(/src="\.\/[a-z]+-[0-9a-z]+\/static\//) - break - case "/vscode": - expect(html).toMatch(/src="\.\/vscode\/[a-z]+-[0-9a-z]+\/static\//) - break + // Check that assets are pointing to the right spot. Some will be + // relative, without a leading dot (VS Code's assets). Some will be + // relative with a leading dot (our assets). Others will have been + // resolved against the origin. + const elements = await codeServerPage.page.locator("[src]").all() + for (const element of elements) { + const src = await element.getAttribute("src") + if (src && !matchers.some((m) => m.test(src))) { + throw new Error(`${src} did not match any validators for route ${route}`) + } } } }) @@ -85,7 +99,7 @@ describe("VS Code Routes with no workspace or folder", ["--disable-workspace-tru // If you visit again without query parameters it will re-attach them by // redirecting. It should always redirect to the same route. - for (const route of routes) { + for (const route of Object.keys(routes)) { await codeServerPage.navigate(route) const url = new URL(codeServerPage.page.url()) const pathname = getMaybeProxiedPathname(url) diff --git a/test/package-lock.json b/test/package-lock.json index 70d29a4fdb30..4e28618543cd 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -7,11 +7,10 @@ "license": "MIT", "devDependencies": { "@jest-mock/express": "^1.4.5", - "@playwright/test": "^1.46.0", + "@playwright/test": "^1.56.1", "@types/jest": "^27.0.2", "@types/jsdom": "^16.2.13", "@types/node-fetch": "^2.5.8", - "@types/supertest": "^2.0.11", "@types/wtfnode": "^0.7.0", "argon2": "^0.28.0", "extract-zip": "^2.0.1", @@ -19,8 +18,7 @@ "jest-fetch-mock": "^3.0.3", "jsdom": "^16.4.0", "node-fetch": "^2.6.7", - "playwright": "^1.46.0", - "supertest": "^6.1.6", + "playwright": "^1.56.1", "ts-jest": "^27.0.7", "wtfnode": "^0.9.1" } @@ -1014,13 +1012,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.2.tgz", - "integrity": "sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==", + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz", + "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.47.2" + "playwright": "1.56.1" }, "bin": { "playwright": "cli.js" @@ -1104,13 +1102,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/cookiejar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", - "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -1171,13 +1162,6 @@ "@types/tough-cookie": "*" } }, - "node_modules/@types/methods": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", - "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/node": { "version": "22.7.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz", @@ -1220,29 +1204,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/superagent": { - "version": "8.1.9", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", - "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cookiejar": "^2.1.5", - "@types/methods": "^1.1.4", - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/supertest": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.16.tgz", - "integrity": "sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/superagent": "*" - } - }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -1464,13 +1425,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "license": "MIT" - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1689,24 +1643,18 @@ "dev": true, "license": "MIT" }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, "node_modules/callsites": { @@ -1883,16 +1831,6 @@ "node": ">= 0.8" } }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1914,13 +1852,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true, - "license": "MIT" - }, "node_modules/cross-fetch": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", @@ -2030,24 +1961,6 @@ "node": ">=0.10.0" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2085,17 +1998,6 @@ "node": ">=8" } }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, "node_modules/diff-sequences": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", @@ -2130,6 +2032,21 @@ "node": ">=8" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.32", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.32.tgz", @@ -2178,14 +2095,11 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -2200,6 +2114,35 @@ "node": ">= 0.4" } }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -2366,13 +2309,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true, - "license": "MIT" - }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -2421,36 +2357,22 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, - "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -2559,17 +2481,22 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -2588,6 +2515,20 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -2637,13 +2578,13 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2666,23 +2607,10 @@ "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", "engines": { @@ -2692,12 +2620,15 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, "engines": { "node": ">= 0.4" }, @@ -2725,16 +2656,6 @@ "node": ">= 0.4" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/html-encoding-sniffer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", @@ -3764,15 +3685,17 @@ } }, "node_modules/jsdom/node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -3918,6 +3841,16 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -3925,16 +3858,6 @@ "dev": true, "license": "MIT" }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -3949,19 +3872,6 @@ "node": ">=8.6" } }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -4216,19 +4126,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4408,13 +4305,13 @@ } }, "node_modules/playwright": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", - "integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz", + "integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.47.2" + "playwright-core": "1.56.1" }, "bin": { "playwright": "cli.js" @@ -4427,9 +4324,9 @@ } }, "node_modules/playwright-core": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", - "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz", + "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4531,22 +4428,6 @@ "node": ">=6" } }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -4722,24 +4603,6 @@ "dev": true, "license": "ISC" }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -4763,25 +4626,6 @@ "node": ">=8" } }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -4932,43 +4776,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/superagent": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", - "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", - "deprecated": "Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net", - "dev": true, - "license": "MIT", - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=6.4.0 <13 || >=14" - } - }, - "node_modules/supertest": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.4.tgz", - "integrity": "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "methods": "^1.1.2", - "superagent": "^8.1.2" - }, - "engines": { - "node": ">=6.4.0" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", diff --git a/test/package.json b/test/package.json index 06d0e3fb3e74..c8211337a79c 100644 --- a/test/package.json +++ b/test/package.json @@ -3,11 +3,10 @@ "#": "We must put jest in a sub-directory otherwise VS Code somehow picks up the types and generates conflicts with mocha.", "devDependencies": { "@jest-mock/express": "^1.4.5", - "@playwright/test": "^1.46.0", + "@playwright/test": "^1.56.1", "@types/jest": "^27.0.2", "@types/jsdom": "^16.2.13", "@types/node-fetch": "^2.5.8", - "@types/supertest": "^2.0.11", "@types/wtfnode": "^0.7.0", "argon2": "^0.28.0", "extract-zip": "^2.0.1", @@ -15,8 +14,7 @@ "jest-fetch-mock": "^3.0.3", "jsdom": "^16.4.0", "node-fetch": "^2.6.7", - "playwright": "^1.46.0", - "supertest": "^6.1.6", + "playwright": "^1.56.1", "ts-jest": "^27.0.7", "wtfnode": "^0.9.1" }, diff --git a/test/tsconfig.json b/test/tsconfig.json index 3e568e887dd6..5197ce2769f4 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,5 +1,4 @@ { "extends": "../tsconfig.json", - "include": ["./**/*.ts"], - "exclude": ["./unit/node/test-plugin"] + "include": ["./**/*.ts"] } diff --git a/test/unit/node/cli.test.ts b/test/unit/node/cli.test.ts index e596549da100..668a3c55776c 100644 --- a/test/unit/node/cli.test.ts +++ b/test/unit/node/cli.test.ts @@ -6,6 +6,7 @@ import { bindAddrFromArgs, defaultConfigFile, parse, + parseConfigFile, setDefaults, shouldOpenInExistingInstance, toCodeArgs, @@ -74,6 +75,7 @@ describe("parser", () => { "--verbose", ["--app-name", "custom instance name"], ["--welcome-text", "welcome to code"], + ["--i18n", "path/to/custom-strings.json"], "2", ["--locale", "ja"], @@ -108,6 +110,8 @@ describe("parser", () => { ["--abs-proxy-base-path", "/codeserver/app1"], + "--skip-auth-preflight", + ["--session-socket", "/tmp/override-code-server-ipc-socket"], ["--host", "0.0.0.0"], @@ -142,10 +146,12 @@ describe("parser", () => { verbose: true, "app-name": "custom instance name", "welcome-text": "welcome to code", + i18n: path.resolve("path/to/custom-strings.json"), version: true, "bind-addr": "192.169.0.1:8080", "session-socket": "/tmp/override-code-server-ipc-socket", "abs-proxy-base-path": "/codeserver/app1", + "skip-auth-preflight": true, }) }) @@ -284,12 +290,17 @@ describe("parser", () => { }) it("should support repeatable flags", async () => { + expect(() => parse(["--proxy-domain", ""])).toThrowError(/--proxy-domain requires a value/) expect(parse(["--proxy-domain", "*.coder.com"])).toEqual({ "proxy-domain": ["*.coder.com"], }) expect(parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "test.com"])).toEqual({ "proxy-domain": ["*.coder.com", "test.com"], }) + // Commas are literal, at the moment. + expect(parse(["--proxy-domain", "*.coder.com,test.com"])).toEqual({ + "proxy-domain": ["*.coder.com,test.com"], + }) }) it("should enforce cert-key with cert value or otherwise generate one", async () => { @@ -338,6 +349,28 @@ describe("parser", () => { }) }) + it("should parse i18n flag with file path", async () => { + // Test with file path (no validation at CLI parsing level) + const args = parse(["--i18n", "/path/to/custom-strings.json"]) + expect(args).toEqual({ + i18n: "/path/to/custom-strings.json", + }) + }) + + it("should parse i18n flag with relative file path", async () => { + // Test with relative file path + expect(() => parse(["--i18n", "./custom-strings.json"])).not.toThrow() + expect(() => parse(["--i18n", "strings.json"])).not.toThrow() + }) + + it("should support app-name and deprecated welcome-text flags", async () => { + const args = parse(["--app-name", "My App", "--welcome-text", "Welcome!"]) + expect(args).toEqual({ + "app-name": "My App", + "welcome-text": "Welcome!", + }) + }) + it("should use env var github token", async () => { process.env.GITHUB_TOKEN = "ga-foo" const args = parse([]) @@ -487,6 +520,20 @@ describe("parser", () => { }), ).toThrowError(expectedErrMsg) }) + it("should fail to parse invalid config", () => { + expect(() => parseConfigFile("test", "/fake-config-path")).toThrowError("invalid config: test") + }) + it("should parse repeatable options", () => { + const configContents = ` + install-extension: + - extension.number1 + - extension.number2 + ` + expect(parseConfigFile(configContents, "/fake-config-path")).toEqual({ + config: "/fake-config-path", + "install-extension": ["extension.number1", "extension.number2"], + }) + }) it("should ignore optional strings set to false", async () => { expect(parse(["--cert=false"])).toEqual({}) }) diff --git a/test/unit/node/heart.test.ts b/test/unit/node/heart.test.ts index 7aa6f08dc2bf..7ad0d21752f2 100644 --- a/test/unit/node/heart.test.ts +++ b/test/unit/node/heart.test.ts @@ -1,6 +1,6 @@ import { logger } from "@coder/logger" import { readFile, writeFile, stat, utimes } from "fs/promises" -import { Heart, heartbeatTimer } from "../../../src/node/heart" +import { Heart } from "../../../src/node/heart" import { clean, mockLogger, tmpdir } from "../../utils/helpers" const mockIsActive = (resolveTo: boolean) => jest.fn().mockResolvedValue(resolveTo) @@ -82,31 +82,81 @@ describe("Heart", () => { }) describe("heartbeatTimer", () => { - beforeAll(() => { + const testName = "heartbeatTimer" + let testDir = "" + beforeAll(async () => { + await clean(testName) + testDir = await tmpdir(testName) mockLogger() }) afterAll(() => { jest.restoreAllMocks() }) + beforeEach(() => { + jest.useFakeTimers() + }) afterEach(() => { jest.resetAllMocks() + jest.clearAllTimers() + jest.useRealTimers() }) - it("should call beat when isActive resolves to true", async () => { + it("should call isActive when timeout expires", async () => { const isActive = true const mockIsActive = jest.fn().mockResolvedValue(isActive) - const mockBeatFn = jest.fn() - await heartbeatTimer(mockIsActive, mockBeatFn) + const heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive) + await heart.beat() + jest.advanceTimersByTime(60 * 1000) expect(mockIsActive).toHaveBeenCalled() - expect(mockBeatFn).toHaveBeenCalled() }) it("should log a warning when isActive rejects", async () => { const errorMsg = "oh no" const error = new Error(errorMsg) const mockIsActive = jest.fn().mockRejectedValue(error) - const mockBeatFn = jest.fn() - await heartbeatTimer(mockIsActive, mockBeatFn) + const heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive) + await heart.beat() + jest.advanceTimersByTime(60 * 1000) + expect(mockIsActive).toHaveBeenCalled() - expect(mockBeatFn).not.toHaveBeenCalled() expect(logger.warn).toHaveBeenCalledWith(errorMsg) }) }) + +describe("stateChange", () => { + const testName = "stateChange" + let testDir = "" + let heart: Heart + beforeAll(async () => { + await clean(testName) + testDir = await tmpdir(testName) + mockLogger() + }) + afterAll(() => { + jest.restoreAllMocks() + }) + afterEach(() => { + jest.resetAllMocks() + if (heart) { + heart.dispose() + } + }) + it("should change to alive after a beat", async () => { + heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive(true)) + const mockOnChange = jest.fn() + heart.onChange(mockOnChange) + await heart.beat() + + expect(mockOnChange.mock.calls[0][0]).toBe("alive") + }) + it.only("should change to expired when not active", async () => { + jest.useFakeTimers() + heart = new Heart(`${testDir}/shutdown.txt`, () => new Promise((resolve) => resolve(false))) + const mockOnChange = jest.fn() + heart.onChange(mockOnChange) + await heart.beat() + + await jest.advanceTimersByTime(60 * 1000) + expect(mockOnChange.mock.calls[1][0]).toBe("expired") + jest.clearAllTimers() + jest.useRealTimers() + }) +}) diff --git a/test/unit/node/i18n.test.ts b/test/unit/node/i18n.test.ts new file mode 100644 index 000000000000..90b10d04822c --- /dev/null +++ b/test/unit/node/i18n.test.ts @@ -0,0 +1,154 @@ +import { promises as fs } from "fs" +import * as os from "os" +import * as path from "path" +import { loadCustomStrings } from "../../../src/node/i18n" + +describe("i18n", () => { + let tempDir: string + let validJsonFile: string + let invalidJsonFile: string + let nonExistentFile: string + + beforeEach(async () => { + // Create temporary directory for test files + tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "code-server-i18n-test-")) + + // Create test files + validJsonFile = path.join(tempDir, "valid.json") + invalidJsonFile = path.join(tempDir, "invalid.json") + nonExistentFile = path.join(tempDir, "does-not-exist.json") + + // Write valid JSON file + await fs.writeFile( + validJsonFile, + JSON.stringify({ + WELCOME: "Custom Welcome", + LOGIN_TITLE: "My Custom App", + LOGIN_BELOW: "Please log in to continue", + }), + ) + + // Write invalid JSON file + await fs.writeFile(invalidJsonFile, '{"WELCOME": "Missing closing quote}') + }) + + afterEach(async () => { + // Clean up temporary directory + await fs.rmdir(tempDir, { recursive: true }) + }) + + describe("loadCustomStrings", () => { + it("should load valid JSON file successfully", async () => { + // Should not throw an error + await expect(loadCustomStrings(validJsonFile)).resolves.toBeUndefined() + }) + + it("should throw clear error for non-existent file", async () => { + await expect(loadCustomStrings(nonExistentFile)).rejects.toThrow( + `Custom strings file not found: ${nonExistentFile}\nPlease ensure the file exists and is readable.`, + ) + }) + + it("should throw clear error for invalid JSON", async () => { + await expect(loadCustomStrings(invalidJsonFile)).rejects.toThrow( + `Invalid JSON in custom strings file: ${invalidJsonFile}`, + ) + }) + + it("should handle empty JSON object", async () => { + const emptyJsonFile = path.join(tempDir, "empty.json") + await fs.writeFile(emptyJsonFile, "{}") + + await expect(loadCustomStrings(emptyJsonFile)).resolves.toBeUndefined() + }) + + it("should handle nested JSON objects", async () => { + const nestedJsonFile = path.join(tempDir, "nested.json") + await fs.writeFile( + nestedJsonFile, + JSON.stringify({ + WELCOME: "Hello World", + NESTED: { + KEY: "Value", + }, + }), + ) + + await expect(loadCustomStrings(nestedJsonFile)).resolves.toBeUndefined() + }) + + it("should handle special characters and unicode", async () => { + const unicodeJsonFile = path.join(tempDir, "unicode.json") + await fs.writeFile( + unicodeJsonFile, + JSON.stringify({ + WELCOME: "欢迎来到 code-server", + LOGIN_TITLE: "Willkommen bei {{app}}", + SPECIAL: "Special chars: àáâãäåæçèéêë 🚀 ♠️ ∆", + }), + "utf8", + ) + + await expect(loadCustomStrings(unicodeJsonFile)).resolves.toBeUndefined() + }) + + it("should handle generic errors that are not ENOENT or SyntaxError", async () => { + const testFile = path.join(tempDir, "test.json") + await fs.writeFile(testFile, "{}") + + // Mock fs.readFile to throw a generic error + const originalReadFile = fs.readFile + const mockError = new Error("Permission denied") + fs.readFile = jest.fn().mockRejectedValue(mockError) + + await expect(loadCustomStrings(testFile)).rejects.toThrow( + `Failed to load custom strings from ${testFile}: Permission denied`, + ) + + // Restore original function + fs.readFile = originalReadFile + }) + + it("should handle errors that are not Error instances", async () => { + const testFile = path.join(tempDir, "test.json") + await fs.writeFile(testFile, "{}") + + // Mock fs.readFile to throw a non-Error object + const originalReadFile = fs.readFile + fs.readFile = jest.fn().mockRejectedValue("String error") + + await expect(loadCustomStrings(testFile)).rejects.toThrow( + `Failed to load custom strings from ${testFile}: String error`, + ) + + // Restore original function + fs.readFile = originalReadFile + }) + + it("should handle null/undefined errors", async () => { + const testFile = path.join(tempDir, "test.json") + await fs.writeFile(testFile, "{}") + + // Mock fs.readFile to throw null + const originalReadFile = fs.readFile + fs.readFile = jest.fn().mockRejectedValue(null) + + await expect(loadCustomStrings(testFile)).rejects.toThrow(`Failed to load custom strings from ${testFile}: null`) + + // Restore original function + fs.readFile = originalReadFile + }) + + it("should complete without errors for valid input", async () => { + const testFile = path.join(tempDir, "resource-test.json") + const customStrings = { + WELCOME: "Custom Welcome Message", + LOGIN_TITLE: "Custom Login Title", + } + await fs.writeFile(testFile, JSON.stringify(customStrings)) + + // Should not throw any errors + await expect(loadCustomStrings(testFile)).resolves.toBeUndefined() + }) + }) +}) diff --git a/test/unit/node/main.test.ts b/test/unit/node/main.test.ts new file mode 100644 index 000000000000..39ba2ca8bf9b --- /dev/null +++ b/test/unit/node/main.test.ts @@ -0,0 +1,176 @@ +import { promises as fs } from "fs" +import * as path from "path" +import { setDefaults, parse } from "../../../src/node/cli" +import { loadCustomStrings } from "../../../src/node/i18n" +import { tmpdir } from "../../utils/helpers" + +// Mock the i18n module +jest.mock("../../../src/node/i18n", () => ({ + loadCustomStrings: jest.fn(), +})) + +// Mock logger to avoid console output during tests +jest.mock("@coder/logger", () => ({ + logger: { + info: jest.fn(), + debug: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + named: jest.fn(), + level: 0, + }, + field: jest.fn(), + Level: { + Trace: 0, + Debug: 1, + Info: 2, + Warn: 3, + Error: 4, + }, +})) + +const mockedLoadCustomStrings = loadCustomStrings as jest.MockedFunction + +describe("main", () => { + let tempDir: string + let mockServer: any + + beforeEach(async () => { + tempDir = await tmpdir("code-server-main-test") + + // Reset mocks + jest.clearAllMocks() + + // Mock the server creation to avoid actually starting a server + mockServer = { + server: { + listen: jest.fn(), + address: jest.fn(() => ({ address: "127.0.0.1", port: 8080 })), + close: jest.fn(), + }, + editorSessionManagerServer: { + address: jest.fn(() => null), + }, + dispose: jest.fn(), + } + }) + + afterEach(async () => { + // Clean up temp directory + try { + await fs.rmdir(tempDir, { recursive: true }) + } catch (error) { + // Ignore cleanup errors + } + }) + + describe("runCodeServer", () => { + it("should load custom strings when i18n flag is provided", async () => { + // Create a test custom strings file + const customStringsFile = path.join(tempDir, "custom-strings.json") + await fs.writeFile( + customStringsFile, + JSON.stringify({ + WELCOME: "Custom Welcome", + LOGIN_TITLE: "My App", + }), + ) + + // Create args with i18n flag + const cliArgs = parse([ + `--config=${path.join(tempDir, "config.yaml")}`, + `--user-data-dir=${tempDir}`, + "--bind-addr=localhost:0", + "--log=warn", + "--auth=none", + `--i18n=${customStringsFile}`, + ]) + const args = await setDefaults(cliArgs) + + // Mock the app module + jest.doMock("../../../src/node/app", () => ({ + createApp: jest.fn().mockResolvedValue(mockServer), + ensureAddress: jest.fn().mockReturnValue(new URL("http://localhost:8080")), + })) + + // Mock routes module + jest.doMock("../../../src/node/routes", () => ({ + register: jest.fn().mockResolvedValue({ disposeRoutes: jest.fn() }), + })) + + // Mock loadCustomStrings to succeed + mockedLoadCustomStrings.mockResolvedValue(undefined) + + // Import runCodeServer after mocking + const mainModule = await import("../../../src/node/main") + const result = await mainModule.runCodeServer(args) + + // Verify that loadCustomStrings was called with the correct file path + expect(mockedLoadCustomStrings).toHaveBeenCalledWith(customStringsFile) + expect(mockedLoadCustomStrings).toHaveBeenCalledTimes(1) + + // Clean up + await result.dispose() + }) + + it("should not load custom strings when i18n flag is not provided", async () => { + // Create args without i18n flag + const cliArgs = parse([ + `--config=${path.join(tempDir, "config.yaml")}`, + `--user-data-dir=${tempDir}`, + "--bind-addr=localhost:0", + "--log=warn", + "--auth=none", + ]) + const args = await setDefaults(cliArgs) + + // Mock the app module + jest.doMock("../../../src/node/app", () => ({ + createApp: jest.fn().mockResolvedValue(mockServer), + ensureAddress: jest.fn().mockReturnValue(new URL("http://localhost:8080")), + })) + + // Mock routes module + jest.doMock("../../../src/node/routes", () => ({ + register: jest.fn().mockResolvedValue({ disposeRoutes: jest.fn() }), + })) + + // Import runCodeServer after mocking + const mainModule = await import("../../../src/node/main") + const result = await mainModule.runCodeServer(args) + + // Verify that loadCustomStrings was NOT called + expect(mockedLoadCustomStrings).not.toHaveBeenCalled() + + // Clean up + await result.dispose() + }) + + it("should handle errors when loadCustomStrings fails", async () => { + // Create args with i18n flag pointing to non-existent file + const nonExistentFile = path.join(tempDir, "does-not-exist.json") + const cliArgs = parse([ + `--config=${path.join(tempDir, "config.yaml")}`, + `--user-data-dir=${tempDir}`, + "--bind-addr=localhost:0", + "--log=warn", + "--auth=none", + `--i18n=${nonExistentFile}`, + ]) + const args = await setDefaults(cliArgs) + + // Mock loadCustomStrings to throw an error + const mockError = new Error("Custom strings file not found") + mockedLoadCustomStrings.mockRejectedValue(mockError) + + // Import runCodeServer after mocking + const mainModule = await import("../../../src/node/main") + + // Verify that runCodeServer throws the error from loadCustomStrings + await expect(mainModule.runCodeServer(args)).rejects.toThrow("Custom strings file not found") + + // Verify that loadCustomStrings was called + expect(mockedLoadCustomStrings).toHaveBeenCalledWith(nonExistentFile) + }) + }) +}) diff --git a/test/unit/node/plugin.test.ts b/test/unit/node/plugin.test.ts deleted file mode 100644 index 8237b1177d53..000000000000 --- a/test/unit/node/plugin.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { logger } from "@coder/logger" -import * as express from "express" -import * as fs from "fs" -import * as path from "path" -import { HttpCode } from "../../../src/common/http" -import { AuthType } from "../../../src/node/cli" -import { codeServer, PluginAPI } from "../../../src/node/plugin" -import * as apps from "../../../src/node/routes/apps" -import * as httpserver from "../../utils/httpserver" -const fsp = fs.promises - -// Jest overrides `require` so our usual override doesn't work. -jest.mock("code-server", () => codeServer, { virtual: true }) - -/** - * Use $LOG_LEVEL=debug to see debug logs. - */ -describe("plugin", () => { - let papi: PluginAPI - let s: httpserver.HttpServer - - beforeAll(async () => { - // Only include the test plugin to avoid contaminating results with other - // plugins that might be on the filesystem. - papi = new PluginAPI(logger, `${path.resolve(__dirname, "test-plugin")}:meow`, "") - await papi.loadPlugins(false) - - const app = express.default() - const wsApp = express.default() - - const common: express.RequestHandler = (req, _, next) => { - // Routes might use these arguments. - req.args = { - _: [], - auth: AuthType.None, - host: "localhost", - port: 8080, - "proxy-domain": [], - config: "~/.config/code-server/config.yaml", - verbose: false, - "disable-file-downloads": false, - usingEnvPassword: false, - usingEnvHashedPassword: false, - "extensions-dir": "", - "user-data-dir": "", - "session-socket": "", - } - next() - } - - app.use(common) - wsApp.use(common) - - papi.mount(app, wsApp) - app.use("/api/applications", apps.router(papi)) - - s = new httpserver.HttpServer() - await s.listen(app) - s.listenUpgrade(wsApp) - }) - - afterAll(async () => { - await s.dispose() - }) - - it("/api/applications", async () => { - const resp = await s.fetch("/api/applications") - expect(resp.status).toBe(200) - const body = await resp.json() - logger.debug(`${JSON.stringify(body)}`) - expect(body).toStrictEqual([ - { - name: "Test App", - version: "4.0.1", - - description: "This app does XYZ.", - iconPath: "/test-plugin/test-app/icon.svg", - homepageURL: "https://example.com", - path: "/test-plugin/test-app", - - plugin: { - name: "test-plugin", - version: "1.0.0", - modulePath: path.join(__dirname, "test-plugin"), - - displayName: "Test Plugin", - description: "Plugin used in code-server tests.", - routerPath: "/test-plugin", - homepageURL: "https://example.com", - }, - }, - ]) - }) - - it("/test-plugin/test-app", async () => { - const indexHTML = await fsp.readFile(path.join(__dirname, "test-plugin/public/index.html"), { - encoding: "utf8", - }) - const resp = await s.fetch("/test-plugin/test-app") - expect(resp.status).toBe(200) - const body = await resp.text() - expect(body).toBe(indexHTML) - }) - - it("/test-plugin/test-app (websocket)", async () => { - const ws = s.ws("/test-plugin/test-app") - const message = await new Promise((resolve) => { - ws.once("message", (message) => resolve(message)) - }) - ws.terminate() - expect(message).toBe("hello") - }) - - it("/test-plugin/error", async () => { - const resp = await s.fetch("/test-plugin/error") - expect(resp.status).toBe(HttpCode.LargePayload) - }) -}) diff --git a/test/unit/node/proxy.test.ts b/test/unit/node/proxy.test.ts index 186cd475b3e2..b3509ed640df 100644 --- a/test/unit/node/proxy.test.ts +++ b/test/unit/node/proxy.test.ts @@ -268,6 +268,21 @@ describe("proxy", () => { const text = await resp.text() expect(text).toBe("app being served behind a prefixed path") }) + + it("should not allow OPTIONS without authentication by default", async () => { + process.env.PASSWORD = "test" + codeServer = await integration.setup(["--auth=password"]) + const resp = await codeServer.fetch(proxyPath, { method: "OPTIONS" }) + expect(resp.status).toBe(401) + }) + + it("should allow OPTIONS with `skip-auth-preflight` flag", async () => { + process.env.PASSWORD = "test" + codeServer = await integration.setup(["--auth=password", "--skip-auth-preflight"]) + e.post("/wsup", (req, res) => {}) + const resp = await codeServer.fetch(proxyPath, { method: "OPTIONS" }) + expect(resp.status).toBe(200) + }) }) // NOTE@jsjoeio diff --git a/test/unit/node/settings.test.ts b/test/unit/node/settings.test.ts index 59c6f46a50bc..ad04b15ff81b 100644 --- a/test/unit/node/settings.test.ts +++ b/test/unit/node/settings.test.ts @@ -29,7 +29,7 @@ describe("settings", () => { const settings = new SettingsProvider(pathToMockSettingsFile) await settings.read() // This happens when we can't parse a JSON (usually error in file) - expect(logger.warn).toHaveBeenCalledWith(expect.stringMatching(/Unexpected token/)) + expect(logger.warn).toHaveBeenCalledWith(expect.stringMatching(/Expected ':'/)) }) }) describe("with invalid settings file path", () => { diff --git a/test/unit/node/test-plugin/.eslintrc.js b/test/unit/node/test-plugin/.eslintrc.js deleted file mode 100644 index 3999419107b0..000000000000 --- a/test/unit/node/test-plugin/.eslintrc.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - settings: { - "import/resolver": { - typescript: { - project: __dirname, - }, - }, - }, -} diff --git a/test/unit/node/test-plugin/.gitignore b/test/unit/node/test-plugin/.gitignore deleted file mode 100644 index 1fcb1529f8e5..000000000000 --- a/test/unit/node/test-plugin/.gitignore +++ /dev/null @@ -1 +0,0 @@ -out diff --git a/test/unit/node/test-plugin/Makefile b/test/unit/node/test-plugin/Makefile deleted file mode 100644 index 397424629bb5..000000000000 --- a/test/unit/node/test-plugin/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -out/index.js: src/index.ts - # Typescript always emits, even on errors. - npm run build || rm out/index.js - -node_modules: package.json package-lock.json - npm install diff --git a/test/unit/node/test-plugin/package-lock.json b/test/unit/node/test-plugin/package-lock.json deleted file mode 100644 index 0bcd9b40eebc..000000000000 --- a/test/unit/node/test-plugin/package-lock.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "name": "test-plugin", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@types/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", - "requires": { - "@types/connect": "*", - "@types/node": "*" - }, - "dev": true - }, - "@types/connect": { - "version": "3.4.33", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", - "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", - "requires": { - "@types/node": "*" - }, - "dev": true - }, - "@types/express": { - "version": "4.17.8", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.8.tgz", - "integrity": "sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ==", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "*", - "@types/qs": "*", - "@types/serve-static": "*" - }, - "dev": true - }, - "@types/express-serve-static-core": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz", - "integrity": "sha512-RgDi5a4nuzam073lRGKTUIaL3eF2+H7LJvJ8eUnCI0wA6SNjXc44DCmWNiTLs/AZ7QlsFWZiw/gTG3nSQGL0fA==", - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - }, - "dev": true - }, - "@types/mime": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", - "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", - "dev": true - }, - "@types/node": { - "version": "14.14.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.6.tgz", - "integrity": "sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==", - "dev": true - }, - "@types/qs": { - "version": "6.9.5", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", - "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", - "dev": true - }, - "@types/range-parser": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", - "dev": true - }, - "@types/serve-static": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.6.tgz", - "integrity": "sha512-nuRJmv7jW7VmCVTn+IgYDkkbbDGyIINOeu/G0d74X3lm6E5KfMeQPJhxIt1ayQeQB3cSxvYs1RA/wipYoFB4EA==", - "requires": { - "@types/mime": "*", - "@types/node": "*" - }, - "dev": true - }, - "typescript": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", - "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==", - "dev": true - } - } -} \ No newline at end of file diff --git a/test/unit/node/test-plugin/package.json b/test/unit/node/test-plugin/package.json deleted file mode 100644 index 0247d60beefb..000000000000 --- a/test/unit/node/test-plugin/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "private": true, - "name": "test-plugin", - "version": "1.0.0", - "engines": { - "code-server": "*" - }, - "main": "out/index.js", - "devDependencies": { - "@types/express": "^4.17.8", - "typescript": "^4.0.5" - }, - "scripts": { - "build": "tsc" - } -} diff --git a/test/unit/node/test-plugin/public/icon.svg b/test/unit/node/test-plugin/public/icon.svg deleted file mode 100644 index 25b9cf0474cf..000000000000 --- a/test/unit/node/test-plugin/public/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/test/unit/node/test-plugin/public/index.html b/test/unit/node/test-plugin/public/index.html deleted file mode 100644 index e3f70cab0c8e..000000000000 --- a/test/unit/node/test-plugin/public/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Test Plugin - - -

Welcome to the test plugin!

- - diff --git a/test/unit/node/test-plugin/src/index.ts b/test/unit/node/test-plugin/src/index.ts deleted file mode 100644 index 22ef723bac1a..000000000000 --- a/test/unit/node/test-plugin/src/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -import * as cs from "code-server" -import * as fspath from "path" - -export const plugin: cs.Plugin = { - displayName: "Test Plugin", - routerPath: "/test-plugin", - homepageURL: "https://example.com", - description: "Plugin used in code-server tests.", - - init(config) { - config.logger.debug("test-plugin loaded!") - }, - - router() { - const r = cs.express.Router() - r.get("/test-app", (_, res) => { - res.sendFile(fspath.resolve(__dirname, "../public/index.html")) - }) - r.get("/goland/icon.svg", (_, res) => { - res.sendFile(fspath.resolve(__dirname, "../public/icon.svg")) - }) - r.get("/error", () => { - throw new cs.HttpError("error", cs.HttpCode.LargePayload) - }) - return r - }, - - wsRouter() { - const wr = cs.WsRouter() - wr.ws("/test-app", (req) => { - cs.wss.handleUpgrade(req, req.ws, req.head, (ws) => { - req.ws.resume() - ws.send("hello") - }) - }) - return wr - }, - - applications() { - return [ - { - name: "Test App", - version: "4.0.1", - iconPath: "/icon.svg", - path: "/test-app", - - description: "This app does XYZ.", - homepageURL: "https://example.com", - }, - ] - }, -} diff --git a/test/unit/node/test-plugin/tsconfig.json b/test/unit/node/test-plugin/tsconfig.json deleted file mode 100644 index 194af1418df9..000000000000 --- a/test/unit/node/test-plugin/tsconfig.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, - "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./out" /* Redirect output structure to the directory. */, - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - "baseUrl": "./" /* Base directory to resolve non-absolute module names. */, - "paths": { - "code-server": ["../../../../typings/pluginapi"] - } /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */, - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - - /* Advanced Options */ - "skipLibCheck": true /* Skip type checking of declaration files. */, - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ - } -} diff --git a/typings/pluginapi.d.ts b/typings/pluginapi.d.ts deleted file mode 100644 index 829709b2d163..000000000000 --- a/typings/pluginapi.d.ts +++ /dev/null @@ -1,297 +0,0 @@ -/** - * This file describes the code-server plugin API for adding new applications. - */ -import { field, Level, Logger } from "@coder/logger" -import * as express from "express" -import * as expressCore from "express-serve-static-core" -import ProxyServer from "http-proxy" -import * as stream from "stream" -import Websocket from "ws" - -/** - * Overlay - * - * The homepage of code-server will launch into VS Code. However, there will be an overlay - * button that when clicked, will show all available applications with their names, - * icons and provider plugins. When one clicks on an app's icon, they will be directed - * to // to access the application. - */ - -/** - * Plugins - * - * Plugins are just node modules that contain a top level export "plugin" that implements - * the Plugin interface. - * - * 1. code-server uses $CS_PLUGIN to find plugins. - * - * e.g. CS_PLUGIN=/tmp/will:/tmp/teffen will cause code-server to load - * /tmp/will and /tmp/teffen as plugins. - * - * 2. code-server uses $CS_PLUGIN_PATH to find plugins. Each subdirectory in - * $CS_PLUGIN_PATH with a package.json where the engine is code-server is - * a valid plugin. - * - * e.g. CS_PLUGIN_PATH=/tmp/nhooyr:/tmp/ash will cause code-server to search - * /tmp/nhooyr and then /tmp/ash for plugins. - * - * CS_PLUGIN_PATH defaults to - * ~/.local/share/code-server/plugins:/usr/share/code-server/plugins - * if unset. - * - * - * 3. Built in plugins are loaded from __dirname/../plugins - * - * Plugins are required as soon as they are found and then initialized. - * See the Plugin interface for details. - * - * If two plugins are found with the exact same name, then code-server will - * use the first one and emit a warning. - * - */ - -/** - * Programmability - * - * There is also a /api/applications endpoint to allow programmatic access to all - * available applications. It could be used to create a custom application dashboard - * for example. An important difference with the API is that all application paths - * will be absolute (i.e have the plugin path prepended) so that they may be used - * directly. - * - * Example output: - * - * [ - * { - * "name": "Test App", - * "version": "4.0.1", - * "iconPath": "/test-plugin/test-app/icon.svg", - * "path": "/test-plugin/test-app", - * "description": "This app does XYZ.", - * "homepageURL": "https://example.com", - * "plugin": { - * "name": "test-plugin", - * "version": "1.0.0", - * "modulePath": "/Users/nhooyr/src/coder/code-server/test/test-plugin", - * "displayName": "Test Plugin", - * "description": "Plugin used in code-server tests.", - * "routerPath": "/test-plugin", - * "homepageURL": "https://example.com" - * } - * } - * ] - */ - -export enum HttpCode { - Ok = 200, - Redirect = 302, - NotFound = 404, - BadRequest = 400, - Unauthorized = 401, - LargePayload = 413, - ServerError = 500, -} - -export declare class HttpError extends Error { - constructor(message: string, status: HttpCode, details?: object) -} - -export interface WebsocketRequest extends express.Request { - ws: stream.Duplex - head: Buffer -} - -export type WebSocketHandler = ( - req: WebsocketRequest, - res: express.Response, - next: express.NextFunction, -) => void | Promise - -export interface WebsocketRouter { - readonly router: express.Router - ws(route: expressCore.PathParams, ...handlers: WebSocketHandler[]): void -} - -/** - * Create a router for websocket routes. - */ -export function WsRouter(): WebsocketRouter - -/** - * The websocket server used by code-server. - */ -export const wss: Websocket.Server - -/** - * The Express import used by code-server. - * - * Re-exported so plugins don't have to import duplicate copies of Express and - * to avoid potential version differences or issues caused by running separate - * instances. - */ -export { express } -/** - * Use to add a field to a log. - * - * Re-exported so plugins don't have to import duplicate copies of the logger. - */ -export { field, Level, Logger } - -/** - * code-server's proxy server. - */ -export const proxy: ProxyServer - -/** - * Middleware to ensure the user is authenticated. Throws if they are not. - */ -export function ensureAuthenticated( - req: express.Request, - res?: express.Response, - next?: express.NextFunction, -): Promise - -/** - * Returns true if the user is authenticated. - */ -export function authenticated(req: express.Request): Promise - -/** - * Replace variables in HTML: TO, BASE, CS_STATIC_BASE, and OPTIONS. - */ -export function replaceTemplates( - req: express.Request, - content: string, - extraOpts?: Omit, -): string - -/** - * Your plugin module must have a top level export "plugin" that implements this interface. - * - * The plugin's router will be mounted at / - */ -export interface Plugin { - /** - * name is used as the plugin's unique identifier. - * No two plugins may share the same name. - * - * Fetched from package.json. - */ - readonly name?: string - - /** - * The version for the plugin in the overlay. - * - * Fetched from package.json. - */ - readonly version?: string - - /** - * Name used in the overlay. - */ - readonly displayName: string - - /** - * Used in overlay. - * Should be a full sentence describing the plugin. - */ - readonly description: string - - /** - * The path at which the plugin router is to be registered. - */ - readonly routerPath: string - - /** - * Link to plugin homepage. - */ - readonly homepageURL: string - - /** - * init is called so that the plugin may initialize itself with the config. - */ - init(config: PluginConfig): void - - /** - * Called when the plugin should dispose/shutdown everything. - */ - deinit?(): Promise - - /** - * Returns the plugin's router. - * - * Mounted at / - * - * If not present, the plugin provides no routes. - */ - router?(): express.Router - - /** - * Returns the plugin's websocket router. - * - * Mounted at / - * - * If not present, the plugin provides no websockets. - */ - wsRouter?(): WebsocketRouter - - /** - * code-server uses this to collect the list of applications that - * the plugin can currently provide. - * It is called when /api/applications is hit or the overlay needs to - * refresh the list of applications - * - * Ensure this is as fast as possible. - * - * If not present, the plugin provides no applications. - */ - applications?(): Application[] | Promise -} - -/** - * PluginConfig contains the configuration required for initializing - * a plugin. - */ -export interface PluginConfig { - /** - * All plugin logs should be logged via this logger. - */ - readonly logger: Logger - - /** - * This can be specified by the user on the command line. Plugins should - * default to this directory when applicable. For example, the Jupyter plugin - * uses this to launch in this directory. - */ - readonly workingDirectory?: string -} - -/** - * Application represents a user accessible application. - */ -export interface Application { - readonly name: string - readonly version: string - - /** - * When the user clicks on the icon in the overlay, they will be - * redirected to // - * where the application should be accessible. - * - * If undefined, then / is used. - */ - readonly path?: string - - readonly description?: string - - /** - * The path at which the icon for this application can be accessed. - * /// - */ - readonly iconPath: string - - /** - * Link to application homepage. - */ - readonly homepageURL: string -}