diff --git a/.dockerignore b/.dockerignore
index cd594ac67..a14c86dfc 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -10,5 +10,10 @@ node/**/node_modules/
node/**/build/
node/**/dist/
+theia/**/node_modules/
+theia/**/lib/
+theia/**/src-gen/
+theia/**/gen-webpack.*.js
+
# Java build artifacts
java/**/target/
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 3b977b016..a0f41f133 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -1,33 +1,33 @@
name: Bug Report
description: Create a bug report to help us improve
-labels: bug
+labels: ["bug"]
body:
-- type: markdown
- attributes:
- value: |
- This template is not meant for security vulnerabilities disclosure. Any such issue, created in this repo, will be deleted on sight.
- Instead, please report vulnerabilities to [security@theia-cloud.io](mailto:security@theia-cloud.io). For more details, please read [SECURITY.md](https://github.com/eclipsesource/theia-cloud/blob/main/SECURITY.md) in the repository root.
-- type: textarea
- validations:
- required: true
- attributes:
- label: Describe the bug
- description: A clear and concise description of what the bug is.
-- type: textarea
- validations:
- required: true
- attributes:
- label: Expected behavior
- description: A clear and concise description of what you expected to happen.
-- type: input
- attributes:
- label: Cluster provider
- description: The cluster provider (e.g. Minikube, AWS, GKE, Azure, Docker Desktop). Please include your OS when using a local cluster. If you are using Terraform, please include the configuration files if possible.
-- type: input
- attributes:
- label: Version
- description: The used version. Such as the helm chart version or the commit id the bug was discovered on
-- type: textarea
- attributes:
- label: Additional information
- description: Additional information to analyze the bug such as logs, screenshots, screencasts.
+ - type: markdown
+ attributes:
+ value: |
+ This template is not meant for security vulnerabilities disclosure. Any such issue, created in this repo, will be deleted on sight.
+ Instead, please report vulnerabilities to [security@theia-cloud.io](mailto:security@theia-cloud.io). For more details, please read [SECURITY.md](https://github.com/eclipse-theia/theia-cloud/blob/main/SECURITY.md) in the repository root.
+ - type: textarea
+ validations:
+ required: true
+ attributes:
+ label: Describe the bug
+ description: A clear and concise description of what the bug is.
+ - type: textarea
+ validations:
+ required: true
+ attributes:
+ label: Expected behavior
+ description: A clear and concise description of what you expected to happen.
+ - type: input
+ attributes:
+ label: Cluster provider
+ description: The cluster provider (e.g. Minikube, AWS, GKE, Azure, Docker Desktop). Please include your OS when using a local cluster. If you are using Terraform, please include the configuration files if possible.
+ - type: input
+ attributes:
+ label: Version
+ description: The used version. Such as the helm chart version or the commit id the bug was discovered on
+ - type: textarea
+ attributes:
+ label: Additional information
+ description: Additional information to analyze the bug such as logs, screenshots, screencasts.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index fa5283c54..5653012d6 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,8 +1,8 @@
-blank_issues_enabled: false
+blank_issues_enabled: true
contact_links:
- name: Question
- url: https://github.com/eclipsesource/theia-cloud/discussions
+ url: https://github.com/eclipse-theia/theia-cloud/discussions
about: Please ask questions on the Theia Cloud discussions page.
- name: Professional support
url: https://theia-cloud.io/support/
- about: Professional support by EclipseSource
\ No newline at end of file
+ about: Professional support by EclipseSource
diff --git a/.github/workflows/auto-assign.yml b/.github/workflows/auto-assign.yml
new file mode 100644
index 000000000..02f3c03d8
--- /dev/null
+++ b/.github/workflows/auto-assign.yml
@@ -0,0 +1,23 @@
+name: Auto Assign PR Author
+
+on:
+ pull_request:
+ types: [opened, reopened, ready_for_review]
+
+jobs:
+ auto-assign:
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - name: Auto assign PR author
+ uses: actions/github-script@v7
+ with:
+ script: |
+ await github.rest.issues.addAssignees({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ assignees: [context.payload.pull_request.user.login]
+ });
+
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 000000000..df4572ae6
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,62 @@
+name: Build and Push Theia Cloud Images
+
+on:
+ pull_request:
+ branches:
+ - main
+
+ push:
+ branches:
+ - main
+
+ schedule:
+ - cron: '0 2 * * *'
+
+ release:
+ types:
+ - published
+ - prereleased
+
+ workflow_dispatch:
+ inputs:
+ image_tag:
+ description: "Optional image tag override (e.g. my-test-tag, or 2.3.0 for the release train)"
+ required: false
+ default: ""
+ type: string
+ disable_layer_cache:
+ description: "Disable the Docker layer cache (--no-cache) for stress testing"
+ required: false
+ default: false
+ type: boolean
+
+permissions:
+ contents: read
+ packages: write
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: false
+
+jobs:
+ build:
+ name: ${{ matrix.component }}
+ strategy:
+ # One component failing must not cancel the other two.
+ fail-fast: false
+ matrix:
+ include:
+ - component: operator
+ - component: service
+ - component: conversion-webhook
+ uses: EduIDE/.github/.github/workflows/build-and-push-docker-image.yml@v1
+ with:
+ docker-file: dockerfiles/${{ matrix.component }}/Dockerfile
+ image-name: eduide/eduide-cloud/${{ matrix.component }}
+ docker-context: .
+ image-tag: ${{ github.event_name == 'release' && github.event.release.tag_name || github.event_name == 'workflow_dispatch' && inputs.image_tag || '' }}
+ no-cache: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.disable_layer_cache) || false }}
+ # These are small Java images; the disk-reclaim step is not worth the time.
+ free-disk-space: false
+ secrets:
+ docker-secrets: ${{ secrets.SENTRY_AUTH_TOKEN != '' && format('SENTRY_AUTH_TOKEN={0}', secrets.SENTRY_AUTH_TOKEN) || '' }}
diff --git a/.github/workflows/ci-conversion-webhook.yml b/.github/workflows/ci-conversion-webhook.yml
deleted file mode 100644
index 36347acbc..000000000
--- a/.github/workflows/ci-conversion-webhook.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: "[DOCKER] conversion-webhook"
-
-on:
- push:
- branches:
- - main
- paths:
- - "java/common/**"
- - "java/conversion/**"
- - "dockerfiles/conversion-webhook/**"
- # Publish when a workflow has changed (this is needed to detect version updates)
- - ".github/workflows/ci-conversion-webhook.yml"
- - ".github/workflows/reusable-docker.yml"
- pull_request:
- branches:
- - main
- paths:
- - "java/common/**"
- - "java/conversion/**"
- - "dockerfiles/conversion-webhook/**"
- release:
- types:
- - published
-
-jobs:
- run:
- uses: ./.github/workflows/reusable-docker.yml
- with:
- docker_org: theiacloud
- docker_image: theia-cloud-conversion-webhook
- docker_file: dockerfiles/conversion-webhook/Dockerfile
- secrets:
- dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
- dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
diff --git a/.github/workflows/ci-landing-page.yml b/.github/workflows/ci-landing-page.yml
deleted file mode 100644
index 1bc303445..000000000
--- a/.github/workflows/ci-landing-page.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-name: "[DOCKER] landing-page"
-
-on:
- push:
- branches:
- - main
- paths:
- - "node/package-lock.json"
- - "node/common/**"
- - "node/landing-page/**"
- - "dockerfiles/landing-page/**"
- # Publish when a workflow has changed (this is needed to detect version updates)
- - ".github/workflows/ci-landing-page.yml"
- - ".github/workflows/reusable-docker.yml"
- pull_request:
- branches:
- - main
- paths:
- - "node/package-lock.json"
- - "node/common/**"
- - "node/landing-page/**"
- - "dockerfiles/landing-page/**"
- release:
- types:
- - published
-
-jobs:
- run:
- uses: ./.github/workflows/reusable-docker.yml
- with:
- docker_org: theiacloud
- docker_image: theia-cloud-landing-page
- docker_file: dockerfiles/landing-page/Dockerfile
- secrets:
- dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
- dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
diff --git a/.github/workflows/ci-maven-common.yml b/.github/workflows/ci-maven-common.yml
deleted file mode 100644
index 1d743bf62..000000000
--- a/.github/workflows/ci-maven-common.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: "[MAVEN] common"
-
-on:
- push:
- branches:
- - main
- paths:
- - "java/common/**"
- pull_request:
- branches:
- - main
- paths:
- - "java/common/**"
- release:
- types:
- - published
-
-jobs:
- run:
- permissions:
- packages: write
- uses: ./.github/workflows/reusable-maven.yml
- with:
- path_to_package: common/org.eclipse.theia.cloud.common/
- secrets: inherit
diff --git a/.github/workflows/ci-maven-conf.yml b/.github/workflows/ci-maven-conf.yml
deleted file mode 100644
index 3596557ac..000000000
--- a/.github/workflows/ci-maven-conf.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: "[MAVEN] maven-conf"
-
-on:
- push:
- branches:
- - main
- paths:
- - "java/common/maven-conf/**"
- pull_request:
- branches:
- - main
- paths:
- - "java/common/maven-conf/**"
- release:
- types:
- - published
-
-jobs:
- run:
- permissions:
- packages: write
- uses: ./.github/workflows/reusable-maven.yml
- with:
- path_to_package: common/maven-conf/
- secrets: inherit
diff --git a/.github/workflows/ci-maven-operator.yml b/.github/workflows/ci-maven-operator.yml
deleted file mode 100644
index 94fe211fc..000000000
--- a/.github/workflows/ci-maven-operator.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-name: "[MAVEN] operator"
-
-on:
- push:
- branches:
- - main
- paths:
- - "java/common/**"
- - "java/operator/org.eclipse.theia.cloud.operator/**"
- pull_request:
- branches:
- - main
- paths:
- - "java/common/**"
- - "java/operator/org.eclipse.theia.cloud.operator/**"
- release:
- types:
- - published
-
-jobs:
- run:
- permissions:
- packages: write
- uses: ./.github/workflows/reusable-maven.yml
- with:
- path_to_package: operator/org.eclipse.theia.cloud.operator/
- dependencies: common/maven-conf/,common/org.eclipse.theia.cloud.common/
- secrets: inherit
diff --git a/.github/workflows/ci-maven-service.yml b/.github/workflows/ci-maven-service.yml
deleted file mode 100644
index 67b13ea76..000000000
--- a/.github/workflows/ci-maven-service.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-name: "[MAVEN] service"
-on:
- push:
- branches:
- - main
- paths:
- - "java/common/**"
- - "java/service/org.eclipse.theia.cloud.service/**"
- pull_request:
- branches:
- - main
- paths:
- - "java/common/**"
- - "java/service/org.eclipse.theia.cloud.service/**"
- release:
- types:
- - published
-
-jobs:
- run:
- permissions:
- packages: write
- uses: ./.github/workflows/reusable-maven.yml
- with:
- path_to_package: service/org.eclipse.theia.cloud.service/
- dependencies: common/maven-conf/,common/org.eclipse.theia.cloud.common/
- secrets: inherit
diff --git a/.github/workflows/ci-monitor-theia.yml b/.github/workflows/ci-monitor-theia.yml
deleted file mode 100644
index 4a0af06d3..000000000
--- a/.github/workflows/ci-monitor-theia.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: "[NPM] monitor-theia"
-
-on:
- push:
- branches:
- - main
- paths:
- - "node/package-lock.json"
- - "node/monitor-theia/**"
- # Publish when a workflow has changed (this is needed to detect version updates)
- - ".github/workflows/ci-monitor-theia.yml"
- - ".github/workflows/reusable-npm.yml"
- pull_request:
- branches:
- - main
- paths:
- - "node/package-lock.json"
- - "node/monitor-theia/**"
- release:
- types:
- - published
-
-jobs:
- run:
- uses: ./.github/workflows/reusable-npm.yml
- permissions:
- contents: read
- id-token: write
- with:
- package_workspace: monitor-theia
- secrets:
- npm-token: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/ci-node-common.yml b/.github/workflows/ci-node-common.yml
deleted file mode 100644
index 4198f309f..000000000
--- a/.github/workflows/ci-node-common.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: "[NPM] common"
-
-on:
- push:
- branches:
- - main
- paths:
- - "node/package-lock.json"
- - "node/common/**"
- # Publish when a workflow has changed (this is needed to detect version updates)
- - ".github/workflows/ci-node-common.yml"
- - ".github/workflows/reusable-npm.yml"
- pull_request:
- branches:
- - main
- paths:
- - "node/package-lock.json"
- - "node/common/**"
- release:
- types:
- - published
-
-jobs:
- run:
- uses: ./.github/workflows/reusable-npm.yml
- permissions:
- contents: read
- id-token: write
- with:
- package_workspace: common
- secrets:
- npm-token: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/ci-operator.yml b/.github/workflows/ci-operator.yml
deleted file mode 100644
index 8dbf82e32..000000000
--- a/.github/workflows/ci-operator.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: "[DOCKER] operator"
-
-on:
- push:
- branches:
- - main
- paths:
- - "java/common/**"
- - "java/operator/**"
- - "dockerfiles/operator/**"
- # Publish when a workflow has changed (this is needed to detect version updates)
- - ".github/workflows/ci-operator.yml"
- - ".github/workflows/reusable-docker.yml"
- pull_request:
- branches:
- - main
- paths:
- - "java/common/**"
- - "java/operator/**"
- - "dockerfiles/operator/**"
- release:
- types:
- - published
-
-jobs:
- run:
- uses: ./.github/workflows/reusable-docker.yml
- with:
- docker_org: theiacloud
- docker_image: theia-cloud-operator
- docker_file: dockerfiles/operator/Dockerfile
- secrets:
- dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
- dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
diff --git a/.github/workflows/ci-service.yml b/.github/workflows/ci-service.yml
deleted file mode 100644
index 317803680..000000000
--- a/.github/workflows/ci-service.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: "[DOCKER] service"
-
-on:
- push:
- branches:
- - main
- paths:
- - "java/common/**"
- - "java/service/**"
- - "dockerfiles/service/**"
- # Publish when a workflow has changed (this is needed to detect version updates)
- - ".github/workflows/ci-service.yml"
- - ".github/workflows/reusable-docker.yml"
- pull_request:
- branches:
- - main
- paths:
- - "java/common/**"
- - "java/service/**"
- - "dockerfiles/service/**"
- release:
- types:
- - published
-
-jobs:
- run:
- uses: ./.github/workflows/reusable-docker.yml
- with:
- docker_org: theiacloud
- docker_image: theia-cloud-service
- docker_file: dockerfiles/service/Dockerfile
- secrets:
- dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
- dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
diff --git a/.github/workflows/ci-wondershaper.yml b/.github/workflows/ci-wondershaper.yml
deleted file mode 100644
index 8bd725bf1..000000000
--- a/.github/workflows/ci-wondershaper.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-name: "[DOCKER] wondershaper"
-
-on:
- push:
- branches:
- - main
- paths:
- - "dockerfiles/wondershaper/**"
- # Publish when a workflow has changed (this is needed to detect version updates)
- - ".github/workflows/ci-wondershaper.yml"
- - ".github/workflows/reusable-docker.yml"
- pull_request:
- branches:
- - main
- paths:
- - "dockerfiles/wondershaper/**"
- release:
- types:
- - published
-
-jobs:
- run:
- uses: ./.github/workflows/reusable-docker.yml
- with:
- docker_org: theiacloud
- docker_image: theia-cloud-wondershaper
- docker_file: dockerfiles/wondershaper/Dockerfile
- secrets:
- dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
- dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
diff --git a/.github/workflows/publish-demos.yml b/.github/workflows/publish-demos.yml
deleted file mode 100644
index 86cbd8192..000000000
--- a/.github/workflows/publish-demos.yml
+++ /dev/null
@@ -1,80 +0,0 @@
-name: "[DOCKER] demos"
-
-on:
- workflow_dispatch:
- inputs:
- publish_theia_cloud_demo:
- description: "Publish theia-cloud-demo?"
- type: boolean
- required: true
- publish_theia_cloud_activity_demo_theia:
- description: "Publish theia-cloud-activity-demo-theia?"
- type: boolean
- required: true
- publish_theia_cloud_activity_demo:
- description: "Publishtheia-cloud-activity-demo?"
- type: boolean
- required: true
-
-env:
- VERSION: 0.12.0-next
-
-jobs:
- check-version:
- runs-on: ubuntu-latest
- outputs:
- is_next_version: ${{ steps.version_check.outputs.is_next_version }}
- version: ${{ env.VERSION }}
- steps:
- - id: version_check
- run: |
- if [[ $VERSION == *"-next" ]]; then
- echo "is_next_version=true" >> $GITHUB_OUTPUT
- else
- echo "is_next_version=false" >> $GITHUB_OUTPUT
- fi
-
- publish-theia-cloud-demo:
- if: inputs.publish_theia_cloud_demo == true
- needs: check-version
- uses: ./.github/workflows/reusable-demo.yml
- with:
- docker_org: theiacloud
- docker_image: theia-cloud-demo
- docker_file: demo/dockerfiles/demo-theia-docker/Dockerfile
- docker_location: demo/dockerfiles/demo-theia-docker/.
- publish_next: ${{ needs.check-version.outputs.is_next_version }}
- version: ${{ needs.check-version.outputs.version }}
- secrets:
- dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
- dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
-
- publish-theia-cloud-activity-demo-theia:
- if: inputs.publish_theia_cloud_activity_demo_theia == true
- needs: check-version
- uses: ./.github/workflows/reusable-demo.yml
- with:
- docker_org: theiacloud
- docker_image: theia-cloud-activity-demo-theia
- docker_file: demo/dockerfiles/demo-theia-monitor-theia/Dockerfile
- docker_location: .
- publish_next: ${{ needs.check-version.outputs.is_next_version }}
- version: ${{ needs.check-version.outputs.version }}
- secrets:
- dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
- dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
-
- publish-theia-cloud-activity-demo:
- if: inputs.publish_theia_cloud_activity_demo == true
- needs: [check-version, publish-theia-cloud-demo]
- uses: ./.github/workflows/reusable-demo.yml
- with:
- docker_org: theiacloud
- docker_image: theia-cloud-activity-demo
- docker_file: demo/dockerfiles/demo-theia-monitor-vscode/Dockerfile
- docker_location: demo/dockerfiles/demo-theia-monitor-vscode/.
- publish_next: ${{ needs.check-version.outputs.is_next_version }}
- version: ${{ needs.check-version.outputs.version }}
- secrets:
- dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
- dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
diff --git a/.github/workflows/reusable-demo.yml b/.github/workflows/reusable-demo.yml
deleted file mode 100644
index a48c17852..000000000
--- a/.github/workflows/reusable-demo.yml
+++ /dev/null
@@ -1,71 +0,0 @@
-name: Reusable workflow for demo applications
-
-on:
- workflow_call:
- inputs:
- docker_org:
- required: true
- type: string
- docker_image:
- required: true
- type: string
- docker_file:
- required: true
- type: string
- docker_location:
- required: true
- type: string
- publish_next:
- required: true
- type: string
- version:
- required: true
- type: string
- secrets:
- dockerhub_username:
- required: true
- dockerhub_token:
- required: true
-
-jobs:
- publish:
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
-
- - name: Create docker tags
- id: get_tags
- run: |
- echo "version_tag=${{ inputs.docker_org }}/${{ inputs.docker_image }}:${{ inputs.version }}" >> $GITHUB_OUTPUT
- echo "next_sha_tag=${{ inputs.docker_org }}/${{ inputs.docker_image }}:${{ inputs.version }}.$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_OUTPUT
- echo "next_tag_for_release=${{ inputs.docker_org }}/${{ inputs.docker_image }}:${{ inputs.version }}-next" >> $GITHUB_OUTPUT
- echo "latest_tag=${{ inputs.docker_org }}/${{ inputs.docker_image }}:latest" >> $GITHUB_OUTPUT
-
- - name: Build Docker image
- run: docker build -t ${{ steps.get_tags.outputs.version_tag }} -f ${{ inputs.docker_file }} ${{ inputs.docker_location }}
-
- - name: Login to DockerHub
- uses: docker/login-action@v3
- with:
- username: ${{ secrets.dockerhub_username }}
- password: ${{ secrets.dockerhub_token }}
-
- # Push version and SHA tag (for next versions)
- - name: Push version and SHA tag
- if: inputs.publish_next == 'true'
- run: |
- docker push ${{ steps.get_tags.outputs.version_tag }}
- docker tag ${{ steps.get_tags.outputs.version_tag }} ${{ steps.get_tags.outputs.next_sha_tag }}
- docker push ${{ steps.get_tags.outputs.next_sha_tag }}
-
- # Push version, next and latest tag for releases (for valid semver versions)
- - name: Push version and latest tag
- if: inputs.publish_next == 'false'
- run: |
- docker push ${{ steps.get_tags.outputs.version_tag }}
- docker tag ${{ steps.get_tags.outputs.version_tag }} ${{ steps.get_tags.outputs.latest_tag }}
- docker push ${{ steps.get_tags.outputs.latest_tag }}
- docker tag ${{ steps.get_tags.outputs.version_tag }} ${{ steps.get_tags.outputs.next_tag_for_release }}
- docker push ${{ steps.get_tags.outputs.next_tag_for_release }}
diff --git a/.github/workflows/reusable-docker.yml b/.github/workflows/reusable-docker.yml
deleted file mode 100644
index 56a615da8..000000000
--- a/.github/workflows/reusable-docker.yml
+++ /dev/null
@@ -1,105 +0,0 @@
-name: Reusable workflow for docker images
-
-on:
- workflow_call:
- inputs:
- docker_org:
- required: true
- type: string
- docker_image:
- required: true
- type: string
- docker_file:
- required: true
- type: string
- secrets:
- dockerhub_username:
- required: true
- dockerhub_token:
- required: true
-
-env:
- VERSION: 0.12.0-next
-
-jobs:
- build:
- runs-on: ubuntu-latest
- if: github.event_name == 'pull_request'
-
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
-
- - name: Create docker tags
- id: get_tags
- run: |
- echo "version_tag=${{ inputs.docker_org }}/${{ inputs.docker_image }}:${{ env.VERSION }}" >> $GITHUB_OUTPUT
-
- - name: Build the Docker image
- run: |
- docker build -t ${{ steps.get_tags.outputs.version_tag }} -f ${{ inputs.docker_file }} .
-
- publish:
- runs-on: ubuntu-latest
- if: github.event_name == 'push' || github.event_name == 'release'
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
-
- # Check if we have a valid *-next version for merges on main
- - name: Check version (only for push events)
- if: github.event_name == 'push'
- id: version_check
- run: |
- if [[ $VERSION == *"-next" ]]; then
- echo "is_next_version=true" >> $GITHUB_OUTPUT
- else
- echo "is_next_version=false" >> $GITHUB_OUTPUT
- fi
-
- # Create tags according to the state to be published:
- # - next version on main merge: create `*-next` and `*-next.` tags
- # - release version: create version, `*-next` and `latest` tags
- - name: Create docker tags
- id: get_tags
- if: github.event_name == 'push' && steps.version_check.outputs.is_next_version == 'true' || github.event_name == 'release'
- run: |
- if [[ $GITHUB_EVENT_NAME == 'push' ]]; then
- echo "sha_tag=${{ inputs.docker_org }}/${{ inputs.docker_image }}:${{ env.VERSION }}.$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_OUTPUT
- echo "version_tag=${{ inputs.docker_org }}/${{ inputs.docker_image }}:${{ env.VERSION }}" >> $GITHUB_OUTPUT
- else
- echo "version_tag=${{ inputs.docker_org }}/${{ inputs.docker_image }}:${{ env.VERSION }}" >> $GITHUB_OUTPUT
- echo "next_tag=${{ inputs.docker_org }}/${{ inputs.docker_image }}:${{ env.VERSION }}-next" >> $GITHUB_OUTPUT
- echo "latest_tag=${{ inputs.docker_org }}/${{ inputs.docker_image }}:latest" >> $GITHUB_OUTPUT
- fi
-
- # Only build when we will publish, so either a main merge with next-version or a release
- - name: Build Docker image
- if: github.event_name == 'push' && steps.version_check.outputs.is_next_version == 'true' || github.event_name == 'release'
- run: docker build -t ${{ steps.get_tags.outputs.version_tag }} -f ${{ inputs.docker_file }} .
-
- # Only log in to dockerhub when we will publish, so either a main merge with next-version or a release
- - name: Login to DockerHub
- if: github.event_name == 'push' && steps.version_check.outputs.is_next_version == 'true' || github.event_name == 'release'
- uses: docker/login-action@v3
- with:
- username: ${{ secrets.dockerhub_username }}
- password: ${{ secrets.dockerhub_token }}
-
- # Push for main merges with next version
- - name: Push Docker tags (for push events with next version)
- if: github.event_name == 'push' && steps.version_check.outputs.is_next_version == 'true'
- run: |
- docker push ${{ steps.get_tags.outputs.version_tag }}
- docker tag ${{ steps.get_tags.outputs.version_tag }} ${{ steps.get_tags.outputs.sha_tag }}
- docker push ${{ steps.get_tags.outputs.sha_tag }}
-
- # Push for releases
- - name: Push Docker tags (for release events)
- if: github.event_name == 'release'
- run: |
- docker push ${{ steps.get_tags.outputs.version_tag }}
- docker tag ${{ steps.get_tags.outputs.version_tag }} ${{ steps.get_tags.outputs.latest_tag }}
- docker push ${{ steps.get_tags.outputs.latest_tag }}
- docker tag ${{ steps.get_tags.outputs.version_tag }} ${{ steps.get_tags.outputs.next_tag }}
- docker push ${{ steps.get_tags.outputs.next_tag }}
diff --git a/.github/workflows/reusable-maven.yml b/.github/workflows/reusable-maven.yml
deleted file mode 100644
index 512f35737..000000000
--- a/.github/workflows/reusable-maven.yml
+++ /dev/null
@@ -1,121 +0,0 @@
-name: Reusable workflow for maven packages
-
-on:
- workflow_call:
- inputs:
- path_to_package:
- required: true
- type: string
- dependencies:
- description: "List of paths to dependencies. Entries are relative to the java directory and separated by comma (no whitespaces)."
- required: false
- type: string
-
-defaults:
- run:
- working-directory: java
-
-jobs:
- build:
- runs-on: ubuntu-latest
- if: github.event_name == 'pull_request'
-
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
-
- - name: Set up JDK 17
- uses: actions/setup-java@v4
- with:
- java-version: "17"
- distribution: "temurin"
-
- - name: Build dependencies
- if: ${{ inputs.dependencies != '' }}
- run: |
- IFS=',' read -r -a dirs <<< "${{ inputs.dependencies }}"
- for dir in "${dirs[@]}"
- do
- cd "$dir"
- mvn clean install --no-transfer-progress
- cd -
- done
-
- - name: Verify package
- run: |
- cd ${{ inputs.path_to_package }}
- mvn verify -fae --no-transfer-progress
-
- publish:
- runs-on: ubuntu-latest
- if: github.event_name == 'push' || github.event_name == 'release'
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
-
- - name: Run Version Check
- id: version_check
- run: |
- cd ${{ inputs.path_to_package }}
- VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
- if [[ "$VERSION" == *-SNAPSHOT ]]; then
- echo "is_snapshot_version=true" >> $GITHUB_OUTPUT
- else
- echo "is_snapshot_version=false" >> $GITHUB_OUTPUT
- fi
-
- # Only continue from here when the version matches the event, so either:
- # - main merge with snapshot version
- # - release with proper version
- - name: Set up JDK 17
- if: |
- (github.event_name == 'push' && steps.version_check.outputs.is_snapshot_version == 'true') ||
- (github.event_name == 'release' && steps.version_check.outputs.is_snapshot_version == 'false')
- uses: actions/setup-java@v4
- with:
- java-version: "17"
- distribution: "temurin"
-
- - name: Build dependencies
- if: |
- ${{ inputs.dependencies != '' }} &&
- (
- (github.event_name == 'push' && steps.version_check.outputs.is_snapshot_version == 'true') ||
- (github.event_name == 'release' && steps.version_check.outputs.is_snapshot_version == 'false')
- )
- run: |
- IFS=',' read -r -a dirs <<< "${{ inputs.dependencies }}"
- for dir in "${dirs[@]}"
- do
- cd "$dir"
- mvn clean install --no-transfer-progress
- cd -
- done
-
- - name: Publish package to GitHub Packages
- if: |
- (github.event_name == 'push' && steps.version_check.outputs.is_snapshot_version == 'true') ||
- (github.event_name == 'release' && steps.version_check.outputs.is_snapshot_version == 'false')
- run: |
- cd ${{ inputs.path_to_package }}
- mvn --batch-mode deploy
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Authenticate to Google Cloud
- if: |
- (github.event_name == 'push' && steps.version_check.outputs.is_snapshot_version == 'true') ||
- (github.event_name == 'release' && steps.version_check.outputs.is_snapshot_version == 'false')
- uses: google-github-actions/auth@v2
- with:
- credentials_json: "${{ secrets.GCP_SA_KEY }}"
-
- - name: Publish package to Google Artifact Registry
- if: |
- (github.event_name == 'push' && steps.version_check.outputs.is_snapshot_version == 'true') ||
- (github.event_name == 'release' && steps.version_check.outputs.is_snapshot_version == 'false')
- run: |
- cd ${{ inputs.path_to_package }}
- mvn --batch-mode deploy -Partifact-registry
- env:
- GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
diff --git a/.github/workflows/reusable-npm.yml b/.github/workflows/reusable-npm.yml
deleted file mode 100644
index c5e300bec..000000000
--- a/.github/workflows/reusable-npm.yml
+++ /dev/null
@@ -1,83 +0,0 @@
-name: Reusable workflow for npm packages
-
-on:
- workflow_call:
- inputs:
- package_workspace:
- required: true
- type: string
- description: The package's workspace path relative to the node directory.
- secrets:
- npm-token:
- required: true
-
-permissions:
- contents: read
- id-token: write
-
-jobs:
- build:
- runs-on: ubuntu-latest
- if: github.event_name == 'pull_request'
- defaults:
- run:
- working-directory: ./node
-
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
-
- - uses: actions/setup-node@v4
- with:
- node-version: 20
-
- - name: Install dependencies
- run: npm ci
-
- - name: Build package
- run: npm run build -w ${{ inputs.package_workspace }}
-
- publish:
- runs-on: ubuntu-latest
- if: github.event_name == 'push' || github.event_name == 'release'
- defaults:
- run:
- working-directory: ./node
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
-
- - name: Check version (only for push events)
- if: github.event_name == 'push'
- id: version_check
- run: |
- VERSION=$(cd ${{ inputs.package_workspace }} && node --print "require('./package.json').version")
- echo "Package version: ${VERSION}"
- if [[ $VERSION == *"-next" ]]; then
- echo "is_next_version=true" >> $GITHUB_OUTPUT
- else
- echo "is_next_version=false" >> $GITHUB_OUTPUT
- fi
-
- - name: Set up Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
- registry-url: "https://registry.npmjs.org"
-
- - name: Install dependencies
- run: npm ci
-
- - name: Publish next version
- if: github.event_name == 'push' && steps.version_check.outputs.is_next_version == 'true'
- run: npm run publish:next -w ${{ inputs.package_workspace }}
- env:
- NODE_AUTH_TOKEN: ${{ secrets.npm-token }}
- NPM_CONFIG_PROVENANCE: true
-
- - name: Publish latest version
- if: github.event_name == 'release'
- run: npm run publish:latest -w ${{ inputs.package_workspace }}
- env:
- NODE_AUTH_TOKEN: ${{ secrets.npm-token }}
- NPM_CONFIG_PROVENANCE: true
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
deleted file mode 100644
index ec563894f..000000000
--- a/.github/workflows/stale.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: Close inactive issues
-on:
- schedule:
- - cron: "30 9 * * *"
-
-jobs:
- close-issues:
- runs-on: ubuntu-latest
- permissions:
- issues: write
- pull-requests: write
- steps:
- - uses: actions/stale@v6.0.1
- with:
- days-before-stale: 180
- days-before-close: 14
- stale-issue-label: "stale"
- stale-issue-message: "This issue is stale because it has been open for 180 days with no activity."
- close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
- stale-pr-message: "This PR is stale because it has been open 180 days with no activity."
- close-pr-message: "This PR was closed because it has been inactive for 14 days since being marked as stale."
- repo-token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 79ffbe1aa..0cab4cd17 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,20 @@
.openapi-generator
.openapi-generator-ignore
openapitools.json
-.DS_Store
\ No newline at end of file
+
+# IDE files
+.classpath
+.project
+.settings/
+.factorypath
+*.iml
+.idea/
+.vscode/
+
+# OS files
+.DS_Store
+
+# Build artifacts
+target/
+node_modules
+node/e2e-tests/test-results/.last-run.json
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 75329ecaa..81e19a2a3 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -2,14 +2,24 @@
"version": "0.2.0",
"configurations": [
{
+ // This works with terraform config 2-01_try-now
"type": "java",
- "name": "Debug Default Operator",
+ "name": "Debug Operator (try-now)",
"request": "launch",
"cwd": "${workspaceFolder}/java/operator/org.eclipse.theia.cloud.defaultoperator",
"mainClass": "org.eclipse.theia.cloud.defaultoperator.DefaultTheiaCloudOperatorLauncher",
"args": [
+ "--keycloak",
+ "--keycloakURL",
+ "https://${input:minikubeIP}.nip.io/keycloak/",
+ "--keycloakRealm",
+ "TheiaCloud",
+ "--keycloakClientId",
+ "theia-cloud",
"--instancesHost",
- "ws.$(minikube.ip).nip.io",
+ "ws.${input:minikubeIP}.nip.io",
+ "--serviceUrl",
+ "https://service.${input:minikubeIP}.nip.io",
"--cloudProvider",
"MINIKUBE",
"--sessionsPerUser",
@@ -19,10 +29,201 @@
"--storageClassName",
"default",
"--requestedStorage",
- "250Mi"
+ "250Mi",
+ "--bandwidthLimiter",
+ "WONDERSHAPER",
+ "--oAuth2ProxyVersion",
+ "v7.5.1"
],
"vmArgs": "-Dlog4j2.configurationFile=log4j2.xml",
"preLaunchTask": "Build and Install Operator library"
+ },
+ {
+ // This works with terraform config 2-02_monitor with authentication
+ "type": "java",
+ "name": "Debug Operator (monitor-auth)",
+ "request": "launch",
+ "cwd": "${workspaceFolder}/java/operator/org.eclipse.theia.cloud.defaultoperator",
+ "mainClass": "org.eclipse.theia.cloud.defaultoperator.DefaultTheiaCloudOperatorLauncher",
+ "args": [
+ "--enableMonitor",
+ "--enableActivityTracker",
+ "--keycloak",
+ "--keycloakURL",
+ "https://${input:minikubeIP}.nip.io/keycloak/",
+ "--keycloakRealm",
+ "TheiaCloud",
+ "--keycloakClientId",
+ "theia-cloud",
+ "--instancesHost",
+ "ws.${input:minikubeIP}.nip.io",
+ "--serviceUrl",
+ "https://service.${input:minikubeIP}.nip.io",
+ "--cloudProvider",
+ "MINIKUBE",
+ "--sessionsPerUser",
+ "3",
+ "--appId",
+ "asdfghjkl",
+ "--storageClassName",
+ "default",
+ "--requestedStorage",
+ "250Mi",
+ "--bandwidthLimiter",
+ "WONDERSHAPER",
+ "--oAuth2ProxyVersion",
+ "v7.5.1"
+ ],
+ "vmArgs": "-Dlog4j2.configurationFile=log4j2.xml",
+ "preLaunchTask": "Build and Install Operator library"
+ },
+ {
+ // This works with terraform config 2-02_monitor with no authentication
+ "type": "java",
+ "name": "Debug Operator (monitor-no-auth)",
+ "request": "launch",
+ "cwd": "${workspaceFolder}/java/operator/org.eclipse.theia.cloud.defaultoperator",
+ "mainClass": "org.eclipse.theia.cloud.defaultoperator.DefaultTheiaCloudOperatorLauncher",
+ "args": [
+ "--enableMonitor",
+ "--enableActivityTracker",
+ "--instancesHost",
+ "ws.${input:minikubeIP}.nip.io",
+ "--serviceUrl",
+ "https://service.${input:minikubeIP}.nip.io",
+ "--cloudProvider",
+ "MINIKUBE",
+ "--sessionsPerUser",
+ "3",
+ "--appId",
+ "asdfghjkl",
+ "--storageClassName",
+ "default",
+ "--requestedStorage",
+ "250Mi",
+ "--bandwidthLimiter",
+ "WONDERSHAPER",
+ "--oAuth2ProxyVersion",
+ "v7.5.1"
+ ],
+ "vmArgs": "-Dlog4j2.configurationFile=log4j2.xml",
+ "preLaunchTask": "Build and Install Operator library"
+ },
+ {
+ // This works with terraform config 2-03_try-now_paths
+ "type": "java",
+ "name": "Debug Operator (try-now-paths)",
+ "request": "launch",
+ "cwd": "${workspaceFolder}/java/operator/org.eclipse.theia.cloud.defaultoperator",
+ "mainClass": "org.eclipse.theia.cloud.defaultoperator.DefaultTheiaCloudOperatorLauncher",
+ "args": [
+ "--keycloak",
+ "--keycloakURL",
+ "https://${input:minikubeIP}.nip.io/keycloak/",
+ "--keycloakRealm",
+ "TheiaCloud",
+ "--keycloakClientId",
+ "theia-cloud",
+ "--usePaths",
+ "--instancesPath",
+ "instances",
+ "--instancesHost",
+ "${input:minikubeIP}.nip.io",
+ "--serviceUrl",
+ "https://${input:minikubeIP}.nip.io/service",
+ "--cloudProvider",
+ "MINIKUBE",
+ "--sessionsPerUser",
+ "3",
+ "--appId",
+ "asdfghjkl",
+ "--storageClassName",
+ "default",
+ "--requestedStorage",
+ "250Mi",
+ "--bandwidthLimiter",
+ "WONDERSHAPER",
+ "--oAuth2ProxyVersion",
+ "v7.5.1"
+ ],
+ "vmArgs": "-Dlog4j2.configurationFile=log4j2.xml",
+ "preLaunchTask": "Build and Install Operator library"
+ },
+ {
+ // This works with terraform config 2-04_try-now_paths_eager_start
+ "type": "java",
+ "name": "Debug Operator (eager start try-now-paths)",
+ "request": "launch",
+ "cwd": "${workspaceFolder}/java/operator/org.eclipse.theia.cloud.defaultoperator",
+ "mainClass": "org.eclipse.theia.cloud.defaultoperator.DefaultTheiaCloudOperatorLauncher",
+ "args": [
+ "--keycloak",
+ "--keycloakURL",
+ "https://${input:minikubeIP}.nip.io/keycloak/",
+ "--keycloakRealm",
+ "TheiaCloud",
+ "--keycloakClientId",
+ "theia-cloud",
+ "--usePaths",
+ "--instancesPath",
+ "instances",
+ "--instancesHost",
+ "${input:minikubeIP}.nip.io",
+ "--serviceUrl",
+ "https://${input:minikubeIP}.nip.io/service",
+ "--cloudProvider",
+ "MINIKUBE",
+ "--sessionsPerUser",
+ "3",
+ "--appId",
+ "asdfghjkl",
+ "--storageClassName",
+ "default",
+ "--requestedStorage",
+ "250Mi",
+ "--bandwidthLimiter",
+ "WONDERSHAPER",
+ "--oAuth2ProxyVersion",
+ "v7.5.1",
+ "--eagerStart"
+ ],
+ "vmArgs": "-Dlog4j2.configurationFile=log4j2.xml",
+ "preLaunchTask": "Build and Install Operator library"
+ },
+ {
+ // Attach to the service running (Task: Run Service)
+ "type": "java",
+ "name": "Attach to Service",
+ "request": "attach",
+ "hostName": "localhost",
+ "port": 5005
+ },
+ {
+ "type": "node",
+ "request": "launch",
+ "name": "Debug Theia Example Browser Backend",
+ "program": "${workspaceRoot}/theia/examples/browser-app/src-gen/backend/main.js",
+ "args": ["--loglevel=debug", "--port=3000", "--no-cluster"],
+ "env": {
+ "NODE_ENV": "development"
+ },
+ "sourceMaps": true,
+ "outFiles": [
+ "${workspaceRoot}/theia/node_modules/@theia/*/lib/**/*.js",
+ "${workspaceRoot}/theia/*/lib/**/*.js",
+ "${workspaceRoot}/theia/examples/browser-app/src-gen/**/*.js"
+ ],
+ "smartStep": true,
+ "internalConsoleOptions": "openOnSessionStart",
+ "outputCapture": "std"
+ }
+ ],
+ "inputs": [
+ {
+ "type": "promptString",
+ "id": "minikubeIP",
+ "description": "Provide your minikube ip address (execute 'minikube ip').",
+ "default": "192.168.59.101"
}
]
}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index eff85040d..e62443c22 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -37,7 +37,33 @@
"label": "Run Service",
"type": "shell",
"command": "mvn",
- "args": ["compile", "quarkus:dev"],
+ "args": [
+ "compile",
+ "quarkus:dev",
+ "-Dtheia.cloud.service.auth.token=asdfghjkl",
+ "-Dquarkus.http.port=8081",
+ "-Dtheia.cloud.use.keycloak=false"
+ ],
+ "options": {
+ "cwd": "${workspaceFolder}/java/service/org.eclipse.theia.cloud.service"
+ },
+ "problemMatcher": [],
+ "dependsOn": "Build and Install Common library"
+ },
+ {
+ "label": "Run Service (with keycloak)",
+ "type": "shell",
+ "command": "mvn",
+ "args": [
+ "compile",
+ "quarkus:dev",
+ "-Dtheia.cloud.service.auth.token=asdfghjkl",
+ "-Dquarkus.http.port=8081",
+ "-Dtheia.cloud.use.keycloak=true",
+ "-Dquarkus.oidc.auth-server-url=${input:keycloakURL}/realms/TheiaCloud",
+ "-Dquarkus.oidc.client-id=theia-cloud",
+ "-Dquarkus.oidc.credentials.secret=publicbutoauth2proxywantsasecret"
+ ],
"options": {
"cwd": "${workspaceFolder}/java/service/org.eclipse.theia.cloud.service"
},
@@ -69,5 +95,13 @@
"problemMatcher": [],
"dependsOn": "Build and Install Common library"
}
+ ],
+ "inputs": [
+ {
+ "type": "promptString",
+ "id": "keycloakURL",
+ "description": "Provide the keycloak url",
+ "default": "https://192.168.59.101.nip.io/keycloak"
+ }
]
}
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 000000000..d9b53d068
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,206 @@
+# AGENTS.md — Theia Cloud Operator
+
+Reference guide for AI coding agents operating in this repository.
+Read this file at the start of every session.
+
+---
+
+## Project Overview
+
+Multi-language, multi-module system for running Theia IDE instances on Kubernetes.
+
+| Module | Language | Purpose |
+|--------|----------|---------|
+| `java/common/` | Java 21 / Maven | Shared K8s client, CRD models, utilities |
+| `java/operator/` | Java 21 / Maven + Guice | Kubernetes Operator (manages Sessions, AppDefinitions) |
+| `java/service/` | Java 21 / Maven + Quarkus | REST API for workspace/session management |
+| `java/conversion/` | Java 21 / Maven | K8s CRD conversion webhooks |
+| `node/` | TypeScript / NPM Workspaces | Landing page (React/Vite), monitor, e2e tests |
+| `theia/` | TypeScript / Yarn + Lerna | Theia extensions (monitor, config-store) |
+| `dockerfiles/` | — | Multi-stage Docker images for all services |
+| `terraform/` | Terraform | Not actively maintained by our team, might be outdated. |
+
+Current version: `1.2.0-SNAPSHOT`. Active feature branch: `feature/external-ls-v2`.
+
+---
+
+## Build & Test Commands
+
+### Java (Maven)
+
+All Java modules share a parent POM at `java/common/maven-conf/pom.xml`.
+Run from `java/` (all modules) or from any individual module directory.
+
+```bash
+# Build everything
+cd java && mvn clean install
+
+# Build a single module (e.g. operator)
+cd java/operator/org.eclipse.theia.cloud.operator && mvn clean install
+
+# Run all tests
+mvn test
+
+# Run a single test class
+mvn test -Dtest=WorkspaceResourceTests
+
+# Run a single test method
+mvn test -Dtest=WorkspaceResourceTests#testCreateWorkspace
+
+# Integration tests (Quarkus @QuarkusTest)
+mvn verify
+
+# Native Quarkus build
+mvn install -Pnative
+
+# Sentry source bundle upload (requires SENTRY_AUTH_TOKEN)
+mvn install -Psentry
+```
+
+### Node / Frontend (`node/`)
+
+Node ≥ 20 required.
+
+```bash
+cd node
+npm install
+npm run build # builds all workspaces + monitor
+npm run lint # lints all workspaces + monitor
+npm run test # runs e2e-tests workspace
+```
+
+### Theia Extensions (`theia/`)
+
+Yarn 1.x + Lerna.
+
+```bash
+cd theia
+yarn
+yarn build # clean + lerna run build
+yarn lint # lerna run lint
+yarn watch # lerna run watch (dev mode)
+```
+
+---
+
+## Java Code Style
+
+### File Header
+
+Every Java file must start with the EPL-2.0 copyright block:
+
+```java
+/********************************************************************************
+ * Copyright (C) EclipseSource and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+```
+
+### General Conventions
+
+- **Java version**: 21. Use records, text blocks, and pattern matching where appropriate.
+- **Indentation**: 4 spaces, no tabs.
+- **Braces**: Egyptian style (opening brace on same line).
+- **Line length**: ~120 characters.
+- **Imports**: No wildcards. Group: `java.*`, `javax.*`/`jakarta.*`, third-party, project internal. Static imports at top, e.g. `import static org.eclipse.theia.cloud.common.util.LogMessageUtil.formatLogMessage;`.
+- **Naming**: PascalCase for classes/records/enums, camelCase for methods/variables, SCREAMING_SNAKE_CASE for constants.
+- **Annotations**: `@Inject` (Google Guice in operator; Jakarta in service), `@Singleton`, `@Override` always present.
+
+### Logging
+
+- Logger declaration: `private static final Logger LOGGER = LogManager.getLogger(ClassName.class);` (Log4j 2).
+- Always pass `correlationId` as first argument via `formatLogMessage(correlationId, "...")`.
+- Use `LOGGER.info(...)` for normal flow, `LOGGER.warn(...)` for best-effort failures, `LOGGER.error(...)` for unexpected errors.
+- Never log sensitive data (tokens, passwords).
+
+### Tracing (Sentry)
+
+Use `Tracing.*` — **not** `SentryHelper.*` (removed):
+
+```java
+ISpan span = Tracing.childSpan("operation.name", "Human description");
+try {
+ // work
+ Tracing.finishSuccess(span);
+} catch (Exception e) {
+ Tracing.finishError(span, e);
+ throw e;
+}
+```
+
+Always finalize spans in `finally` or catch blocks — never leave them open.
+
+### Error Handling
+
+- Catch specific exceptions first; add `catch (RuntimeException e)` as a safety net in factory/resource methods so callers always receive `Optional.empty()` rather than propagated exceptions.
+- Never use empty catch blocks.
+- Service layer: throw `TheiaCloudWebException` (maps to HTTP error responses).
+- Operator layer: return `boolean` / `Optional` to signal success/failure; log at `warn`/`error` level.
+
+### Kubernetes (Fabric8)
+
+- Always null-check `getEnv()`, `getVolumes()`, `getVolumeMounts()` — they can return `null` on freshly-templated objects. Initialize to `new ArrayList<>()` before mutation.
+- Use `TheiaCloudPersistentVolumeUtil.getTheiaContainer(podSpec, appDefSpec)` to locate the Theia container (never match by container name directly).
+- Fabric8 `.edit()` lambdas cannot return values; use `AtomicBoolean` / `AtomicReference` to communicate state out of the lambda.
+
+### Sidecar Specifics (operator module)
+
+- Sidecar operations are **best-effort**: if `createSidecars()` returns `false`, log a warning but continue the session flow.
+- Sidecar configuration is read from `AppDefinitionSpec.getSidecars()` (v1beta11 CRD) **only**. Do not reintroduce `options["langserver-image"]` fallback.
+- No hardcoded language detection — sidecar names and languages are fully user-defined in the CRD.
+- Sidecar resources (Deployment + Service) are created via Fabric8 builders in `SidecarResourceFactory` — no YAML templates.
+- Rollback orphaned sidecar resources via `factory.deleteResources(session, correlationId)`, not via the K8s client directly.
+- Eager path: sidecar Deployment+Service must be created **before** Theia Deployment so K8s DNS resolves service names at pod startup.
+- Session release (eager): only restart sidecar Pods (`restartPrewarmedSidecarPods`), do **not** delete Deployment+Service — they are reused by the pool.
+- Lazy path: use the same naming source for injected sidecar host env vars and created Service names (session-based naming) to avoid DNS mismatch.
+
+### Session Launch Rules (service module)
+
+- Ephemeral session launch is **not supported** for sidecar-enabled app definitions. Return `400 Bad Request` and instruct callers to use workspace-backed session launch.
+- Sidecar-enabled sessions should be workspace-backed so Theia and sidecar can operate on shared persistent storage.
+
+---
+
+## TypeScript / Frontend Code Style
+
+Configured via `.prettierrc.js` (in `theia/`) and ESLint with `typescript-eslint` + `simple-import-sort`.
+
+| Setting | Value |
+|---------|-------|
+| Indent | 2 spaces |
+| Quotes | Single (`'`) |
+| JSX quotes | Single |
+| Trailing commas | None |
+| Print width | 120 (100 for JSON/YAML) |
+| Arrow parens | Avoid for single arg |
+| End of line | LF |
+
+- Imports sorted by `eslint-plugin-simple-import-sort`.
+- File headers: same EPL-2.0 copyright block as Java (adapted for JS/TS comment syntax).
+
+---
+
+## Key Patterns to Follow
+
+### Dependency Injection
+
+- **Operator**: Google Guice — use `@Inject` on fields, bind in a `Module`.
+- **Service**: Quarkus/Jakarta CDI — `@Inject`, `@ApplicationScoped`, `@RequestScoped`.
+- Never instantiate service classes with `new` inside production code.
+
+### Optional Usage
+
+- Prefer `Optional` return types over `null` for "may not exist" results.
+- Always check `Optional.isEmpty()` before acting, and log at warn/error if absent when presence was expected.
+
+### Tests
+
+- **Framework**: JUnit 5 (`@Test`, `@BeforeEach`), Mockito (`@Mock`, `@InjectMocks`).
+- **Quarkus tests**: `@QuarkusTest` + `@InjectMock` for CDI bean injection in tests.
+- Test class names end with `Tests` (e.g., `WorkspaceResourceTests`).
+- Static imports: `org.junit.jupiter.api.Assertions.*`, `org.mockito.Mockito.*`.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 669370037..1e5194dfe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,50 +1,111 @@
# Changelog
-## [0.12.0] - estimated 2024-10
+## [1.2.0] - estimated between 2025-11 and 2026-05
+
+## [1.1.2] - 2025-09-26
+
+- [java/operator] Use ingress path type ImplementationSpecific instead of Prefix [#449](https://github.com/eclipse-theia/theia-cloud/pull/449)
+
+## [1.1.1] - 2025-08-01
+
+- [java/operator] Create missing internal services in eager start app definition handler [#441](https://github.com/eclipse-theia/theia-cloud/pull/441)
+
+## [1.1.0] - 2025-08-01
+
+- [theia] Introduce new folder `theia` for all Theia extensions and an example app to test these [#389](https://github.com/eclipse-theia/theia-cloud/pull/389)
+- [node/monitor-theia] Move Theia monitor extension to `theia/extensions/monitor-theia` [#389](https://github.com/eclipse-theia/theia-cloud/pull/389)
+- [theia/extensions/monitor-theia] Update Theia dependencies to `^1.55.0` [#389](https://github.com/eclipse-theia/theia-cloud/pull/389)
+- [ci] Add Theia CI workflow, add reusable Theia extension publish workflow [#389](https://github.com/eclipse-theia/theia-cloud/pull/388)
+- [java] Fix and improve eager start handling [#393](https://github.com/eclipse-theia/theia-cloud/pull/393), [#396](https://github.com/eclipse-theia/theia-cloud/pull/396)
+- [java/service] Add admin user concept and app definition scaling endpoint [#400](https://github.com/eclipse-theia/theia-cloud/pull/400)
+- [docker] Provide ARM64 images [#404](https://github.com/eclipse-theia/theia-cloud/pull/404), [#408](https://github.com/eclipse-theia/theia-cloud/pull/408)
+- [java/common] Truncate labels longer than 63 chars [#413](https://github.com/eclipse-theia/theia-cloud/pull/413), [#417](https://github.com/eclipse-theia/theia-cloud/pull/417)
+- [java/conversion] Allow to set conversion webhook certificate reload period [#410](https://github.com/eclipse-theia/theia-cloud/pull/410)
+- [all components] Introduce config store Theia extension and REST endpoint [#432](https://github.com/eclipse-theia/theia-cloud/pull/432), [#438](https://github.com/eclipse-theia/theia-cloud/pull/438)
+
+### Breaking Changes in 1.1.0
+
+- [java/common] Changed `LabelsUtil.createSessionLabels` to accept `AppDefinition` instead of `AppDefinitionSpec`
+
+## [1.0.1] - 2025-03-24
+
+- [java/common] Truncate labels longer than 63 chars [#413](https://github.com/eclipse-theia/theia-cloud/pull/413)
+
+## [1.0.0] - 2024-11-29
+
+- [java/operator] Add Theia Cloud Labels on Resources created by operator [#362](https://github.com/eclipse-theia/theia-cloud/pull/362)
+- [node] Updated Node Dependencies [#371](https://github.com/eclipse-theia/theia-cloud/pull/371) - contributed on behalf of STMicroelectronics
+- [terraform] Update `cert-manager`, `ingress-nginx`, and `keycloak` [#371](https://github.com/eclipse-theia/theia-cloud/pull/371) - contributed on behalf of STMicroelectronics
+
+## [0.12.0] - 2024-10-30
+
+- [All components] Documented naming conventions and updated code to follow [#368](https://github.com/eclipse-theia/theia-cloud/pull/368)
+- [documentation] Add REST API markdown docs [#363](https://github.com/eclipse-theia/theia-cloud/pull/363)
+- [node/common] Update Common Package to support listing app definitions [#361](https://github.com/eclipse-theia/theia-cloud/pull/361)
+- [java/service] Extend service with a list app definitions endpoint [#361](https://github.com/eclipse-theia/theia-cloud/pull/361)
+- [documentation] Improved debugging documentation [#354](https://github.com/eclipse-theia/theia-cloud/pull/354)
+- [java] Fixed issue where monitor might have stopped session during session startup [#354](https://github.com/eclipse-theia/theia-cloud/pull/354)
+- [terraform] Added Devcontainer for GKE demo [#353](https://github.com/eclipse-theia/theia-cloud/pull/353)
+- [github] Moved from `eclipsesource` to `eclipse-theia` org as an official Theia subproject [#353](https://github.com/eclipse-theia/theia-cloud/pull/353) [#358](https://github.com/eclipse-theia/theia-cloud/pull/358)
+- [node] Updated Node Dependencies [#345](https://github.com/eclipse-theia/theia-cloud/pull/345) - contributed on behalf of STMicroelectronics
+- [java] Updated Maven Dependencies [#345](https://github.com/eclipse-theia/theia-cloud/pull/345) - contributed on behalf of STMicroelectronics
+
+### Breaking Changes in 0.12.0
+
+See the helm chart Changelog for [more details](https://github.com/eclipse-theia/theia-cloud-helm/blob/main/CHANGELOG.md#breaking-changes-in-0120).
+
+We did some renaming in preparation for 1.0.0
+
+- NOTE: default namespace renamed from `theiacloud` to `theia-cloud`
+- Config Map template label key updated from `theiacloud` to `theia-cloud.io/template-purpose`
+- PVC label `theia.cloud.workspace.name` renamed to `theia-cloud.io/workspace-name`
+- System property `THEIA_CLOUD_APP_ID` renamed to `THEIACLOUD_APP_ID`
+- System property `THEIA_CLOUD_USE_KEYCLOAK` renamed to `THEIACLOUD_USE_KEYCLOAK`
+- Theia Monitor paths changed from `/services/theiacloud-` to `/services/theia-cloud-`
## [0.11.0] - 2024-07-23
-- [common] Add option field to CRDs and increase version to `Session.v1beta8`, `Workspace.v1beta5` and `AppDefinition.v1beta10` [#293](https://github.com/eclipsesource/theia-cloud/pull/293) | [#55](https://github.com/eclipsesource/theia-cloud-helm/pull/55)
-- [java] Separate operator default implementation from library to allow for easier customization [#303](https://github.com/eclipsesource/theia-cloud/pull/303)
-- [node] Unify the existing landing and try now pages to a new ViteJS based landing page [#304](https://github.com/eclipsesource/theia-cloud/pull/304) | [#58](https://github.com/eclipsesource/theia-cloud-helm/pull/58) - contributed on behalf of STMicroelectronics
+- [common] Add option field to CRDs and increase version to `Session.v1beta8`, `Workspace.v1beta5` and `AppDefinition.v1beta10` [#293](https://github.com/eclipse-theia/theia-cloud/pull/293) | [#55](https://github.com/eclipse-theia/theia-cloud-helm/pull/55)
+- [java] Separate operator default implementation from library to allow for easier customization [#303](https://github.com/eclipse-theia/theia-cloud/pull/303)
+- [node] Unify the existing landing and try now pages to a new ViteJS based landing page [#304](https://github.com/eclipse-theia/theia-cloud/pull/304) | [#58](https://github.com/eclipse-theia/theia-cloud-helm/pull/58) - contributed on behalf of STMicroelectronics
- The new page is based on the old try now page but uses ViteJS instead of the deprecated Create React App
- Extend configuration options for the new landing page for texts and logo file type
- Removed terms and conditions
- Build the common package as ESM and CJS bundles for extended compatibility
-- [common] Add `ingressHostnamePrefixes` list to `AppDefinition.v1beta10` [#298](https://github.com/eclipsesource/theia-cloud/pull/298) | [#57](https://github.com/eclipsesource/theia-cloud-helm/pull/57)
-- [java] Improved naming for kubernetes resources [#326](https://github.com/eclipsesource/theia-cloud/pull/326)
+- [common] Add `ingressHostnamePrefixes` list to `AppDefinition.v1beta10` [#298](https://github.com/eclipse-theia/theia-cloud/pull/298) | [#57](https://github.com/eclipse-theia/theia-cloud-helm/pull/57)
+- [java] Improved naming for kubernetes resources [#326](https://github.com/eclipse-theia/theia-cloud/pull/326)
### Breaking Changes in 0.11.0
-See the helm chart Changelog for [more details](https://github.com/eclipsesource/theia-cloud-helm/blob/main/CHANGELOG.md).
+See the helm chart Changelog for [more details](https://github.com/eclipse-theia/theia-cloud-helm/blob/main/CHANGELOG.md).
## [0.10.0] - 2024-04-02
-- [.github/workflows] Improve version detection in workflows (do not build release commits, auto-detect version for demo publishing) [#280](https://github.com/eclipsesource/theia-cloud/pull/280) - contributed on behalf of STMicroelectronics
-- [node] Separate `monitor` package from other workspaces to fix bundling the extension [#280](https://github.com/eclipsesource/theia-cloud/pull/280) - contributed on behalf of STMicroelectronics
-- [conversion] Provide java conversion webhook for CRD updates [#283](https://github.com/eclipsesource/theia-cloud/pull/283) | [#49](https://github.com/eclipsesource/theia-cloud-helm/pull/49) - contributed on behalf of STMicroelectronics
-- [.github/workflows] Add ci for `conversion-webhook` and fix typo to build on version bumps [#283](https://github.com/eclipsesource/theia-cloud/pull/283) | [#49](https://github.com/eclipsesource/theia-cloud-helm/pull/49) - contributed on behalf of STMicroelectronics
-- [common] Update CRs, keep previous version and offer Hub (used by conversion-webhook) [#283](https://github.com/eclipsesource/theia-cloud/pull/283) | [#49](https://github.com/eclipsesource/theia-cloud-helm/pull/49) - contributed on behalf of STMicroelectronics
+- [.github/workflows] Improve version detection in workflows (do not build release commits, auto-detect version for demo publishing) [#280](https://github.com/eclipse-theia/theia-cloud/pull/280) - contributed on behalf of STMicroelectronics
+- [node] Separate `monitor` package from other workspaces to fix bundling the extension [#280](https://github.com/eclipse-theia/theia-cloud/pull/280) - contributed on behalf of STMicroelectronics
+- [conversion] Provide java conversion webhook for CRD updates [#283](https://github.com/eclipse-theia/theia-cloud/pull/283) | [#49](https://github.com/eclipse-theia/theia-cloud-helm/pull/49) - contributed on behalf of STMicroelectronics
+- [.github/workflows] Add ci for `conversion-webhook` and fix typo to build on version bumps [#283](https://github.com/eclipse-theia/theia-cloud/pull/283) | [#49](https://github.com/eclipse-theia/theia-cloud-helm/pull/49) - contributed on behalf of STMicroelectronics
+- [common] Update CRs, keep previous version and offer Hub (used by conversion-webhook) [#283](https://github.com/eclipse-theia/theia-cloud/pull/283) | [#49](https://github.com/eclipse-theia/theia-cloud-helm/pull/49) - contributed on behalf of STMicroelectronics
- Move status like fields to status
- `Session.v1beta7`: Move `url`, `lastActivity` and `error` fields from the spec to the status.
- `Workspace.v1beta4`: Move the `error` field from the spec to the status. Also add the `error` field to `Workspace.v1beta3` as it was missing
- Remove `timeout.strategy` from AppDefinition
- `AppDefinition.v1beta9`: Removed `timeout.strategy` and `timeout.limit` is now just `timeout`. This was done, as there is only one Strategy left.
-- [java] Update io.fabric8.kubernetes-client to version 6.10.0. Also update Quarkus platform to 3.8.1. This provides kubernetes 1.29 support [#287](https://github.com/eclipsesource/theia-cloud/pull/287)
-- [terraform] Change terraform values to conform to helm chart changes [#289](https://github.com/eclipsesource/theia-cloud/pull/289) | [#52](https://github.com/eclipsesource/theia-cloud-helm/pull/52) - contributed on behalf of STMicroelectronics
+- [java] Update io.fabric8.kubernetes-client to version 6.10.0. Also update Quarkus platform to 3.8.1. This provides kubernetes 1.29 support [#287](https://github.com/eclipse-theia/theia-cloud/pull/287)
+- [terraform] Change terraform values to conform to helm chart changes [#289](https://github.com/eclipse-theia/theia-cloud/pull/289) | [#52](https://github.com/eclipse-theia/theia-cloud-helm/pull/52) - contributed on behalf of STMicroelectronics
## [0.9.0] - 2024-01-23
-- [node/landing-page] Make npm workspace and remove yarn references from repo [#258](https://github.com/eclipsesource/theia-cloud/pull/258) | [#45](https://github.com/eclipsesource/theia-cloud-helm/pull/45) - contributed on behalf of STMicroelectronics
-- [All components] Align [versioning](https://github.com/eclipsesource/theia-cloud#versioning) between all components and introduce Changelog [#258](https://github.com/eclipsesource/theia-cloud/pull/258) | [#45](https://github.com/eclipsesource/theia-cloud-helm/pull/45) - contributed on behalf of STMicroelectronics
-- [All components] Update CRD version scheme for all CRDs from `theia.cloud/vXbeta` to `theia.cloud/v1betaX` [#266](https://github.com/eclipsesource/theia-cloud/pull/266) | [#46](https://github.com/eclipsesource/theia-cloud-helm/pull/46) - contributed on behalf of STMicroelectronics
-- [java/service] Update Quarkus from 2.12.0 to 3.3.1 [#266](https://github.com/eclipsesource/theia-cloud/pull/266) - contributed on behalf of STMicroelectronics
-- [java] Update fabric8 Kubernetes client from 5.12.2 to 6.7.2 [#266](https://github.com/eclipsesource/theia-cloud/pull/266) - contributed on behalf of STMicroelectronics
-- [java/common] Introduce hub-objects for all custom resources [#266](https://github.com/eclipsesource/theia-cloud/pull/266) - contributed on behalf of STMicroelectronics
-- [demo] Fix demo applications to Theia's latest Community Release (1.43.1) [#267](https://github.com/eclipsesource/theia-cloud/pull/267) - contributed on behalf of STMicroelectronics
-- [node/monitor-theia] Make `@theia` dependencies peerDependencies [#267](https://github.com/eclipsesource/theia-cloud/pull/267) - contributed on behalf of STMicroelectronics
-- [node] Update to node 20 [#269](https://github.com/eclipsesource/theia-cloud/pull/269)
-- [All components] Clean up repository [#275](https://github.com/eclipsesource/theia-cloud/pull/275) - contributed on behalf of STMicroelectronics
+- [node/landing-page] Make npm workspace and remove yarn references from repo [#258](https://github.com/eclipse-theia/theia-cloud/pull/258) | [#45](https://github.com/eclipse-theia/theia-cloud-helm/pull/45) - contributed on behalf of STMicroelectronics
+- [All components] Align [versioning](https://github.com/eclipse-theia/theia-cloud#versioning) between all components and introduce Changelog [#258](https://github.com/eclipse-theia/theia-cloud/pull/258) | [#45](https://github.com/eclipse-theia/theia-cloud-helm/pull/45) - contributed on behalf of STMicroelectronics
+- [All components] Update CRD version scheme for all CRDs from `theia.cloud/vXbeta` to `theia.cloud/v1betaX` [#266](https://github.com/eclipse-theia/theia-cloud/pull/266) | [#46](https://github.com/eclipse-theia/theia-cloud-helm/pull/46) - contributed on behalf of STMicroelectronics
+- [java/service] Update Quarkus from 2.12.0 to 3.3.1 [#266](https://github.com/eclipse-theia/theia-cloud/pull/266) - contributed on behalf of STMicroelectronics
+- [java] Update fabric8 Kubernetes client from 5.12.2 to 6.7.2 [#266](https://github.com/eclipse-theia/theia-cloud/pull/266) - contributed on behalf of STMicroelectronics
+- [java/common] Introduce hub-objects for all custom resources [#266](https://github.com/eclipse-theia/theia-cloud/pull/266) - contributed on behalf of STMicroelectronics
+- [demo] Fix demo applications to Theia's latest Community Release (1.43.1) [#267](https://github.com/eclipse-theia/theia-cloud/pull/267) - contributed on behalf of STMicroelectronics
+- [node/monitor-theia] Make `@theia` dependencies peerDependencies [#267](https://github.com/eclipse-theia/theia-cloud/pull/267) - contributed on behalf of STMicroelectronics
+- [node] Update to node 20 [#269](https://github.com/eclipse-theia/theia-cloud/pull/269)
+- [All components] Clean up repository [#275](https://github.com/eclipse-theia/theia-cloud/pull/275) - contributed on behalf of STMicroelectronics
## [0.8.1] - 2023-10-01
diff --git a/README.md b/README.md
index ead3fb9df..15039aa6d 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,15 @@
-# Theia Cloud
+# EduIDE Cloud
-[](https://try.theia-cloud.io/) [](https://theia-cloud.io/)
-
-The goal of Theia Cloud is to simplify the deployment of Theia-based (and similar) products on Kubernetes. We follow a convention over configuration approach allowing users to get started fast. At the same time, we aim for extensibility allowing developers to customize all aspects of the kubernetes deployment as required.
+EduIDE Cloud is based on the Theia Cloud project. The goal of EduIDE Cloud is to simplify the deployment of Theia-based (and similar) products on Kubernetes. We follow a convention over configuration approach allowing users to get started fast. At the same time, we aim for extensibility allowing developers to customize all aspects of the kubernetes deployment as required.
For more information, please also visit [our website](https://theia-cloud.io/).
## Feedback, Help and Support
-If you encounter any problems feel free to [open an issue](https://github.com/eclipsesource/theia-cloud/issues/new/choose) on the repo.
-For questions and discussions please use the [the Github discussions](https://github.com/eclipsesource/theia-cloud/discussions).
+If you encounter any problems feel free to [open an issue](https://github.com/eclipse-theia/theia-cloud/issues/new/choose) on the repo.
+For questions and discussions please use the [the Github discussions](https://github.com/eclipse-theia/theia-cloud/discussions).
You can also reach us via [email](mailto:support@theia-cloud.io?subject=Theia_Cloud).
-In addition, EclipseSource also offers [professional support](https://eclipsesource.com/services/developer-support/) for Theia and Theia Cloud.
+In addition, EclipseSource also offers [professional support](https://eclipse-theia.com/services/developer-support/) for Theia and Theia Cloud.
## Components
@@ -28,11 +26,15 @@ See [Architecture.md](documentation/Architecture.md) for more information on the
This REST Service acts as the API for creating and stopping Theia-based products for an authenticated user as well as providing additional information.\
The Theia Cloud service creates, modifies, and deletes the custom resources the operator listens to.
-### Sample Dashboard and reusable JS/UI components
+#### Scaling admin endpoints
+
+Scaling operations for `AppDefinition` resources are exposed through admin endpoints under `/service/admin/appdefinition`.
+Access is restricted to callers presenting the `X-Admin-Api-Token` header with the token from `theia.cloud.admin.api.token`.
+In Helm-based deployments, provide this token through a pre-created Kubernetes Secret referenced by `service.adminApiTokenSecret`.
-Theia Cloud comes with a basic landing page/dashboard which allows to launch sessions.
+### Reusable JS/UI components
-We offer a common ts library for the API of the Theia Cloud service, which may be reused by clients to write their own custom dashboards.
+We offer a common ts library for the API of the Theia Cloud service, which may be reused by clients to write custom dashboards.
We plan to offer reusable ui components in the future as well.
@@ -45,7 +47,7 @@ From version 0.9.0 onwards, every component in this repository (and the helm cha
- **Releases:** Standard releases will occur every three months. We recommend to use these for deployments as those are thoroughly tested and are stable versions. You can then update, after three months, when the next version is available.
- **Pre-Releases:** Pre-release versions will be released on every commit. These versions will be tagged as `-next.`. The latest version of a next version is available at `-next`. Pre-releases are ideal for testing the latest features and changes or for making contributions. However we do not recommend, to use those versions in deployments.
-The [helm charts](https://github.com/eclipsesource/theia-cloud-helm) are referencing the compatible version in their `appVersion` field.
+The [helm charts](https://github.com/eclipse-theia/theia-cloud-helm) are referencing the compatible version in their `appVersion` field.
Since, npm does not allow tags that follow Semver, next artifacts published to npm have the `next` tag instead of ``.
This means, that those dependencies will be updated to newer version, once they are available. So again, for deployments you should either pin the version to a specific commit or use the released versions.
@@ -77,13 +79,23 @@ All components are deployed as docker images and may be built with docker. See [
## Installation
-We offer a helm chart at which may be used to install Theia Cloud. Please check our getting started guides below as well, which will explain the possible values in more detail.
+We offer a helm chart at which may be used to install Theia Cloud. Please check our getting started guides below as well, which will explain the possible values in more detail.
We offer three charts:
- `theia-cloud-base` installs cluster wide resources that may be reused by multiple Theia Cloud installations in different namespaces
- `theia-cloud-crds` (starting with version 0.8.1) installs the custom resource definitions for Theia Cloud and migration servers for older custom resources. This may be reused by multiple Theia Cloud installations in different namespaces.
-- `theia-cloud` installs the Theia Cloud operators, service, and landing-page. It depends on the two above charts.
+- `theia-cloud` installs the Theia Cloud operators and service. It depends on the two above charts.
+
+### Cluster prerequisites (Gateway API / Envoy Gateway)
+
+Theia Cloud now configures session traffic using Gateway API resources (`HTTPRoute` + `Gateway`).
+
+- Gateway API CRDs must be installed on the cluster.
+- Envoy Gateway must be installed and used as the Gateway controller (GatewayClass should match your Helm value `gateway.className`, default: `envoy`).
+- The operator sets `X-Forwarded-Uri` using Envoy runtime syntax (`%REQ(:PATH)%`) in route rules; on non-Envoy controllers this value may be treated as a literal string.
+
+For setup details, see [documentation/Install.md](documentation/Install.md).
Starting with version 0.8.1 you may use helm upgrade to update to newer Theia Cloud version.\
Older versions (before the introduction of `theia-cloud-crds`) require a manual uninstall and reinstall as well as a manual CRD upgrade step.
@@ -91,7 +103,7 @@ Older versions (before the introduction of `theia-cloud-crds`) require a manual
### Add & update Theia Cloud helm repo
```bash
-helm repo add theia-cloud-remote https://github.eclipsesource.com/theia-cloud-helm
+helm repo add theia-cloud-remote https://eclipse-theia.github.io/theia-cloud-helm
helm repo update
```
@@ -100,9 +112,9 @@ helm repo update
```bash
helm install theia-cloud-base theia-cloud-remote/theia-cloud-base --set issuer.email=your-mail@example.com
-helm install theia-cloud-crds theia-cloud-remote/theia-cloud-crds --namespace theiacloud --create-namespace
+helm install theia-cloud-crds theia-cloud-remote/theia-cloud-crds --namespace theia-cloud --create-namespace
-helm install theia-cloud theia-cloud-remote/theia-cloud --namespace theiacloud
+helm install theia-cloud theia-cloud-remote/theia-cloud --namespace theia-cloud
```
### Install the current next version
@@ -112,21 +124,21 @@ Simply add the `--devel` flag:
```bash
helm install theia-cloud-base theia-cloud-remote/theia-cloud-base --set issuer.email=your-mail@example.com --devel
-helm install theia-cloud-crds theia-cloud-remote/theia-cloud-crds --namespace theiacloud --create-namespace --devel
+helm install theia-cloud-crds theia-cloud-remote/theia-cloud-crds --namespace theia-cloud --create-namespace --devel
-helm install theia-cloud theia-cloud-remote/theia-cloud --namespace theiacloud --devel
+helm install theia-cloud theia-cloud-remote/theia-cloud --namespace theia-cloud --devel
```
### Optional: switch to the newly created namespace
```bash
-kubectl config set-context --current --namespace=theiacloud
+kubectl config set-context --current --namespace=theia-cloud
```
### Uninstall
```bash
-helm uninstall theia-cloud -n theiacloud
+helm uninstall theia-cloud -n theia-cloud
```
### Getting started with
diff --git a/SECURITY.md b/SECURITY.md
index c0cd05402..ded8fca7e 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -16,4 +16,4 @@ Full disclosure to the general public happens once there is a patch, fix, or wor
Non-committers will be acknowledged for their security research on disclosure if the issue has been reported responsibly.
-Publicly disclosed issues are tagged with the [security label](https://github.com/eclipsesource/theia-cloud/labels/security).
+Publicly disclosed issues are tagged with the [security label](https://github.com/eclipse-theia/theia-cloud/labels/security).
diff --git a/demo/dockerfiles/demo-operator-custom-template/.project b/demo/dockerfiles/demo-operator-custom-template/.project
deleted file mode 100644
index 9d0ea3a53..000000000
--- a/demo/dockerfiles/demo-operator-custom-template/.project
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- demo-operator-custom-template
-
-
-
-
-
-
-
-
diff --git a/demo/dockerfiles/demo-operator-custom-template/templateDeployment.yaml b/demo/dockerfiles/demo-operator-custom-template/templateDeployment.yaml
index 686dc6304..3ad484399 100644
--- a/demo/dockerfiles/demo-operator-custom-template/templateDeployment.yaml
+++ b/demo/dockerfiles/demo-operator-custom-template/templateDeployment.yaml
@@ -22,7 +22,7 @@ spec:
containers:
- name: oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:v7.4.0
- imagePullPolicy: Always
+ imagePullPolicy: IfNotPresent
args:
- --config=/etc/oauth2-proxy.cfg
- --authenticated-emails-file=/emails/authenticated-emails-list
diff --git a/demo/dockerfiles/demo-theia-docker/.project b/demo/dockerfiles/demo-theia-docker/.project
deleted file mode 100644
index 655e42dfc..000000000
--- a/demo/dockerfiles/demo-theia-docker/.project
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- demo-theia-docker
-
-
-
-
-
-
-
-
diff --git a/demo/dockerfiles/demo-theia-docker/Dockerfile b/demo/dockerfiles/demo-theia-docker/Dockerfile
index 6fb560cf6..952615a44 100644
--- a/demo/dockerfiles/demo-theia-docker/Dockerfile
+++ b/demo/dockerfiles/demo-theia-docker/Dockerfile
@@ -1,4 +1,4 @@
-FROM ghcr.io/eclipse-theia/theia-blueprint/theia-ide:1.52.0 as production-stage
+FROM ghcr.io/eclipse-theia/theia-ide/theia-ide:1.56.1 AS production-stage
ENV THEIA_MINI_BROWSER_HOST_PATTERN {{hostname}}
ENV THEIA_WEBVIEW_ENDPOINT {{hostname}}
@@ -6,10 +6,9 @@ ENV THEIA_WEBVIEW_ENDPOINT {{hostname}}
# Copy project
COPY --chown=theia:theia project /home/project
COPY --chown=theia:theia settings.json /home/theia/theia-blueprint/applications/browser/.theia/settings.json
-COPY --chown=theia:theia settings.json /home/theia/theia-blueprint/applications/browser/.theia/settings.json
# Build projects once
RUN mvn clean verify -f /home/project/java/pom.xml && \
cd /home/project/web && \
npm install && npm run build && \
- cd /home/theia
\ No newline at end of file
+ cd /home/theia
diff --git a/demo/dockerfiles/demo-theia-docker/package.json b/demo/dockerfiles/demo-theia-docker/package.json
deleted file mode 100644
index 920515cbf..000000000
--- a/demo/dockerfiles/demo-theia-docker/package.json
+++ /dev/null
@@ -1,87 +0,0 @@
-{
- "private": true,
- "theia": {
- "frontend": {
- "config": {
- "applicationName": "Theia Cloud Example",
- "warnOnPotentiallyInsecureHostPattern": false,
- "preferences": {
- "files.enableTrash": false
- }
- }
- },
- "backend": {
- "config": {
- "warnOnPotentiallyInsecureHostPattern": false
- }
- }
- },
- "dependencies": {
- "@theia/bulk-edit": "1.43.1",
- "@theia/callhierarchy": "1.43.1",
- "@theia/console": "1.43.1",
- "@theia/core": "1.43.1",
- "@theia/debug": "1.43.1",
- "@theia/editor": "1.43.1",
- "@theia/editor-preview": "1.43.1",
- "@theia/electron": "1.43.1",
- "@theia/external-terminal": "1.43.1",
- "@theia/file-search": "1.43.1",
- "@theia/filesystem": "1.43.1",
- "@theia/getting-started": "1.43.1",
- "@theia/keymaps": "1.43.1",
- "@theia/markers": "1.43.1",
- "@theia/messages": "1.43.1",
- "@theia/metrics": "1.43.1",
- "@theia/mini-browser": "1.43.1",
- "@theia/monaco": "1.43.1",
- "@theia/navigator": "1.43.1",
- "@theia/outline-view": "1.43.1",
- "@theia/output": "1.43.1",
- "@theia/plugin-dev": "1.43.1",
- "@theia/plugin-ext": "1.43.1",
- "@theia/plugin-ext-vscode": "1.43.1",
- "@theia/preferences": "1.43.1",
- "@theia/process": "1.43.1",
- "@theia/property-view": "1.43.1",
- "@theia/scm": "1.43.1",
- "@theia/scm-extra": "1.43.1",
- "@theia/search-in-workspace": "1.43.1",
- "@theia/task": "1.43.1",
- "@theia/terminal": "1.43.1",
- "@theia/timeline": "1.43.1",
- "@theia/toolbar": "1.43.1",
- "@theia/typehierarchy": "1.43.1",
- "@theia/userstorage": "1.43.1",
- "@theia/variable-resolver": "1.43.1",
- "@theia/vsx-registry": "1.43.1",
- "@theia/workspace": "1.43.1"
- },
- "devDependencies": {
- "@theia/cli": "1.43.1"
- },
- "scripts": {
- "preinstall": "node-gyp install"
- },
- "theiaPluginsDir": "plugins",
- "theiaPlugins": {
- "vscode.git": "https://open-vsx.org/api/vscode/git/1.52.1/file/vscode.git-1.52.1.vsix",
- "vscode.markdown-language-features": "https://open-vsx.org/api/vscode/markdown-language-features/1.39.2/file/vscode.markdown-language-features-1.39.2.vsix",
- "vscode-builtin-extensions-pack": "https://open-vsx.org/api/eclipse-theia/builtin-extension-pack/1.50.1/file/eclipse-theia.builtin-extension-pack-1.50.1.vsix",
- "redhat.java": "https://open-vsx.org/api/redhat/java/0.73.0/file/redhat.java-0.73.0.vsix",
- "vscjava.vscode-java-debug": "https://open-vsx.org/api/vscjava/vscode-java-debug/0.30.0/file/vscjava.vscode-java-debug-0.30.0.vsix",
- "vscjava.vscode-java-test": "https://open-vsx.org/api/vscjava/vscode-java-test/0.26.1/file/vscjava.vscode-java-test-0.26.1.vsix",
- "vscjava.vscode-maven": "https://open-vsx.org/api/vscjava/vscode-maven/0.21.2/file/vscjava.vscode-maven-0.21.2.vsix",
- "vscjava.vscode-java-dependency": "https://open-vsx.org/api/vscjava/vscode-java-dependency/0.16.0/file/vscjava.vscode-java-dependency-0.16.0.vsix",
- "redhat.vscode-xml": "https://open-vsx.org/api/redhat/vscode-xml/0.20.0/file/redhat.vscode-xml-0.20.0.vsix",
- "cdt-gdb-vscode": "https://open-vsx.org/api/eclipse-cdt/cdt-gdb-vscode/0.0.92/file/eclipse-cdt.cdt-gdb-vscode-0.0.92.vsix",
- "vscode-builtin-cpp": "https://open-vsx.org/api/vscode/cpp/1.62.3/file/vscode.cpp-1.62.3.vsix",
- "vscode-clangd": "https://open-vsx.org/api/llvm-vs-code-extensions/vscode-clangd/0.1.15/file/llvm-vs-code-extensions.vscode-clangd-0.1.15.vsix",
- "vscode-cmake": "https://open-vsx.org/api/ms-vscode/cmake-tools/1.9.2/file/ms-vscode.cmake-tools-1.9.2.vsix"
- },
- "theiaPluginsExcludeIds": [
- "vscode.extension-editing",
- "vscode.microsoft-authentication",
- "vscode.html-language-features"
- ]
-}
diff --git a/demo/dockerfiles/demo-theia-docker/project/java/pom.xml b/demo/dockerfiles/demo-theia-docker/project/java/pom.xml
index c4ff1c434..d228e2ef2 100644
--- a/demo/dockerfiles/demo-theia-docker/project/java/pom.xml
+++ b/demo/dockerfiles/demo-theia-docker/project/java/pom.xml
@@ -7,7 +7,7 @@
com.exampledemo
- 1.0-SNAPSHOT
+ 0.0.1demo
@@ -21,7 +21,7 @@
junitjunit
- 4.11
+ 4.13.2test
diff --git a/demo/dockerfiles/demo-theia-monitor-theia/.project b/demo/dockerfiles/demo-theia-monitor-theia/.project
deleted file mode 100644
index 19470603a..000000000
--- a/demo/dockerfiles/demo-theia-monitor-theia/.project
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- demo-theia-docker-theia
-
-
-
-
-
-
-
-
diff --git a/demo/dockerfiles/demo-theia-monitor-theia/Dockerfile b/demo/dockerfiles/demo-theia-monitor-theia/Dockerfile
index 283c24ef7..bc6c5c819 100644
--- a/demo/dockerfiles/demo-theia-monitor-theia/Dockerfile
+++ b/demo/dockerfiles/demo-theia-monitor-theia/Dockerfile
@@ -1,19 +1,12 @@
-FROM node:20-bookworm as build-stage
+FROM node:20-bookworm AS build-stage
RUN apt-get update && apt-get install -y libxkbfile-dev libsecret-1-dev
-WORKDIR /home/theia
-ADD demo/dockerfiles/demo-theia-monitor-theia/package.json ./package.json
-## Link package from local
-ADD node ./node
-WORKDIR /home/theia/node
-RUN npm ci && npm run build -w monitor-theia
WORKDIR /home/theia
-RUN sed -i 's/"@eclipse-theiacloud\/monitor-theia": "next"/"@eclipse-theiacloud\/monitor-theia": "file:node\/monitor-theia"/' package.json
+COPY theia ./
-RUN yarn --pure-lockfile && \
- NODE_OPTIONS="--max_old_space_size=4096" yarn theia build && \
- yarn theia download:plugins && \
- yarn --production && \
+RUN yarn --frozen-lockfile && \
+ NODE_OPTIONS="--max_old_space_size=4096" yarn build && \
+ yarn --production --frozen-lockfile && \
yarn autoclean --init && \
sed -i '/__tests__\|test\|tests\|powered-test/ s/^/#/' .yarnclean && \
echo *.ts >> .yarnclean && \
@@ -22,7 +15,7 @@ RUN yarn --pure-lockfile && \
yarn autoclean --force && \
yarn cache clean
-FROM node:20-bookworm-slim as production-stage
+FROM node:20-bookworm-slim AS production-stage
# Use fixed user id 101 to guarantee it matches the app definition
RUN adduser --system --group --uid 101 theia
@@ -31,23 +24,16 @@ RUN chmod g+rw /home && \
mkdir -p /home/theia && \
chown -R theia:theia /home/theia && \
chown -R theia:theia /home/project;
-RUN apt-get update && apt-get install -y wget apt-transport-https && \
- wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | tee /usr/share/keyrings/adoptium.asc && \
- echo "deb [signed-by=/usr/share/keyrings/adoptium.asc] https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list && \
- apt-get update && apt-get install -y git openssh-client openssh-server bash libsecret-1-0 temurin-17-jdk maven && \
- apt-get purge -y wget && \
- apt-get clean
-ENV HOME /home/theia
-ENV THEIA_MINI_BROWSER_HOST_PATTERN {{hostname}}
-ENV THEIA_WEBVIEW_ENDPOINT {{hostname}}
+ENV HOME=/home/theia
+ENV THEIA_MINI_BROWSER_HOST_PATTERN={{hostname}}
+ENV THEIA_WEBVIEW_ENDPOINT={{hostname}}
WORKDIR /home/theia
COPY --from=build-stage --chown=theia:theia /home/theia /home/theia
EXPOSE 3000
-ENV SHELL=/bin/bash \
- THEIA_DEFAULT_PLUGINS=local-dir:/home/theia/plugins
-ENV USE_LOCAL_GIT true
+ENV SHELL=/bin/bash
+ENV USE_LOCAL_GIT=true
USER theia
WORKDIR /home/theia
-ENTRYPOINT [ "node", "/home/theia/src-gen/backend/main.js" ]
+ENTRYPOINT [ "node", "/home/theia/examples/browser-app/src-gen/backend/main.js" ]
CMD [ "/home/project", "--hostname=0.0.0.0" ]
\ No newline at end of file
diff --git a/demo/dockerfiles/demo-theia-monitor-theia/package.json b/demo/dockerfiles/demo-theia-monitor-theia/package.json
deleted file mode 100644
index c7d0e4e3a..000000000
--- a/demo/dockerfiles/demo-theia-monitor-theia/package.json
+++ /dev/null
@@ -1,89 +0,0 @@
-{
- "private": true,
- "theia": {
- "frontend": {
- "config": {
- "applicationName": "Theia Cloud Example",
- "warnOnPotentiallyInsecureHostPattern": false,
- "preferences": {
- "files.enableTrash": false
- }
- }
- },
- "backend": {
- "config": {
- "warnOnPotentiallyInsecureHostPattern": false
- }
- }
- },
- "dependencies": {
- "@theia/bulk-edit": "1.43.1",
- "@theia/callhierarchy": "1.43.1",
- "@theia/console": "1.43.1",
- "@theia/core": "1.43.1",
- "@theia/debug": "1.43.1",
- "@theia/editor": "1.43.1",
- "@theia/editor-preview": "1.43.1",
- "@theia/electron": "1.43.1",
- "@theia/external-terminal": "1.43.1",
- "@theia/file-search": "1.43.1",
- "@theia/filesystem": "1.43.1",
- "@theia/getting-started": "1.43.1",
- "@theia/keymaps": "1.43.1",
- "@theia/markers": "1.43.1",
- "@theia/messages": "1.43.1",
- "@theia/metrics": "1.43.1",
- "@theia/mini-browser": "1.43.1",
- "@theia/monaco": "1.43.1",
- "@theia/navigator": "1.43.1",
- "@theia/outline-view": "1.43.1",
- "@theia/output": "1.43.1",
- "@theia/plugin-dev": "1.43.1",
- "@theia/plugin-ext": "1.43.1",
- "@theia/plugin-ext-vscode": "1.43.1",
- "@theia/preferences": "1.43.1",
- "@theia/process": "1.43.1",
- "@theia/property-view": "1.43.1",
- "@theia/scm": "1.43.1",
- "@theia/scm-extra": "1.43.1",
- "@theia/search-in-workspace": "1.43.1",
- "@theia/task": "1.43.1",
- "@theia/terminal": "1.43.1",
- "@theia/test": "1.43.1",
- "@theia/timeline": "1.43.1",
- "@theia/toolbar": "1.43.1",
- "@theia/typehierarchy": "1.43.1",
- "@theia/userstorage": "1.43.1",
- "@theia/variable-resolver": "1.43.1",
- "@theia/vsx-registry": "1.43.1",
- "@theia/workspace": "1.43.1",
- "@eclipse-theiacloud/monitor-theia": "next"
- },
- "devDependencies": {
- "@theia/cli": "1.43.1"
- },
- "scripts": {
- "preinstall": "node-gyp install"
- },
- "theiaPluginsDir": "plugins",
- "theiaPlugins": {
- "vscode.git": "https://open-vsx.org/api/vscode/git/1.52.1/file/vscode.git-1.52.1.vsix",
- "vscode.markdown-language-features": "https://open-vsx.org/api/vscode/markdown-language-features/1.39.2/file/vscode.markdown-language-features-1.39.2.vsix",
- "vscode-builtin-extensions-pack": "https://open-vsx.org/api/eclipse-theia/builtin-extension-pack/1.50.1/file/eclipse-theia.builtin-extension-pack-1.50.1.vsix",
- "redhat.java": "https://open-vsx.org/api/redhat/java/0.73.0/file/redhat.java-0.73.0.vsix",
- "vscjava.vscode-java-debug": "https://open-vsx.org/api/vscjava/vscode-java-debug/0.30.0/file/vscjava.vscode-java-debug-0.30.0.vsix",
- "vscjava.vscode-java-test": "https://open-vsx.org/api/vscjava/vscode-java-test/0.26.1/file/vscjava.vscode-java-test-0.26.1.vsix",
- "vscjava.vscode-maven": "https://open-vsx.org/api/vscjava/vscode-maven/0.21.2/file/vscjava.vscode-maven-0.21.2.vsix",
- "vscjava.vscode-java-dependency": "https://open-vsx.org/api/vscjava/vscode-java-dependency/0.16.0/file/vscjava.vscode-java-dependency-0.16.0.vsix",
- "redhat.vscode-xml": "https://open-vsx.org/api/redhat/vscode-xml/0.20.0/file/redhat.vscode-xml-0.20.0.vsix",
- "cdt-gdb-vscode": "https://open-vsx.org/api/eclipse-cdt/cdt-gdb-vscode/0.0.92/file/eclipse-cdt.cdt-gdb-vscode-0.0.92.vsix",
- "vscode-builtin-cpp": "https://open-vsx.org/api/vscode/cpp/1.62.3/file/vscode.cpp-1.62.3.vsix",
- "vscode-clangd": "https://open-vsx.org/api/llvm-vs-code-extensions/vscode-clangd/0.1.15/file/llvm-vs-code-extensions.vscode-clangd-0.1.15.vsix",
- "vscode-cmake": "https://open-vsx.org/api/ms-vscode/cmake-tools/1.9.2/file/ms-vscode.cmake-tools-1.9.2.vsix"
- },
- "theiaPluginsExcludeIds": [
- "vscode.extension-editing",
- "vscode.microsoft-authentication",
- "vscode.html-language-features"
- ]
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-theia/yarn.lock b/demo/dockerfiles/demo-theia-monitor-theia/yarn.lock
new file mode 100644
index 000000000..1c9241265
--- /dev/null
+++ b/demo/dockerfiles/demo-theia-monitor-theia/yarn.lock
@@ -0,0 +1,8539 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@ampproject/remapping@^2.2.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
+ integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2":
+ version "7.26.2"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
+ integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.25.9"
+ js-tokens "^4.0.0"
+ picocolors "^1.0.0"
+
+"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.9", "@babel/compat-data@^7.26.0":
+ version "7.26.3"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.3.tgz#99488264a56b2aded63983abd6a417f03b92ed02"
+ integrity sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==
+
+"@babel/core@^7.10.0":
+ version "7.26.0"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40"
+ integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==
+ dependencies:
+ "@ampproject/remapping" "^2.2.0"
+ "@babel/code-frame" "^7.26.0"
+ "@babel/generator" "^7.26.0"
+ "@babel/helper-compilation-targets" "^7.25.9"
+ "@babel/helper-module-transforms" "^7.26.0"
+ "@babel/helpers" "^7.26.0"
+ "@babel/parser" "^7.26.0"
+ "@babel/template" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+ "@babel/types" "^7.26.0"
+ convert-source-map "^2.0.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.2"
+ json5 "^2.2.3"
+ semver "^6.3.1"
+
+"@babel/generator@^7.26.0", "@babel/generator@^7.26.3":
+ version "7.26.3"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.3.tgz#ab8d4360544a425c90c248df7059881f4b2ce019"
+ integrity sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==
+ dependencies:
+ "@babel/parser" "^7.26.3"
+ "@babel/types" "^7.26.3"
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.25"
+ jsesc "^3.0.2"
+
+"@babel/helper-annotate-as-pure@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4"
+ integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==
+ dependencies:
+ "@babel/types" "^7.25.9"
+
+"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875"
+ integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==
+ dependencies:
+ "@babel/compat-data" "^7.25.9"
+ "@babel/helper-validator-option" "^7.25.9"
+ browserslist "^4.24.0"
+ lru-cache "^5.1.1"
+ semver "^6.3.1"
+
+"@babel/helper-create-class-features-plugin@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz#7644147706bb90ff613297d49ed5266bde729f83"
+ integrity sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.25.9"
+ "@babel/helper-member-expression-to-functions" "^7.25.9"
+ "@babel/helper-optimise-call-expression" "^7.25.9"
+ "@babel/helper-replace-supers" "^7.25.9"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+ semver "^6.3.1"
+
+"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.25.9":
+ version "7.26.3"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz#5169756ecbe1d95f7866b90bb555b022595302a0"
+ integrity sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.25.9"
+ regexpu-core "^6.2.0"
+ semver "^6.3.1"
+
+"@babel/helper-define-polyfill-provider@^0.6.2", "@babel/helper-define-polyfill-provider@^0.6.3":
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz#f4f2792fae2ef382074bc2d713522cf24e6ddb21"
+ integrity sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==
+ dependencies:
+ "@babel/helper-compilation-targets" "^7.22.6"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ debug "^4.1.1"
+ lodash.debounce "^4.0.8"
+ resolve "^1.14.2"
+
+"@babel/helper-member-expression-to-functions@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3"
+ integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==
+ dependencies:
+ "@babel/traverse" "^7.25.9"
+ "@babel/types" "^7.25.9"
+
+"@babel/helper-module-imports@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715"
+ integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==
+ dependencies:
+ "@babel/traverse" "^7.25.9"
+ "@babel/types" "^7.25.9"
+
+"@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0":
+ version "7.26.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae"
+ integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==
+ dependencies:
+ "@babel/helper-module-imports" "^7.25.9"
+ "@babel/helper-validator-identifier" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+
+"@babel/helper-optimise-call-expression@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e"
+ integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==
+ dependencies:
+ "@babel/types" "^7.25.9"
+
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46"
+ integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==
+
+"@babel/helper-remap-async-to-generator@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz#e53956ab3d5b9fb88be04b3e2f31b523afd34b92"
+ integrity sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.25.9"
+ "@babel/helper-wrap-function" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+
+"@babel/helper-replace-supers@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz#ba447224798c3da3f8713fc272b145e33da6a5c5"
+ integrity sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==
+ dependencies:
+ "@babel/helper-member-expression-to-functions" "^7.25.9"
+ "@babel/helper-optimise-call-expression" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+
+"@babel/helper-skip-transparent-expression-wrappers@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9"
+ integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==
+ dependencies:
+ "@babel/traverse" "^7.25.9"
+ "@babel/types" "^7.25.9"
+
+"@babel/helper-string-parser@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
+ integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
+
+"@babel/helper-validator-identifier@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
+ integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
+
+"@babel/helper-validator-option@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72"
+ integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==
+
+"@babel/helper-wrap-function@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz#d99dfd595312e6c894bd7d237470025c85eea9d0"
+ integrity sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==
+ dependencies:
+ "@babel/template" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+ "@babel/types" "^7.25.9"
+
+"@babel/helpers@^7.26.0":
+ version "7.26.0"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4"
+ integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==
+ dependencies:
+ "@babel/template" "^7.25.9"
+ "@babel/types" "^7.26.0"
+
+"@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3":
+ version "7.26.3"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234"
+ integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==
+ dependencies:
+ "@babel/types" "^7.26.3"
+
+"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe"
+ integrity sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+
+"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz#af9e4fb63ccb8abcb92375b2fcfe36b60c774d30"
+ integrity sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz#e8dc26fcd616e6c5bf2bd0d5a2c151d4f92a9137"
+ integrity sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz#807a667f9158acac6f6164b4beb85ad9ebc9e1d1"
+ integrity sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
+ "@babel/plugin-transform-optional-chaining" "^7.25.9"
+
+"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz#de7093f1e7deaf68eadd7cc6b07f2ab82543269e"
+ integrity sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+
+"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2":
+ version "7.21.0-placeholder-for-preset-env.2"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703"
+ integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==
+
+"@babel/plugin-syntax-import-assertions@^7.26.0":
+ version "7.26.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f"
+ integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-syntax-import-attributes@^7.26.0":
+ version "7.26.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7"
+ integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-syntax-unicode-sets-regex@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357"
+ integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.18.6"
+ "@babel/helper-plugin-utils" "^7.18.6"
+
+"@babel/plugin-transform-arrow-functions@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845"
+ integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-async-generator-functions@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz#1b18530b077d18a407c494eb3d1d72da505283a2"
+ integrity sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-remap-async-to-generator" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+
+"@babel/plugin-transform-async-to-generator@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz#c80008dacae51482793e5a9c08b39a5be7e12d71"
+ integrity sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==
+ dependencies:
+ "@babel/helper-module-imports" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-remap-async-to-generator" "^7.25.9"
+
+"@babel/plugin-transform-block-scoped-functions@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz#5700691dbd7abb93de300ca7be94203764fce458"
+ integrity sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-block-scoping@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz#c33665e46b06759c93687ca0f84395b80c0473a1"
+ integrity sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-class-properties@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz#a8ce84fedb9ad512549984101fa84080a9f5f51f"
+ integrity sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-class-static-block@^7.26.0":
+ version "7.26.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz#6c8da219f4eb15cae9834ec4348ff8e9e09664a0"
+ integrity sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-classes@^7.10.0", "@babel/plugin-transform-classes@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52"
+ integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.25.9"
+ "@babel/helper-compilation-targets" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-replace-supers" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+ globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b"
+ integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/template" "^7.25.9"
+
+"@babel/plugin-transform-destructuring@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1"
+ integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-dotall-regex@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz#bad7945dd07734ca52fe3ad4e872b40ed09bb09a"
+ integrity sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-duplicate-keys@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz#8850ddf57dce2aebb4394bb434a7598031059e6d"
+ integrity sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz#6f7259b4de127721a08f1e5165b852fcaa696d31"
+ integrity sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-dynamic-import@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz#23e917de63ed23c6600c5dd06d94669dce79f7b8"
+ integrity sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-exponentiation-operator@^7.25.9":
+ version "7.26.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz#e29f01b6de302c7c2c794277a48f04a9ca7f03bc"
+ integrity sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-export-namespace-from@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz#90745fe55053394f554e40584cda81f2c8a402a2"
+ integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-for-of@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz#4bdc7d42a213397905d89f02350c5267866d5755"
+ integrity sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
+
+"@babel/plugin-transform-function-name@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97"
+ integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==
+ dependencies:
+ "@babel/helper-compilation-targets" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+
+"@babel/plugin-transform-json-strings@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz#c86db407cb827cded902a90c707d2781aaa89660"
+ integrity sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-literals@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de"
+ integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-logical-assignment-operators@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz#b19441a8c39a2fda0902900b306ea05ae1055db7"
+ integrity sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-member-expression-literals@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de"
+ integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-modules-amd@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz#49ba478f2295101544abd794486cd3088dddb6c5"
+ integrity sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-modules-commonjs@^7.25.9":
+ version "7.26.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb"
+ integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.26.0"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-modules-systemjs@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz#8bd1b43836269e3d33307151a114bcf3ba6793f8"
+ integrity sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-validator-identifier" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+
+"@babel/plugin-transform-modules-umd@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz#6710079cdd7c694db36529a1e8411e49fcbf14c9"
+ integrity sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz#454990ae6cc22fd2a0fa60b3a2c6f63a38064e6a"
+ integrity sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-new-target@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz#42e61711294b105c248336dcb04b77054ea8becd"
+ integrity sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-nullish-coalescing-operator@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz#bcb1b0d9e948168102d5f7104375ca21c3266949"
+ integrity sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-numeric-separator@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz#bfed75866261a8b643468b0ccfd275f2033214a1"
+ integrity sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-object-rest-spread@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz#0203725025074164808bcf1a2cfa90c652c99f18"
+ integrity sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==
+ dependencies:
+ "@babel/helper-compilation-targets" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/plugin-transform-parameters" "^7.25.9"
+
+"@babel/plugin-transform-object-super@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03"
+ integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-replace-supers" "^7.25.9"
+
+"@babel/plugin-transform-optional-catch-binding@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz#10e70d96d52bb1f10c5caaac59ac545ea2ba7ff3"
+ integrity sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-optional-chaining@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz#e142eb899d26ef715435f201ab6e139541eee7dd"
+ integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
+
+"@babel/plugin-transform-parameters@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257"
+ integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-private-methods@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57"
+ integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-private-property-in-object@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz#9c8b73e64e6cc3cbb2743633885a7dd2c385fe33"
+ integrity sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.25.9"
+ "@babel/helper-create-class-features-plugin" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-property-literals@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f"
+ integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-regenerator@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz#03a8a4670d6cebae95305ac6defac81ece77740b"
+ integrity sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+ regenerator-transform "^0.15.2"
+
+"@babel/plugin-transform-regexp-modifiers@^7.26.0":
+ version "7.26.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz#2f5837a5b5cd3842a919d8147e9903cc7455b850"
+ integrity sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-reserved-words@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz#0398aed2f1f10ba3f78a93db219b27ef417fb9ce"
+ integrity sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-runtime@^7.10.0":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz#62723ea3f5b31ffbe676da9d6dae17138ae580ea"
+ integrity sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==
+ dependencies:
+ "@babel/helper-module-imports" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+ babel-plugin-polyfill-corejs2 "^0.4.10"
+ babel-plugin-polyfill-corejs3 "^0.10.6"
+ babel-plugin-polyfill-regenerator "^0.6.1"
+ semver "^6.3.1"
+
+"@babel/plugin-transform-shorthand-properties@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2"
+ integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-spread@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9"
+ integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
+
+"@babel/plugin-transform-sticky-regex@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz#c7f02b944e986a417817b20ba2c504dfc1453d32"
+ integrity sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-template-literals@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz#6dbd4a24e8fad024df76d1fac6a03cf413f60fe1"
+ integrity sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-typeof-symbol@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz#224ba48a92869ddbf81f9b4a5f1204bbf5a2bc4b"
+ integrity sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-unicode-escapes@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz#a75ef3947ce15363fccaa38e2dd9bc70b2788b82"
+ integrity sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-unicode-property-regex@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz#a901e96f2c1d071b0d1bb5dc0d3c880ce8f53dd3"
+ integrity sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-unicode-regex@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz#5eae747fe39eacf13a8bd006a4fb0b5d1fa5e9b1"
+ integrity sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-unicode-sets-regex@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz#65114c17b4ffc20fa5b163c63c70c0d25621fabe"
+ integrity sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/preset-env@^7.10.0":
+ version "7.26.0"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.26.0.tgz#30e5c6bc1bcc54865bff0c5a30f6d4ccdc7fa8b1"
+ integrity sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==
+ dependencies:
+ "@babel/compat-data" "^7.26.0"
+ "@babel/helper-compilation-targets" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-validator-option" "^7.25.9"
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.25.9"
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.25.9"
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.25.9"
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.25.9"
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.25.9"
+ "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2"
+ "@babel/plugin-syntax-import-assertions" "^7.26.0"
+ "@babel/plugin-syntax-import-attributes" "^7.26.0"
+ "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6"
+ "@babel/plugin-transform-arrow-functions" "^7.25.9"
+ "@babel/plugin-transform-async-generator-functions" "^7.25.9"
+ "@babel/plugin-transform-async-to-generator" "^7.25.9"
+ "@babel/plugin-transform-block-scoped-functions" "^7.25.9"
+ "@babel/plugin-transform-block-scoping" "^7.25.9"
+ "@babel/plugin-transform-class-properties" "^7.25.9"
+ "@babel/plugin-transform-class-static-block" "^7.26.0"
+ "@babel/plugin-transform-classes" "^7.25.9"
+ "@babel/plugin-transform-computed-properties" "^7.25.9"
+ "@babel/plugin-transform-destructuring" "^7.25.9"
+ "@babel/plugin-transform-dotall-regex" "^7.25.9"
+ "@babel/plugin-transform-duplicate-keys" "^7.25.9"
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.25.9"
+ "@babel/plugin-transform-dynamic-import" "^7.25.9"
+ "@babel/plugin-transform-exponentiation-operator" "^7.25.9"
+ "@babel/plugin-transform-export-namespace-from" "^7.25.9"
+ "@babel/plugin-transform-for-of" "^7.25.9"
+ "@babel/plugin-transform-function-name" "^7.25.9"
+ "@babel/plugin-transform-json-strings" "^7.25.9"
+ "@babel/plugin-transform-literals" "^7.25.9"
+ "@babel/plugin-transform-logical-assignment-operators" "^7.25.9"
+ "@babel/plugin-transform-member-expression-literals" "^7.25.9"
+ "@babel/plugin-transform-modules-amd" "^7.25.9"
+ "@babel/plugin-transform-modules-commonjs" "^7.25.9"
+ "@babel/plugin-transform-modules-systemjs" "^7.25.9"
+ "@babel/plugin-transform-modules-umd" "^7.25.9"
+ "@babel/plugin-transform-named-capturing-groups-regex" "^7.25.9"
+ "@babel/plugin-transform-new-target" "^7.25.9"
+ "@babel/plugin-transform-nullish-coalescing-operator" "^7.25.9"
+ "@babel/plugin-transform-numeric-separator" "^7.25.9"
+ "@babel/plugin-transform-object-rest-spread" "^7.25.9"
+ "@babel/plugin-transform-object-super" "^7.25.9"
+ "@babel/plugin-transform-optional-catch-binding" "^7.25.9"
+ "@babel/plugin-transform-optional-chaining" "^7.25.9"
+ "@babel/plugin-transform-parameters" "^7.25.9"
+ "@babel/plugin-transform-private-methods" "^7.25.9"
+ "@babel/plugin-transform-private-property-in-object" "^7.25.9"
+ "@babel/plugin-transform-property-literals" "^7.25.9"
+ "@babel/plugin-transform-regenerator" "^7.25.9"
+ "@babel/plugin-transform-regexp-modifiers" "^7.26.0"
+ "@babel/plugin-transform-reserved-words" "^7.25.9"
+ "@babel/plugin-transform-shorthand-properties" "^7.25.9"
+ "@babel/plugin-transform-spread" "^7.25.9"
+ "@babel/plugin-transform-sticky-regex" "^7.25.9"
+ "@babel/plugin-transform-template-literals" "^7.25.9"
+ "@babel/plugin-transform-typeof-symbol" "^7.25.9"
+ "@babel/plugin-transform-unicode-escapes" "^7.25.9"
+ "@babel/plugin-transform-unicode-property-regex" "^7.25.9"
+ "@babel/plugin-transform-unicode-regex" "^7.25.9"
+ "@babel/plugin-transform-unicode-sets-regex" "^7.25.9"
+ "@babel/preset-modules" "0.1.6-no-external-plugins"
+ babel-plugin-polyfill-corejs2 "^0.4.10"
+ babel-plugin-polyfill-corejs3 "^0.10.6"
+ babel-plugin-polyfill-regenerator "^0.6.1"
+ core-js-compat "^3.38.1"
+ semver "^6.3.1"
+
+"@babel/preset-modules@0.1.6-no-external-plugins":
+ version "0.1.6-no-external-plugins"
+ resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a"
+ integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/types" "^7.4.4"
+ esutils "^2.0.2"
+
+"@babel/runtime@^7.10.0", "@babel/runtime@^7.8.4":
+ version "7.26.0"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
+ integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==
+ dependencies:
+ regenerator-runtime "^0.14.0"
+
+"@babel/template@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016"
+ integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==
+ dependencies:
+ "@babel/code-frame" "^7.25.9"
+ "@babel/parser" "^7.25.9"
+ "@babel/types" "^7.25.9"
+
+"@babel/traverse@^7.25.9":
+ version "7.26.3"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.3.tgz#1ebfc75bd748d8f96b3cc63af5e82ebd4c37ba35"
+ integrity sha512-yTmc8J+Sj8yLzwr4PD5Xb/WF3bOYu2C2OoSZPzbuqRm4n98XirsbzaX+GloeO376UnSYIYJ4NCanwV5/ugZkwA==
+ dependencies:
+ "@babel/code-frame" "^7.26.2"
+ "@babel/generator" "^7.26.3"
+ "@babel/parser" "^7.26.3"
+ "@babel/template" "^7.25.9"
+ "@babel/types" "^7.26.3"
+ debug "^4.3.1"
+ globals "^11.1.0"
+
+"@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3", "@babel/types@^7.4.4":
+ version "7.26.3"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0"
+ integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==
+ dependencies:
+ "@babel/helper-string-parser" "^7.25.9"
+ "@babel/helper-validator-identifier" "^7.25.9"
+
+"@discoveryjs/json-ext@^0.5.0":
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
+ integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
+
+"@eclipse-theiacloud/monitor-theia@file:node/monitor-theia":
+ version "1.1.0-next"
+
+"@electron/get@^2.0.0":
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/@electron/get/-/get-2.0.3.tgz#fba552683d387aebd9f3fcadbcafc8e12ee4f960"
+ integrity sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==
+ dependencies:
+ debug "^4.1.1"
+ env-paths "^2.2.0"
+ fs-extra "^8.1.0"
+ got "^11.8.5"
+ progress "^2.0.3"
+ semver "^6.2.0"
+ sumchecker "^3.0.1"
+ optionalDependencies:
+ global-agent "^3.0.0"
+
+"@gar/promisify@^1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
+ integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
+
+"@isaacs/cliui@^8.0.2":
+ version "8.0.2"
+ resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
+ integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==
+ dependencies:
+ string-width "^5.1.2"
+ string-width-cjs "npm:string-width@^4.2.0"
+ strip-ansi "^7.0.1"
+ strip-ansi-cjs "npm:strip-ansi@^6.0.1"
+ wrap-ansi "^8.1.0"
+ wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
+
+"@jridgewell/gen-mapping@^0.3.5":
+ version "0.3.5"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
+ integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
+ dependencies:
+ "@jridgewell/set-array" "^1.2.1"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
+"@jridgewell/resolve-uri@^3.1.0":
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
+ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+
+"@jridgewell/set-array@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
+ integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
+
+"@jridgewell/source-map@^0.3.3":
+ version "0.3.6"
+ resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a"
+ integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.25"
+
+"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
+ integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
+
+"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
+ version "0.3.25"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
+ integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.1.0"
+ "@jridgewell/sourcemap-codec" "^1.4.14"
+
+"@malept/cross-spawn-promise@^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz#d0772de1aa680a0bfb9ba2f32b4c828c7857cb9d"
+ integrity sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==
+ dependencies:
+ cross-spawn "^7.0.1"
+
+"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz#9edec61b22c3082018a79f6d1c30289ddf3d9d11"
+ integrity sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==
+
+"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz#33677a275204898ad8acbf62734fc4dc0b6a4855"
+ integrity sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==
+
+"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz#19edf7cdc2e7063ee328403c1d895a86dd28f4bb"
+ integrity sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==
+
+"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz#94fb0543ba2e28766c3fc439cabbe0440ae70159"
+ integrity sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==
+
+"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz#4a0609ab5fe44d07c9c60a11e4484d3c38bbd6e3"
+ integrity sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==
+
+"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz#0aa5502d547b57abfc4ac492de68e2006e417242"
+ integrity sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==
+
+"@nodelib/fs.scandir@2.1.5":
+ version "2.1.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+ integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+ dependencies:
+ "@nodelib/fs.stat" "2.0.5"
+ run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+ integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
+"@nodelib/fs.walk@^1.2.3":
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+ integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+ dependencies:
+ "@nodelib/fs.scandir" "2.1.5"
+ fastq "^1.6.0"
+
+"@npmcli/fs@^2.1.0":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865"
+ integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==
+ dependencies:
+ "@gar/promisify" "^1.1.3"
+ semver "^7.3.5"
+
+"@npmcli/move-file@^2.0.0":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4"
+ integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==
+ dependencies:
+ mkdirp "^1.0.4"
+ rimraf "^3.0.2"
+
+"@parcel/watcher-android-arm64@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz#e32d3dda6647791ee930556aee206fcd5ea0fb7a"
+ integrity sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==
+
+"@parcel/watcher-darwin-arm64@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz#0d9e680b7e9ec1c8f54944f1b945aa8755afb12f"
+ integrity sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==
+
+"@parcel/watcher-darwin-x64@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz#f9f1d5ce9d5878d344f14ef1856b7a830c59d1bb"
+ integrity sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==
+
+"@parcel/watcher-freebsd-x64@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz#2b77f0c82d19e84ff4c21de6da7f7d096b1a7e82"
+ integrity sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==
+
+"@parcel/watcher-linux-arm-glibc@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz#92ed322c56dbafa3d2545dcf2803334aee131e42"
+ integrity sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==
+
+"@parcel/watcher-linux-arm-musl@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz#cd48e9bfde0cdbbd2ecd9accfc52967e22f849a4"
+ integrity sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==
+
+"@parcel/watcher-linux-arm64-glibc@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz#7b81f6d5a442bb89fbabaf6c13573e94a46feb03"
+ integrity sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==
+
+"@parcel/watcher-linux-arm64-musl@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz#dcb8ff01077cdf59a18d9e0a4dff7a0cfe5fd732"
+ integrity sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==
+
+"@parcel/watcher-linux-x64-glibc@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz#2e254600fda4e32d83942384d1106e1eed84494d"
+ integrity sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==
+
+"@parcel/watcher-linux-x64-musl@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz#01fcea60fedbb3225af808d3f0a7b11229792eef"
+ integrity sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==
+
+"@parcel/watcher-win32-arm64@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz#87cdb16e0783e770197e52fb1dc027bb0c847154"
+ integrity sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==
+
+"@parcel/watcher-win32-ia32@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz#778c39b56da33e045ba21c678c31a9f9d7c6b220"
+ integrity sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==
+
+"@parcel/watcher-win32-x64@2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz#33873876d0bbc588aacce38e90d1d7480ce81cb7"
+ integrity sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==
+
+"@parcel/watcher@^2.5.0":
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.0.tgz#5c88818b12b8de4307a9d3e6dc3e28eba0dfbd10"
+ integrity sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==
+ dependencies:
+ detect-libc "^1.0.3"
+ is-glob "^4.0.3"
+ micromatch "^4.0.5"
+ node-addon-api "^7.0.0"
+ optionalDependencies:
+ "@parcel/watcher-android-arm64" "2.5.0"
+ "@parcel/watcher-darwin-arm64" "2.5.0"
+ "@parcel/watcher-darwin-x64" "2.5.0"
+ "@parcel/watcher-freebsd-x64" "2.5.0"
+ "@parcel/watcher-linux-arm-glibc" "2.5.0"
+ "@parcel/watcher-linux-arm-musl" "2.5.0"
+ "@parcel/watcher-linux-arm64-glibc" "2.5.0"
+ "@parcel/watcher-linux-arm64-musl" "2.5.0"
+ "@parcel/watcher-linux-x64-glibc" "2.5.0"
+ "@parcel/watcher-linux-x64-musl" "2.5.0"
+ "@parcel/watcher-win32-arm64" "2.5.0"
+ "@parcel/watcher-win32-ia32" "2.5.0"
+ "@parcel/watcher-win32-x64" "2.5.0"
+
+"@phosphor/algorithm@1", "@phosphor/algorithm@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@phosphor/algorithm/-/algorithm-1.2.0.tgz#4a19aa59261b7270be696672dc3f0663f7bef152"
+ integrity sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==
+
+"@phosphor/collections@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@phosphor/collections/-/collections-1.2.0.tgz#a8cdd0edc0257de7c33306a91caf47910036307f"
+ integrity sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==
+ dependencies:
+ "@phosphor/algorithm" "^1.2.0"
+
+"@phosphor/commands@1", "@phosphor/commands@^1.7.2":
+ version "1.7.2"
+ resolved "https://registry.yarnpkg.com/@phosphor/commands/-/commands-1.7.2.tgz#df724f2896ae43c4a3a9e2b5a6445a15e0d60487"
+ integrity sha512-iSyBIWMHsus323BVEARBhuVZNnVel8USo+FIPaAxGcq+icTSSe6+NtSxVQSmZblGN6Qm4iw6I6VtiSx0e6YDgQ==
+ dependencies:
+ "@phosphor/algorithm" "^1.2.0"
+ "@phosphor/coreutils" "^1.3.1"
+ "@phosphor/disposable" "^1.3.1"
+ "@phosphor/domutils" "^1.1.4"
+ "@phosphor/keyboard" "^1.1.3"
+ "@phosphor/signaling" "^1.3.1"
+
+"@phosphor/coreutils@1", "@phosphor/coreutils@^1.3.1":
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/@phosphor/coreutils/-/coreutils-1.3.1.tgz#441e34f42340f7faa742a88b2a181947a88d7226"
+ integrity sha512-9OHCn8LYRcPU/sbHm5v7viCA16Uev3gbdkwqoQqlV+EiauDHl70jmeL7XVDXdigl66Dz0LI11C99XOxp+s3zOA==
+
+"@phosphor/disposable@^1.3.1":
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/@phosphor/disposable/-/disposable-1.3.1.tgz#be98fe12bd8c9a4600741cb83b0a305df28628f3"
+ integrity sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==
+ dependencies:
+ "@phosphor/algorithm" "^1.2.0"
+ "@phosphor/signaling" "^1.3.1"
+
+"@phosphor/domutils@1", "@phosphor/domutils@^1.1.4":
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/@phosphor/domutils/-/domutils-1.1.4.tgz#4c6aecf7902d3793b45db325319340e0a0b5543b"
+ integrity sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==
+
+"@phosphor/dragdrop@1", "@phosphor/dragdrop@^1.4.1":
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz#45887dfe8f5849db2b4d1c0329a377f0f0854464"
+ integrity sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==
+ dependencies:
+ "@phosphor/coreutils" "^1.3.1"
+ "@phosphor/disposable" "^1.3.1"
+
+"@phosphor/keyboard@^1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@phosphor/keyboard/-/keyboard-1.1.3.tgz#e5fd13af0479034ef0b5fffcf43ef2d4a266b5b6"
+ integrity sha512-dzxC/PyHiD6mXaESRy6PZTd9JeK+diwG1pyngkyUf127IXOEzubTIbu52VSdpGBklszu33ws05BAGDa4oBE4mQ==
+
+"@phosphor/messaging@1", "@phosphor/messaging@^1.3.0":
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/@phosphor/messaging/-/messaging-1.3.0.tgz#a140e6dd28a496260779acf74860f738c654c65e"
+ integrity sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==
+ dependencies:
+ "@phosphor/algorithm" "^1.2.0"
+ "@phosphor/collections" "^1.2.0"
+
+"@phosphor/properties@1", "@phosphor/properties@^1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@phosphor/properties/-/properties-1.1.3.tgz#63e4355be5e22a411c566fd1860207038f171598"
+ integrity sha512-GiglqzU77s6+tFVt6zPq9uuyu/PLQPFcqZt914ZhJ4cN/7yNI/SLyMzpYZ56IRMXvzK9TUgbRna6URE3XAwFUg==
+
+"@phosphor/signaling@1", "@phosphor/signaling@^1.3.1":
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/@phosphor/signaling/-/signaling-1.3.1.tgz#1cd10b069bdb2c9adb3ba74245b30141e5afc2d7"
+ integrity sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==
+ dependencies:
+ "@phosphor/algorithm" "^1.2.0"
+
+"@phosphor/virtualdom@1", "@phosphor/virtualdom@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz#6a233312f817eb02555a0359c4ae3e501fa62bca"
+ integrity sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==
+ dependencies:
+ "@phosphor/algorithm" "^1.2.0"
+
+"@phosphor/widgets@1":
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/@phosphor/widgets/-/widgets-1.9.3.tgz#b8b7ad69fd7cc7af8e8c312ebead0e0965a4cefd"
+ integrity sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==
+ dependencies:
+ "@phosphor/algorithm" "^1.2.0"
+ "@phosphor/commands" "^1.7.2"
+ "@phosphor/coreutils" "^1.3.1"
+ "@phosphor/disposable" "^1.3.1"
+ "@phosphor/domutils" "^1.1.4"
+ "@phosphor/dragdrop" "^1.4.1"
+ "@phosphor/keyboard" "^1.1.3"
+ "@phosphor/messaging" "^1.3.0"
+ "@phosphor/properties" "^1.1.3"
+ "@phosphor/signaling" "^1.3.1"
+ "@phosphor/virtualdom" "^1.2.0"
+
+"@pkgjs/parseargs@^0.11.0":
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
+ integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
+
+"@puppeteer/browsers@2.3.1":
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-2.3.1.tgz#238200dbdce5c00ae28c8f2a55ac053c3be71668"
+ integrity sha512-uK7o3hHkK+naEobMSJ+2ySYyXtQkBxIH8Gn4MK9ciePjNV+Pf+PgY/W7iPzn2MTjl3stcYB5AlcTmPYw7AXDwA==
+ dependencies:
+ debug "^4.3.6"
+ extract-zip "^2.0.1"
+ progress "^2.0.3"
+ proxy-agent "^6.4.0"
+ semver "^7.6.3"
+ tar-fs "^3.0.6"
+ unbzip2-stream "^1.4.3"
+ yargs "^17.7.2"
+
+"@sindresorhus/df@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@sindresorhus/df/-/df-1.0.1.tgz#c69b66f52f6fcdd287c807df210305dbaf78500d"
+ integrity sha512-1Hyp7NQnD/u4DSxR2DGW78TF9k7R0wZ8ev0BpMAIzA6yTQSHqNb5wTuvtcPYf4FWbVse2rW7RgDsyL8ua2vXHw==
+
+"@sindresorhus/df@^3.1.1":
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/@sindresorhus/df/-/df-3.1.1.tgz#94200f9277e4a7fdd35ce8ab8b6bc5b52b164d31"
+ integrity sha512-SME/vtXaJcnQ/HpeV6P82Egy+jThn11IKfwW8+/XVoRD0rmPHVTeKMtww1oWdVnMykzVPjmrDN9S8NBndPEHCQ==
+ dependencies:
+ execa "^2.0.1"
+
+"@sindresorhus/is@^4.0.0":
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
+ integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==
+
+"@socket.io/component-emitter@~3.1.0":
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2"
+ integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==
+
+"@stroncium/procfs@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@stroncium/procfs/-/procfs-1.2.1.tgz#6b9be6fd20fb0a4c20e99a8695e083c699bb2b45"
+ integrity sha512-X1Iui3FUNZP18EUvysTHxt+Avu2nlVzyf90YM8OYgP6SGzTzzX/0JgObfO1AQQDzuZtNNz29bVh8h5R97JrjxA==
+
+"@szmarczak/http-timer@^4.0.5":
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807"
+ integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==
+ dependencies:
+ defer-to-connect "^2.0.0"
+
+"@theia/application-manager@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/application-manager/-/application-manager-1.56.0.tgz#331e24b354c31104a9cd395df6d8fe65e5823b53"
+ integrity sha512-ZnSj3snNGBC/+T65T0vDLxg7eOYLpiw4V064tZc21Ae8FRMzmmIcjeHJlodaWmodvnxtLw2CNLtFYiMSMTZYxg==
+ dependencies:
+ "@babel/core" "^7.10.0"
+ "@babel/plugin-transform-classes" "^7.10.0"
+ "@babel/plugin-transform-runtime" "^7.10.0"
+ "@babel/preset-env" "^7.10.0"
+ "@theia/application-package" "1.56.0"
+ "@theia/ffmpeg" "1.56.0"
+ "@theia/native-webpack-plugin" "1.56.0"
+ "@types/fs-extra" "^4.0.2"
+ "@types/semver" "^7.5.0"
+ babel-loader "^8.2.2"
+ buffer "^6.0.3"
+ compression-webpack-plugin "^9.0.0"
+ copy-webpack-plugin "^8.1.1"
+ css-loader "^6.2.0"
+ electron-rebuild "^3.2.7"
+ fs-extra "^4.0.2"
+ http-server "^14.1.1"
+ ignore-loader "^0.1.2"
+ less "^3.0.3"
+ mini-css-extract-plugin "^2.6.1"
+ node-abi "*"
+ node-loader "^2.0.0"
+ path-browserify "^1.0.1"
+ semver "^7.5.4"
+ setimmediate "^1.0.5"
+ source-map "^0.6.1"
+ source-map-loader "^2.0.1"
+ source-map-support "^0.5.19"
+ style-loader "^2.0.0"
+ tslib "^2.6.2"
+ umd-compat-loader "^2.1.2"
+ webpack "^5.76.0"
+ webpack-cli "4.7.0"
+ worker-loader "^3.0.8"
+ yargs "^15.3.1"
+
+"@theia/application-package@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/application-package/-/application-package-1.56.0.tgz#9fcc43a1888418d4dfbf4335a85065aad7ae5642"
+ integrity sha512-NRdrfAeAYgeS8tRgEwozk9hb8287Kz4ps5h0B/dJe97VGxV1pv7atZXf9MWU9ct/8P2fGHal1dJ7apgKsQG59A==
+ dependencies:
+ "@theia/request" "1.56.0"
+ "@types/fs-extra" "^4.0.2"
+ "@types/semver" "^7.5.0"
+ "@types/write-json-file" "^2.2.1"
+ deepmerge "^4.2.2"
+ fs-extra "^4.0.2"
+ is-electron "^2.1.0"
+ nano "^10.1.3"
+ resolve-package-path "^4.0.3"
+ semver "^7.5.4"
+ tslib "^2.6.2"
+ write-json-file "^2.2.0"
+
+"@theia/bulk-edit@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/bulk-edit/-/bulk-edit-1.56.0.tgz#89ffea433b29d85d5e64c8907f158e08dc063841"
+ integrity sha512-JPMU7uZerA7en1CcHKS9b+mbYzK2n2xYHV7G6Ybe0wKsCrTk3r435iJc/XudpC79MnVXPtu9RbNnKmmGdGrK5A==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ "@theia/workspace" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/callhierarchy@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/callhierarchy/-/callhierarchy-1.56.0.tgz#a85e7784e1cf3688c1c983b397c44ac46ffe99a6"
+ integrity sha512-2QxJSEUcVn37dER6DXGanLjn1RazzUChbwnuWdKq7AR2GnHdX1APX/hG8HI7ncTofRRReauWolRBwhzYwqhJCQ==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ ts-md5 "^1.2.2"
+ tslib "^2.6.2"
+
+"@theia/cli@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/cli/-/cli-1.56.0.tgz#9fa28f77e6243af6121dbf7a779b334e70c3305d"
+ integrity sha512-CPMwNtaIhi0LhtXrYkIArkEsZqhAESrZOz1l9ol5p0Etk/j4yx80QxYziE9LxpdfTj2BF9fah5Y+SQdlb88gbQ==
+ dependencies:
+ "@theia/application-manager" "1.56.0"
+ "@theia/application-package" "1.56.0"
+ "@theia/ffmpeg" "1.56.0"
+ "@theia/localization-manager" "1.56.0"
+ "@theia/ovsx-client" "1.56.0"
+ "@theia/request" "1.56.0"
+ "@types/chai" "^4.2.7"
+ "@types/mocha" "^10.0.0"
+ "@types/node-fetch" "^2.5.7"
+ chai "^4.3.10"
+ chalk "4.0.0"
+ decompress "^4.2.1"
+ escape-string-regexp "4.0.0"
+ glob "^8.0.3"
+ http-server "^14.1.1"
+ limiter "^2.1.0"
+ log-update "^4.0.0"
+ mocha "^10.1.0"
+ patch-package "^8.0.0"
+ puppeteer "23.1.0"
+ puppeteer-core "23.1.0"
+ puppeteer-to-istanbul "1.4.0"
+ temp "^0.9.1"
+ tslib "^2.6.2"
+ yargs "^15.3.1"
+
+"@theia/console@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/console/-/console-1.56.0.tgz#5fed7929d58b1c9053fb9833fa5bf45e341dcd4f"
+ integrity sha512-A4P+usFZDcmR45kgc4ccxtLDTEqKQgUongpug6uwLD6Y21FXBWUYvi8DszpLlEA0/NmKG9jboKYs59bMIhc19w==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ anser "^2.0.1"
+ tslib "^2.6.2"
+
+"@theia/core@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/core/-/core-1.56.0.tgz#54ee090206866e199584c96411d3c45576700558"
+ integrity sha512-IkaHnVIM3asqz+LL4h4Tk0QXTneh9TIZxdyTCv3orY6Mr3UDu+NrTKd3qe0zmcWmUyKlOIivJ/MONWMj6xQJSw==
+ dependencies:
+ "@babel/runtime" "^7.10.0"
+ "@parcel/watcher" "^2.5.0"
+ "@phosphor/algorithm" "1"
+ "@phosphor/commands" "1"
+ "@phosphor/coreutils" "1"
+ "@phosphor/domutils" "1"
+ "@phosphor/dragdrop" "1"
+ "@phosphor/messaging" "1"
+ "@phosphor/properties" "1"
+ "@phosphor/signaling" "1"
+ "@phosphor/virtualdom" "1"
+ "@phosphor/widgets" "1"
+ "@theia/application-package" "1.56.0"
+ "@theia/request" "1.56.0"
+ "@types/body-parser" "^1.16.4"
+ "@types/cookie" "^0.3.3"
+ "@types/dompurify" "^2.2.2"
+ "@types/express" "^4.17.21"
+ "@types/fs-extra" "^4.0.2"
+ "@types/lodash.debounce" "4.0.3"
+ "@types/lodash.throttle" "^4.1.3"
+ "@types/markdown-it" "^12.2.3"
+ "@types/react" "^18.0.15"
+ "@types/react-dom" "^18.0.6"
+ "@types/route-parser" "^0.1.1"
+ "@types/safer-buffer" "^2.1.0"
+ "@types/uuid" "^9.0.8"
+ "@types/ws" "^8.5.5"
+ "@types/yargs" "^15"
+ "@vscode/codicons" "*"
+ ajv "^6.5.3"
+ async-mutex "^0.4.0"
+ body-parser "^1.17.2"
+ cookie "^0.4.0"
+ dompurify "^2.2.9"
+ drivelist "^9.0.2"
+ es6-promise "^4.2.4"
+ express "^4.21.0"
+ fast-json-stable-stringify "^2.1.0"
+ file-icons-js "~1.0.3"
+ font-awesome "^4.7.0"
+ fs-extra "^4.0.2"
+ fuzzy "^0.1.3"
+ http-proxy-agent "^5.0.0"
+ https-proxy-agent "^5.0.0"
+ iconv-lite "^0.6.0"
+ inversify "^6.1.3"
+ jschardet "^2.1.1"
+ keytar "7.2.0"
+ lodash.debounce "^4.0.8"
+ lodash.throttle "^4.1.1"
+ markdown-it "^12.3.2"
+ msgpackr "^1.10.2"
+ p-debounce "^2.1.0"
+ perfect-scrollbar "^1.3.0"
+ react "^18.2.0"
+ react-dom "^18.2.0"
+ react-tooltip "^4.2.21"
+ react-virtuoso "^2.17.0"
+ reflect-metadata "^0.1.10"
+ route-parser "^0.0.5"
+ safer-buffer "^2.1.2"
+ socket.io "^4.5.3"
+ socket.io-client "^4.5.3"
+ tslib "^2.6.2"
+ uuid "^9.0.1"
+ vscode-languageserver-protocol "^3.17.2"
+ vscode-uri "^2.1.1"
+ ws "^8.17.1"
+ yargs "^15.3.1"
+
+"@theia/debug@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/debug/-/debug-1.56.0.tgz#6afee26523f8f0f436a73cebe4802c48ae202e6e"
+ integrity sha512-gZLyx77cYdsou9PrniPe9xgI/SwGHDQmplBL0n4AczetWs+yn0lcBLsKQ7P+Ny7HXwLQBlRoXJkZpm68TN1Zfw==
+ dependencies:
+ "@theia/console" "1.56.0"
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/markers" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ "@theia/output" "1.56.0"
+ "@theia/process" "1.56.0"
+ "@theia/task" "1.56.0"
+ "@theia/terminal" "1.56.0"
+ "@theia/test" "1.56.0"
+ "@theia/variable-resolver" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ "@vscode/debugprotocol" "^1.51.0"
+ fast-deep-equal "^3.1.3"
+ jsonc-parser "^2.2.0"
+ p-debounce "^2.1.0"
+ tslib "^2.6.2"
+
+"@theia/editor-preview@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/editor-preview/-/editor-preview-1.56.0.tgz#66dafb123041a26d553832d9308c8e78681e2350"
+ integrity sha512-0oWtt3qaEXc/0WDwTrCvg7dc2+P5J97qreADPp8JI4KUGHuofMXHZqI0uemc3CxK9Pd/l7FpWBrXjCcEftEK5A==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/navigator" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/editor@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/editor/-/editor-1.56.0.tgz#0ba15b7fa52c7a898175081162bc577ac91ef094"
+ integrity sha512-XxNmgCI67U1DOaKQmg33x/UTuLpePIEmqvL7rjeoILGtptSBP5Ah7wqcy3ZaJxmMKMsOtdHwmffMfGo0blgbcg==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/variable-resolver" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/electron@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/electron/-/electron-1.56.0.tgz#b1986f934f24739f37eb68371eefa65537ec64cd"
+ integrity sha512-4ejcZvOnFfhtzzfxA3UWUV0HmAf3bAj7S7iMcG4lZcBQDcTp7YCfcmqIAI3zQwFTWxzfPP/26KGx/FH8ZAKAlw==
+ dependencies:
+ electron-store "^8.0.0"
+ fix-path "^3.0.0"
+ native-keymap "^2.2.1"
+
+"@theia/external-terminal@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/external-terminal/-/external-terminal-1.56.0.tgz#9f207864e9c9d01178b2db34a8ffb7e392657866"
+ integrity sha512-9rwHjD2Ck9eknlCpdPEr5KEK8zMuTWFIqQ+HO+j/EN+u/R0Zcjf+Z9FjoZ3Uy91oos9gwaJNuNSzquZ/4gm8yQ==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/ffmpeg@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/ffmpeg/-/ffmpeg-1.56.0.tgz#f3f2d02c04155011a24b55a1ef67cba0fde6c1a3"
+ integrity sha512-/zrpqtqMJBu89KeRM/9NgPRisVZ323K0ZiBd9iqa/O4CTHJlD0+rEmt6Dv2Nb4okNNhpIhUQGYzvhMJFwiYfww==
+ dependencies:
+ "@electron/get" "^2.0.0"
+ tslib "^2.6.2"
+ unzipper "^0.9.11"
+
+"@theia/file-search@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/file-search/-/file-search-1.56.0.tgz#269cc7694864e6436d70d0398c4a7e9943ed0a66"
+ integrity sha512-+pHSXXNz0vUpd6I1BPfwI3LQfgRQ/EGULH07wVFxMm9V3x9gb+/WTMUujaEmHOAYP+nSYL5yTT7Bjhk+GGqeKw==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/process" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ "@vscode/ripgrep" "^1.14.2"
+ tslib "^2.6.2"
+
+"@theia/filesystem@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/filesystem/-/filesystem-1.56.0.tgz#c7108e1576713be243ff4bf27dbd26e193990d06"
+ integrity sha512-cE7JLSZS78A7e5V6Fv1dVVKyGiz0jg4qijtJ7fPhv/ehvczh3gv7vyQh8PX7Blkzri9PkT79SrFty8RErdMCOQ==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@types/body-parser" "^1.17.0"
+ "@types/multer" "^1.4.7"
+ "@types/tar-fs" "^1.16.1"
+ async-mutex "^0.3.1"
+ body-parser "^1.18.3"
+ http-status-codes "^1.3.0"
+ minimatch "^5.1.0"
+ multer "1.4.4-lts.1"
+ rimraf "^5.0.0"
+ stat-mode "^1.0.0"
+ tar-fs "^1.16.2"
+ trash "^7.2.0"
+ tslib "^2.6.2"
+ vscode-languageserver-textdocument "^1.0.1"
+
+"@theia/getting-started@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/getting-started/-/getting-started-1.56.0.tgz#43feee63771326a0b1bd31e15159702aa98eb628"
+ integrity sha512-yMlAAQCfXEDWQbkmCo/4RFu4KM99K3KqFuJe2OxV8BmCdKySNc1N1xQXsynAqn1YRP8tJ2nNo7kbCwqCt3EC8w==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/keymaps" "1.56.0"
+ "@theia/preview" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/keymaps@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/keymaps/-/keymaps-1.56.0.tgz#d2e01fc40b4ab12c0ade25af116f95be6d29352e"
+ integrity sha512-avVByqXkOKkPzpeRfcASfT5f0KQBkszp0zGxA4VRbRxLZCsZytwK4V3tuc9xrU1JmTLetHBAhH3Jrxe1aoWxsA==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ "@theia/preferences" "1.56.0"
+ "@theia/userstorage" "1.56.0"
+ jsonc-parser "^2.2.0"
+ tslib "^2.6.2"
+
+"@theia/localization-manager@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/localization-manager/-/localization-manager-1.56.0.tgz#9148b6692c23738edc74c0dd114c120d3b05e021"
+ integrity sha512-Jt2K7H8PGqBinjuSb8pyoqxSguO0qU91dfGDj30UoaD68cjUaiVrmyH6N1N2qZGbsOOil5BcwAcbC3IcHYm1jQ==
+ dependencies:
+ "@types/bent" "^7.0.1"
+ "@types/fs-extra" "^4.0.2"
+ bent "^7.1.0"
+ chalk "4.0.0"
+ deepmerge "^4.2.2"
+ fs-extra "^4.0.2"
+ glob "^7.2.0"
+ tslib "^2.6.2"
+ typescript "~5.4.5"
+
+"@theia/markers@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/markers/-/markers-1.56.0.tgz#1954ea1d3e2ab3c9a7062c0739ccad7503e5245e"
+ integrity sha512-JnCe+cVbpKa1RVDba8fSnJ9K1wmVceG+dHwml403cNx+ALNFDEl2V9S189722X9px+Ex27cBcTSexH52FTw6RA==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/messages@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/messages/-/messages-1.56.0.tgz#09a3889461cf5d4b1f76201720079c106ecf5686"
+ integrity sha512-deDdwK0O8RD7rynArDbBNekMsmxYbWy20ZDLqhJfZkYJD+nA0oCfnxIEAQ2dCtUvI4lMRfisFYv4vLR1fcHP/w==
+ dependencies:
+ "@theia/core" "1.56.0"
+ react-perfect-scrollbar "^1.5.3"
+ ts-md5 "^1.2.2"
+ tslib "^2.6.2"
+
+"@theia/metrics@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/metrics/-/metrics-1.56.0.tgz#d9220c844a98013744e9d4de298f361245bc16b9"
+ integrity sha512-fqPrZPVwodK4ue0fyrIWump7833hIwJz0L371oYILyNKBpveqBig8geYvfgk1o5I5hkYISTja/r7BR76NwLdCg==
+ dependencies:
+ "@theia/core" "1.56.0"
+ prom-client "^10.2.0"
+ tslib "^2.6.2"
+
+"@theia/mini-browser@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/mini-browser/-/mini-browser-1.56.0.tgz#9fb92c57bc139401325d267019dca7a06224c13c"
+ integrity sha512-eB3uZiB2lCgWLkyRjok3KfIT2THMwiZae3A6EqmVcYMUKb90SfaQ8ffsQrG2w2c7Y/HxiSsCnASggTYUads86A==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@types/mime-types" "^2.1.0"
+ mime-types "^2.1.18"
+ pdfobject "^2.0.201604172"
+ tslib "^2.6.2"
+ vhost "^3.0.2"
+
+"@theia/monaco-editor-core@1.83.101":
+ version "1.83.101"
+ resolved "https://registry.yarnpkg.com/@theia/monaco-editor-core/-/monaco-editor-core-1.83.101.tgz#a0577396fb4c69540536df2d7fed2de4399c4fde"
+ integrity sha512-UaAi6CEvain/qbGD3o6Ufe8plLyzAVQ53p9Ke+MoBYDhb391+r+MuK++JtITqIrXqoa8OCjbt8wQxEFSNNO0Mw==
+
+"@theia/monaco@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/monaco/-/monaco-1.56.0.tgz#94392bac50bf6916837b158ba270bd17af75978b"
+ integrity sha512-v+sXnxWZbMVVP9p74UBal3dxWEr8j3BrbYOkyznq0RDjSU3zO5dVkztINBLdICCAt60bRp1IwfavNUJKhyW8PQ==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/markers" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ "@theia/outline-view" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ fast-plist "^0.1.2"
+ idb "^4.0.5"
+ jsonc-parser "^2.2.0"
+ tslib "^2.6.2"
+ vscode-oniguruma "1.6.1"
+ vscode-textmate "^9.0.0"
+
+"@theia/native-webpack-plugin@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/native-webpack-plugin/-/native-webpack-plugin-1.56.0.tgz#3cc941268d3e03d070047e61ed71685aa8a8967d"
+ integrity sha512-+yhNQW74oZpTkDxD3hYjbiNNx3ROEIQPh4AEJ9N52GB2g4ZqCHugwOw7vssxRveCow05/OrCfffkdG4AVkz1CQ==
+ dependencies:
+ detect-libc "^2.0.2"
+ tslib "^2.6.2"
+ webpack "^5.76.0"
+
+"@theia/navigator@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/navigator/-/navigator-1.56.0.tgz#b05de893c07acbd6d708cec072163d3970be6c0e"
+ integrity sha512-TTD4HwCfAITAARB1qQWMIAe7+8is3OsWGfzuC+lFXNGeJZW3WOWd+jYj01C9KUlith0FvN4Z6QhhRlivqeGd+A==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ minimatch "^5.1.0"
+ tslib "^2.6.2"
+
+"@theia/notebook@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/notebook/-/notebook-1.56.0.tgz#6970f5e549e9124f251741180cd246d6b4262d33"
+ integrity sha512-HuMq/c6LQP5xl7WxStc0fEBn+4y6IWIsHz/tv7x+VocIIZ6QWlBsMfcDzKuIDp2Ro6voGHKtJemgKHSL6T6Hag==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ "@theia/outline-view" "1.56.0"
+ advanced-mark.js "^2.6.0"
+ react-perfect-scrollbar "^1.5.8"
+ tslib "^2.6.2"
+
+"@theia/outline-view@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/outline-view/-/outline-view-1.56.0.tgz#3702a660cf7231d398cc66228571d160cb9e4365"
+ integrity sha512-03SS7rmYCa03aVo6FHo9KW9Y+b9l9UvqhSCIW6UCQYxRCNraCuTtfP2EjcWsAWAO+lBVy2Tp8hf8+X7XgReZVQ==
+ dependencies:
+ "@theia/core" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/output@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/output/-/output-1.56.0.tgz#0ca20dda97c7a533950f50b00aa20ecc3638bb26"
+ integrity sha512-BNzkSWdU4K+Ak2Eli35mrBHhMCu9fHHddZt1LE1zebKcjvldg/KkgafZ5ALxr5ylmyz3yU6KwxzrZR+gqA9x5w==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ "@types/p-queue" "^2.3.1"
+ p-queue "^2.4.2"
+ tslib "^2.6.2"
+
+"@theia/ovsx-client@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/ovsx-client/-/ovsx-client-1.56.0.tgz#6e46fa0c8b0c8b7da2b7f128dcd57c2073bb5243"
+ integrity sha512-kPtmnQpKF6+sN29pzJ1SNZSBcWcFv+807c0JvUUPXTRlbjIYtZObMxdf5FZ8iMcRLJmu31GzcaMeLK/qztOmuQ==
+ dependencies:
+ "@theia/request" "1.56.0"
+ limiter "^2.1.0"
+ semver "^7.5.4"
+ tslib "^2.6.2"
+
+"@theia/plugin-dev@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/plugin-dev/-/plugin-dev-1.56.0.tgz#57e92ab4787f8bbc5a1daa62f0783374176e4f05"
+ integrity sha512-/lTGIp8XxZc/g6mE0UWP9P1rNbopYk+hgbRlST5UwH648fHj1JzDNBpFMQ0BK4ynY8AxF1yHmolj0nDl3ohTgw==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/debug" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/output" "1.56.0"
+ "@theia/plugin-ext" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ ps-tree "^1.2.0"
+ tslib "^2.6.2"
+
+"@theia/plugin-ext-vscode@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/plugin-ext-vscode/-/plugin-ext-vscode-1.56.0.tgz#bf243a153dffefe87bc6a58ee8ad1c466821c609"
+ integrity sha512-pGLZC2/J54k6s4iA4v5QsK+bxQx11F/YPJOFZ6kgD3ttU5fNC7bGoN7UWU1mS2njm6lNf4bKpvFxZqK0w4EmYw==
+ dependencies:
+ "@theia/callhierarchy" "1.56.0"
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ "@theia/navigator" "1.56.0"
+ "@theia/outline-view" "1.56.0"
+ "@theia/plugin" "1.56.0"
+ "@theia/plugin-ext" "1.56.0"
+ "@theia/terminal" "1.56.0"
+ "@theia/typehierarchy" "1.56.0"
+ "@theia/userstorage" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ decompress "^4.2.1"
+ filenamify "^4.1.0"
+ tslib "^2.6.2"
+
+"@theia/plugin-ext@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/plugin-ext/-/plugin-ext-1.56.0.tgz#f5f060a2afd615f2e15e5f8e668e60f44e16824e"
+ integrity sha512-ETl1K7ZTIaWBHoP3KB4vEGHc88orxm8F57aPNz7rs0L4G/YC6yg52ptvGtUkwL+80p0jkSZC/LVoTyiwMiOvUQ==
+ dependencies:
+ "@theia/bulk-edit" "1.56.0"
+ "@theia/callhierarchy" "1.56.0"
+ "@theia/console" "1.56.0"
+ "@theia/core" "1.56.0"
+ "@theia/debug" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/editor-preview" "1.56.0"
+ "@theia/file-search" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/markers" "1.56.0"
+ "@theia/messages" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ "@theia/navigator" "1.56.0"
+ "@theia/notebook" "1.56.0"
+ "@theia/output" "1.56.0"
+ "@theia/plugin" "1.56.0"
+ "@theia/preferences" "1.56.0"
+ "@theia/scm" "1.56.0"
+ "@theia/search-in-workspace" "1.56.0"
+ "@theia/task" "1.56.0"
+ "@theia/terminal" "1.56.0"
+ "@theia/test" "1.56.0"
+ "@theia/timeline" "1.56.0"
+ "@theia/typehierarchy" "1.56.0"
+ "@theia/variable-resolver" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ "@types/mime" "^2.0.1"
+ "@vscode/debugprotocol" "^1.51.0"
+ "@vscode/proxy-agent" "^0.13.2"
+ async-mutex "^0.4.0"
+ decompress "^4.2.1"
+ escape-html "^1.0.3"
+ filenamify "^4.1.0"
+ is-electron "^2.2.0"
+ jsonc-parser "^2.2.0"
+ lodash.clonedeep "^4.5.0"
+ macaddress "^0.5.3"
+ mime "^2.4.4"
+ ps-tree "^1.2.0"
+ semver "^7.5.4"
+ tslib "^2.6.2"
+ vhost "^3.0.2"
+ vscode-textmate "^9.0.0"
+
+"@theia/plugin@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/plugin/-/plugin-1.56.0.tgz#fa9976750945ca21c5fbf47613b7d057240264e4"
+ integrity sha512-v/wrgRi7lJUpKSdWfBd4Qm1UDj2TQnBfYjUKTNyQNlUzLNXbjeY2BR3RZaMiGYrTBnDW4gt8KmmVLme12UKXGQ==
+
+"@theia/preferences@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/preferences/-/preferences-1.56.0.tgz#99a609990fef5efad0cf51d05cbccbd9af3b4079"
+ integrity sha512-QvDYWVPkxSkS45b6CcgejkeyNFCGcNCElMYn4EuaNfxgO2bqfFFOqkYtGsb+/6E8TKUb28G3hF89CwWAFHdaTw==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ "@theia/userstorage" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ async-mutex "^0.3.1"
+ fast-deep-equal "^3.1.3"
+ jsonc-parser "^2.2.0"
+ p-debounce "^2.1.0"
+ tslib "^2.6.2"
+
+"@theia/preview@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/preview/-/preview-1.56.0.tgz#4f3065643b9a9b7db86868c07df1b250311c1c61"
+ integrity sha512-8c83rGCx/3VCdzqdN/RiOYcqsBg2kY0M3dpklowBvgudlbx02Hk4SFqPl/wbkAbZTXjeZ4qludku7iddoSnvTg==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/mini-browser" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@types/highlight.js" "^10.1.0"
+ "@types/markdown-it-anchor" "^4.0.1"
+ highlight.js "10.4.1"
+ markdown-it-anchor "~5.0.0"
+ tslib "^2.6.2"
+
+"@theia/process@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/process/-/process-1.56.0.tgz#35be6d26fd80ac9584bebbae10cb3d028a1866e8"
+ integrity sha512-zCHql9/4OXicHA90Hxm6fNXmaCfdEa1O/VTaFEc2kp27gIk2IoPQa4U/wAMHZKp6QU2kcE1hRJTRVS/SE45FLA==
+ dependencies:
+ "@theia/core" "1.56.0"
+ node-pty "0.11.0-beta24"
+ string-argv "^0.1.1"
+ tslib "^2.6.2"
+
+"@theia/property-view@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/property-view/-/property-view-1.56.0.tgz#e85da61f5efa8a26b3d97ddc819d40330a5249b9"
+ integrity sha512-tdgn8A3nyCz+NpHnkBC3Esi0Ho+mi1hetk2bNsXnrPJ3Znar5f9I2n167pN002ayKIRAP1cZ/ZQ/Zg8SNRJvgA==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/request@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/request/-/request-1.56.0.tgz#b5e1b4516c1c6a50e4eaca64a3dbff683b142785"
+ integrity sha512-za3drcBr3UESGn26DrShZElw0+ecD4U8uSC6TtiiqmMtf156lYfElxf0CYnMAbHkveCu6V4scs3JqGTt9DUYaQ==
+ dependencies:
+ http-proxy-agent "^5.0.0"
+ https-proxy-agent "^5.0.0"
+ tslib "^2.6.2"
+
+"@theia/scm-extra@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/scm-extra/-/scm-extra-1.56.0.tgz#069360a11a3fe8b4522e88fbf30f7ecfe5d3a510"
+ integrity sha512-Pmaso0pZcSGLegfHgvf1n3LoAgH/Nzyqj5c8GTU8plqdn45ot3bEIjEaZFPREk8YncNMgWhbR0PY0NhoXyEUYg==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/navigator" "1.56.0"
+ "@theia/scm" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/scm@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/scm/-/scm-1.56.0.tgz#6417e730b0b0c06cb3f7c3440d242198ac3c97f3"
+ integrity sha512-/3l7JFRATQqXz2y/v0C/90uEltgcIvqQfnyfRmXLUbBkXn6LWQZDWtC36ZX+HcNEmdkQbuj6akaA8p6u/HV/aw==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ "@types/diff" "^5.2.1"
+ diff "^5.2.0"
+ p-debounce "^2.1.0"
+ react-autosize-textarea "^7.0.0"
+ ts-md5 "^1.2.2"
+ tslib "^2.6.2"
+
+"@theia/search-in-workspace@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/search-in-workspace/-/search-in-workspace-1.56.0.tgz#f8bef2155f21b049b96016ca5ae0126313bd376d"
+ integrity sha512-lhA5c+gpjkznN3TPpI9xr9kwctIqO9JcpdFFtTfZnD1I66y/eEgaEAEI/G9fAlMpIMT5OrTK4xGwDDdMLogVPQ==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/navigator" "1.56.0"
+ "@theia/process" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ "@vscode/ripgrep" "^1.14.2"
+ minimatch "^5.1.0"
+ react-autosize-textarea "^7.0.0"
+ tslib "^2.6.2"
+
+"@theia/task@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/task/-/task-1.56.0.tgz#be8fbe2009fbba8723843113e3c5f1c8044bbee9"
+ integrity sha512-rApGOf8mtF7XAmGZsqXAqy71EBT/VMLJsOf7Nc2OrbC0j4SuroL5kR1v5uE14NCcJaqRPTLkQc0VaSrOe/IIFg==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/markers" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ "@theia/process" "1.56.0"
+ "@theia/terminal" "1.56.0"
+ "@theia/userstorage" "1.56.0"
+ "@theia/variable-resolver" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ async-mutex "^0.3.1"
+ jsonc-parser "^2.2.0"
+ p-debounce "^2.1.0"
+ tslib "^2.6.2"
+
+"@theia/terminal@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/terminal/-/terminal-1.56.0.tgz#034d571f4823f84992c9f9564393ab9b1ab308e7"
+ integrity sha512-KruO7gQtWwzla3XfeRBXC/shVBGIZUe63QjIQJJezyEKBDq3nA4lGuE9XC/lFbqD0qqK0lmTnCn70Aa4ZRKiQA==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/file-search" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/process" "1.56.0"
+ "@theia/variable-resolver" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ tslib "^2.6.2"
+ xterm "^5.3.0"
+ xterm-addon-fit "^0.8.0"
+ xterm-addon-search "^0.13.0"
+
+"@theia/test@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/test/-/test-1.56.0.tgz#87216e49cc41c99b2c3da2523ccb46c80d81c041"
+ integrity sha512-5wLHhBkxuip9V0fI2lDrCzN1AJWRyMkrgpzduzQcX/m3qtPWfhOrZWOHdRp6rVbyAIKrMrPQ3dqdQoI8/SwCpg==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/navigator" "1.56.0"
+ "@theia/terminal" "1.56.0"
+ xterm "^4.16.0"
+ xterm-addon-fit "^0.5.0"
+
+"@theia/timeline@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/timeline/-/timeline-1.56.0.tgz#a2251288826c9a4fb8b5d4f10542de8d1f7749c7"
+ integrity sha512-jdpzTMtcoceu+zNLcwBvWi4Yq3P+xAoZac3v5QJbSkhTG005OETLP+4SJYAe9bHu+K/ibc9p4gDnqxPxrh0YHA==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/navigator" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/toolbar@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/toolbar/-/toolbar-1.56.0.tgz#3890df7ff1b702a077be6e5409db80605c5ed36e"
+ integrity sha512-qFjPLyBur1mMhb8hj26/R5GgVx5WqCTsS8E94NNNE9LZdCJUAKcDufLDlNt3zJOZAW0HF9krcsiSuIYsfB0+dQ==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ "@theia/file-search" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/monaco" "1.56.0"
+ "@theia/monaco-editor-core" "1.83.101"
+ "@theia/search-in-workspace" "1.56.0"
+ "@theia/userstorage" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ ajv "^6.5.3"
+ jsonc-parser "^2.2.0"
+ perfect-scrollbar "^1.3.0"
+ tslib "^2.6.2"
+
+"@theia/typehierarchy@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/typehierarchy/-/typehierarchy-1.56.0.tgz#cb3fd1903225e43e870e00a1386e720855fe0728"
+ integrity sha512-B3FrRfQyahS17PyAb/NuwRAlenz9i6K7Pf4yOYfLao5K7H1+QHKKm7GIJLbuA8355jc3yaiasgij2clIMXAfsA==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/editor" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/userstorage@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/userstorage/-/userstorage-1.56.0.tgz#dd9b5ee5085c89a4b80ff11f71fcb5fe4cb2fd5e"
+ integrity sha512-e2EPkPWKGgQsvUV5yXKkGLGC/POCgCW85O76/HhEuiRLDRpx7LCPH+uRaB9mXOOFgGGRtHPxtWSFGLMNpGKuNg==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/variable-resolver@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/variable-resolver/-/variable-resolver-1.56.0.tgz#1d4130898a61cd92a0ef0a0c7344dff2b87eb3e6"
+ integrity sha512-VIPL+17aOlH4OhJTSV0EssUGJCHN3G00WADQViRYPQQma7+lP/sivAr5U/IMahjh2nK8S3SvBDCSZ6wiKIjNvA==
+ dependencies:
+ "@theia/core" "1.56.0"
+ tslib "^2.6.2"
+
+"@theia/vsx-registry@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/vsx-registry/-/vsx-registry-1.56.0.tgz#a03c5ab5f2cee0d8c69c2c5f5ac28f911225d010"
+ integrity sha512-1f+P4lkBot3WX3Vtts6KDPr3bfimoQq135pZzyokO9hHUHg9S5jHkBLQ7LTAvgaF/wfBUtK85EzZRBTkCpuNew==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/navigator" "1.56.0"
+ "@theia/ovsx-client" "1.56.0"
+ "@theia/plugin-ext" "1.56.0"
+ "@theia/plugin-ext-vscode" "1.56.0"
+ "@theia/preferences" "1.56.0"
+ "@theia/workspace" "1.56.0"
+ limiter "^2.1.0"
+ luxon "^2.4.0"
+ p-debounce "^2.1.0"
+ semver "^7.5.4"
+ tslib "^2.6.2"
+
+"@theia/workspace@1.56.0":
+ version "1.56.0"
+ resolved "https://registry.yarnpkg.com/@theia/workspace/-/workspace-1.56.0.tgz#2e7cbc56a31d3cdfd5997afe5a77b6982518a475"
+ integrity sha512-pmEfCgBY3G0eGYwEMdn7ZYaHV8T5uTmHdXB3GRxJFGFmWeKbScqlJPQzxBgNuWKNFufPqkO+EF0JkpupoLfjcQ==
+ dependencies:
+ "@theia/core" "1.56.0"
+ "@theia/filesystem" "1.56.0"
+ "@theia/variable-resolver" "1.56.0"
+ jsonc-parser "^2.2.0"
+ tslib "^2.6.2"
+ valid-filename "^2.0.1"
+
+"@tootallnate/once@1", "@tootallnate/once@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
+ integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
+
+"@tootallnate/once@2":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
+ integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
+
+"@tootallnate/quickjs-emscripten@^0.23.0":
+ version "0.23.0"
+ resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c"
+ integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==
+
+"@types/bent@^7.0.1":
+ version "7.3.8"
+ resolved "https://registry.yarnpkg.com/@types/bent/-/bent-7.3.8.tgz#69c3ee49bf6593d831006794e7bd2f84bb573e58"
+ integrity sha512-yZ09JA1KsA5Fl6Oh/ahK00+H5bV0qCy2bYnyfiFY42wnaMK4n7IDC6HaFe3WW45Zhnak7iqJBKlWD0nVxzrGWg==
+ dependencies:
+ "@types/node" "*"
+
+"@types/body-parser@*", "@types/body-parser@^1.16.4", "@types/body-parser@^1.17.0":
+ version "1.19.5"
+ resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4"
+ integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==
+ dependencies:
+ "@types/connect" "*"
+ "@types/node" "*"
+
+"@types/cacheable-request@^6.0.1":
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183"
+ integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==
+ dependencies:
+ "@types/http-cache-semantics" "*"
+ "@types/keyv" "^3.1.4"
+ "@types/node" "*"
+ "@types/responselike" "^1.0.0"
+
+"@types/chai@^4.2.7":
+ version "4.3.20"
+ resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.20.tgz#cb291577ed342ca92600430841a00329ba05cecc"
+ integrity sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==
+
+"@types/connect@*":
+ version "3.4.38"
+ resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858"
+ integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==
+ dependencies:
+ "@types/node" "*"
+
+"@types/cookie@^0.3.3":
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803"
+ integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==
+
+"@types/cookie@^0.4.1":
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d"
+ integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==
+
+"@types/cors@^2.8.12":
+ version "2.8.17"
+ resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.17.tgz#5d718a5e494a8166f569d986794e49c48b216b2b"
+ integrity sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==
+ dependencies:
+ "@types/node" "*"
+
+"@types/diff@^5.2.1":
+ version "5.2.3"
+ resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.2.3.tgz#dcdcfa40df9f011f9465180e0196dfbd921971d9"
+ integrity sha512-K0Oqlrq3kQMaO2RhfrNQX5trmt+XLyom88zS0u84nnIcLvFnRUMRRHmrGny5GSM+kNO9IZLARsdQHDzkhAgmrQ==
+
+"@types/dompurify@^2.2.2":
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.4.0.tgz#fd9706392a88e0e0e6d367f3588482d817df0ab9"
+ integrity sha512-IDBwO5IZhrKvHFUl+clZxgf3hn2b/lU6H1KaBShPkQyGJUQ0xwebezIPSuiyGwfz1UzJWQl4M7BDxtHtCCPlTg==
+ dependencies:
+ "@types/trusted-types" "*"
+
+"@types/eslint-scope@^3.7.7":
+ version "3.7.7"
+ resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5"
+ integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==
+ dependencies:
+ "@types/eslint" "*"
+ "@types/estree" "*"
+
+"@types/eslint@*":
+ version "9.6.1"
+ resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584"
+ integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==
+ dependencies:
+ "@types/estree" "*"
+ "@types/json-schema" "*"
+
+"@types/estree@*", "@types/estree@^1.0.6":
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
+ integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
+
+"@types/express-serve-static-core@^4.17.33":
+ version "4.19.6"
+ resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz#e01324c2a024ff367d92c66f48553ced0ab50267"
+ integrity sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==
+ dependencies:
+ "@types/node" "*"
+ "@types/qs" "*"
+ "@types/range-parser" "*"
+ "@types/send" "*"
+
+"@types/express-serve-static-core@^5.0.0":
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.2.tgz#812d2871e5eea17fb0bd5214dda7a7b748c0e12a"
+ integrity sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==
+ dependencies:
+ "@types/node" "*"
+ "@types/qs" "*"
+ "@types/range-parser" "*"
+ "@types/send" "*"
+
+"@types/express@*":
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.0.tgz#13a7d1f75295e90d19ed6e74cab3678488eaa96c"
+ integrity sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==
+ dependencies:
+ "@types/body-parser" "*"
+ "@types/express-serve-static-core" "^5.0.0"
+ "@types/qs" "*"
+ "@types/serve-static" "*"
+
+"@types/express@^4.17.21":
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d"
+ integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==
+ dependencies:
+ "@types/body-parser" "*"
+ "@types/express-serve-static-core" "^4.17.33"
+ "@types/qs" "*"
+ "@types/serve-static" "*"
+
+"@types/fs-extra@^4.0.2":
+ version "4.0.15"
+ resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-4.0.15.tgz#82d70b4a2e5e3dd17474ce9e8fe951a03aeddd31"
+ integrity sha512-zU/EU2kZ1tv+p4pswQLntA7dFQq84wXrSCfmLjZvMbLjf4N46cPOWHg+WKfc27YnEOQ0chVFlBui55HRsvzHPA==
+ dependencies:
+ "@types/node" "*"
+
+"@types/highlight.js@^10.1.0":
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-10.1.0.tgz#89bb0c202997d7a90a07bd2ec1f7d00c56bb90b4"
+ integrity sha512-77hF2dGBsOgnvZll1vymYiNUtqJ8cJfXPD6GG/2M0aLRc29PkvB7Au6sIDjIEFcSICBhCh2+Pyq6WSRS7LUm6A==
+ dependencies:
+ highlight.js "*"
+
+"@types/http-cache-semantics@*":
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4"
+ integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==
+
+"@types/http-errors@*":
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f"
+ integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==
+
+"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
+ version "7.0.15"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
+ integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
+
+"@types/keyv@^3.1.4":
+ version "3.1.4"
+ resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6"
+ integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==
+ dependencies:
+ "@types/node" "*"
+
+"@types/linkify-it@*", "@types/linkify-it@^5":
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-5.0.0.tgz#21413001973106cda1c3a9b91eedd4ccd5469d76"
+ integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==
+
+"@types/lodash.debounce@4.0.3":
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.3.tgz#d712aee9e6136be77f70523ed9f0fc049a6cf15a"
+ integrity sha512-/2RpcexzkSH16nENwuL/Gd3Y2xvdkNwX32KPESB/D8K2c6HBs7GdSnoj6ngyFWNT1UhXNrIpJd0lgSC3Rmt/3g==
+ dependencies:
+ "@types/lodash" "*"
+
+"@types/lodash.throttle@^4.1.3":
+ version "4.1.9"
+ resolved "https://registry.yarnpkg.com/@types/lodash.throttle/-/lodash.throttle-4.1.9.tgz#f17a6ae084f7c0117bd7df145b379537bc9615c5"
+ integrity sha512-PCPVfpfueguWZQB7pJQK890F2scYKoDUL3iM522AptHWn7d5NQmeS/LTEHIcLr5PaTzl3dK2Z0xSUHHTHwaL5g==
+ dependencies:
+ "@types/lodash" "*"
+
+"@types/lodash@*":
+ version "4.17.13"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.13.tgz#786e2d67cfd95e32862143abe7463a7f90c300eb"
+ integrity sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==
+
+"@types/markdown-it-anchor@^4.0.1":
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/@types/markdown-it-anchor/-/markdown-it-anchor-4.0.4.tgz#f36b67608d238d15024fb6508efd7ad3990209f6"
+ integrity sha512-3CFeYzh8q7DaQboo1g+nnyXjPIvvtzoM+iBQU25sfcWVnjXckAvZIwAmPSVwHxnIblcTVcGq1XjEJjiBZFKkRA==
+ dependencies:
+ "@types/markdown-it" "*"
+
+"@types/markdown-it@*":
+ version "14.1.2"
+ resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-14.1.2.tgz#57f2532a0800067d9b934f3521429a2e8bfb4c61"
+ integrity sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==
+ dependencies:
+ "@types/linkify-it" "^5"
+ "@types/mdurl" "^2"
+
+"@types/markdown-it@^12.2.3":
+ version "12.2.3"
+ resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.3.tgz#0d6f6e5e413f8daaa26522904597be3d6cd93b51"
+ integrity sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==
+ dependencies:
+ "@types/linkify-it" "*"
+ "@types/mdurl" "*"
+
+"@types/mdurl@*", "@types/mdurl@^2":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-2.0.0.tgz#d43878b5b20222682163ae6f897b20447233bdfd"
+ integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==
+
+"@types/mime-types@^2.1.0":
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.4.tgz#93a1933e24fed4fb9e4adc5963a63efcbb3317a2"
+ integrity sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==
+
+"@types/mime@^1":
+ version "1.3.5"
+ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690"
+ integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==
+
+"@types/mime@^2.0.1":
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.3.tgz#c893b73721db73699943bfc3653b1deb7faa4a3a"
+ integrity sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==
+
+"@types/mocha@^10.0.0":
+ version "10.0.10"
+ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.10.tgz#91f62905e8d23cbd66225312f239454a23bebfa0"
+ integrity sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==
+
+"@types/multer@^1.4.7":
+ version "1.4.12"
+ resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.12.tgz#da67bd0c809f3a63fe097c458c0d4af1fea50ab7"
+ integrity sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==
+ dependencies:
+ "@types/express" "*"
+
+"@types/node-fetch@^2.5.7":
+ version "2.6.12"
+ resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.12.tgz#8ab5c3ef8330f13100a7479e2cd56d3386830a03"
+ integrity sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==
+ dependencies:
+ "@types/node" "*"
+ form-data "^4.0.0"
+
+"@types/node@*", "@types/node@>=10.0.0":
+ version "22.10.1"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.1.tgz#41ffeee127b8975a05f8c4f83fb89bcb2987d766"
+ integrity sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==
+ dependencies:
+ undici-types "~6.20.0"
+
+"@types/p-queue@^2.3.1":
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/@types/p-queue/-/p-queue-2.3.2.tgz#16bc5fece69ef85efaf2bce8b13f3ebe39c5a1c8"
+ integrity sha512-eKAv5Ql6k78dh3ULCsSBxX6bFNuGjTmof5Q/T6PiECDq0Yf8IIn46jCyp3RJvCi8owaEmm3DZH1PEImjBMd/vQ==
+
+"@types/prop-types@*":
+ version "15.7.13"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451"
+ integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==
+
+"@types/qs@*":
+ version "6.9.17"
+ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.17.tgz#fc560f60946d0aeff2f914eb41679659d3310e1a"
+ integrity sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==
+
+"@types/range-parser@*":
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb"
+ integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==
+
+"@types/react-dom@^18.0.6":
+ version "18.3.1"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07"
+ integrity sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==
+ dependencies:
+ "@types/react" "*"
+
+"@types/react@*", "@types/react@^18.0.15":
+ version "18.3.13"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.13.tgz#84c9690d9a271f548659760754ea8745701bfd82"
+ integrity sha512-ii/gswMmOievxAJed4PAHT949bpYjPKXvXo1v6cRB/kqc2ZR4n+SgyCyvyc5Fec5ez8VnUumI1Vk7j6fRyRogg==
+ dependencies:
+ "@types/prop-types" "*"
+ csstype "^3.0.2"
+
+"@types/responselike@^1.0.0":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50"
+ integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==
+ dependencies:
+ "@types/node" "*"
+
+"@types/route-parser@^0.1.1":
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/@types/route-parser/-/route-parser-0.1.7.tgz#76d324537c9f0aaf65c96588c6ab5f3b84ae1505"
+ integrity sha512-haO+3HVio/4w+yuMJTjqfSo0ivOV8WnXaOReVD6QN729UGBEyizWNGc2Jd0OLsJDucIod4aJSsPLBeLj2uzMCQ==
+
+"@types/safer-buffer@^2.1.0":
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/@types/safer-buffer/-/safer-buffer-2.1.3.tgz#901b21c2da54344cf73a205fb5d400592a43b5fd"
+ integrity sha512-5o3RcpBa7mUFnnnoMa9UIGOf9naD4DCKKMYzqkm9OSY7NNTd26k7adNC+fphcVRI9BUJglBs2yJQiRZYqBF/8w==
+ dependencies:
+ "@types/node" "*"
+
+"@types/semver@^7.5.0":
+ version "7.5.8"
+ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
+ integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
+
+"@types/send@*":
+ version "0.17.4"
+ resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a"
+ integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==
+ dependencies:
+ "@types/mime" "^1"
+ "@types/node" "*"
+
+"@types/serve-static@*":
+ version "1.15.7"
+ resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714"
+ integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==
+ dependencies:
+ "@types/http-errors" "*"
+ "@types/node" "*"
+ "@types/send" "*"
+
+"@types/tar-fs@^1.16.1":
+ version "1.16.3"
+ resolved "https://registry.yarnpkg.com/@types/tar-fs/-/tar-fs-1.16.3.tgz#425b2b817c405d13d051f36ec6ec6ebd25e31069"
+ integrity sha512-Y+fdeg11tb9J3UNIatNtrTPM1i8U+WLv2mMhZ3W13mtU19stCgrXJ4iXLkTpoF8jqHi3T/qTS8+fQ3IPzXxpuA==
+ dependencies:
+ "@types/node" "*"
+ "@types/tar-stream" "*"
+
+"@types/tar-stream@*":
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-3.1.3.tgz#f61427229691eda1b7d5719f34acdc4fc8a558ce"
+ integrity sha512-Zbnx4wpkWBMBSu5CytMbrT5ZpMiF55qgM+EpHzR4yIDu7mv52cej8hTkOc6K+LzpkOAbxwn/m7j3iO+/l42YkQ==
+ dependencies:
+ "@types/node" "*"
+
+"@types/trusted-types@*":
+ version "2.0.7"
+ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
+ integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
+
+"@types/uuid@^9.0.8":
+ version "9.0.8"
+ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba"
+ integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==
+
+"@types/write-json-file@^2.2.1":
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/@types/write-json-file/-/write-json-file-2.2.1.tgz#74155aaccbb0d532be21f9d66bebc4ea875a5a62"
+ integrity sha512-JdO/UpPm9RrtQBNVcZdt3M7j3mHO/kXaea9LBGx3UgWJd1f9BkIWP7jObLBG6ZtRyqp7KzLFEsaPhWcidVittA==
+
+"@types/ws@^8.5.5":
+ version "8.5.13"
+ resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.13.tgz#6414c280875e2691d0d1e080b05addbf5cb91e20"
+ integrity sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==
+ dependencies:
+ "@types/node" "*"
+
+"@types/yargs-parser@*":
+ version "21.0.3"
+ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
+ integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==
+
+"@types/yargs@^15":
+ version "15.0.19"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.19.tgz#328fb89e46109ecbdb70c295d96ff2f46dfd01b9"
+ integrity sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==
+ dependencies:
+ "@types/yargs-parser" "*"
+
+"@types/yauzl@^2.9.1":
+ version "2.10.3"
+ resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999"
+ integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==
+ dependencies:
+ "@types/node" "*"
+
+"@virtuoso.dev/react-urx@^0.2.12":
+ version "0.2.13"
+ resolved "https://registry.yarnpkg.com/@virtuoso.dev/react-urx/-/react-urx-0.2.13.tgz#e2cfc42d259d2a002695e7517d34cb97b64ee9c4"
+ integrity sha512-MY0ugBDjFb5Xt8v2HY7MKcRGqw/3gTpMlLXId2EwQvYJoC8sP7nnXjAxcBtTB50KTZhO0SbzsFimaZ7pSdApwA==
+ dependencies:
+ "@virtuoso.dev/urx" "^0.2.13"
+
+"@virtuoso.dev/urx@^0.2.12", "@virtuoso.dev/urx@^0.2.13":
+ version "0.2.13"
+ resolved "https://registry.yarnpkg.com/@virtuoso.dev/urx/-/urx-0.2.13.tgz#a65e7e8d923cb03397ac876bfdd45c7f71c8edf1"
+ integrity sha512-iirJNv92A1ZWxoOHHDYW/1KPoi83939o83iUBQHIim0i3tMeSKEh+bxhJdTHQ86Mr4uXx9xGUTq69cp52ZP8Xw==
+
+"@vscode/codicons@*":
+ version "0.0.36"
+ resolved "https://registry.yarnpkg.com/@vscode/codicons/-/codicons-0.0.36.tgz#ccdabfaef5db596b266644ab85fc25aa701058f0"
+ integrity sha512-wsNOvNMMJ2BY8rC2N2MNBG7yOowV3ov8KlvUE/AiVUlHKTfWsw3OgAOQduX7h0Un6GssKD3aoTVH+TF3DSQwKQ==
+
+"@vscode/debugprotocol@^1.51.0":
+ version "1.68.0"
+ resolved "https://registry.yarnpkg.com/@vscode/debugprotocol/-/debugprotocol-1.68.0.tgz#e558ba6affe1be7aff4ec824599f316b61d9a69d"
+ integrity sha512-2J27dysaXmvnfuhFGhfeuxfHRXunqNPxtBoR3koiTOA9rdxWNDTa1zIFLCFMSHJ9MPTPKFcBeblsyaCJCIlQxg==
+
+"@vscode/proxy-agent@^0.13.2":
+ version "0.13.2"
+ resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.13.2.tgz#0d289826c07faecc4ca07de80a8e5a9459d06119"
+ integrity sha512-BSUd0NTj44WvG4O9A6N+4R1XhxtPqCYltWeHyNkquX9T//a1US+cd8fxzcZCPd3z7dygdYIPkZAKM+CrefWWOA==
+ dependencies:
+ "@tootallnate/once" "^1.1.2"
+ agent-base "^6.0.2"
+ debug "^4.3.1"
+ http-proxy-agent "^4.0.1"
+ https-proxy-agent "^5.0.0"
+ socks-proxy-agent "^5.0.0"
+ optionalDependencies:
+ "@vscode/windows-ca-certs" "^0.3.1"
+
+"@vscode/ripgrep@^1.14.2":
+ version "1.15.9"
+ resolved "https://registry.yarnpkg.com/@vscode/ripgrep/-/ripgrep-1.15.9.tgz#92279f7f28e1e49ad9a89603e10b17a4c7f9f5f1"
+ integrity sha512-4q2PXRvUvr3bF+LsfrifmUZgSPmCNcUZo6SbEAZgArIChchkezaxLoIeQMJe/z3CCKStvaVKpBXLxN3Z8lQjFQ==
+ dependencies:
+ https-proxy-agent "^7.0.2"
+ proxy-from-env "^1.1.0"
+ yauzl "^2.9.2"
+
+"@vscode/windows-ca-certs@^0.3.1":
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/@vscode/windows-ca-certs/-/windows-ca-certs-0.3.3.tgz#7943d78db7d1d99bcd82eb8a0765a870b111d003"
+ integrity sha512-C0Iq5RcH+H31GUZ8bsMORsX3LySVkGAqe4kQfUSVcCqJ0QOhXkhgwUMU7oCiqYLXaQWyXFp6Fj6eMdt05uK7VA==
+ dependencies:
+ node-addon-api "^8.2.0"
+
+"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1":
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6"
+ integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==
+ dependencies:
+ "@webassemblyjs/helper-numbers" "1.13.2"
+ "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+
+"@webassemblyjs/floating-point-hex-parser@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb"
+ integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==
+
+"@webassemblyjs/helper-api-error@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7"
+ integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==
+
+"@webassemblyjs/helper-buffer@1.14.1":
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b"
+ integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==
+
+"@webassemblyjs/helper-numbers@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d"
+ integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==
+ dependencies:
+ "@webassemblyjs/floating-point-hex-parser" "1.13.2"
+ "@webassemblyjs/helper-api-error" "1.13.2"
+ "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/helper-wasm-bytecode@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b"
+ integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==
+
+"@webassemblyjs/helper-wasm-section@1.14.1":
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348"
+ integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==
+ dependencies:
+ "@webassemblyjs/ast" "1.14.1"
+ "@webassemblyjs/helper-buffer" "1.14.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+ "@webassemblyjs/wasm-gen" "1.14.1"
+
+"@webassemblyjs/ieee754@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba"
+ integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==
+ dependencies:
+ "@xtuc/ieee754" "^1.2.0"
+
+"@webassemblyjs/leb128@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0"
+ integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==
+ dependencies:
+ "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/utf8@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1"
+ integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==
+
+"@webassemblyjs/wasm-edit@^1.14.1":
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597"
+ integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==
+ dependencies:
+ "@webassemblyjs/ast" "1.14.1"
+ "@webassemblyjs/helper-buffer" "1.14.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+ "@webassemblyjs/helper-wasm-section" "1.14.1"
+ "@webassemblyjs/wasm-gen" "1.14.1"
+ "@webassemblyjs/wasm-opt" "1.14.1"
+ "@webassemblyjs/wasm-parser" "1.14.1"
+ "@webassemblyjs/wast-printer" "1.14.1"
+
+"@webassemblyjs/wasm-gen@1.14.1":
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570"
+ integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==
+ dependencies:
+ "@webassemblyjs/ast" "1.14.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+ "@webassemblyjs/ieee754" "1.13.2"
+ "@webassemblyjs/leb128" "1.13.2"
+ "@webassemblyjs/utf8" "1.13.2"
+
+"@webassemblyjs/wasm-opt@1.14.1":
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b"
+ integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==
+ dependencies:
+ "@webassemblyjs/ast" "1.14.1"
+ "@webassemblyjs/helper-buffer" "1.14.1"
+ "@webassemblyjs/wasm-gen" "1.14.1"
+ "@webassemblyjs/wasm-parser" "1.14.1"
+
+"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1":
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb"
+ integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==
+ dependencies:
+ "@webassemblyjs/ast" "1.14.1"
+ "@webassemblyjs/helper-api-error" "1.13.2"
+ "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+ "@webassemblyjs/ieee754" "1.13.2"
+ "@webassemblyjs/leb128" "1.13.2"
+ "@webassemblyjs/utf8" "1.13.2"
+
+"@webassemblyjs/wast-printer@1.14.1":
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07"
+ integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==
+ dependencies:
+ "@webassemblyjs/ast" "1.14.1"
+ "@xtuc/long" "4.2.2"
+
+"@webpack-cli/configtest@^1.0.3":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5"
+ integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==
+
+"@webpack-cli/info@^1.2.4":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1"
+ integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==
+ dependencies:
+ envinfo "^7.7.3"
+
+"@webpack-cli/serve@^1.4.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1"
+ integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==
+
+"@xtuc/ieee754@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
+ integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
+
+"@xtuc/long@4.2.2":
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
+ integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+
+"@yarnpkg/lockfile@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
+ integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==
+
+abab@^2.0.5:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
+ integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
+
+abbrev@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
+ integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
+
+accepts@~1.3.4, accepts@~1.3.8:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
+ integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
+ dependencies:
+ mime-types "~2.1.34"
+ negotiator "0.6.3"
+
+acorn@^8.14.0, acorn@^8.8.2:
+ version "8.14.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
+ integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
+
+advanced-mark.js@^2.6.0:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/advanced-mark.js/-/advanced-mark.js-2.6.0.tgz#86ea8b81152b543db91b1602df4e69282c3b0083"
+ integrity sha512-b6Q7iNkXk1BTUvmbvtig+/p3Z54qDQqOoOKPJUZXYtCdgeZPz/qj9xZs6GKKQA2/OIOrlNwSt/OvyIq06eYPVw==
+
+agent-base@6, agent-base@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
+ integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
+ dependencies:
+ debug "4"
+
+agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317"
+ integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==
+ dependencies:
+ debug "^4.3.4"
+
+agentkeepalive@^4.2.1:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923"
+ integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==
+ dependencies:
+ humanize-ms "^1.2.1"
+
+aggregate-error@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
+ integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
+ dependencies:
+ clean-stack "^2.0.0"
+ indent-string "^4.0.0"
+
+ajv-formats@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
+ integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==
+ dependencies:
+ ajv "^8.0.0"
+
+ajv-keywords@^3.5.2:
+ version "3.5.2"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
+ integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
+
+ajv-keywords@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16"
+ integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==
+ dependencies:
+ fast-deep-equal "^3.1.3"
+
+ajv@^6.12.4, ajv@^6.12.5, ajv@^6.5.3:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ajv@^8.0.0, ajv@^8.6.3, ajv@^8.9.0:
+ version "8.17.1"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
+ integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
+ dependencies:
+ fast-deep-equal "^3.1.3"
+ fast-uri "^3.0.1"
+ json-schema-traverse "^1.0.0"
+ require-from-string "^2.0.2"
+
+anser@^2.0.1:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/anser/-/anser-2.3.0.tgz#9b30c8cffe864e646eebd6cd578ba67f2dfe8b2e"
+ integrity sha512-pGGR7Nq1K/i9KGs29PvHDXA8AsfZ3OiYRMDClT3FIC085Kbns9CJ7ogq9MEiGnrjd9THOGoh7B+kWzePHzZyJQ==
+
+ansi-colors@^4.1.3:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
+ integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==
+
+ansi-escapes@^4.3.0:
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
+ integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
+ dependencies:
+ type-fest "^0.21.3"
+
+ansi-regex@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+ integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==
+
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-regex@^6.0.1:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654"
+ integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+ansi-styles@^6.1.0:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
+ integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
+
+anymatch@~3.1.2:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
+ integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
+ dependencies:
+ normalize-path "^3.0.0"
+ picomatch "^2.0.4"
+
+append-field@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56"
+ integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==
+
+aproba@^1.0.3:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
+ integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
+
+"aproba@^1.0.3 || ^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
+ integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
+
+are-we-there-yet@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd"
+ integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==
+ dependencies:
+ delegates "^1.0.0"
+ readable-stream "^3.6.0"
+
+are-we-there-yet@~1.1.2:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146"
+ integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==
+ dependencies:
+ delegates "^1.0.0"
+ readable-stream "^2.0.6"
+
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+array-flatten@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
+ integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
+
+array-union@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+ integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==
+ dependencies:
+ array-uniq "^1.0.1"
+
+array-union@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
+ integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
+
+array-uniq@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+ integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==
+
+assertion-error@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
+ integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
+
+ast-types@0.9.6:
+ version "0.9.6"
+ resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9"
+ integrity sha512-qEdtR2UH78yyHX/AUNfXmJTlM48XoFZKBdwi1nzkI1mJL21cmbu0cvjxjpkXJ5NENMq42H+hNs8VLJcqXLerBQ==
+
+ast-types@^0.13.4:
+ version "0.13.4"
+ resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782"
+ integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==
+ dependencies:
+ tslib "^2.0.1"
+
+ast-types@^0.9.2:
+ version "0.9.14"
+ resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.14.tgz#d34ba5dffb9d15a44351fd2a9d82e4ab2838b5ba"
+ integrity sha512-Ebvx7/0lLboCdyEmAw/4GqwBeKIijPveXNiVGhCGCNxc7z26T5he7DC6ARxu8ByKuzUZZcLog+VP8GMyZrBzJw==
+
+astral-regex@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
+ integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
+
+async-mutex@^0.3.1:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df"
+ integrity sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==
+ dependencies:
+ tslib "^2.3.1"
+
+async-mutex@^0.4.0:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.1.tgz#bccf55b96f2baf8df90ed798cb5544a1f6ee4c2c"
+ integrity sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA==
+ dependencies:
+ tslib "^2.4.0"
+
+async@^2.6.4:
+ version "2.6.4"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
+ integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==
+ dependencies:
+ lodash "^4.17.14"
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+at-least-node@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
+ integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
+
+atomically@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/atomically/-/atomically-1.7.0.tgz#c07a0458432ea6dbc9a3506fffa424b48bccaafe"
+ integrity sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==
+
+autosize@^4.0.2:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/autosize/-/autosize-4.0.4.tgz#924f13853a466b633b9309330833936d8bccce03"
+ integrity sha512-5yxLQ22O0fCRGoxGfeLSNt3J8LB1v+umtpMnPW6XjkTWXKoN0AmXAIhelJcDtFT/Y/wYWmfE+oqU10Q0b8FhaQ==
+
+axios@^1.7.4:
+ version "1.7.9"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a"
+ integrity sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==
+ dependencies:
+ follow-redirects "^1.15.6"
+ form-data "^4.0.0"
+ proxy-from-env "^1.1.0"
+
+b4a@^1.6.4:
+ version "1.6.7"
+ resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.7.tgz#a99587d4ebbfbd5a6e3b21bdb5d5fa385767abe4"
+ integrity sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==
+
+babel-loader@^8.2.2:
+ version "8.4.1"
+ resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.4.1.tgz#6ccb75c66e62c3b144e1c5f2eaec5b8f6c08c675"
+ integrity sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==
+ dependencies:
+ find-cache-dir "^3.3.1"
+ loader-utils "^2.0.4"
+ make-dir "^3.1.0"
+ schema-utils "^2.6.5"
+
+babel-plugin-polyfill-corejs2@^0.4.10:
+ version "0.4.12"
+ resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz#ca55bbec8ab0edeeef3d7b8ffd75322e210879a9"
+ integrity sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==
+ dependencies:
+ "@babel/compat-data" "^7.22.6"
+ "@babel/helper-define-polyfill-provider" "^0.6.3"
+ semver "^6.3.1"
+
+babel-plugin-polyfill-corejs3@^0.10.6:
+ version "0.10.6"
+ resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz#2deda57caef50f59c525aeb4964d3b2f867710c7"
+ integrity sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==
+ dependencies:
+ "@babel/helper-define-polyfill-provider" "^0.6.2"
+ core-js-compat "^3.38.0"
+
+babel-plugin-polyfill-regenerator@^0.6.1:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz#abeb1f3f1c762eace37587f42548b08b57789bc8"
+ integrity sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==
+ dependencies:
+ "@babel/helper-define-polyfill-provider" "^0.6.3"
+
+balanced-match@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+bare-events@^2.0.0, bare-events@^2.2.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.5.0.tgz#305b511e262ffd8b9d5616b056464f8e1b3329cc"
+ integrity sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==
+
+bare-fs@^2.1.1:
+ version "2.3.5"
+ resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-2.3.5.tgz#05daa8e8206aeb46d13c2fe25a2cd3797b0d284a"
+ integrity sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==
+ dependencies:
+ bare-events "^2.0.0"
+ bare-path "^2.0.0"
+ bare-stream "^2.0.0"
+
+bare-os@^2.1.0:
+ version "2.4.4"
+ resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-2.4.4.tgz#01243392eb0a6e947177bb7c8a45123d45c9b1a9"
+ integrity sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==
+
+bare-path@^2.0.0, bare-path@^2.1.0:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-2.1.3.tgz#594104c829ef660e43b5589ec8daef7df6cedb3e"
+ integrity sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==
+ dependencies:
+ bare-os "^2.1.0"
+
+bare-stream@^2.0.0:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.4.2.tgz#5a4241ff8a3bdd6d037fc459ab3e41189d2f2576"
+ integrity sha512-XZ4ln/KV4KT+PXdIWTKjsLY+quqCaEtqqtgGJVPw9AoM73By03ij64YjepK0aQvHSWDb6AfAZwqKaFu68qkrdA==
+ dependencies:
+ streamx "^2.20.0"
+
+base64-js@^1.3.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+ integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
+base64id@2.0.0, base64id@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6"
+ integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==
+
+basic-auth@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
+ integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==
+ dependencies:
+ safe-buffer "5.1.2"
+
+basic-ftp@^5.0.2:
+ version "5.0.5"
+ resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0"
+ integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==
+
+bent@^7.1.0:
+ version "7.3.12"
+ resolved "https://registry.yarnpkg.com/bent/-/bent-7.3.12.tgz#e0a2775d4425e7674c64b78b242af4f49da6b035"
+ integrity sha512-T3yrKnVGB63zRuoco/7Ybl7BwwGZR0lceoVG5XmQyMIH9s19SV5m+a8qam4if0zQuAmOQTyPTPmsQBdAorGK3w==
+ dependencies:
+ bytesish "^0.4.1"
+ caseless "~0.12.0"
+ is-stream "^2.0.0"
+
+big-integer@^1.6.17:
+ version "1.6.52"
+ resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85"
+ integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==
+
+big.js@^5.2.2:
+ version "5.2.2"
+ resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
+ integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
+
+binary-extensions@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
+ integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
+
+binary@~0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79"
+ integrity sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==
+ dependencies:
+ buffers "~0.1.1"
+ chainsaw "~0.1.0"
+
+bindings@^1.3.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
+ integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
+ dependencies:
+ file-uri-to-path "1.0.0"
+
+bintrees@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8"
+ integrity sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==
+
+bl@^1.0.0:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7"
+ integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==
+ dependencies:
+ readable-stream "^2.3.5"
+ safe-buffer "^5.1.1"
+
+bl@^4.0.3, bl@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
+ integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
+ dependencies:
+ buffer "^5.5.0"
+ inherits "^2.0.4"
+ readable-stream "^3.4.0"
+
+bluebird@~3.4.1:
+ version "3.4.7"
+ resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3"
+ integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==
+
+body-parser@1.20.3, body-parser@^1.17.2, body-parser@^1.18.3:
+ version "1.20.3"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
+ integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
+ dependencies:
+ bytes "3.1.2"
+ content-type "~1.0.5"
+ debug "2.6.9"
+ depd "2.0.0"
+ destroy "1.2.0"
+ http-errors "2.0.0"
+ iconv-lite "0.4.24"
+ on-finished "2.4.1"
+ qs "6.13.0"
+ raw-body "2.5.2"
+ type-is "~1.6.18"
+ unpipe "1.0.0"
+
+boolean@^3.0.1:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b"
+ integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+ dependencies:
+ balanced-match "^1.0.0"
+
+braces@^3.0.3, braces@~3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
+ integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
+ dependencies:
+ fill-range "^7.1.1"
+
+browser-stdout@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
+ integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
+
+browserslist@^4.24.0, browserslist@^4.24.2:
+ version "4.24.2"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580"
+ integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==
+ dependencies:
+ caniuse-lite "^1.0.30001669"
+ electron-to-chromium "^1.5.41"
+ node-releases "^2.0.18"
+ update-browserslist-db "^1.1.1"
+
+buffer-alloc-unsafe@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
+ integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
+
+buffer-alloc@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
+ integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
+ dependencies:
+ buffer-alloc-unsafe "^1.1.0"
+ buffer-fill "^1.0.0"
+
+buffer-crc32@~0.2.3:
+ version "0.2.13"
+ resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
+ integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
+
+buffer-fill@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
+ integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==
+
+buffer-from@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+ integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+buffer-indexof-polyfill@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c"
+ integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==
+
+buffer@^5.2.1, buffer@^5.5.0:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+ integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.1.13"
+
+buffer@^6.0.3:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
+ integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.2.1"
+
+buffers@~0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb"
+ integrity sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==
+
+busboy@^1.0.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
+ integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
+ dependencies:
+ streamsearch "^1.1.0"
+
+bytes@3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
+ integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
+
+bytesish@^0.4.1:
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/bytesish/-/bytesish-0.4.4.tgz#f3b535a0f1153747427aee27256748cff92347e6"
+ integrity sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ==
+
+cacache@^16.1.0:
+ version "16.1.3"
+ resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e"
+ integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==
+ dependencies:
+ "@npmcli/fs" "^2.1.0"
+ "@npmcli/move-file" "^2.0.0"
+ chownr "^2.0.0"
+ fs-minipass "^2.1.0"
+ glob "^8.0.1"
+ infer-owner "^1.0.4"
+ lru-cache "^7.7.1"
+ minipass "^3.1.6"
+ minipass-collect "^1.0.2"
+ minipass-flush "^1.0.5"
+ minipass-pipeline "^1.2.4"
+ mkdirp "^1.0.4"
+ p-map "^4.0.0"
+ promise-inflight "^1.0.1"
+ rimraf "^3.0.2"
+ ssri "^9.0.0"
+ tar "^6.1.11"
+ unique-filename "^2.0.0"
+
+cacheable-lookup@^5.0.3:
+ version "5.0.4"
+ resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005"
+ integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==
+
+cacheable-request@^7.0.2:
+ version "7.0.4"
+ resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817"
+ integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==
+ dependencies:
+ clone-response "^1.0.2"
+ get-stream "^5.1.0"
+ http-cache-semantics "^4.0.0"
+ keyv "^4.0.0"
+ lowercase-keys "^2.0.0"
+ normalize-url "^6.0.1"
+ responselike "^2.0.0"
+
+call-bind@^1.0.5, call-bind@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
+ integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
+ 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"
+
+callsites@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+camelcase@^5.0.0:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+ integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
+camelcase@^6.0.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
+ integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
+
+caniuse-lite@^1.0.30001669:
+ version "1.0.30001686"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001686.tgz#0e04b8d90de8753188e93c9989d56cb19d902670"
+ integrity sha512-Y7deg0Aergpa24M3qLC5xjNklnKnhsmSyR/V89dLZ1n0ucJIFNs7PgR2Yfa/Zf6W79SbBicgtGxZr2juHkEUIA==
+
+caseless@~0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+ integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==
+
+chai@^4.3.10:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/chai/-/chai-4.5.0.tgz#707e49923afdd9b13a8b0b47d33d732d13812fd8"
+ integrity sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==
+ dependencies:
+ assertion-error "^1.1.0"
+ check-error "^1.0.3"
+ deep-eql "^4.1.3"
+ get-func-name "^2.0.2"
+ loupe "^2.3.6"
+ pathval "^1.1.1"
+ type-detect "^4.1.0"
+
+chainsaw@~0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98"
+ integrity sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==
+ dependencies:
+ traverse ">=0.3.0 <0.4"
+
+chalk@4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72"
+ integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+check-error@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694"
+ integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==
+ dependencies:
+ get-func-name "^2.0.2"
+
+chokidar@^3.5.3:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
+ integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
+ dependencies:
+ anymatch "~3.1.2"
+ braces "~3.0.2"
+ glob-parent "~5.1.2"
+ is-binary-path "~2.1.0"
+ is-glob "~4.0.1"
+ normalize-path "~3.0.0"
+ readdirp "~3.6.0"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+chownr@^1.0.1, chownr@^1.1.1:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
+ integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
+
+chownr@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
+ integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
+
+chrome-trace-event@^1.0.2:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b"
+ integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==
+
+chromium-bidi@0.6.4:
+ version "0.6.4"
+ resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.6.4.tgz#627d76bae2819d59b61a413babe9664e0a16b71d"
+ integrity sha512-8zoq6ogmhQQkAKZVKO2ObFTl4uOkqoX1PlKQX3hZQ5E9cbUotcAb7h4pTNVAGGv8Z36PF3CtdOriEp/Rz82JqQ==
+ dependencies:
+ mitt "3.0.1"
+ urlpattern-polyfill "10.0.0"
+ zod "3.23.8"
+
+ci-info@^3.7.0:
+ version "3.9.0"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4"
+ integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==
+
+clean-stack@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
+ integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
+
+cli-cursor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+ integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+ dependencies:
+ restore-cursor "^3.1.0"
+
+cli-spinners@^2.5.0:
+ version "2.9.2"
+ resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41"
+ integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==
+
+cliui@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
+ integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.0"
+ wrap-ansi "^6.2.0"
+
+cliui@^7.0.2:
+ version "7.0.4"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
+ integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.0"
+ wrap-ansi "^7.0.0"
+
+cliui@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
+ integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.1"
+ wrap-ansi "^7.0.0"
+
+clone-deep@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
+ integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
+ dependencies:
+ is-plain-object "^2.0.4"
+ kind-of "^6.0.2"
+ shallow-clone "^3.0.0"
+
+clone-response@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3"
+ integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==
+ dependencies:
+ mimic-response "^1.0.0"
+
+clone@^1.0.2:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+ integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
+
+clone@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
+ integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
+
+code-point-at@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+ integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+color-support@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
+ integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
+
+colorette@^1.2.1:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40"
+ integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
+
+combined-stream@^1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ dependencies:
+ delayed-stream "~1.0.0"
+
+commander@^2.20.0, commander@^2.8.1:
+ version "2.20.3"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+commander@^7.0.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+ integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+
+commondir@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+ integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
+
+compression-webpack-plugin@^9.0.0:
+ version "9.2.0"
+ resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-9.2.0.tgz#57fd539d17c5907eebdeb4e83dcfe2d7eceb9ef6"
+ integrity sha512-R/Oi+2+UHotGfu72fJiRoVpuRifZT0tTC6UqFD/DUo+mv8dbOow9rVOuTvDv5nPPm3GZhHL/fKkwxwIHnJ8Nyw==
+ dependencies:
+ schema-utils "^4.0.0"
+ serialize-javascript "^6.0.0"
+
+computed-style@~0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/computed-style/-/computed-style-0.1.4.tgz#7f344fd8584b2e425bedca4a1afc0e300bb05d74"
+ integrity sha512-WpAmaKbMNmS3OProfHIdJiNleNJdgUrJfbKArXua28QF7+0CoZjlLn0lp6vlc+dl5r2/X9GQiQRQQU4BzSa69w==
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+concat-stream@^1.5.2:
+ version "1.6.2"
+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
+ integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
+ dependencies:
+ buffer-from "^1.0.0"
+ inherits "^2.0.3"
+ readable-stream "^2.2.2"
+ typedarray "^0.0.6"
+
+conf@^10.2.0:
+ version "10.2.0"
+ resolved "https://registry.yarnpkg.com/conf/-/conf-10.2.0.tgz#838e757be963f1a2386dfe048a98f8f69f7b55d6"
+ integrity sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==
+ dependencies:
+ ajv "^8.6.3"
+ ajv-formats "^2.1.1"
+ atomically "^1.7.0"
+ debounce-fn "^4.0.0"
+ dot-prop "^6.0.1"
+ env-paths "^2.2.1"
+ json-schema-typed "^7.0.3"
+ onetime "^5.1.2"
+ pkg-up "^3.1.0"
+ semver "^7.3.5"
+
+console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+ integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
+
+content-disposition@0.5.4:
+ version "0.5.4"
+ resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
+ integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
+ dependencies:
+ safe-buffer "5.2.1"
+
+content-type@~1.0.4, content-type@~1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
+ integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
+
+convert-source-map@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
+ integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
+
+cookie-signature@1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
+ integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
+
+cookie@0.7.1:
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9"
+ integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==
+
+cookie@^0.4.0:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
+ integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
+
+cookie@~0.7.2:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7"
+ integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==
+
+copy-anything@^2.0.1:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.6.tgz#092454ea9584a7b7ad5573062b2a87f5900fc480"
+ integrity sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==
+ dependencies:
+ is-what "^3.14.1"
+
+copy-webpack-plugin@^8.1.1:
+ version "8.1.1"
+ resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-8.1.1.tgz#3f697e162764925c2f0d235f380676125508fd26"
+ integrity sha512-rYM2uzRxrLRpcyPqGceRBDpxxUV8vcDqIKxAUKfcnFpcrPxT5+XvhTxv7XLjo5AvEJFPdAE3zCogG2JVahqgSQ==
+ dependencies:
+ fast-glob "^3.2.5"
+ glob-parent "^5.1.1"
+ globby "^11.0.3"
+ normalize-path "^3.0.0"
+ p-limit "^3.1.0"
+ schema-utils "^3.0.0"
+ serialize-javascript "^5.0.1"
+
+core-js-compat@^3.38.0, core-js-compat@^3.38.1:
+ version "3.39.0"
+ resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.39.0.tgz#b12dccb495f2601dc860bdbe7b4e3ffa8ba63f61"
+ integrity sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==
+ dependencies:
+ browserslist "^4.24.2"
+
+core-util-is@~1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
+ integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
+
+cors@~2.8.5:
+ version "2.8.5"
+ resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
+ integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
+ dependencies:
+ object-assign "^4"
+ vary "^1"
+
+corser@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87"
+ integrity sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==
+
+cosmiconfig@^9.0.0:
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d"
+ integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==
+ dependencies:
+ env-paths "^2.2.1"
+ import-fresh "^3.3.0"
+ js-yaml "^4.1.0"
+ parse-json "^5.2.0"
+
+cross-spawn@^4.0.0:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41"
+ integrity sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==
+ dependencies:
+ lru-cache "^4.0.1"
+ which "^1.2.9"
+
+cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3:
+ version "7.0.6"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
+ integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
+css-loader@^6.2.0:
+ version "6.11.0"
+ resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.11.0.tgz#33bae3bf6363d0a7c2cf9031c96c744ff54d85ba"
+ integrity sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==
+ dependencies:
+ icss-utils "^5.1.0"
+ postcss "^8.4.33"
+ postcss-modules-extract-imports "^3.1.0"
+ postcss-modules-local-by-default "^4.0.5"
+ postcss-modules-scope "^3.2.0"
+ postcss-modules-values "^4.0.0"
+ postcss-value-parser "^4.2.0"
+ semver "^7.5.4"
+
+cssesc@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+ integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
+csstype@^3.0.2:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
+ integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
+
+data-uri-to-buffer@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz#8a58bb67384b261a38ef18bea1810cb01badd28b"
+ integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==
+
+debounce-fn@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/debounce-fn/-/debounce-fn-4.0.0.tgz#ed76d206d8a50e60de0dd66d494d82835ffe61c7"
+ integrity sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==
+ dependencies:
+ mimic-fn "^3.0.0"
+
+debug@2.6.9:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4:
+ version "4.3.7"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
+ integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
+ dependencies:
+ ms "^2.1.3"
+
+debug@^3.1.0, debug@^3.2.7:
+ version "3.2.7"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
+ integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
+ dependencies:
+ ms "^2.1.1"
+
+decamelize@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+ integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
+
+decamelize@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
+ integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
+
+decompress-response@^4.2.0:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986"
+ integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
+ dependencies:
+ mimic-response "^2.0.0"
+
+decompress-response@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
+ integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
+ dependencies:
+ mimic-response "^3.1.0"
+
+decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
+ integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==
+ dependencies:
+ file-type "^5.2.0"
+ is-stream "^1.1.0"
+ tar-stream "^1.5.2"
+
+decompress-tarbz2@^4.0.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
+ integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==
+ dependencies:
+ decompress-tar "^4.1.0"
+ file-type "^6.1.0"
+ is-stream "^1.1.0"
+ seek-bzip "^1.0.5"
+ unbzip2-stream "^1.0.9"
+
+decompress-targz@^4.0.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
+ integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==
+ dependencies:
+ decompress-tar "^4.1.1"
+ file-type "^5.2.0"
+ is-stream "^1.1.0"
+
+decompress-unzip@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
+ integrity sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==
+ dependencies:
+ file-type "^3.8.0"
+ get-stream "^2.2.0"
+ pify "^2.3.0"
+ yauzl "^2.4.2"
+
+decompress@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118"
+ integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==
+ dependencies:
+ decompress-tar "^4.0.0"
+ decompress-tarbz2 "^4.0.0"
+ decompress-targz "^4.0.0"
+ decompress-unzip "^4.0.1"
+ graceful-fs "^4.1.10"
+ make-dir "^1.0.0"
+ pify "^2.3.0"
+ strip-dirs "^2.0.0"
+
+deep-eql@^4.1.3:
+ version "4.1.4"
+ resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.4.tgz#d0d3912865911bb8fac5afb4e3acfa6a28dc72b7"
+ integrity sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==
+ dependencies:
+ type-detect "^4.0.0"
+
+deep-extend@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
+ integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+
+deepmerge@^4.2.2:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
+ integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
+
+default-shell@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/default-shell/-/default-shell-1.0.1.tgz#752304bddc6174f49eb29cb988feea0b8813c8bc"
+ integrity sha512-/Os8tTMPSriNHCsVj3VLjMZblIl1sIg8EXz3qg7C5K+y9calfTA/qzlfPvCQ+LEgLWmtZ9wCnzE1w+S6TPPFyQ==
+
+defaults@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a"
+ integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==
+ dependencies:
+ clone "^1.0.2"
+
+defer-to-connect@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587"
+ integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==
+
+define-data-property@^1.0.1, define-data-property@^1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
+ integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
+ dependencies:
+ es-define-property "^1.0.0"
+ es-errors "^1.3.0"
+ gopd "^1.0.1"
+
+define-properties@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
+ integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
+ dependencies:
+ define-data-property "^1.0.1"
+ has-property-descriptors "^1.0.0"
+ object-keys "^1.1.1"
+
+degenerator@^5.0.0:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5"
+ integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==
+ dependencies:
+ ast-types "^0.13.4"
+ escodegen "^2.1.0"
+ esprima "^4.0.1"
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
+delegates@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+ integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
+
+depd@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
+ integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
+
+destroy@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
+ integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
+
+detect-indent@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d"
+ integrity sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==
+
+detect-libc@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+ integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
+
+detect-libc@^2.0.1, detect-libc@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
+ integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
+
+detect-node@^2.0.4:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
+ integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
+
+devtools-protocol@0.0.1312386:
+ version "0.0.1312386"
+ resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz#5ab824d6f1669ec6c6eb0fba047e73601d969052"
+ integrity sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==
+
+diff@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531"
+ integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==
+
+dir-glob@^2.0.0:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4"
+ integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==
+ dependencies:
+ path-type "^3.0.0"
+
+dir-glob@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
+ integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
+ dependencies:
+ path-type "^4.0.0"
+
+dompurify@^2.2.9:
+ version "2.5.7"
+ resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.5.7.tgz#6e0d36b9177db5a99f18ade1f28579db5ab839d7"
+ integrity sha512-2q4bEI+coQM8f5ez7kt2xclg1XsecaV9ASJk/54vwlfRRNQfDqJz2pzQ8t0Ix/ToBpXlVjrRIx7pFC/o8itG2Q==
+
+dot-prop@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083"
+ integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==
+ dependencies:
+ is-obj "^2.0.0"
+
+drivelist@^9.0.2:
+ version "9.2.4"
+ resolved "https://registry.yarnpkg.com/drivelist/-/drivelist-9.2.4.tgz#f89d2233d86c9fcdbc0daacae257d7b27a658d20"
+ integrity sha512-F36yn+qXwiOGZM16FYPKcIRjC7qXDIA0SBZ0vvTEe01ai788Se8z78acYdgXC8NAsghiO+9c/GYXgU7E9hhUpg==
+ dependencies:
+ bindings "^1.3.0"
+ debug "^3.1.0"
+ nan "^2.14.0"
+ prebuild-install "^5.2.4"
+
+duplexer2@~0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
+ integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==
+ dependencies:
+ readable-stream "^2.0.2"
+
+duplexer@~0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
+ integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
+
+eastasianwidth@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
+ integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
+
+ee-first@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+ integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
+
+electron-rebuild@^3.2.7:
+ version "3.2.9"
+ resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-3.2.9.tgz#ea372be15f591f8d6d978ee9bca6526dadbcf20f"
+ integrity sha512-FkEZNFViUem3P0RLYbZkUjC8LUFIK+wKq09GHoOITSJjfDAVQv964hwaNseTTWt58sITQX3/5fHNYcTefqaCWw==
+ dependencies:
+ "@malept/cross-spawn-promise" "^2.0.0"
+ chalk "^4.0.0"
+ debug "^4.1.1"
+ detect-libc "^2.0.1"
+ fs-extra "^10.0.0"
+ got "^11.7.0"
+ lzma-native "^8.0.5"
+ node-abi "^3.0.0"
+ node-api-version "^0.1.4"
+ node-gyp "^9.0.0"
+ ora "^5.1.0"
+ semver "^7.3.5"
+ tar "^6.0.5"
+ yargs "^17.0.1"
+
+electron-store@^8.0.0:
+ version "8.2.0"
+ resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-8.2.0.tgz#114e6e453e8bb746ab4ccb542424d8c881ad2ca1"
+ integrity sha512-ukLL5Bevdil6oieAOXz3CMy+OgaItMiVBg701MNlG6W5RaC0AHN7rvlqTCmeb6O7jP0Qa1KKYTE0xV0xbhF4Hw==
+ dependencies:
+ conf "^10.2.0"
+ type-fest "^2.17.0"
+
+electron-to-chromium@^1.5.41:
+ version "1.5.70"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.70.tgz#8be2f92e54a3d069c0eefcf766a48bf0f78d93e0"
+ integrity sha512-P6FPqAWIZrC3sHDAwBitJBs7N7IF58m39XVny7DFseQXK2eiMn7nNQizFf63mWDDUnFvaqsM8FI0+ZZfLkdUGA==
+
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+emoji-regex@^9.2.2:
+ version "9.2.2"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
+ integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
+
+emojis-list@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
+ integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
+
+encodeurl@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+ integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
+
+encodeurl@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
+ integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
+
+encoding@^0.1.13:
+ version "0.1.13"
+ resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
+ integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
+ dependencies:
+ iconv-lite "^0.6.2"
+
+end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
+ integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
+ dependencies:
+ once "^1.4.0"
+
+engine.io-client@~6.6.1:
+ version "6.6.2"
+ resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.6.2.tgz#e0a09e1c90effe5d6264da1c56d7281998f1e50b"
+ integrity sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==
+ dependencies:
+ "@socket.io/component-emitter" "~3.1.0"
+ debug "~4.3.1"
+ engine.io-parser "~5.2.1"
+ ws "~8.17.1"
+ xmlhttprequest-ssl "~2.1.1"
+
+engine.io-parser@~5.2.1:
+ version "5.2.3"
+ resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f"
+ integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==
+
+engine.io@~6.6.0:
+ version "6.6.2"
+ resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.6.2.tgz#32bd845b4db708f8c774a4edef4e5c8a98b3da72"
+ integrity sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==
+ dependencies:
+ "@types/cookie" "^0.4.1"
+ "@types/cors" "^2.8.12"
+ "@types/node" ">=10.0.0"
+ accepts "~1.3.4"
+ base64id "2.0.0"
+ cookie "~0.7.2"
+ cors "~2.8.5"
+ debug "~4.3.1"
+ engine.io-parser "~5.2.1"
+ ws "~8.17.1"
+
+enhanced-resolve@^5.17.1:
+ version "5.17.1"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15"
+ integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==
+ dependencies:
+ graceful-fs "^4.2.4"
+ tapable "^2.2.0"
+
+entities@~2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
+ integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
+
+env-paths@^2.2.0, env-paths@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
+ integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
+
+envinfo@^7.7.3:
+ version "7.14.0"
+ resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae"
+ integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==
+
+err-code@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
+ integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
+
+errno@^0.1.1:
+ version "0.1.8"
+ resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
+ integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
+ dependencies:
+ prr "~1.0.1"
+
+error-ex@^1.3.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+ integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+ dependencies:
+ is-arrayish "^0.2.1"
+
+es-define-property@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
+ integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
+ dependencies:
+ get-intrinsic "^1.2.4"
+
+es-errors@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
+ integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
+
+es-module-lexer@^1.2.1:
+ version "1.5.4"
+ resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78"
+ integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==
+
+es6-error@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
+ integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
+
+es6-promise@^4.2.4:
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
+ integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
+
+escalade@^3.1.1, escalade@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
+ integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
+
+escape-html@^1.0.3, escape-html@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+ integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
+
+escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+escape-string-regexp@^1.0.2:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+escodegen@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17"
+ integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==
+ dependencies:
+ esprima "^4.0.1"
+ estraverse "^5.2.0"
+ esutils "^2.0.2"
+ optionalDependencies:
+ source-map "~0.6.1"
+
+eslint-scope@5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+ integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^4.1.1"
+
+esprima@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+esprima@~3.1.0:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
+ integrity sha512-AWwVMNxwhN8+NIPQzAQZCm7RkLC4RbM3B1OobMuyp3i+w73X57KCKaVIxaRZb+DYCojq7rspo+fmuQfAboyhFg==
+
+esrecurse@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ dependencies:
+ estraverse "^5.2.0"
+
+estraverse@^4.1.1:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+estraverse@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+etag@~1.8.1:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+ integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
+
+event-stream@=3.3.4:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
+ integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==
+ dependencies:
+ duplexer "~0.1.1"
+ from "~0"
+ map-stream "~0.1.0"
+ pause-stream "0.0.11"
+ split "0.3"
+ stream-combiner "~0.0.4"
+ through "~2.3.1"
+
+eventemitter3@^4.0.0:
+ version "4.0.7"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
+ integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
+
+events@^3.2.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
+ integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
+
+execa@^0.5.0:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-0.5.1.tgz#de3fb85cb8d6e91c85bcbceb164581785cb57b36"
+ integrity sha512-R66dW/hW3I8yV77Wg4xn6zMguRPUgt59VLm5e85NrOF05ZdPn7YOfPBSw0E9epJDvuzwVWEG+HmEaQ4muYuWKQ==
+ dependencies:
+ cross-spawn "^4.0.0"
+ get-stream "^2.2.0"
+ is-stream "^1.1.0"
+ npm-run-path "^2.0.0"
+ p-finally "^1.0.0"
+ signal-exit "^3.0.0"
+ strip-eof "^1.0.0"
+
+execa@^2.0.1:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-2.1.0.tgz#e5d3ecd837d2a60ec50f3da78fd39767747bbe99"
+ integrity sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw==
+ dependencies:
+ cross-spawn "^7.0.0"
+ get-stream "^5.0.0"
+ is-stream "^2.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^3.0.0"
+ onetime "^5.1.0"
+ p-finally "^2.0.0"
+ signal-exit "^3.0.2"
+ strip-final-newline "^2.0.0"
+
+execa@^5.0.0:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
+ integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
+ dependencies:
+ cross-spawn "^7.0.3"
+ get-stream "^6.0.0"
+ human-signals "^2.1.0"
+ is-stream "^2.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^4.0.1"
+ onetime "^5.1.2"
+ signal-exit "^3.0.3"
+ strip-final-newline "^2.0.0"
+
+expand-template@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
+ integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
+
+exponential-backoff@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
+ integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==
+
+express@^4.21.0:
+ version "4.21.1"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281"
+ integrity sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==
+ dependencies:
+ accepts "~1.3.8"
+ array-flatten "1.1.1"
+ body-parser "1.20.3"
+ content-disposition "0.5.4"
+ content-type "~1.0.4"
+ cookie "0.7.1"
+ cookie-signature "1.0.6"
+ debug "2.6.9"
+ depd "2.0.0"
+ encodeurl "~2.0.0"
+ escape-html "~1.0.3"
+ etag "~1.8.1"
+ finalhandler "1.3.1"
+ fresh "0.5.2"
+ http-errors "2.0.0"
+ merge-descriptors "1.0.3"
+ methods "~1.1.2"
+ on-finished "2.4.1"
+ parseurl "~1.3.3"
+ path-to-regexp "0.1.10"
+ proxy-addr "~2.0.7"
+ qs "6.13.0"
+ range-parser "~1.2.1"
+ safe-buffer "5.2.1"
+ send "0.19.0"
+ serve-static "1.16.2"
+ setprototypeof "1.2.0"
+ statuses "2.0.1"
+ type-is "~1.6.18"
+ utils-merge "1.0.1"
+ vary "~1.1.2"
+
+extract-zip@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
+ integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==
+ dependencies:
+ debug "^4.1.1"
+ get-stream "^5.1.0"
+ yauzl "^2.10.0"
+ optionalDependencies:
+ "@types/yauzl" "^2.9.1"
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-fifo@^1.2.0, fast-fifo@^1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c"
+ integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==
+
+fast-glob@^3.2.5, fast-glob@^3.2.9:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
+ integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
+ 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"
+
+fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-plist@^0.1.2:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/fast-plist/-/fast-plist-0.1.3.tgz#328cd9335e93a2479ac90814a1302437574ea925"
+ integrity sha512-d9cEfo/WcOezgPLAC/8t8wGb6YOD6JTCPMw2QcG2nAdFmyY+9rTUizCTaGjIZAloWENTEUMAPpkUAIJJJ0i96A==
+
+fast-uri@^3.0.1:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241"
+ integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==
+
+fastest-levenshtein@^1.0.12:
+ version "1.0.16"
+ resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
+ integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
+
+fastq@^1.6.0:
+ version "1.17.1"
+ resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47"
+ integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==
+ dependencies:
+ reusify "^1.0.4"
+
+fd-slicer@~1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
+ integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==
+ dependencies:
+ pend "~1.2.0"
+
+file-icons-js@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/file-icons-js/-/file-icons-js-1.0.3.tgz#d0765dc1d86aba4b2d7664a39e4ef7af9f12c5af"
+ integrity sha512-n4zoKEpMaAxBTUB7wtgrFBa4dM3b7mBLLA1VI/Q5Cdk/k2UA8S8oaxvnECp3QOzg0Dn+KKRzfIHF7qSdRkA65Q==
+
+file-type@^3.8.0:
+ version "3.9.0"
+ resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
+ integrity sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==
+
+file-type@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
+ integrity sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==
+
+file-type@^6.1.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
+ integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==
+
+file-uri-to-path@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
+ integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
+
+filename-reserved-regex@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229"
+ integrity sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==
+
+filenamify@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-4.3.0.tgz#62391cb58f02b09971c9d4f9d63b3cf9aba03106"
+ integrity sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==
+ dependencies:
+ filename-reserved-regex "^2.0.0"
+ strip-outer "^1.0.1"
+ trim-repeated "^1.0.0"
+
+fill-range@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
+ integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+finalhandler@1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019"
+ integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==
+ dependencies:
+ debug "2.6.9"
+ encodeurl "~2.0.0"
+ escape-html "~1.0.3"
+ on-finished "2.4.1"
+ parseurl "~1.3.3"
+ statuses "2.0.1"
+ unpipe "~1.0.0"
+
+find-cache-dir@^3.3.1:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b"
+ integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==
+ dependencies:
+ commondir "^1.0.1"
+ make-dir "^3.0.2"
+ pkg-dir "^4.1.0"
+
+find-up@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+ integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+ dependencies:
+ locate-path "^3.0.0"
+
+find-up@^4.0.0, find-up@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+ integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+ dependencies:
+ locate-path "^5.0.0"
+ path-exists "^4.0.0"
+
+find-up@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+ integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+ dependencies:
+ locate-path "^6.0.0"
+ path-exists "^4.0.0"
+
+find-yarn-workspace-root@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd"
+ integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==
+ dependencies:
+ micromatch "^4.0.2"
+
+fix-path@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/fix-path/-/fix-path-3.0.0.tgz#c6b82fd5f5928e520b392a63565ebfef0ddf037e"
+ integrity sha512-opGAl4+ip5jUikHR2C8Jo7czZ80pz8EK/0gMlAZu7xgDmBqIynlX8SMYg9KowYjAU6HT0nxsSJEWru0u+n+N2Q==
+ dependencies:
+ shell-path "^2.1.0"
+
+flat@^5.0.2:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
+ integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
+
+follow-redirects@^1.0.0, follow-redirects@^1.15.6:
+ version "1.15.9"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1"
+ integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
+
+font-awesome@^4.7.0:
+ version "4.7.0"
+ resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133"
+ integrity sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==
+
+foreground-child@^3.1.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77"
+ integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==
+ dependencies:
+ cross-spawn "^7.0.0"
+ signal-exit "^4.0.1"
+
+form-data@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48"
+ integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.8"
+ mime-types "^2.1.12"
+
+forwarded@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
+ integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
+
+fresh@0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+ integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
+
+from@~0:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
+ integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==
+
+fs-constants@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
+ integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
+
+fs-extra@^10.0.0:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
+ integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
+ dependencies:
+ graceful-fs "^4.2.0"
+ jsonfile "^6.0.1"
+ universalify "^2.0.0"
+
+fs-extra@^4.0.2:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
+ integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==
+ dependencies:
+ graceful-fs "^4.1.2"
+ jsonfile "^4.0.0"
+ universalify "^0.1.0"
+
+fs-extra@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+ integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
+ dependencies:
+ graceful-fs "^4.2.0"
+ jsonfile "^4.0.0"
+ universalify "^0.1.0"
+
+fs-extra@^9.0.0:
+ version "9.1.0"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
+ integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
+ dependencies:
+ at-least-node "^1.0.0"
+ graceful-fs "^4.2.0"
+ jsonfile "^6.0.1"
+ universalify "^2.0.0"
+
+fs-minipass@^2.0.0, fs-minipass@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
+ integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
+ dependencies:
+ minipass "^3.0.0"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
+fsevents@~2.3.2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+ integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
+fstream@^1.0.12:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045"
+ integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==
+ dependencies:
+ graceful-fs "^4.1.2"
+ inherits "~2.0.0"
+ mkdirp ">=0.5 0"
+ rimraf "2"
+
+function-bind@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+ integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
+fuzzy@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/fuzzy/-/fuzzy-0.1.3.tgz#4c76ec2ff0ac1a36a9dccf9a00df8623078d4ed8"
+ integrity sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w==
+
+gauge@^4.0.3:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce"
+ integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==
+ dependencies:
+ aproba "^1.0.3 || ^2.0.0"
+ color-support "^1.1.3"
+ console-control-strings "^1.1.0"
+ has-unicode "^2.0.1"
+ signal-exit "^3.0.7"
+ string-width "^4.2.3"
+ strip-ansi "^6.0.1"
+ wide-align "^1.1.5"
+
+gauge@~2.7.3:
+ version "2.7.4"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
+ integrity sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==
+ dependencies:
+ aproba "^1.0.3"
+ console-control-strings "^1.0.0"
+ has-unicode "^2.0.0"
+ object-assign "^4.1.0"
+ signal-exit "^3.0.0"
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+ wide-align "^1.1.0"
+
+gensync@^1.0.0-beta.2:
+ version "1.0.0-beta.2"
+ resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
+ integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
+
+get-caller-file@^2.0.1, get-caller-file@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+ integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-func-name@^2.0.1, get-func-name@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41"
+ integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==
+
+get-intrinsic@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
+ integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
+ dependencies:
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ has-proto "^1.0.1"
+ has-symbols "^1.0.3"
+ hasown "^2.0.0"
+
+get-stream@^2.2.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
+ integrity sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==
+ dependencies:
+ object-assign "^4.0.1"
+ pinkie-promise "^2.0.0"
+
+get-stream@^5.0.0, get-stream@^5.1.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
+ integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
+ dependencies:
+ pump "^3.0.0"
+
+get-stream@^6.0.0:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
+ integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
+
+get-uri@^6.0.1:
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.4.tgz#6daaee9e12f9759e19e55ba313956883ef50e0a7"
+ integrity sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==
+ dependencies:
+ basic-ftp "^5.0.2"
+ data-uri-to-buffer "^6.0.2"
+ debug "^4.3.4"
+
+github-from-package@0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
+ integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==
+
+glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+ dependencies:
+ is-glob "^4.0.1"
+
+glob-to-regexp@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
+ integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
+
+glob@^10.3.7:
+ version "10.4.5"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
+ integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
+ dependencies:
+ foreground-child "^3.1.0"
+ jackspeak "^3.1.2"
+ minimatch "^9.0.4"
+ minipass "^7.1.2"
+ package-json-from-dist "^1.0.0"
+ path-scurry "^1.11.1"
+
+glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.2.0:
+ version "7.2.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+ integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.1.1"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+glob@^8.0.1, glob@^8.0.3, glob@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
+ integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^5.0.1"
+ once "^1.3.0"
+
+global-agent@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6"
+ integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==
+ dependencies:
+ boolean "^3.0.1"
+ es6-error "^4.1.1"
+ matcher "^3.0.0"
+ roarr "^2.15.3"
+ semver "^7.3.2"
+ serialize-error "^7.0.1"
+
+globals@^11.1.0:
+ version "11.12.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+ integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+globalthis@^1.0.1:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236"
+ integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==
+ dependencies:
+ define-properties "^1.2.1"
+ gopd "^1.0.1"
+
+globby@^11.0.3:
+ version "11.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
+ integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
+ dependencies:
+ array-union "^2.1.0"
+ dir-glob "^3.0.1"
+ fast-glob "^3.2.9"
+ ignore "^5.2.0"
+ merge2 "^1.4.1"
+ slash "^3.0.0"
+
+globby@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680"
+ integrity sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g==
+ dependencies:
+ array-union "^1.0.1"
+ dir-glob "^2.0.0"
+ glob "^7.1.2"
+ ignore "^3.3.5"
+ pify "^3.0.0"
+ slash "^1.0.0"
+
+gopd@^1.0.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
+ integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
+
+got@^11.7.0, got@^11.8.5:
+ version "11.8.6"
+ resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a"
+ integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==
+ dependencies:
+ "@sindresorhus/is" "^4.0.0"
+ "@szmarczak/http-timer" "^4.0.5"
+ "@types/cacheable-request" "^6.0.1"
+ "@types/responselike" "^1.0.0"
+ cacheable-lookup "^5.0.3"
+ cacheable-request "^7.0.2"
+ decompress-response "^6.0.0"
+ http2-wrapper "^1.0.0-beta.5.2"
+ lowercase-keys "^2.0.0"
+ p-cancelable "^2.0.0"
+ responselike "^2.0.0"
+
+graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6:
+ version "4.2.11"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+ integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
+ integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
+ dependencies:
+ es-define-property "^1.0.0"
+
+has-proto@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.1.0.tgz#deb10494cbbe8809bce168a3b961f42969f5ed43"
+ integrity sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==
+ dependencies:
+ call-bind "^1.0.7"
+
+has-symbols@^1.0.3:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
+ integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
+
+has-unicode@^2.0.0, has-unicode@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+ integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
+
+hasown@^2.0.0, hasown@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
+ integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
+ dependencies:
+ function-bind "^1.1.2"
+
+he@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+ integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+
+highlight.js@*:
+ version "11.10.0"
+ resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.10.0.tgz#6e3600dc4b33d6dc23d5bd94fbf72405f5892b92"
+ integrity sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==
+
+highlight.js@10.4.1:
+ version "10.4.1"
+ resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.4.1.tgz#d48fbcf4a9971c4361b3f95f302747afe19dbad0"
+ integrity sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg==
+
+html-encoding-sniffer@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
+ integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==
+ dependencies:
+ whatwg-encoding "^2.0.0"
+
+http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
+ integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
+
+http-errors@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
+ integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
+ dependencies:
+ depd "2.0.0"
+ inherits "2.0.4"
+ setprototypeof "1.2.0"
+ statuses "2.0.1"
+ toidentifier "1.0.1"
+
+http-proxy-agent@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a"
+ integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==
+ dependencies:
+ "@tootallnate/once" "1"
+ agent-base "6"
+ debug "4"
+
+http-proxy-agent@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
+ integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
+ dependencies:
+ "@tootallnate/once" "2"
+ agent-base "6"
+ debug "4"
+
+http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1:
+ version "7.0.2"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
+ integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==
+ dependencies:
+ agent-base "^7.1.0"
+ debug "^4.3.4"
+
+http-proxy@^1.18.1:
+ version "1.18.1"
+ resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
+ integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
+ dependencies:
+ eventemitter3 "^4.0.0"
+ follow-redirects "^1.0.0"
+ requires-port "^1.0.0"
+
+http-server@^14.1.1:
+ version "14.1.1"
+ resolved "https://registry.yarnpkg.com/http-server/-/http-server-14.1.1.tgz#d60fbb37d7c2fdff0f0fbff0d0ee6670bd285e2e"
+ integrity sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==
+ dependencies:
+ basic-auth "^2.0.1"
+ chalk "^4.1.2"
+ corser "^2.0.1"
+ he "^1.2.0"
+ html-encoding-sniffer "^3.0.0"
+ http-proxy "^1.18.1"
+ mime "^1.6.0"
+ minimist "^1.2.6"
+ opener "^1.5.1"
+ portfinder "^1.0.28"
+ secure-compare "3.0.1"
+ union "~0.5.0"
+ url-join "^4.0.1"
+
+http-status-codes@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-1.4.0.tgz#6e4c15d16ff3a9e2df03b89f3a55e1aae05fb477"
+ integrity sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ==
+
+http2-wrapper@^1.0.0-beta.5.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
+ integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==
+ dependencies:
+ quick-lru "^5.1.1"
+ resolve-alpn "^1.0.0"
+
+https-proxy-agent@^5.0.0:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
+ integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
+ dependencies:
+ agent-base "6"
+ debug "4"
+
+https-proxy-agent@^7.0.2, https-proxy-agent@^7.0.3, https-proxy-agent@^7.0.5:
+ version "7.0.5"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2"
+ integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==
+ dependencies:
+ agent-base "^7.0.2"
+ debug "4"
+
+human-signals@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
+ integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
+
+humanize-ms@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
+ integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
+ dependencies:
+ ms "^2.0.0"
+
+iconv-lite@0.4.24:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+ integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
+iconv-lite@0.6.3, iconv-lite@^0.6.0, iconv-lite@^0.6.2:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
+ integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3.0.0"
+
+icss-utils@^5.0.0, icss-utils@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
+ integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
+
+idb@^4.0.5:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/idb/-/idb-4.0.5.tgz#23b930fbb0abce391e939c35b7b31a669e74041f"
+ integrity sha512-P+Fk9HT2h1DhXoE1YNK183SY+CRh2GHNh28de94sGwhe0bUA75JJeVJWt3SenE5p0BXK7maflIq29dl6UZHrFw==
+
+ieee754@^1.1.13, ieee754@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+ integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+
+ignore-loader@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/ignore-loader/-/ignore-loader-0.1.2.tgz#d81f240376d0ba4f0d778972c3ad25874117a463"
+ integrity sha512-yOJQEKrNwoYqrWLS4DcnzM7SEQhRKis5mB+LdKKh4cPmGYlLPR0ozRzHV5jmEk2IxptqJNQA5Cc0gw8Fj12bXA==
+
+ignore@^3.3.5:
+ version "3.3.10"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
+ integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==
+
+ignore@^5.2.0:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
+ integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
+
+image-size@~0.5.0:
+ version "0.5.5"
+ resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
+ integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==
+
+import-fresh@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
+
+import-local@^3.0.2:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260"
+ integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==
+ dependencies:
+ pkg-dir "^4.2.0"
+ resolve-cwd "^3.0.0"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+ integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+
+indent-string@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
+ integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
+
+infer-owner@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"
+ integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+ini@~1.3.0:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
+ integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
+
+interpret@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
+ integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
+
+inversify@6.0.3, inversify@^6.1.3:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/inversify/-/inversify-6.0.3.tgz#b09e44dad0aaf79bdf92fe788f37507274da21b7"
+ integrity sha512-s/svzcRQ/scaGUUyaVtFSL1dvOaRgyvE7VvpGcJwXmFz7CCzfSfxC/Uyl7iSHDEmBabJ2gbDES72DaygtMmwvg==
+
+ip-address@^9.0.5:
+ version "9.0.5"
+ resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a"
+ integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==
+ dependencies:
+ jsbn "1.1.0"
+ sprintf-js "^1.1.3"
+
+ipaddr.js@1.9.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
+ integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+ integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+
+is-binary-path@~2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
+ integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
+ dependencies:
+ binary-extensions "^2.0.0"
+
+is-core-module@^2.13.0:
+ version "2.15.1"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37"
+ integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==
+ dependencies:
+ hasown "^2.0.2"
+
+is-docker@^2.0.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
+ integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
+
+is-electron@^2.1.0, is-electron@^2.2.0:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.2.2.tgz#3778902a2044d76de98036f5dc58089ac4d80bb9"
+ integrity sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-fullwidth-code-point@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+ integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+ integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-interactive@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
+ integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
+
+is-lambda@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5"
+ integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==
+
+is-natural-number@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
+ integrity sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==
+
+is-number@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-obj@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
+ integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
+
+is-path-inside@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
+ integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
+
+is-plain-obj@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
+ integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==
+
+is-plain-obj@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
+ integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
+
+is-plain-object@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+ integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+ dependencies:
+ isobject "^3.0.1"
+
+is-stream@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+ integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==
+
+is-stream@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
+ integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
+
+is-unicode-supported@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
+ integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
+
+is-what@^3.14.1:
+ version "3.14.1"
+ resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1"
+ integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==
+
+is-wsl@^2.1.1:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
+ integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
+ dependencies:
+ is-docker "^2.0.0"
+
+isarray@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
+ integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
+
+isarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+ integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+isobject@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+ integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
+
+jackspeak@^3.1.2:
+ version "3.4.3"
+ resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
+ integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==
+ dependencies:
+ "@isaacs/cliui" "^8.0.2"
+ optionalDependencies:
+ "@pkgjs/parseargs" "^0.11.0"
+
+jest-worker@^27.4.5:
+ version "27.5.1"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0"
+ integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==
+ dependencies:
+ "@types/node" "*"
+ merge-stream "^2.0.0"
+ supports-color "^8.0.0"
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
+jsbn@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
+ integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==
+
+jschardet@^2.1.1:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.3.0.tgz#06e2636e16c8ada36feebbdc08aa34e6a9b3ff75"
+ integrity sha512-6I6xT7XN/7sBB7q8ObzKbmv5vN+blzLcboDE1BNEsEfmRXJValMxO6OIRT69ylPBRemS3rw6US+CMCar0OBc9g==
+
+jsesc@^3.0.2, jsesc@~3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e"
+ integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==
+
+json-buffer@3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
+ integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+
+json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+ integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema-traverse@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
+ integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
+json-schema-typed@^7.0.3:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz#23ff481b8b4eebcd2ca123b4fa0409e66469a2d9"
+ integrity sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==
+
+json-stable-stringify@^1.0.2:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz#52d4361b47d49168bcc4e564189a42e5a7439454"
+ integrity sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==
+ dependencies:
+ call-bind "^1.0.5"
+ isarray "^2.0.5"
+ jsonify "^0.0.1"
+ object-keys "^1.1.1"
+
+json-stringify-safe@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+ integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
+
+json5@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
+ integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
+ dependencies:
+ minimist "^1.2.0"
+
+json5@^2.1.2, json5@^2.2.3:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+ integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
+jsonc-parser@^2.2.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342"
+ integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==
+
+jsonfile@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+ integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+jsonfile@^6.0.1:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
+ integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
+ dependencies:
+ universalify "^2.0.0"
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+jsonify@^0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978"
+ integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==
+
+just-performance@4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/just-performance/-/just-performance-4.3.0.tgz#cc2bc8c9227f09e97b6b1df4cd0de2df7ae16db1"
+ integrity sha512-L7RjvtJsL0QO8xFs5wEoDDzzJwoiowRw6Rn/GnvldlchS2JQr9wFYPiwZcDfrbbujEKqKN0tvENdbjXdYhDp5Q==
+
+keytar@7.2.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.2.0.tgz#4db2bec4f9700743ffd9eda22eebb658965c8440"
+ integrity sha512-ECSaWvoLKI5SI0pGpZQeUV1/lpBYfkaxvoSp3zkiPOz05VavwSfLi8DdEaa9N2ekQZv3Chy+o7aP6n9mairBgw==
+ dependencies:
+ node-addon-api "^3.0.0"
+ prebuild-install "^6.0.0"
+
+keyv@^4.0.0:
+ version "4.5.4"
+ resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
+ integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
+ dependencies:
+ json-buffer "3.0.1"
+
+kind-of@^6.0.2:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
+klaw-sync@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c"
+ integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==
+ dependencies:
+ graceful-fs "^4.1.11"
+
+less@^3.0.3:
+ version "3.13.1"
+ resolved "https://registry.yarnpkg.com/less/-/less-3.13.1.tgz#0ebc91d2a0e9c0c6735b83d496b0ab0583077909"
+ integrity sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==
+ dependencies:
+ copy-anything "^2.0.1"
+ tslib "^1.10.0"
+ optionalDependencies:
+ errno "^0.1.1"
+ graceful-fs "^4.1.2"
+ image-size "~0.5.0"
+ make-dir "^2.1.0"
+ mime "^1.4.1"
+ native-request "^1.0.5"
+ source-map "~0.6.0"
+
+limiter@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/limiter/-/limiter-2.1.0.tgz#d38d7c5b63729bb84fb0c4d8594b7e955a5182a2"
+ integrity sha512-361TYz6iay6n+9KvUUImqdLuFigK+K79qrUtBsXhJTLdH4rIt/r1y8r1iozwh8KbZNpujbFTSh74mJ7bwbAMOw==
+ dependencies:
+ just-performance "4.3.0"
+
+line-height@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/line-height/-/line-height-0.3.1.tgz#4b1205edde182872a5efa3c8f620b3187a9c54c9"
+ integrity sha512-YExecgqPwnp5gplD2+Y8e8A5+jKpr25+DzMbFdI1/1UAr0FJrTFv4VkHLf8/6B590i1wUPJWMKKldkd/bdQ//w==
+ dependencies:
+ computed-style "~0.1.3"
+
+lines-and-columns@^1.1.6:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
+ integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+
+linkify-it@^3.0.1:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e"
+ integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==
+ dependencies:
+ uc.micro "^1.0.1"
+
+listenercount@~1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937"
+ integrity sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==
+
+loader-runner@^4.2.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1"
+ integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
+
+loader-utils@^1.0.3:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3"
+ integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==
+ dependencies:
+ big.js "^5.2.2"
+ emojis-list "^3.0.0"
+ json5 "^1.0.1"
+
+loader-utils@^2.0.0, loader-utils@^2.0.3, loader-utils@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c"
+ integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==
+ dependencies:
+ big.js "^5.2.2"
+ emojis-list "^3.0.0"
+ json5 "^2.1.2"
+
+locate-path@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+ integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+ dependencies:
+ p-locate "^3.0.0"
+ path-exists "^3.0.0"
+
+locate-path@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+ integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+ dependencies:
+ p-locate "^4.1.0"
+
+locate-path@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+ integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+ dependencies:
+ p-locate "^5.0.0"
+
+lodash.clonedeep@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
+ integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==
+
+lodash.debounce@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
+ integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
+
+lodash.throttle@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
+ integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==
+
+lodash@^4.17.14:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+log-symbols@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
+ integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
+ dependencies:
+ chalk "^4.1.0"
+ is-unicode-supported "^0.1.0"
+
+log-update@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1"
+ integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==
+ dependencies:
+ ansi-escapes "^4.3.0"
+ cli-cursor "^3.1.0"
+ slice-ansi "^4.0.0"
+ wrap-ansi "^6.2.0"
+
+loose-envify@^1.1.0, loose-envify@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+ integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+ dependencies:
+ js-tokens "^3.0.0 || ^4.0.0"
+
+loupe@^2.3.6:
+ version "2.3.7"
+ resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697"
+ integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==
+ dependencies:
+ get-func-name "^2.0.1"
+
+lowercase-keys@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
+ integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
+
+lru-cache@^10.2.0:
+ version "10.4.3"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
+ integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
+
+lru-cache@^4.0.1:
+ version "4.1.5"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
+ integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
+ dependencies:
+ pseudomap "^1.0.2"
+ yallist "^2.1.2"
+
+lru-cache@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
+ integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
+ dependencies:
+ yallist "^3.0.2"
+
+lru-cache@^7.14.1, lru-cache@^7.7.1:
+ version "7.18.3"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
+ integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
+
+luxon@^2.4.0:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.5.2.tgz#17ed497f0277e72d58a4756d6a9abee4681457b6"
+ integrity sha512-Yg7/RDp4nedqmLgyH0LwgGRvMEKVzKbUdkBYyCosbHgJ+kaOUx0qzSiSatVc3DFygnirTPYnMM2P5dg2uH1WvA==
+
+lzma-native@^8.0.5:
+ version "8.0.6"
+ resolved "https://registry.yarnpkg.com/lzma-native/-/lzma-native-8.0.6.tgz#3ea456209d643bafd9b5d911781bdf0b396b2665"
+ integrity sha512-09xfg67mkL2Lz20PrrDeNYZxzeW7ADtpYFbwSQh9U8+76RIzx5QsJBMy8qikv3hbUPfpy6hqwxt6FcGK81g9AA==
+ dependencies:
+ node-addon-api "^3.1.0"
+ node-gyp-build "^4.2.1"
+ readable-stream "^3.6.0"
+
+macaddress@^0.5.3:
+ version "0.5.3"
+ resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.5.3.tgz#2b9d6832be934cb775749f30f57d6537184a2bda"
+ integrity sha512-vGBKTA+jwM4KgjGZ+S/8/Mkj9rWzePyGY6jManXPGhiWu63RYwW8dKPyk5koP+8qNVhPhHgFa1y/MJ4wrjsNrg==
+
+make-dir@^1.0.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
+ integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==
+ dependencies:
+ pify "^3.0.0"
+
+make-dir@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
+ integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
+ dependencies:
+ pify "^4.0.1"
+ semver "^5.6.0"
+
+make-dir@^3.0.2, make-dir@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
+ integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
+ dependencies:
+ semver "^6.0.0"
+
+make-fetch-happen@^10.0.3:
+ version "10.2.1"
+ resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164"
+ integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==
+ dependencies:
+ agentkeepalive "^4.2.1"
+ cacache "^16.1.0"
+ http-cache-semantics "^4.1.0"
+ http-proxy-agent "^5.0.0"
+ https-proxy-agent "^5.0.0"
+ is-lambda "^1.0.1"
+ lru-cache "^7.7.1"
+ minipass "^3.1.6"
+ minipass-collect "^1.0.2"
+ minipass-fetch "^2.0.3"
+ minipass-flush "^1.0.5"
+ minipass-pipeline "^1.2.4"
+ negotiator "^0.6.3"
+ promise-retry "^2.0.1"
+ socks-proxy-agent "^7.0.0"
+ ssri "^9.0.0"
+
+map-stream@~0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
+ integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==
+
+markdown-it-anchor@~5.0.0:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-5.0.2.tgz#cdd917a05b7bf92fb736a6dae3385c6d0d0fa552"
+ integrity sha512-AFM/woBI8QDJMS/9+MmsBMT5/AR+ImfOsunQZTZhzcTmna3rIzAzbOh5E0l6mlFM/i9666BpUtkqQ9bS7WApCg==
+
+markdown-it@^12.3.2:
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90"
+ integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==
+ dependencies:
+ argparse "^2.0.1"
+ entities "~2.1.0"
+ linkify-it "^3.0.1"
+ mdurl "^1.0.1"
+ uc.micro "^1.0.5"
+
+matcher@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
+ integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==
+ dependencies:
+ escape-string-regexp "^4.0.0"
+
+mdurl@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
+ integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==
+
+media-typer@0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+ integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
+
+merge-descriptors@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
+ integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
+
+merge-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+ integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
+merge2@^1.3.0, merge2@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+ integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+
+methods@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
+ integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
+
+micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
+ integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
+ dependencies:
+ braces "^3.0.3"
+ picomatch "^2.3.1"
+
+mime-db@1.52.0:
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+mime@1.6.0, mime@^1.4.1, mime@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+ integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+
+mime@^2.4.4:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
+ integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==
+
+mimic-fn@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+ integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+mimic-fn@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74"
+ integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==
+
+mimic-response@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
+ integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
+
+mimic-response@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
+ integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
+
+mimic-response@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
+ integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
+
+mini-css-extract-plugin@^2.6.1:
+ version "2.9.2"
+ resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz#966031b468917a5446f4c24a80854b2947503c5b"
+ integrity sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==
+ dependencies:
+ schema-utils "^4.0.0"
+ tapable "^2.2.1"
+
+minimatch@^3.1.1:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimatch@^5.0.1, minimatch@^5.1.0, minimatch@^5.1.6:
+ version "5.1.6"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
+ integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
+ dependencies:
+ brace-expansion "^2.0.1"
+
+minimatch@^9.0.4:
+ version "9.0.5"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
+ integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
+ dependencies:
+ brace-expansion "^2.0.1"
+
+minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6:
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+ integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
+
+minipass-collect@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
+ integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==
+ dependencies:
+ minipass "^3.0.0"
+
+minipass-fetch@^2.0.3:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add"
+ integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==
+ dependencies:
+ minipass "^3.1.6"
+ minipass-sized "^1.0.3"
+ minizlib "^2.1.2"
+ optionalDependencies:
+ encoding "^0.1.13"
+
+minipass-flush@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373"
+ integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==
+ dependencies:
+ minipass "^3.0.0"
+
+minipass-pipeline@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c"
+ integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==
+ dependencies:
+ minipass "^3.0.0"
+
+minipass-sized@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70"
+ integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==
+ dependencies:
+ minipass "^3.0.0"
+
+minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6:
+ version "3.3.6"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a"
+ integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
+ dependencies:
+ yallist "^4.0.0"
+
+minipass@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d"
+ integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==
+
+"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2:
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
+ integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
+
+minizlib@^2.1.1, minizlib@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
+ integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
+ dependencies:
+ minipass "^3.0.0"
+ yallist "^4.0.0"
+
+mitt@3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1"
+ integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==
+
+mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
+ version "0.5.3"
+ resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
+ integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
+
+"mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@^0.5.6:
+ version "0.5.6"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
+ integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
+ dependencies:
+ minimist "^1.2.6"
+
+mkdirp@^1.0.3, mkdirp@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
+ integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
+
+mocha@^10.1.0:
+ version "10.8.2"
+ resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.8.2.tgz#8d8342d016ed411b12a429eb731b825f961afb96"
+ integrity sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==
+ dependencies:
+ ansi-colors "^4.1.3"
+ browser-stdout "^1.3.1"
+ chokidar "^3.5.3"
+ debug "^4.3.5"
+ diff "^5.2.0"
+ escape-string-regexp "^4.0.0"
+ find-up "^5.0.0"
+ glob "^8.1.0"
+ he "^1.2.0"
+ js-yaml "^4.1.0"
+ log-symbols "^4.1.0"
+ minimatch "^5.1.6"
+ ms "^2.1.3"
+ serialize-javascript "^6.0.2"
+ strip-json-comments "^3.1.1"
+ supports-color "^8.1.1"
+ workerpool "^6.5.1"
+ yargs "^16.2.0"
+ yargs-parser "^20.2.9"
+ yargs-unparser "^2.0.0"
+
+mount-point@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/mount-point/-/mount-point-3.0.0.tgz#665cb9edebe80d110e658db56c31d0aef51a8f97"
+ integrity sha512-jAhfD7ZCG+dbESZjcY1SdFVFqSJkh/yGbdsifHcPkvuLRO5ugK0Ssmd9jdATu29BTd4JiN+vkpMzVvsUgP3SZA==
+ dependencies:
+ "@sindresorhus/df" "^1.0.1"
+ pify "^2.3.0"
+ pinkie-promise "^2.0.1"
+
+move-file@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/move-file/-/move-file-2.1.0.tgz#3bec9d34fbe4832df6865f112cda4492b56e8507"
+ integrity sha512-i9qLW6gqboJ5Ht8bauZi7KlTnQ3QFpBCvMvFfEcHADKgHGeJ9BZMO7SFCTwHPV9Qa0du9DYY1Yx3oqlGt30nXA==
+ dependencies:
+ path-exists "^4.0.0"
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
+
+ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+msgpackr-extract@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz#e9d87023de39ce714872f9e9504e3c1996d61012"
+ integrity sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==
+ dependencies:
+ node-gyp-build-optional-packages "5.2.2"
+ optionalDependencies:
+ "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.3"
+ "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.3"
+ "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.3"
+ "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.3"
+ "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.3"
+ "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.3"
+
+msgpackr@^1.10.2:
+ version "1.11.2"
+ resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.11.2.tgz#4463b7f7d68f2e24865c395664973562ad24473d"
+ integrity sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==
+ optionalDependencies:
+ msgpackr-extract "^3.0.2"
+
+multer@1.4.4-lts.1:
+ version "1.4.4-lts.1"
+ resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.4-lts.1.tgz#24100f701a4611211cfae94ae16ea39bb314e04d"
+ integrity sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==
+ dependencies:
+ append-field "^1.0.0"
+ busboy "^1.0.0"
+ concat-stream "^1.5.2"
+ mkdirp "^0.5.4"
+ object-assign "^4.1.1"
+ type-is "^1.6.4"
+ xtend "^4.0.0"
+
+nan@^2.14.0, nan@^2.17.0:
+ version "2.22.0"
+ resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3"
+ integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==
+
+nano@^10.1.3:
+ version "10.1.4"
+ resolved "https://registry.yarnpkg.com/nano/-/nano-10.1.4.tgz#cb4cabd677733ddb81c88c1b8635101e2d84e926"
+ integrity sha512-bJOFIPLExIbF6mljnfExXX9Cub4W0puhDjVMp+qV40xl/DBvgKao7St4+6/GB6EoHZap7eFnrnx4mnp5KYgwJA==
+ dependencies:
+ axios "^1.7.4"
+ node-abort-controller "^3.1.1"
+ qs "^6.13.0"
+
+nanoid@^3.3.7:
+ version "3.3.8"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf"
+ integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==
+
+napi-build-utils@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806"
+ integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==
+
+native-keymap@^2.2.1:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-2.5.0.tgz#4d567497efb0d2efbfd53048f14093f25984c2b4"
+ integrity sha512-EfdMpTcX40mlHBJSWidFV4WLpwwaebK3D3JFuO/42voOAnG2WHgDdg6JerbqcxXvRhvIg934GV+9PjB3jzfu9A==
+
+native-request@^1.0.5:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.1.2.tgz#b677952757429db6cd41972a29c3b781977413ed"
+ integrity sha512-/etjwrK0J4Ebbcnt35VMWnfiUX/B04uwGJxyJInagxDqf2z5drSt/lsOvEMWGYunz1kaLZAFrV4NDAbOoDKvAQ==
+
+negotiator@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
+ integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
+
+negotiator@^0.6.3:
+ version "0.6.4"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7"
+ integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==
+
+neo-async@^2.6.2:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
+ integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
+
+netmask@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7"
+ integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==
+
+node-abi@*, node-abi@^3.0.0:
+ version "3.71.0"
+ resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.71.0.tgz#52d84bbcd8575efb71468fbaa1f9a49b2c242038"
+ integrity sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==
+ dependencies:
+ semver "^7.3.5"
+
+node-abi@^2.21.0, node-abi@^2.7.0:
+ version "2.30.1"
+ resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.30.1.tgz#c437d4b1fe0e285aaf290d45b45d4d7afedac4cf"
+ integrity sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==
+ dependencies:
+ semver "^5.4.1"
+
+node-abort-controller@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548"
+ integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==
+
+node-addon-api@^3.0.0, node-addon-api@^3.1.0:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
+ integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
+
+node-addon-api@^7.0.0:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
+ integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
+
+node-addon-api@^8.2.0:
+ version "8.3.0"
+ resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-8.3.0.tgz#ec3763f18befc1cdf66d11e157ce44d5eddc0603"
+ integrity sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==
+
+node-api-version@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/node-api-version/-/node-api-version-0.1.4.tgz#1ed46a485e462d55d66b5aa1fe2821720dedf080"
+ integrity sha512-KGXihXdUChwJAOHO53bv9/vXcLmdUsZ6jIptbvYvkpKfth+r7jw44JkVxQFA3kX5nQjzjmGu1uAu/xNNLNlI5g==
+ dependencies:
+ semver "^7.3.5"
+
+node-gyp-build-optional-packages@5.2.2:
+ version "5.2.2"
+ resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz#522f50c2d53134d7f3a76cd7255de4ab6c96a3a4"
+ integrity sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==
+ dependencies:
+ detect-libc "^2.0.1"
+
+node-gyp-build@^4.2.1:
+ version "4.8.4"
+ resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8"
+ integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==
+
+node-gyp@^9.0.0:
+ version "9.4.1"
+ resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.1.tgz#8a1023e0d6766ecb52764cc3a734b36ff275e185"
+ integrity sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==
+ dependencies:
+ env-paths "^2.2.0"
+ exponential-backoff "^3.1.1"
+ glob "^7.1.4"
+ graceful-fs "^4.2.6"
+ make-fetch-happen "^10.0.3"
+ nopt "^6.0.0"
+ npmlog "^6.0.0"
+ rimraf "^3.0.2"
+ semver "^7.3.5"
+ tar "^6.1.2"
+ which "^2.0.2"
+
+node-loader@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/node-loader/-/node-loader-2.1.0.tgz#8c4eb926e8bdcacb7349d17b40ebcc49fd2458d5"
+ integrity sha512-OwjPkyh8+7jW8DMd/iq71uU1Sspufr/C2+c3t0p08J3CrM9ApZ4U53xuisNrDXOHyGi5OYHgtfmmh+aK9zJA6g==
+ dependencies:
+ loader-utils "^2.0.3"
+
+node-pty@0.11.0-beta24:
+ version "0.11.0-beta24"
+ resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta24.tgz#084841017187656edaf14b459946c4a1d7cf8392"
+ integrity sha512-CzItw3hitX+wnpw9dHA/A+kcbV7ETNKrsyQJ+s0ZGzsu70+CSGuIGPLPfMnAc17vOrQktxjyRQfaqij75GVJFw==
+ dependencies:
+ nan "^2.17.0"
+
+node-releases@^2.0.18:
+ version "2.0.18"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
+ integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
+
+noop-logger@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2"
+ integrity sha512-6kM8CLXvuW5crTxsAtva2YLrRrDaiTIkIePWs9moLHqbFWT94WpNFjwS/5dfLfECg5i/lkmw3aoqVidxt23TEQ==
+
+nopt@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d"
+ integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==
+ dependencies:
+ abbrev "^1.0.0"
+
+normalize-path@^3.0.0, normalize-path@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+ integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+normalize-url@^6.0.1:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
+ integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
+
+npm-run-path@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
+ integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==
+ dependencies:
+ path-key "^2.0.0"
+
+npm-run-path@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-3.1.0.tgz#7f91be317f6a466efed3c9f2980ad8a4ee8b0fa5"
+ integrity sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==
+ dependencies:
+ path-key "^3.0.0"
+
+npm-run-path@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
+ integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
+ dependencies:
+ path-key "^3.0.0"
+
+npmlog@^4.0.1:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
+ integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
+ dependencies:
+ are-we-there-yet "~1.1.2"
+ console-control-strings "~1.1.0"
+ gauge "~2.7.3"
+ set-blocking "~2.0.0"
+
+npmlog@^6.0.0:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830"
+ integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==
+ dependencies:
+ are-we-there-yet "^3.0.0"
+ console-control-strings "^1.1.0"
+ gauge "^4.0.3"
+ set-blocking "^2.0.0"
+
+number-is-nan@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+ integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==
+
+object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
+
+object-inspect@^1.13.1:
+ version "1.13.3"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.3.tgz#f14c183de51130243d6d18ae149375ff50ea488a"
+ integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==
+
+object-keys@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+ integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+on-finished@2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
+ integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
+ dependencies:
+ ee-first "1.1.1"
+
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+ dependencies:
+ wrappy "1"
+
+onetime@^5.1.0, onetime@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+ integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+ dependencies:
+ mimic-fn "^2.1.0"
+
+open@^7.4.2:
+ version "7.4.2"
+ resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321"
+ integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==
+ dependencies:
+ is-docker "^2.0.0"
+ is-wsl "^2.1.1"
+
+opener@^1.5.1:
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
+ integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
+
+ora@^5.1.0:
+ version "5.4.1"
+ resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18"
+ integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==
+ dependencies:
+ bl "^4.1.0"
+ chalk "^4.1.0"
+ cli-cursor "^3.1.0"
+ cli-spinners "^2.5.0"
+ is-interactive "^1.0.0"
+ is-unicode-supported "^0.1.0"
+ log-symbols "^4.1.0"
+ strip-ansi "^6.0.0"
+ wcwidth "^1.0.1"
+
+os-homedir@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+ integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==
+
+os-tmpdir@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+ integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
+
+p-cancelable@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
+ integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==
+
+p-debounce@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/p-debounce/-/p-debounce-2.1.0.tgz#e79f70c6e325cbb9bddbcbec0b81025084671ad3"
+ integrity sha512-M9bMt62TTnozdZhqFgs+V7XD2MnuKCaz+7fZdlu2/T7xruI3uIE5CicQ0vx1hV7HIUYF0jF+4/R1AgfOkl74Qw==
+
+p-finally@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+ integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==
+
+p-finally@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561"
+ integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==
+
+p-limit@^2.0.0, p-limit@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+ integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
+ dependencies:
+ p-try "^2.0.0"
+
+p-limit@^3.0.2, p-limit@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+ integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+ dependencies:
+ yocto-queue "^0.1.0"
+
+p-locate@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+ integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+ dependencies:
+ p-limit "^2.0.0"
+
+p-locate@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+ integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+ dependencies:
+ p-limit "^2.2.0"
+
+p-locate@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+ integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+ dependencies:
+ p-limit "^3.0.2"
+
+p-map@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
+ integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==
+ dependencies:
+ aggregate-error "^3.0.0"
+
+p-queue@^2.4.2:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-2.4.2.tgz#03609826682b743be9a22dba25051bd46724fc34"
+ integrity sha512-n8/y+yDJwBjoLQe1GSJbbaYQLTI7QHNZI2+rpmCDbe++WLf9HC3gf6iqj5yfPAV71W4UF3ql5W1+UBPXoXTxng==
+
+p-try@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+pac-proxy-agent@^7.0.1:
+ version "7.0.2"
+ resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz#0fb02496bd9fb8ae7eb11cfd98386daaac442f58"
+ integrity sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==
+ dependencies:
+ "@tootallnate/quickjs-emscripten" "^0.23.0"
+ agent-base "^7.0.2"
+ debug "^4.3.4"
+ get-uri "^6.0.1"
+ http-proxy-agent "^7.0.0"
+ https-proxy-agent "^7.0.5"
+ pac-resolver "^7.0.1"
+ socks-proxy-agent "^8.0.4"
+
+pac-resolver@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.1.tgz#54675558ea368b64d210fd9c92a640b5f3b8abb6"
+ integrity sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==
+ dependencies:
+ degenerator "^5.0.0"
+ netmask "^2.0.2"
+
+package-json-from-dist@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505"
+ integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
+
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
+parse-json@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+ integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ error-ex "^1.3.1"
+ json-parse-even-better-errors "^2.3.0"
+ lines-and-columns "^1.1.6"
+
+parseurl@~1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+ integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
+patch-package@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-8.0.0.tgz#d191e2f1b6e06a4624a0116bcb88edd6714ede61"
+ integrity sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==
+ dependencies:
+ "@yarnpkg/lockfile" "^1.1.0"
+ chalk "^4.1.2"
+ ci-info "^3.7.0"
+ cross-spawn "^7.0.3"
+ find-yarn-workspace-root "^2.0.0"
+ fs-extra "^9.0.0"
+ json-stable-stringify "^1.0.2"
+ klaw-sync "^6.0.0"
+ minimist "^1.2.6"
+ open "^7.4.2"
+ rimraf "^2.6.3"
+ semver "^7.5.3"
+ slash "^2.0.0"
+ tmp "^0.0.33"
+ yaml "^2.2.2"
+
+path-browserify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
+ integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
+
+path-exists@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+ integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==
+
+path-exists@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+ integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
+
+path-key@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+ integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==
+
+path-key@^3.0.0, path-key@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-parse@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+path-root-regex@^0.1.0:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d"
+ integrity sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==
+
+path-root@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7"
+ integrity sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==
+ dependencies:
+ path-root-regex "^0.1.0"
+
+path-scurry@^1.11.1:
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2"
+ integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==
+ dependencies:
+ lru-cache "^10.2.0"
+ minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
+
+path-to-regexp@0.1.10:
+ version "0.1.10"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
+ integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
+
+path-type@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
+ integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==
+ dependencies:
+ pify "^3.0.0"
+
+path-type@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+ integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+pathval@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
+ integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==
+
+pause-stream@0.0.11:
+ version "0.0.11"
+ resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
+ integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==
+ dependencies:
+ through "~2.3"
+
+pdfobject@^2.0.201604172:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pdfobject/-/pdfobject-2.3.0.tgz#467b4ffcd621518aa0e66fc191fdd669f3118b06"
+ integrity sha512-w/9pXDXTDs3IDmOri/w8lM/w6LHR0/F4fcBLLzH+4csSoyshQ5su0TE7k0FLHZO7aOjVLDGecqd1M89+PVpVAA==
+
+pend@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
+ integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
+
+perfect-scrollbar@^1.3.0, perfect-scrollbar@^1.5.0:
+ version "1.5.6"
+ resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.6.tgz#f1aead2588ba896435ee41b246812b2080573b7c"
+ integrity sha512-rixgxw3SxyJbCaSpo1n35A/fwI1r2rdwMKOTCg/AcG+xOEyZcE8UHVjpZMFCVImzsFoCZeJTT+M/rdEIQYO2nw==
+
+picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+ integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+pify@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+ integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
+
+pify@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
+ integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==
+
+pify@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
+ integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+
+pinkie-promise@^2.0.0, pinkie-promise@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==
+ dependencies:
+ pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+ integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==
+
+pkg-dir@^4.1.0, pkg-dir@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
+ integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
+ dependencies:
+ find-up "^4.0.0"
+
+pkg-up@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
+ integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
+ dependencies:
+ find-up "^3.0.0"
+
+portfinder@^1.0.28:
+ version "1.0.32"
+ resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81"
+ integrity sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==
+ dependencies:
+ async "^2.6.4"
+ debug "^3.2.7"
+ mkdirp "^0.5.6"
+
+postcss-modules-extract-imports@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz#b4497cb85a9c0c4b5aabeb759bb25e8d89f15002"
+ integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==
+
+postcss-modules-local-by-default@^4.0.5:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.1.0.tgz#b0db6bc81ffc7bdc52eb0f84d6ca0bedf0e36d21"
+ integrity sha512-rm0bdSv4jC3BDma3s9H19ZddW0aHX6EoqwDYU2IfZhRN+53QrufTRo2IdkAbRqLx4R2IYbZnbjKKxg4VN5oU9Q==
+ dependencies:
+ icss-utils "^5.0.0"
+ postcss-selector-parser "^7.0.0"
+ postcss-value-parser "^4.1.0"
+
+postcss-modules-scope@^3.2.0:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz#1bbccddcb398f1d7a511e0a2d1d047718af4078c"
+ integrity sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==
+ dependencies:
+ postcss-selector-parser "^7.0.0"
+
+postcss-modules-values@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c"
+ integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==
+ dependencies:
+ icss-utils "^5.0.0"
+
+postcss-selector-parser@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz#41bd8b56f177c093ca49435f65731befe25d6b9c"
+ integrity sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==
+ dependencies:
+ cssesc "^3.0.0"
+ util-deprecate "^1.0.2"
+
+postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
+ integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
+postcss@^8.4.33:
+ version "8.4.49"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19"
+ integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==
+ dependencies:
+ nanoid "^3.3.7"
+ picocolors "^1.1.1"
+ source-map-js "^1.2.1"
+
+prebuild-install@^5.2.4:
+ version "5.3.6"
+ resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.6.tgz#7c225568d864c71d89d07f8796042733a3f54291"
+ integrity sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==
+ dependencies:
+ detect-libc "^1.0.3"
+ expand-template "^2.0.3"
+ github-from-package "0.0.0"
+ minimist "^1.2.3"
+ mkdirp-classic "^0.5.3"
+ napi-build-utils "^1.0.1"
+ node-abi "^2.7.0"
+ noop-logger "^0.1.1"
+ npmlog "^4.0.1"
+ pump "^3.0.0"
+ rc "^1.2.7"
+ simple-get "^3.0.3"
+ tar-fs "^2.0.0"
+ tunnel-agent "^0.6.0"
+ which-pm-runs "^1.0.0"
+
+prebuild-install@^6.0.0:
+ version "6.1.4"
+ resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.1.4.tgz#ae3c0142ad611d58570b89af4986088a4937e00f"
+ integrity sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==
+ dependencies:
+ detect-libc "^1.0.3"
+ expand-template "^2.0.3"
+ github-from-package "0.0.0"
+ minimist "^1.2.3"
+ mkdirp-classic "^0.5.3"
+ napi-build-utils "^1.0.1"
+ node-abi "^2.21.0"
+ npmlog "^4.0.1"
+ pump "^3.0.0"
+ rc "^1.2.7"
+ simple-get "^3.0.3"
+ tar-fs "^2.0.0"
+ tunnel-agent "^0.6.0"
+
+private@~0.1.5:
+ version "0.1.8"
+ resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
+ integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
+
+process-nextick-args@~2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+ integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+progress@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
+ integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
+
+prom-client@^10.2.0:
+ version "10.2.3"
+ resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-10.2.3.tgz#a51bf21c239c954a6c5be4b1361fdd380218bb41"
+ integrity sha512-Xboq5+TdUwuQtSSDRZRNnb5NprINlgQN999VqUjZxnLKydUNLeIPx6Eiahg6oJua3XBg2TGnh5Cth1s4I6+r7g==
+ dependencies:
+ tdigest "^0.1.1"
+
+promise-inflight@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
+ integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==
+
+promise-retry@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22"
+ integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==
+ dependencies:
+ err-code "^2.0.2"
+ retry "^0.12.0"
+
+prop-types@^15.5.6, prop-types@^15.6.1, prop-types@^15.8.1:
+ version "15.8.1"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
+ integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.13.1"
+
+proxy-addr@~2.0.7:
+ version "2.0.7"
+ resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
+ integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
+ dependencies:
+ forwarded "0.2.0"
+ ipaddr.js "1.9.1"
+
+proxy-agent@^6.4.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.4.0.tgz#b4e2dd51dee2b377748aef8d45604c2d7608652d"
+ integrity sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==
+ dependencies:
+ agent-base "^7.0.2"
+ debug "^4.3.4"
+ http-proxy-agent "^7.0.1"
+ https-proxy-agent "^7.0.3"
+ lru-cache "^7.14.1"
+ pac-proxy-agent "^7.0.1"
+ proxy-from-env "^1.1.0"
+ socks-proxy-agent "^8.0.2"
+
+proxy-from-env@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
+ integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+
+prr@~1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
+ integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==
+
+ps-tree@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd"
+ integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==
+ dependencies:
+ event-stream "=3.3.4"
+
+pseudomap@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+ integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==
+
+pump@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954"
+ integrity sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==
+ dependencies:
+ end-of-stream "^1.1.0"
+ once "^1.3.1"
+
+pump@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8"
+ integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==
+ dependencies:
+ end-of-stream "^1.1.0"
+ once "^1.3.1"
+
+punycode@^2.1.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+ integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+
+puppeteer-core@23.1.0:
+ version "23.1.0"
+ resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-23.1.0.tgz#50703d2e27c1d73d523c25b807f6e6d95a6b1c47"
+ integrity sha512-SvAsu+xnLN2FMXE/59bp3s3WXp8ewqUGzVV4AQtml/2xmsciZnU/bXcCW+eETHPWQ6Agg2vTI7QzWXPpEARK2g==
+ dependencies:
+ "@puppeteer/browsers" "2.3.1"
+ chromium-bidi "0.6.4"
+ debug "^4.3.6"
+ devtools-protocol "0.0.1312386"
+ typed-query-selector "^2.12.0"
+ ws "^8.18.0"
+
+puppeteer-to-istanbul@1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/puppeteer-to-istanbul/-/puppeteer-to-istanbul-1.4.0.tgz#451dced6f42652448f55cf0bc780b35512c8d1b0"
+ integrity sha512-dzW8u/PMqMZppvoXCFod8IkCTI2JL0yP2YUBbaALnX+iJJ6gqjk77fIoK9MqnMqRZAcoa81GLFfZExakWg/Q4Q==
+ dependencies:
+ clone "^2.1.2"
+ mkdirp "^1.0.4"
+ v8-to-istanbul "^1.2.1"
+ yargs "^15.3.1"
+
+puppeteer@23.1.0:
+ version "23.1.0"
+ resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-23.1.0.tgz#3abe4980670f214c8edfe689012e83418f81f9aa"
+ integrity sha512-m+CyicDlGN1AVUeOsCa6/+KQydJzxfsPowL7fQy+VGNeaWafB0m8G5aGfXdfZztKMxzCsdz7KNNzbJPeG9wwFw==
+ dependencies:
+ "@puppeteer/browsers" "2.3.1"
+ chromium-bidi "0.6.4"
+ cosmiconfig "^9.0.0"
+ devtools-protocol "0.0.1312386"
+ puppeteer-core "23.1.0"
+ typed-query-selector "^2.12.0"
+
+qs@6.13.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
+ integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
+ dependencies:
+ side-channel "^1.0.6"
+
+qs@^6.13.0, qs@^6.4.0:
+ version "6.13.1"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.1.tgz#3ce5fc72bd3a8171b85c99b93c65dd20b7d1b16e"
+ integrity sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==
+ dependencies:
+ side-channel "^1.0.6"
+
+queue-microtask@^1.2.2:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+ integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
+queue-tick@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142"
+ integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==
+
+quick-lru@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
+ integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
+
+randombytes@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+ integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+ dependencies:
+ safe-buffer "^5.1.0"
+
+range-parser@~1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+ integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
+
+raw-body@2.5.2:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
+ integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
+ dependencies:
+ bytes "3.1.2"
+ http-errors "2.0.0"
+ iconv-lite "0.4.24"
+ unpipe "1.0.0"
+
+rc@^1.2.7:
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
+ integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
+ dependencies:
+ deep-extend "^0.6.0"
+ ini "~1.3.0"
+ minimist "^1.2.0"
+ strip-json-comments "~2.0.1"
+
+react-autosize-textarea@^7.0.0:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/react-autosize-textarea/-/react-autosize-textarea-7.1.0.tgz#902c84fc395a689ca3a484dfb6bc2be9ba3694d1"
+ integrity sha512-BHpjCDkuOlllZn3nLazY2F8oYO1tS2jHnWhcjTWQdcKiiMU6gHLNt/fzmqMSyerR0eTdKtfSIqtSeTtghNwS+g==
+ dependencies:
+ autosize "^4.0.2"
+ line-height "^0.3.1"
+ prop-types "^15.5.6"
+
+react-dom@^18.2.0:
+ version "18.3.1"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4"
+ integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
+ dependencies:
+ loose-envify "^1.1.0"
+ scheduler "^0.23.2"
+
+react-is@^16.13.1:
+ version "16.13.1"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+ integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
+react-perfect-scrollbar@^1.5.3, react-perfect-scrollbar@^1.5.8:
+ version "1.5.8"
+ resolved "https://registry.yarnpkg.com/react-perfect-scrollbar/-/react-perfect-scrollbar-1.5.8.tgz#380959387a325c5c9d0268afc08b3f73ed5b3078"
+ integrity sha512-bQ46m70gp/HJtiBOF3gRzBISSZn8FFGNxznTdmTG8AAwpxG1bJCyn7shrgjEvGSQ5FJEafVEiosY+ccER11OSA==
+ dependencies:
+ perfect-scrollbar "^1.5.0"
+ prop-types "^15.6.1"
+
+react-tooltip@^4.2.21:
+ version "4.5.1"
+ resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-4.5.1.tgz#77eccccdf16adec804132e558ec20ca5783b866a"
+ integrity sha512-Zo+CSFUGXar1uV+bgXFFDe7VeS2iByeIp5rTgTcc2HqtuOS5D76QapejNNfx320MCY91TlhTQat36KGFTqgcvw==
+ dependencies:
+ prop-types "^15.8.1"
+ uuid "^7.0.3"
+
+react-virtuoso@^2.17.0:
+ version "2.19.1"
+ resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-2.19.1.tgz#a660a5c3cafcc7a84b59dfc356e1916e632c1e3a"
+ integrity sha512-zF6MAwujNGy2nJWCx/Df92ay/RnV2Kj4glUZfdyadI4suAn0kAZHB1BeI7yPFVp2iSccLzFlszhakWyr+fJ4Dw==
+ dependencies:
+ "@virtuoso.dev/react-urx" "^0.2.12"
+ "@virtuoso.dev/urx" "^0.2.12"
+
+react@^18.2.0:
+ version "18.3.1"
+ resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
+ integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
+ dependencies:
+ loose-envify "^1.1.0"
+
+readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
+ integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.3"
+ isarray "~1.0.0"
+ process-nextick-args "~2.0.0"
+ safe-buffer "~5.1.1"
+ string_decoder "~1.1.1"
+ util-deprecate "~1.0.1"
+
+readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
+ version "3.6.2"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
+ integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
+ dependencies:
+ inherits "^2.0.3"
+ string_decoder "^1.1.1"
+ util-deprecate "^1.0.1"
+
+readdirp@~3.6.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
+ integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
+ dependencies:
+ picomatch "^2.2.1"
+
+recast@^0.11.17:
+ version "0.11.23"
+ resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3"
+ integrity sha512-+nixG+3NugceyR8O1bLU45qs84JgI3+8EauyRZafLgC9XbdAOIVgwV1Pe2da0YzGo62KzWoZwUpVEQf6qNAXWA==
+ dependencies:
+ ast-types "0.9.6"
+ esprima "~3.1.0"
+ private "~0.1.5"
+ source-map "~0.5.0"
+
+rechoir@^0.7.0:
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
+ integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==
+ dependencies:
+ resolve "^1.9.0"
+
+reflect-metadata@^0.1.10:
+ version "0.1.14"
+ resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.14.tgz#24cf721fe60677146bb77eeb0e1f9dece3d65859"
+ integrity sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==
+
+regenerate-unicode-properties@^10.2.0:
+ version "10.2.0"
+ resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0"
+ integrity sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==
+ dependencies:
+ regenerate "^1.4.2"
+
+regenerate@^1.4.2:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
+ integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
+
+regenerator-runtime@^0.14.0:
+ version "0.14.1"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
+ integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
+
+regenerator-transform@^0.15.2:
+ version "0.15.2"
+ resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4"
+ integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==
+ dependencies:
+ "@babel/runtime" "^7.8.4"
+
+regexpu-core@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.2.0.tgz#0e5190d79e542bf294955dccabae04d3c7d53826"
+ integrity sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==
+ dependencies:
+ regenerate "^1.4.2"
+ regenerate-unicode-properties "^10.2.0"
+ regjsgen "^0.8.0"
+ regjsparser "^0.12.0"
+ unicode-match-property-ecmascript "^2.0.0"
+ unicode-match-property-value-ecmascript "^2.1.0"
+
+regjsgen@^0.8.0:
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab"
+ integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==
+
+regjsparser@^0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc"
+ integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==
+ dependencies:
+ jsesc "~3.0.2"
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+
+require-from-string@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
+ integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+
+require-main-filename@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+ integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+
+requires-port@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+ integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
+
+resolve-alpn@^1.0.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
+ integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==
+
+resolve-cwd@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
+ integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
+ dependencies:
+ resolve-from "^5.0.0"
+
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve-from@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
+ integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+
+resolve-package-path@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/resolve-package-path/-/resolve-package-path-4.0.3.tgz#31dab6897236ea6613c72b83658d88898a9040aa"
+ integrity sha512-SRpNAPW4kewOaNUt8VPqhJ0UMxawMwzJD8V7m1cJfdSTK9ieZwS6K7Dabsm4bmLFM96Z5Y/UznrpG5kt1im8yA==
+ dependencies:
+ path-root "^0.1.1"
+
+resolve@^1.14.2, resolve@^1.9.0:
+ version "1.22.8"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
+ integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
+ dependencies:
+ is-core-module "^2.13.0"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
+responselike@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc"
+ integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==
+ dependencies:
+ lowercase-keys "^2.0.0"
+
+restore-cursor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+ integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+ dependencies:
+ onetime "^5.1.0"
+ signal-exit "^3.0.2"
+
+retry@^0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
+ integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==
+
+reusify@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+ integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
+
+rimraf@2, rimraf@^2.6.3:
+ version "2.7.1"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+ integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+ dependencies:
+ glob "^7.1.3"
+
+rimraf@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+ integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+ dependencies:
+ glob "^7.1.3"
+
+rimraf@^5.0.0:
+ version "5.0.10"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.10.tgz#23b9843d3dc92db71f96e1a2ce92e39fd2a8221c"
+ integrity sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==
+ dependencies:
+ glob "^10.3.7"
+
+rimraf@~2.6.2:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+ integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+ dependencies:
+ glob "^7.1.3"
+
+roarr@^2.15.3:
+ version "2.15.4"
+ resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd"
+ integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==
+ dependencies:
+ boolean "^3.0.1"
+ detect-node "^2.0.4"
+ globalthis "^1.0.1"
+ json-stringify-safe "^5.0.1"
+ semver-compare "^1.0.0"
+ sprintf-js "^1.1.2"
+
+route-parser@^0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/route-parser/-/route-parser-0.0.5.tgz#7d1d09d335e49094031ea16991a4a79b01bbe1f4"
+ integrity sha512-nsii+MXoNb7NyF05LP9kaktx6AoBVT/7zUgDnzIb5IoYAvYkbZOAuoLJjVdsyEVxWv0swCxWkKDK4cMva+WDBA==
+
+run-parallel@^1.1.9:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+ integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+ dependencies:
+ queue-microtask "^1.2.2"
+
+safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.2.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+scheduler@^0.23.2:
+ version "0.23.2"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"
+ integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==
+ dependencies:
+ loose-envify "^1.1.0"
+
+schema-utils@^2.6.5:
+ version "2.7.1"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"
+ integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==
+ dependencies:
+ "@types/json-schema" "^7.0.5"
+ ajv "^6.12.4"
+ ajv-keywords "^3.5.2"
+
+schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe"
+ integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==
+ dependencies:
+ "@types/json-schema" "^7.0.8"
+ ajv "^6.12.5"
+ ajv-keywords "^3.5.2"
+
+schema-utils@^4.0.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b"
+ integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==
+ dependencies:
+ "@types/json-schema" "^7.0.9"
+ ajv "^8.9.0"
+ ajv-formats "^2.1.1"
+ ajv-keywords "^5.1.0"
+
+secure-compare@3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3"
+ integrity sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==
+
+seek-bzip@^1.0.5:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4"
+ integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==
+ dependencies:
+ commander "^2.8.1"
+
+semver-compare@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
+ integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
+
+semver@^5.4.1, semver@^5.6.0:
+ version "5.7.2"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
+ integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
+
+semver@^6.0.0, semver@^6.2.0, semver@^6.3.1:
+ version "6.3.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
+ integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+
+semver@^7.3.2, semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3:
+ version "7.6.3"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
+ integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
+
+send@0.19.0:
+ version "0.19.0"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
+ integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
+ dependencies:
+ debug "2.6.9"
+ depd "2.0.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 "1.6.0"
+ ms "2.1.3"
+ on-finished "2.4.1"
+ range-parser "~1.2.1"
+ statuses "2.0.1"
+
+serialize-error@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"
+ integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==
+ dependencies:
+ type-fest "^0.13.1"
+
+serialize-javascript@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
+ integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==
+ dependencies:
+ randombytes "^2.1.0"
+
+serialize-javascript@^6.0.0, serialize-javascript@^6.0.1, serialize-javascript@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2"
+ integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==
+ dependencies:
+ randombytes "^2.1.0"
+
+serve-static@1.16.2:
+ version "1.16.2"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296"
+ integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==
+ dependencies:
+ encodeurl "~2.0.0"
+ escape-html "~1.0.3"
+ parseurl "~1.3.3"
+ send "0.19.0"
+
+set-blocking@^2.0.0, set-blocking@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+ integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
+
+set-function-length@^1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
+ integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
+ 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"
+
+setimmediate@^1.0.5, setimmediate@~1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+ integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==
+
+setprototypeof@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
+ integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
+
+shallow-clone@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
+ integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
+ dependencies:
+ kind-of "^6.0.2"
+
+shebang-command@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ dependencies:
+ shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+shell-env@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/shell-env/-/shell-env-0.3.0.tgz#2250339022989165bda4eb7bf383afeaaa92dc34"
+ integrity sha512-VrC6OSm5riGAFWvlYExA80Rrlfi4STsztNXjyet9Jf20hbiVeeKvJIesb92gJk7zlmpQjB0wOZpy8ClzVdPVWQ==
+ dependencies:
+ default-shell "^1.0.0"
+ execa "^0.5.0"
+ strip-ansi "^3.0.0"
+
+shell-path@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/shell-path/-/shell-path-2.1.0.tgz#ea7d06ae1070874a1bac5c65bb9bdd62e4f67a38"
+ integrity sha512-w+mbrnpA+r5jSFS4MgFfxZJ1Wx8qMKkR4gvQ+wgaZEoZCMMYZ6Yl/dcNjW/zLMfmx5a9IVIFwGAtUJcnDMmFrg==
+ dependencies:
+ shell-env "^0.3.0"
+
+side-channel@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
+ integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
+ dependencies:
+ call-bind "^1.0.7"
+ es-errors "^1.3.0"
+ get-intrinsic "^1.2.4"
+ object-inspect "^1.13.1"
+
+signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+ integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
+signal-exit@^4.0.1:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
+ integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
+
+simple-concat@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
+ integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
+
+simple-get@^3.0.3:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55"
+ integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==
+ dependencies:
+ decompress-response "^4.2.0"
+ once "^1.3.1"
+ simple-concat "^1.0.0"
+
+slash@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
+ integrity sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==
+
+slash@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
+ integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
+
+slash@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
+ integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+
+slice-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
+ integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
+ dependencies:
+ ansi-styles "^4.0.0"
+ astral-regex "^2.0.0"
+ is-fullwidth-code-point "^3.0.0"
+
+smart-buffer@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
+ integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
+
+socket.io-adapter@~2.5.2:
+ version "2.5.5"
+ resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz#c7a1f9c703d7756844751b6ff9abfc1780664082"
+ integrity sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==
+ dependencies:
+ debug "~4.3.4"
+ ws "~8.17.1"
+
+socket.io-client@^4.5.3:
+ version "4.8.1"
+ resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.8.1.tgz#1941eca135a5490b94281d0323fe2a35f6f291cb"
+ integrity sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==
+ dependencies:
+ "@socket.io/component-emitter" "~3.1.0"
+ debug "~4.3.2"
+ engine.io-client "~6.6.1"
+ socket.io-parser "~4.2.4"
+
+socket.io-parser@~4.2.4:
+ version "4.2.4"
+ resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83"
+ integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==
+ dependencies:
+ "@socket.io/component-emitter" "~3.1.0"
+ debug "~4.3.1"
+
+socket.io@^4.5.3:
+ version "4.8.1"
+ resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.8.1.tgz#fa0eaff965cc97fdf4245e8d4794618459f7558a"
+ integrity sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==
+ dependencies:
+ accepts "~1.3.4"
+ base64id "~2.0.0"
+ cors "~2.8.5"
+ debug "~4.3.2"
+ engine.io "~6.6.0"
+ socket.io-adapter "~2.5.2"
+ socket.io-parser "~4.2.4"
+
+socks-proxy-agent@^5.0.0:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e"
+ integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==
+ dependencies:
+ agent-base "^6.0.2"
+ debug "4"
+ socks "^2.3.3"
+
+socks-proxy-agent@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6"
+ integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==
+ dependencies:
+ agent-base "^6.0.2"
+ debug "^4.3.3"
+ socks "^2.6.2"
+
+socks-proxy-agent@^8.0.2, socks-proxy-agent@^8.0.4:
+ version "8.0.4"
+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz#9071dca17af95f483300316f4b063578fa0db08c"
+ integrity sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==
+ dependencies:
+ agent-base "^7.1.1"
+ debug "^4.3.4"
+ socks "^2.8.3"
+
+socks@^2.3.3, socks@^2.6.2, socks@^2.8.3:
+ version "2.8.3"
+ resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5"
+ integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==
+ dependencies:
+ ip-address "^9.0.5"
+ smart-buffer "^4.2.0"
+
+sort-keys@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
+ integrity sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==
+ dependencies:
+ is-plain-obj "^1.0.0"
+
+source-map-js@^0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
+ integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
+
+source-map-js@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
+ integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+
+source-map-loader@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-2.0.2.tgz#4ab634f8f8bac4f406b61383f87a2bc804c24697"
+ integrity sha512-yIYkFOsKn+OdOirRJUPQpnZiMkF74raDVQjj5ni3SzbOiA57SabeX80R5zyMQAKpvKySA3Z4a85vFX3bvpC6KQ==
+ dependencies:
+ abab "^2.0.5"
+ iconv-lite "^0.6.2"
+ source-map-js "^0.6.2"
+
+source-map-support@^0.5.19, source-map-support@~0.5.20:
+ version "0.5.21"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+ integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+source-map@~0.5.0:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+ integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
+
+split@0.3:
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f"
+ integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==
+ dependencies:
+ through "2"
+
+sprintf-js@^1.1.2, sprintf-js@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a"
+ integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==
+
+ssri@^9.0.0:
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057"
+ integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==
+ dependencies:
+ minipass "^3.1.1"
+
+stat-mode@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-1.0.0.tgz#68b55cb61ea639ff57136f36b216a291800d1465"
+ integrity sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==
+
+statuses@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
+ integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
+
+stream-combiner@~0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14"
+ integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==
+ dependencies:
+ duplexer "~0.1.1"
+
+streamsearch@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
+ integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
+
+streamx@^2.15.0, streamx@^2.20.0:
+ version "2.21.0"
+ resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.21.0.tgz#ef42a3b3fada6887ba06964443adbbbec60c5851"
+ integrity sha512-Qz6MsDZXJ6ur9u+b+4xCG18TluU7PGlRfXVAAjNiGsFrBUt/ioyLkxbFaKJygoPs+/kW4VyBj0bSj89Qu0IGyg==
+ dependencies:
+ fast-fifo "^1.3.2"
+ queue-tick "^1.0.1"
+ text-decoder "^1.1.0"
+ optionalDependencies:
+ bare-events "^2.2.0"
+
+string-argv@^0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738"
+ integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA==
+
+"string-width-cjs@npm:string-width@^4.2.0":
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string-width@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+ integrity sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ strip-ansi "^3.0.0"
+
+"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string-width@^5.0.1, string-width@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
+ integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
+ dependencies:
+ eastasianwidth "^0.2.0"
+ emoji-regex "^9.2.2"
+ strip-ansi "^7.0.1"
+
+string_decoder@^1.1.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+ integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+ dependencies:
+ safe-buffer "~5.2.0"
+
+string_decoder@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+ integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+ dependencies:
+ safe-buffer "~5.1.0"
+
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+ integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==
+ dependencies:
+ ansi-regex "^2.0.0"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-ansi@^7.0.1:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
+ integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
+ dependencies:
+ ansi-regex "^6.0.1"
+
+strip-dirs@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
+ integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==
+ dependencies:
+ is-natural-number "^4.0.1"
+
+strip-eof@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
+ integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==
+
+strip-final-newline@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
+ integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
+
+strip-json-comments@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+ integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+strip-json-comments@~2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+ integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
+
+strip-outer@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631"
+ integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==
+ dependencies:
+ escape-string-regexp "^1.0.2"
+
+style-loader@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c"
+ integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==
+ dependencies:
+ loader-utils "^2.0.0"
+ schema-utils "^3.0.0"
+
+sumchecker@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42"
+ integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==
+ dependencies:
+ debug "^4.1.0"
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-color@^8.0.0, supports-color@^8.1.1:
+ version "8.1.1"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+ integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
+ integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
+
+tar-fs@^1.16.2:
+ version "1.16.3"
+ resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509"
+ integrity sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==
+ dependencies:
+ chownr "^1.0.1"
+ mkdirp "^0.5.1"
+ pump "^1.0.0"
+ tar-stream "^1.1.2"
+
+tar-fs@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
+ integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==
+ dependencies:
+ chownr "^1.1.1"
+ mkdirp-classic "^0.5.2"
+ pump "^3.0.0"
+ tar-stream "^2.1.4"
+
+tar-fs@^3.0.6:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.6.tgz#eaccd3a67d5672f09ca8e8f9c3d2b89fa173f217"
+ integrity sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==
+ dependencies:
+ pump "^3.0.0"
+ tar-stream "^3.1.5"
+ optionalDependencies:
+ bare-fs "^2.1.1"
+ bare-path "^2.1.0"
+
+tar-stream@^1.1.2, tar-stream@^1.5.2:
+ version "1.6.2"
+ resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555"
+ integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==
+ dependencies:
+ bl "^1.0.0"
+ buffer-alloc "^1.2.0"
+ end-of-stream "^1.0.0"
+ fs-constants "^1.0.0"
+ readable-stream "^2.3.0"
+ to-buffer "^1.1.1"
+ xtend "^4.0.0"
+
+tar-stream@^2.1.4:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
+ integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
+ dependencies:
+ bl "^4.0.3"
+ end-of-stream "^1.4.1"
+ fs-constants "^1.0.0"
+ inherits "^2.0.3"
+ readable-stream "^3.1.1"
+
+tar-stream@^3.1.5:
+ version "3.1.7"
+ resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b"
+ integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==
+ dependencies:
+ b4a "^1.6.4"
+ fast-fifo "^1.2.0"
+ streamx "^2.15.0"
+
+tar@^6.0.5, tar@^6.1.11, tar@^6.1.2:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a"
+ integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==
+ dependencies:
+ chownr "^2.0.0"
+ fs-minipass "^2.0.0"
+ minipass "^5.0.0"
+ minizlib "^2.1.1"
+ mkdirp "^1.0.3"
+ yallist "^4.0.0"
+
+tdigest@^0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.2.tgz#96c64bac4ff10746b910b0e23b515794e12faced"
+ integrity sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==
+ dependencies:
+ bintrees "1.0.2"
+
+temp@^0.9.1:
+ version "0.9.4"
+ resolved "https://registry.yarnpkg.com/temp/-/temp-0.9.4.tgz#cd20a8580cb63635d0e4e9d4bd989d44286e7620"
+ integrity sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==
+ dependencies:
+ mkdirp "^0.5.1"
+ rimraf "~2.6.2"
+
+terser-webpack-plugin@^5.3.10:
+ version "5.3.10"
+ resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199"
+ integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==
+ dependencies:
+ "@jridgewell/trace-mapping" "^0.3.20"
+ jest-worker "^27.4.5"
+ schema-utils "^3.1.1"
+ serialize-javascript "^6.0.1"
+ terser "^5.26.0"
+
+terser@^5.26.0:
+ version "5.36.0"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.36.0.tgz#8b0dbed459ac40ff7b4c9fd5a3a2029de105180e"
+ integrity sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==
+ dependencies:
+ "@jridgewell/source-map" "^0.3.3"
+ acorn "^8.8.2"
+ commander "^2.20.0"
+ source-map-support "~0.5.20"
+
+text-decoder@^1.1.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.1.tgz#e173f5121d97bfa3ff8723429ad5ba92e1ead67e"
+ integrity sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==
+
+through@2, through@^2.3.8, through@~2.3, through@~2.3.1:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+ integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
+
+tmp@^0.0.33:
+ version "0.0.33"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+ integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
+ dependencies:
+ os-tmpdir "~1.0.2"
+
+to-buffer@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
+ integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
+
+to-regex-range@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+ dependencies:
+ is-number "^7.0.0"
+
+toidentifier@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+ integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
+trash@^7.2.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/trash/-/trash-7.2.0.tgz#c5ad0c9b13d7e7cad0b4187b3cfe38cd8b39abe2"
+ integrity sha512-3bR8Z5aWO8b9qybS6skBoaavH/hX9Onb1RrdIIhJxv9VpH3aBtpbKuAX4rIh/0xpDZ7K4ga36wONk/okbhjTlA==
+ dependencies:
+ "@stroncium/procfs" "^1.2.1"
+ globby "^7.1.1"
+ is-path-inside "^3.0.2"
+ make-dir "^3.1.0"
+ move-file "^2.0.0"
+ p-map "^4.0.0"
+ uuid "^8.3.2"
+ xdg-trashdir "^3.1.0"
+
+"traverse@>=0.3.0 <0.4":
+ version "0.3.9"
+ resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9"
+ integrity sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==
+
+trim-repeated@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21"
+ integrity sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==
+ dependencies:
+ escape-string-regexp "^1.0.2"
+
+ts-md5@^1.2.2:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/ts-md5/-/ts-md5-1.3.1.tgz#f5b860c0d5241dd9bb4e909dd73991166403f511"
+ integrity sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==
+
+tslib@^1.10.0:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+ integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+
+tslib@^2.0.1, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.6.2:
+ version "2.8.1"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
+ integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
+
+tunnel-agent@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+ integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==
+ dependencies:
+ safe-buffer "^5.0.1"
+
+type-detect@^4.0.0, type-detect@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c"
+ integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==
+
+type-fest@^0.13.1:
+ version "0.13.1"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
+ integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
+
+type-fest@^0.21.3:
+ version "0.21.3"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
+ integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
+
+type-fest@^2.17.0:
+ version "2.19.0"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
+ integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
+
+type-is@^1.6.4, type-is@~1.6.18:
+ version "1.6.18"
+ resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
+ integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
+ dependencies:
+ media-typer "0.3.0"
+ mime-types "~2.1.24"
+
+typed-query-selector@^2.12.0:
+ version "2.12.0"
+ resolved "https://registry.yarnpkg.com/typed-query-selector/-/typed-query-selector-2.12.0.tgz#92b65dbc0a42655fccf4aeb1a08b1dddce8af5f2"
+ integrity sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==
+
+typedarray@^0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+ integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==
+
+typescript@~5.4.5:
+ version "5.4.5"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611"
+ integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==
+
+uc.micro@^1.0.1, uc.micro@^1.0.5:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
+ integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
+
+umd-compat-loader@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/umd-compat-loader/-/umd-compat-loader-2.1.2.tgz#abf89be1591940a236cf8fa87f88d6d6f5a8da35"
+ integrity sha512-RkTlsfrCxUISWqiTtYFFJank7b2Hhl4V2pc29nl0xOEGvvuVkpy1xnufhXfTituxgpW0HSrDk0JHlvPYZxEXKQ==
+ dependencies:
+ ast-types "^0.9.2"
+ loader-utils "^1.0.3"
+ recast "^0.11.17"
+
+unbzip2-stream@^1.0.9, unbzip2-stream@^1.4.3:
+ version "1.4.3"
+ resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
+ integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==
+ dependencies:
+ buffer "^5.2.1"
+ through "^2.3.8"
+
+undici-types@~6.20.0:
+ version "6.20.0"
+ resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
+ integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
+
+unicode-canonical-property-names-ecmascript@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz#cb3173fe47ca743e228216e4a3ddc4c84d628cc2"
+ integrity sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==
+
+unicode-match-property-ecmascript@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3"
+ integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==
+ dependencies:
+ unicode-canonical-property-names-ecmascript "^2.0.0"
+ unicode-property-aliases-ecmascript "^2.0.0"
+
+unicode-match-property-value-ecmascript@^2.1.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz#a0401aee72714598f739b68b104e4fe3a0cb3c71"
+ integrity sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==
+
+unicode-property-aliases-ecmascript@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd"
+ integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==
+
+union@~0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/union/-/union-0.5.0.tgz#b2c11be84f60538537b846edb9ba266ba0090075"
+ integrity sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==
+ dependencies:
+ qs "^6.4.0"
+
+unique-filename@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2"
+ integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==
+ dependencies:
+ unique-slug "^3.0.0"
+
+unique-slug@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9"
+ integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==
+ dependencies:
+ imurmurhash "^0.1.4"
+
+universalify@^0.1.0:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+ integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
+universalify@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
+ integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
+
+unpipe@1.0.0, unpipe@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+ integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
+
+unzipper@^0.9.11:
+ version "0.9.15"
+ resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.9.15.tgz#97d99203dad17698ee39882483c14e4845c7549c"
+ integrity sha512-2aaUvO4RAeHDvOCuEtth7jrHFaCKTSXPqUkXwADaLBzGbgZGzUDccoEdJ5lW+3RmfpOZYNx0Rw6F6PUzM6caIA==
+ dependencies:
+ big-integer "^1.6.17"
+ binary "~0.3.0"
+ bluebird "~3.4.1"
+ buffer-indexof-polyfill "~1.0.0"
+ duplexer2 "~0.1.4"
+ fstream "^1.0.12"
+ listenercount "~1.0.1"
+ readable-stream "~2.3.6"
+ setimmediate "~1.0.4"
+
+update-browserslist-db@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5"
+ integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==
+ dependencies:
+ escalade "^3.2.0"
+ picocolors "^1.1.0"
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+url-join@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
+ integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
+
+urlpattern-polyfill@10.0.0:
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz#f0a03a97bfb03cdf33553e5e79a2aadd22cac8ec"
+ integrity sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==
+
+user-home@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f"
+ integrity sha512-KMWqdlOcjCYdtIJpicDSFBQ8nFwS2i9sslAd6f4+CBGcU4gist2REnr2fxj2YocvJFxSF3ZOHLYLVZnUxv4BZQ==
+ dependencies:
+ os-homedir "^1.0.0"
+
+util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+
+utils-merge@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+ integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
+
+uuid@^7.0.3:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
+ integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
+
+uuid@^8.3.2:
+ version "8.3.2"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
+ integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
+
+uuid@^9.0.1:
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
+ integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
+
+v8-compile-cache@^2.2.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz#cdada8bec61e15865f05d097c5f4fd30e94dc128"
+ integrity sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==
+
+v8-to-istanbul@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-1.2.1.tgz#8f63a94b7f91243f5dcc6a495540b5653beb1279"
+ integrity sha512-NglPycIwSQeSJj7VJ6L8vTsPKC9MG5Lcx4n3SvYqNHzklbMI4dGcLJnkLPEPJ3uB8UyTdWviMhM0Ptq+xD5UFQ==
+
+valid-filename@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/valid-filename/-/valid-filename-2.0.1.tgz#0768d6f364b1ed3bdf68f0d15abffb0d9d6cecaf"
+ integrity sha512-7eF/iUZ5SPd3FighoKgatSjXDJ25Vopo/6yvEKGyX4FIeZVHcLjHmyvbQ1WdFD9RQZ9PoBA7nrSxxAz/oC64SQ==
+ dependencies:
+ filename-reserved-regex "^2.0.0"
+
+vary@^1, vary@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+ integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
+
+vhost@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/vhost/-/vhost-3.0.2.tgz#2fb1decd4c466aa88b0f9341af33dc1aff2478d5"
+ integrity sha512-S3pJdWrpFWrKMboRU4dLYgMrTgoPALsmYwOvyebK2M6X95b9kQrjZy5rwl3uzzpfpENe/XrNYu/2U+e7/bmT5g==
+
+vscode-jsonrpc@8.2.0:
+ version "8.2.0"
+ resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9"
+ integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==
+
+vscode-languageserver-protocol@^3.17.2:
+ version "3.17.5"
+ resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz#864a8b8f390835572f4e13bd9f8313d0e3ac4bea"
+ integrity sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==
+ dependencies:
+ vscode-jsonrpc "8.2.0"
+ vscode-languageserver-types "3.17.5"
+
+vscode-languageserver-textdocument@^1.0.1:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz#457ee04271ab38998a093c68c2342f53f6e4a631"
+ integrity sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==
+
+vscode-languageserver-types@3.17.5:
+ version "3.17.5"
+ resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a"
+ integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==
+
+vscode-oniguruma@1.6.1:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.6.1.tgz#2bf4dfcfe3dd2e56eb549a3068c8ee39e6c30ce5"
+ integrity sha512-vc4WhSIaVpgJ0jJIejjYxPvURJavX6QG41vu0mGhqywMkQqulezEqEQ3cO3gc8GvcOpX6ycmKGqRoROEMBNXTQ==
+
+vscode-textmate@^9.0.0:
+ version "9.1.0"
+ resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-9.1.0.tgz#656a6aa163a9578397ba810733952bedb2b47202"
+ integrity sha512-lxKSVp2DkFOx9RDAvpiYUrB9/KT1fAfi1aE8CBGstP8N7rLF+Seifj8kDA198X0mYj1CjQUC+81+nQf8CO0nVA==
+
+vscode-uri@^2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c"
+ integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==
+
+watchpack@^2.4.1:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da"
+ integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==
+ dependencies:
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.1.2"
+
+wcwidth@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
+ integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==
+ dependencies:
+ defaults "^1.0.3"
+
+webpack-cli@4.7.0:
+ version "4.7.0"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.7.0.tgz#3195a777f1f802ecda732f6c95d24c0004bc5a35"
+ integrity sha512-7bKr9182/sGfjFm+xdZSwgQuFjgEcy0iCTIBxRUeteJ2Kr8/Wz0qNJX+jw60LU36jApt4nmMkep6+W5AKhok6g==
+ dependencies:
+ "@discoveryjs/json-ext" "^0.5.0"
+ "@webpack-cli/configtest" "^1.0.3"
+ "@webpack-cli/info" "^1.2.4"
+ "@webpack-cli/serve" "^1.4.0"
+ colorette "^1.2.1"
+ commander "^7.0.0"
+ execa "^5.0.0"
+ fastest-levenshtein "^1.0.12"
+ import-local "^3.0.2"
+ interpret "^2.2.0"
+ rechoir "^0.7.0"
+ v8-compile-cache "^2.2.0"
+ webpack-merge "^5.7.3"
+
+webpack-merge@^5.7.3:
+ version "5.10.0"
+ resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177"
+ integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==
+ dependencies:
+ clone-deep "^4.0.1"
+ flat "^5.0.2"
+ wildcard "^2.0.0"
+
+webpack-sources@^3.2.3:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
+ integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
+
+webpack@^5.76.0:
+ version "5.97.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.97.0.tgz#1c5e3b9319f8c6decb19b142e776d90e629d5c40"
+ integrity sha512-CWT8v7ShSfj7tGs4TLRtaOLmOCPWhoKEvp+eA7FVx8Xrjb3XfT0aXdxDItnRZmE8sHcH+a8ayDrJCOjXKxVFfQ==
+ dependencies:
+ "@types/eslint-scope" "^3.7.7"
+ "@types/estree" "^1.0.6"
+ "@webassemblyjs/ast" "^1.14.1"
+ "@webassemblyjs/wasm-edit" "^1.14.1"
+ "@webassemblyjs/wasm-parser" "^1.14.1"
+ acorn "^8.14.0"
+ browserslist "^4.24.0"
+ chrome-trace-event "^1.0.2"
+ enhanced-resolve "^5.17.1"
+ es-module-lexer "^1.2.1"
+ eslint-scope "5.1.1"
+ events "^3.2.0"
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.2.11"
+ json-parse-even-better-errors "^2.3.1"
+ loader-runner "^4.2.0"
+ mime-types "^2.1.27"
+ neo-async "^2.6.2"
+ schema-utils "^3.2.0"
+ tapable "^2.1.1"
+ terser-webpack-plugin "^5.3.10"
+ watchpack "^2.4.1"
+ webpack-sources "^3.2.3"
+
+whatwg-encoding@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53"
+ integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==
+ dependencies:
+ iconv-lite "0.6.3"
+
+which-module@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409"
+ integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==
+
+which-pm-runs@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.1.0.tgz#35ccf7b1a0fce87bd8b92a478c9d045785d3bf35"
+ integrity sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==
+
+which@^1.2.9:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+ integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+ dependencies:
+ isexe "^2.0.0"
+
+which@^2.0.1, which@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+wide-align@^1.1.0, wide-align@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
+ integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
+ dependencies:
+ string-width "^1.0.2 || 2 || 3 || 4"
+
+wildcard@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67"
+ integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==
+
+worker-loader@^3.0.8:
+ version "3.0.8"
+ resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-3.0.8.tgz#5fc5cda4a3d3163d9c274a4e3a811ce8b60dbb37"
+ integrity sha512-XQyQkIFeRVC7f7uRhFdNMe/iJOdO6zxAaR3EWbDp45v3mDhrTi+++oswKNxShUNjPC/1xUp5DB29YKLhFo129g==
+ dependencies:
+ loader-utils "^2.0.0"
+ schema-utils "^3.0.0"
+
+workerpool@^6.5.1:
+ version "6.5.1"
+ resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544"
+ integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==
+
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrap-ansi@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+ integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrap-ansi@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
+ integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
+ dependencies:
+ ansi-styles "^6.1.0"
+ string-width "^5.0.1"
+ strip-ansi "^7.0.1"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+write-file-atomic@^2.0.0:
+ version "2.4.3"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481"
+ integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==
+ dependencies:
+ graceful-fs "^4.1.11"
+ imurmurhash "^0.1.4"
+ signal-exit "^3.0.2"
+
+write-json-file@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-2.3.0.tgz#2b64c8a33004d54b8698c76d585a77ceb61da32f"
+ integrity sha512-84+F0igFp2dPD6UpAQjOUX3CdKUOqUzn6oE9sDBNzUXINR5VceJ1rauZltqQB/bcYsx3EpKys4C7/PivKUAiWQ==
+ dependencies:
+ detect-indent "^5.0.0"
+ graceful-fs "^4.1.2"
+ make-dir "^1.0.0"
+ pify "^3.0.0"
+ sort-keys "^2.0.0"
+ write-file-atomic "^2.0.0"
+
+ws@^8.17.1, ws@^8.18.0:
+ version "8.18.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
+ integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
+
+ws@~8.17.1:
+ version "8.17.1"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
+ integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==
+
+xdg-basedir@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
+ integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
+
+xdg-trashdir@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/xdg-trashdir/-/xdg-trashdir-3.1.0.tgz#7294262d5793eb5488c2f529fba883ec32a24ea0"
+ integrity sha512-N1XQngeqMBoj9wM4ZFadVV2MymImeiFfYD+fJrNlcVcOHsJFFQe7n3b+aBoTPwARuq2HQxukfzVpQmAk1gN4sQ==
+ dependencies:
+ "@sindresorhus/df" "^3.1.1"
+ mount-point "^3.0.0"
+ user-home "^2.0.0"
+ xdg-basedir "^4.0.0"
+
+xmlhttprequest-ssl@~2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz#e9e8023b3f29ef34b97a859f584c5e6c61418e23"
+ integrity sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==
+
+xtend@^4.0.0:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
+ integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
+
+xterm-addon-fit@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.5.0.tgz#2d51b983b786a97dcd6cde805e700c7f913bc596"
+ integrity sha512-DsS9fqhXHacEmsPxBJZvfj2la30Iz9xk+UKjhQgnYNkrUIN5CYLbw7WEfz117c7+S86S/tpHPfvNxJsF5/G8wQ==
+
+xterm-addon-fit@^0.8.0:
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.8.0.tgz#48ca99015385141918f955ca7819e85f3691d35f"
+ integrity sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw==
+
+xterm-addon-search@^0.13.0:
+ version "0.13.0"
+ resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0.tgz#21286f4db48aa949fbefce34bb8bc0c9d3cec627"
+ integrity sha512-sDUwG4CnqxUjSEFh676DlS3gsh3XYCzAvBPSvJ5OPgF3MRL3iHLPfsb06doRicLC2xXNpeG2cWk8x1qpESWJMA==
+
+xterm@^4.16.0:
+ version "4.19.0"
+ resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0.tgz#c0f9d09cd61de1d658f43ca75f992197add9ef6d"
+ integrity sha512-c3Cp4eOVsYY5Q839dR5IejghRPpxciGmLWWaP9g+ppfMeBChMeLa1DCA+pmX/jyDZ+zxFOmlJL/82qVdayVoGQ==
+
+xterm@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0.tgz#867daf9cc826f3d45b5377320aabd996cb0fce46"
+ integrity sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==
+
+y18n@^4.0.0:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
+ integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
+
+y18n@^5.0.5:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+ integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
+yallist@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
+ integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==
+
+yallist@^3.0.2:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
+ integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+
+yallist@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+ integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
+yaml@^2.2.2:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773"
+ integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==
+
+yargs-parser@^18.1.2:
+ version "18.1.3"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
+ integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
+ dependencies:
+ camelcase "^5.0.0"
+ decamelize "^1.2.0"
+
+yargs-parser@^20.2.2, yargs-parser@^20.2.9:
+ version "20.2.9"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
+ integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
+
+yargs-parser@^21.1.1:
+ version "21.1.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
+ integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
+
+yargs-unparser@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
+ integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
+ dependencies:
+ camelcase "^6.0.0"
+ decamelize "^4.0.0"
+ flat "^5.0.2"
+ is-plain-obj "^2.1.0"
+
+yargs@^15.3.1:
+ version "15.4.1"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
+ integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
+ dependencies:
+ cliui "^6.0.0"
+ decamelize "^1.2.0"
+ find-up "^4.1.0"
+ get-caller-file "^2.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^2.0.0"
+ set-blocking "^2.0.0"
+ string-width "^4.2.0"
+ which-module "^2.0.0"
+ y18n "^4.0.0"
+ yargs-parser "^18.1.2"
+
+yargs@^16.2.0:
+ version "16.2.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
+ integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
+ dependencies:
+ cliui "^7.0.2"
+ escalade "^3.1.1"
+ get-caller-file "^2.0.5"
+ require-directory "^2.1.1"
+ string-width "^4.2.0"
+ y18n "^5.0.5"
+ yargs-parser "^20.2.2"
+
+yargs@^17.0.1, yargs@^17.7.2:
+ version "17.7.2"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
+ integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
+ dependencies:
+ cliui "^8.0.1"
+ escalade "^3.1.1"
+ get-caller-file "^2.0.5"
+ require-directory "^2.1.1"
+ string-width "^4.2.3"
+ y18n "^5.0.5"
+ yargs-parser "^21.1.1"
+
+yauzl@^2.10.0, yauzl@^2.4.2, yauzl@^2.9.2:
+ version "2.10.0"
+ resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
+ integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==
+ dependencies:
+ buffer-crc32 "~0.2.3"
+ fd-slicer "~1.1.0"
+
+yocto-queue@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+ integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+
+zod@3.23.8:
+ version "3.23.8"
+ resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
+ integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/.project b/demo/dockerfiles/demo-theia-monitor-vscode/.project
deleted file mode 100644
index 655e42dfc..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/.project
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- demo-theia-docker
-
-
-
-
-
-
-
-
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/Dockerfile b/demo/dockerfiles/demo-theia-monitor-vscode/Dockerfile
index e30ca90a9..3e1267d6d 100644
--- a/demo/dockerfiles/demo-theia-monitor-vscode/Dockerfile
+++ b/demo/dockerfiles/demo-theia-monitor-vscode/Dockerfile
@@ -1,10 +1,10 @@
-FROM node:18-bullseye-slim as build-stage
+FROM node:18-bullseye-slim AS build-stage
# Copy and unzip the vsix file
RUN apt-get update && apt-get install -y unzip && rm -rf /var/lib/apt/lists/*
-COPY theiacloud-monitor-0.12.0-next.vsix /tmp/theiacloud-monitor.vsix
-RUN mkdir /tmp/extracted && unzip /tmp/theiacloud-monitor.vsix -d /tmp/extracted
+COPY theia-cloud-monitor-1.2.0-next.vsix /tmp/theia-cloud-monitor.vsix
+RUN mkdir /tmp/extracted && unzip /tmp/theia-cloud-monitor.vsix -d /tmp/extracted
-FROM theiacloud/theia-cloud-demo:0.12.0-next as production-stage
+FROM theiacloud/theia-cloud-demo:1.2.0-next AS production-stage
-COPY --chown=theia:theia --from=build-stage /tmp/extracted /home/theia/plugins
\ No newline at end of file
+COPY --chown=theia:theia --from=build-stage /tmp/extracted /home/theia/plugins
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/package.json b/demo/dockerfiles/demo-theia-monitor-vscode/package.json
deleted file mode 100644
index 920515cbf..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/package.json
+++ /dev/null
@@ -1,87 +0,0 @@
-{
- "private": true,
- "theia": {
- "frontend": {
- "config": {
- "applicationName": "Theia Cloud Example",
- "warnOnPotentiallyInsecureHostPattern": false,
- "preferences": {
- "files.enableTrash": false
- }
- }
- },
- "backend": {
- "config": {
- "warnOnPotentiallyInsecureHostPattern": false
- }
- }
- },
- "dependencies": {
- "@theia/bulk-edit": "1.43.1",
- "@theia/callhierarchy": "1.43.1",
- "@theia/console": "1.43.1",
- "@theia/core": "1.43.1",
- "@theia/debug": "1.43.1",
- "@theia/editor": "1.43.1",
- "@theia/editor-preview": "1.43.1",
- "@theia/electron": "1.43.1",
- "@theia/external-terminal": "1.43.1",
- "@theia/file-search": "1.43.1",
- "@theia/filesystem": "1.43.1",
- "@theia/getting-started": "1.43.1",
- "@theia/keymaps": "1.43.1",
- "@theia/markers": "1.43.1",
- "@theia/messages": "1.43.1",
- "@theia/metrics": "1.43.1",
- "@theia/mini-browser": "1.43.1",
- "@theia/monaco": "1.43.1",
- "@theia/navigator": "1.43.1",
- "@theia/outline-view": "1.43.1",
- "@theia/output": "1.43.1",
- "@theia/plugin-dev": "1.43.1",
- "@theia/plugin-ext": "1.43.1",
- "@theia/plugin-ext-vscode": "1.43.1",
- "@theia/preferences": "1.43.1",
- "@theia/process": "1.43.1",
- "@theia/property-view": "1.43.1",
- "@theia/scm": "1.43.1",
- "@theia/scm-extra": "1.43.1",
- "@theia/search-in-workspace": "1.43.1",
- "@theia/task": "1.43.1",
- "@theia/terminal": "1.43.1",
- "@theia/timeline": "1.43.1",
- "@theia/toolbar": "1.43.1",
- "@theia/typehierarchy": "1.43.1",
- "@theia/userstorage": "1.43.1",
- "@theia/variable-resolver": "1.43.1",
- "@theia/vsx-registry": "1.43.1",
- "@theia/workspace": "1.43.1"
- },
- "devDependencies": {
- "@theia/cli": "1.43.1"
- },
- "scripts": {
- "preinstall": "node-gyp install"
- },
- "theiaPluginsDir": "plugins",
- "theiaPlugins": {
- "vscode.git": "https://open-vsx.org/api/vscode/git/1.52.1/file/vscode.git-1.52.1.vsix",
- "vscode.markdown-language-features": "https://open-vsx.org/api/vscode/markdown-language-features/1.39.2/file/vscode.markdown-language-features-1.39.2.vsix",
- "vscode-builtin-extensions-pack": "https://open-vsx.org/api/eclipse-theia/builtin-extension-pack/1.50.1/file/eclipse-theia.builtin-extension-pack-1.50.1.vsix",
- "redhat.java": "https://open-vsx.org/api/redhat/java/0.73.0/file/redhat.java-0.73.0.vsix",
- "vscjava.vscode-java-debug": "https://open-vsx.org/api/vscjava/vscode-java-debug/0.30.0/file/vscjava.vscode-java-debug-0.30.0.vsix",
- "vscjava.vscode-java-test": "https://open-vsx.org/api/vscjava/vscode-java-test/0.26.1/file/vscjava.vscode-java-test-0.26.1.vsix",
- "vscjava.vscode-maven": "https://open-vsx.org/api/vscjava/vscode-maven/0.21.2/file/vscjava.vscode-maven-0.21.2.vsix",
- "vscjava.vscode-java-dependency": "https://open-vsx.org/api/vscjava/vscode-java-dependency/0.16.0/file/vscjava.vscode-java-dependency-0.16.0.vsix",
- "redhat.vscode-xml": "https://open-vsx.org/api/redhat/vscode-xml/0.20.0/file/redhat.vscode-xml-0.20.0.vsix",
- "cdt-gdb-vscode": "https://open-vsx.org/api/eclipse-cdt/cdt-gdb-vscode/0.0.92/file/eclipse-cdt.cdt-gdb-vscode-0.0.92.vsix",
- "vscode-builtin-cpp": "https://open-vsx.org/api/vscode/cpp/1.62.3/file/vscode.cpp-1.62.3.vsix",
- "vscode-clangd": "https://open-vsx.org/api/llvm-vs-code-extensions/vscode-clangd/0.1.15/file/llvm-vs-code-extensions.vscode-clangd-0.1.15.vsix",
- "vscode-cmake": "https://open-vsx.org/api/ms-vscode/cmake-tools/1.9.2/file/ms-vscode.cmake-tools-1.9.2.vsix"
- },
- "theiaPluginsExcludeIds": [
- "vscode.extension-editing",
- "vscode.microsoft-authentication",
- "vscode.html-language-features"
- ]
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/.vscode/launch.json b/demo/dockerfiles/demo-theia-monitor-vscode/project/.vscode/launch.json
deleted file mode 100644
index a2ea02c46..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/.vscode/launch.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- "version": "0.2.0",
- "configurations": []
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/.vscode/settings.json b/demo/dockerfiles/demo-theia-monitor-vscode/project/.vscode/settings.json
deleted file mode 100644
index 8aa63df4a..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/.vscode/settings.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "files.exclude": {
- "**/.classpath": true,
- "**/.project": true,
- "**/.settings": true,
- "**/.factorypath": true
- },
- "java.configuration.updateBuildConfiguration": "automatic",
- "xml.downloadExternalResources.enabled": true,
- "cmake.sourceDirectory": "${workspaceFolder}/cpp",
- "cmake.buildDirectory": "${workspaceFolder}/cpp/build",
- "java.project.importOnFirstTimeStartup": "automatic",
- "cmake.configureOnOpen": true
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/README.md b/demo/dockerfiles/demo-theia-monitor-vscode/project/README.md
deleted file mode 100644
index 295def8ad..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Theia Examples
-
-This workspace contains some sample projects for Java-, C++-, and Web-Development.
-
-## Web
-
-The `web` directory contains a simple HTML+CSS+Javascript+Typescript website. You may build this by running `npm install && npm run build` in the `web` directory. The build results are present in the `web/bin` directory. You may open the `web/bin/index.html` in Theia's Preview-Browser via right clicking the `index.html` -> `Open With...` -> `Preview`
-
-## Java
-
-The `java` directory contains a simple Java Problem. Opening a Java file activates the Java support for this project. After activation you may Run/Debug the main method/test cases.
-
-## XML, JSON
-
-The `files` directory contains sample XML and JSON files with schema-files.
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/files/template.json b/demo/dockerfiles/demo-theia-monitor-vscode/project/files/template.json
deleted file mode 100644
index 189377147..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/files/template.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "$schema": "./template.schema.json",
- "name": "theia-cloud.demo",
- "image": "my.docker.registry/theia-cloud-demo",
- "limits": {
- "cpu": "1",
- "memory": "2000M"
- }
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/files/template.schema.json b/demo/dockerfiles/demo-theia-monitor-vscode/project/files/template.schema.json
deleted file mode 100644
index 5aa28de35..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/files/template.schema.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type": "object",
- "properties": {
- "name": {
- "type": "string"
- },
- "image": {
- "type": "string"
- },
- "limits": {
- "type": "object",
- "properties": {
- "cpu": {
- "type": "string"
- },
- "memory": {
- "type": "string"
- }
- },
- "required": ["cpu", "memory"]
- }
- },
- "required": ["name", "image", "limits"]
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/files/template.xml b/demo/dockerfiles/demo-theia-monitor-vscode/project/files/template.xml
deleted file mode 100644
index 4dff323e4..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/files/template.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
- my.docker.registry/theia-cloud-demo
-
- 1
- 2000M
-
-
\ No newline at end of file
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/files/template.xsd b/demo/dockerfiles/demo-theia-monitor-vscode/project/files/template.xsd
deleted file mode 100644
index 16f58acad..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/files/template.xsd
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.classpath b/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.classpath
deleted file mode 100644
index 9ba41a249..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.classpath
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.gitignore b/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.gitignore
deleted file mode 100644
index 1de565933..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-target
\ No newline at end of file
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.project b/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.project
deleted file mode 100644
index bf3a5a7b7..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.project
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
- demo
-
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
- org.eclipse.m2e.core.maven2Builder
-
-
-
-
-
- org.eclipse.jdt.core.javanature
- org.eclipse.m2e.core.maven2Nature
-
-
-
- 1648557854974
-
- 30
-
- org.eclipse.core.resources.regexFilterMatcher
- node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
-
-
-
-
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.settings/org.eclipse.core.resources.prefs b/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.settings/org.eclipse.core.resources.prefs
deleted file mode 100644
index f9fe34593..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.settings/org.eclipse.core.resources.prefs
+++ /dev/null
@@ -1,4 +0,0 @@
-eclipse.preferences.version=1
-encoding//src/main/java=UTF-8
-encoding//src/test/java=UTF-8
-encoding/=UTF-8
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.settings/org.eclipse.jdt.apt.core.prefs b/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.settings/org.eclipse.jdt.apt.core.prefs
deleted file mode 100644
index d4313d4b2..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.settings/org.eclipse.jdt.apt.core.prefs
+++ /dev/null
@@ -1,2 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.apt.aptEnabled=false
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.settings/org.eclipse.jdt.core.prefs b/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index 46235dc07..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,9 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
-org.eclipse.jdt.core.compiler.compliance=11
-org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
-org.eclipse.jdt.core.compiler.processAnnotations=disabled
-org.eclipse.jdt.core.compiler.release=disabled
-org.eclipse.jdt.core.compiler.source=11
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.settings/org.eclipse.m2e.core.prefs b/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.settings/org.eclipse.m2e.core.prefs
deleted file mode 100644
index f897a7f1c..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/.settings/org.eclipse.m2e.core.prefs
+++ /dev/null
@@ -1,4 +0,0 @@
-activeProfiles=
-eclipse.preferences.version=1
-resolveWorkspaceProjects=true
-version=1
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/pom.xml b/demo/dockerfiles/demo-theia-monitor-vscode/project/java/pom.xml
deleted file mode 100644
index c4ff1c434..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/pom.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
- 4.0.0
-
- com.example
- demo
- 1.0-SNAPSHOT
-
- demo
-
-
- UTF-8
- 11
- 11
-
-
-
-
- junit
- junit
- 4.11
- test
-
-
-
\ No newline at end of file
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/src/main/java/com/example/App.java b/demo/dockerfiles/demo-theia-monitor-vscode/project/java/src/main/java/com/example/App.java
deleted file mode 100644
index ccc602e25..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/src/main/java/com/example/App.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.example;
-
-public class App {
-
- private String name;
-
- public App(String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
-
- public static void main(String[] args) {
- App app = new App("Hello World!");
- System.out.println(app.getName());
- }
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/src/test/java/com/example/AppTest.java b/demo/dockerfiles/demo-theia-monitor-vscode/project/java/src/test/java/com/example/AppTest.java
deleted file mode 100644
index bca4bf744..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/java/src/test/java/com/example/AppTest.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.example;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-public class AppTest {
-
- @Test
- public void shouldAnswerWithTrue() {
- String name = "foo";
- App app = new App(name);
- assertEquals(name, app.getName());
- }
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/README.md b/demo/dockerfiles/demo-theia-monitor-vscode/project/web/README.md
deleted file mode 100644
index 92bda5246..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Web Example
-
-## Building
-
-Run `npm run build` in the `web` directory
-
-## Running
-
-Open `bin/index.html` and select `Open Preview to the Side` from the editor's toolbar
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/package-lock.json b/demo/dockerfiles/demo-theia-monitor-vscode/project/web/package-lock.json
deleted file mode 100644
index 2f218557a..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/package-lock.json
+++ /dev/null
@@ -1,469 +0,0 @@
-{
- "name": "web-example",
- "version": "0.0.1",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "web-example",
- "version": "0.0.1",
- "devDependencies": {
- "copyfiles": "^2.4.1",
- "rimraf": "^3.0.2",
- "typescript": "^5.3.3"
- }
- },
- "node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
- },
- "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==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/cliui": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
- "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
- "dev": true,
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^7.0.0"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true
- },
- "node_modules/copyfiles": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz",
- "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==",
- "dev": true,
- "dependencies": {
- "glob": "^7.0.5",
- "minimatch": "^3.0.3",
- "mkdirp": "^1.0.4",
- "noms": "0.0.0",
- "through2": "^2.0.1",
- "untildify": "^4.0.0",
- "yargs": "^16.1.0"
- },
- "bin": {
- "copyfiles": "copyfiles",
- "copyup": "copyfiles"
- }
- },
- "node_modules/core-util-is": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
- "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
- "dev": true
- },
- "node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
- },
- "node_modules/escalade": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
- "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true
- },
- "node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true,
- "engines": {
- "node": "6.* || 8.* || >= 10.*"
- }
- },
- "node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "dev": true,
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
- "dev": true,
- "dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/isarray": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
- "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
- "dev": true
- },
- "node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "dev": true,
- "bin": {
- "mkdirp": "bin/cmd.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/noms": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz",
- "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==",
- "dev": true,
- "dependencies": {
- "inherits": "^2.0.1",
- "readable-stream": "~1.0.31"
- }
- },
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "dev": true,
- "dependencies": {
- "wrappy": "1"
- }
- },
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
- "dev": true
- },
- "node_modules/readable-stream": {
- "version": "1.0.34",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
- "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==",
- "dev": true,
- "dependencies": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.1",
- "isarray": "0.0.1",
- "string_decoder": "~0.10.x"
- }
- },
- "node_modules/require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
- "dev": true,
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "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==",
- "dev": true
- },
- "node_modules/string_decoder": {
- "version": "0.10.31",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
- "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
- "dev": true
- },
- "node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "dev": true,
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/through2/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
- "dev": true
- },
- "node_modules/through2/node_modules/readable-stream": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
- "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
- "dev": true,
- "dependencies": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "node_modules/through2/node_modules/string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "dev": true,
- "dependencies": {
- "safe-buffer": "~5.1.0"
- }
- },
- "node_modules/typescript": {
- "version": "5.5.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
- "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
- "dev": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/untildify": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
- "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "dev": true
- },
- "node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "dev": true
- },
- "node_modules/xtend": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
- "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
- "dev": true,
- "engines": {
- "node": ">=0.4"
- }
- },
- "node_modules/y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
- "dev": true,
- "dependencies": {
- "cliui": "^7.0.2",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.0",
- "y18n": "^5.0.5",
- "yargs-parser": "^20.2.2"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yargs-parser": {
- "version": "20.2.9",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
- "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
- "dev": true,
- "engines": {
- "node": ">=10"
- }
- }
- }
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/package.json b/demo/dockerfiles/demo-theia-monitor-vscode/project/web/package.json
deleted file mode 100644
index b51adf165..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/package.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "name": "web-example",
- "version": "0.0.1",
- "scripts": {
- "build": "rimraf bin && tsc && copyfiles -u 1 \"src/**/*.js\" \"src/**/*.css\" \"src/**/*.html\" bin"
- },
- "devDependencies": {
- "copyfiles": "^2.4.1",
- "rimraf": "^3.0.2",
- "typescript": "^5.3.3"
- }
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/src/contentgenerator.ts b/demo/dockerfiles/demo-theia-monitor-vscode/project/web/src/contentgenerator.ts
deleted file mode 100644
index 87c297600..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/src/contentgenerator.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-interface Config {
- name: string;
- time: Date;
-}
-
-function generateContents(config: Config) {
- return (
- "Hello " +
- config.name +
- "! It's " +
- config.time.getHours() +
- " o'clock and " +
- config.time.getMinutes() +
- " minutes."
- );
-}
-
-var config: Config = {
- name: "Sasha",
- time: new Date(),
-};
-
-document.body.innerHTML = generateContents(config);
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/src/index.html b/demo/dockerfiles/demo-theia-monitor-vscode/project/web/src/index.html
deleted file mode 100644
index ea996bdde..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/src/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
- Theia Cloud
-
-
-
-
-
-
-
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/src/styles.css b/demo/dockerfiles/demo-theia-monitor-vscode/project/web/src/styles.css
deleted file mode 100644
index 1ffaa593f..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/src/styles.css
+++ /dev/null
@@ -1,16 +0,0 @@
-body {
- background: rgb(255, 218, 218);
- background: linear-gradient(
- 90deg,
- rgba(255, 218, 218, 1) 0%,
- rgba(255, 218, 218, 1) 3%,
- rgba(215, 248, 255, 1) 100%
- );
-}
-
-html {
- font-family: Arial, Helvetica, sans-serif;
- text-align: center;
- font-size: large;
- color: darkslategray;
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/src/updatecontents.js b/demo/dockerfiles/demo-theia-monitor-vscode/project/web/src/updatecontents.js
deleted file mode 100644
index 788176406..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/src/updatecontents.js
+++ /dev/null
@@ -1,3 +0,0 @@
-setTimeout(function () {
- document.body.innerHTML = "Bye bye!";
-}, 10000);
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/tsconfig.json b/demo/dockerfiles/demo-theia-monitor-vscode/project/web/tsconfig.json
deleted file mode 100644
index f181a4d22..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/project/web/tsconfig.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "compilerOptions": {
- "skipLibCheck": true,
- "declaration": true,
- "declarationMap": true,
- "noImplicitAny": true,
- "noEmitOnError": false,
- "noImplicitThis": true,
- "noUnusedLocals": true,
- "strictNullChecks": true,
- "experimentalDecorators": true,
- "emitDecoratorMetadata": true,
- "downlevelIteration": true,
- "resolveJsonModule": true,
- "module": "commonjs",
- "outDir": "bin",
- "moduleResolution": "node",
- "target": "ES2017",
- "jsx": "react",
- "lib": ["ES2017", "dom"],
- "sourceMap": true
- },
- "include": ["src/**/*"],
- "exclude": ["node_modules", "**/*.spec.ts"]
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/settings.json b/demo/dockerfiles/demo-theia-monitor-vscode/settings.json
deleted file mode 100644
index 24cd36cc8..000000000
--- a/demo/dockerfiles/demo-theia-monitor-vscode/settings.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "java.project.importOnFirstTimeStartup": "automatic"
-}
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/theia-cloud-monitor-1.1.0-next.vsix b/demo/dockerfiles/demo-theia-monitor-vscode/theia-cloud-monitor-1.1.0-next.vsix
new file mode 100644
index 000000000..80f934a77
Binary files /dev/null and b/demo/dockerfiles/demo-theia-monitor-vscode/theia-cloud-monitor-1.1.0-next.vsix differ
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/theia-cloud-monitor-1.1.2.vsix b/demo/dockerfiles/demo-theia-monitor-vscode/theia-cloud-monitor-1.1.2.vsix
new file mode 100644
index 000000000..8dea4d542
Binary files /dev/null and b/demo/dockerfiles/demo-theia-monitor-vscode/theia-cloud-monitor-1.1.2.vsix differ
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/theia-cloud-monitor-1.2.0-next.vsix b/demo/dockerfiles/demo-theia-monitor-vscode/theia-cloud-monitor-1.2.0-next.vsix
new file mode 100644
index 000000000..a9be16cb2
Binary files /dev/null and b/demo/dockerfiles/demo-theia-monitor-vscode/theia-cloud-monitor-1.2.0-next.vsix differ
diff --git a/demo/dockerfiles/demo-theia-monitor-vscode/theiacloud-monitor-0.12.0-next.vsix b/demo/dockerfiles/demo-theia-monitor-vscode/theiacloud-monitor-0.12.0-next.vsix
deleted file mode 100644
index 579df495f..000000000
Binary files a/demo/dockerfiles/demo-theia-monitor-vscode/theiacloud-monitor-0.12.0-next.vsix and /dev/null differ
diff --git a/dockerfiles/conversion-webhook/Dockerfile b/dockerfiles/conversion-webhook/Dockerfile
index afe835406..6a2fcebee 100644
--- a/dockerfiles/conversion-webhook/Dockerfile
+++ b/dockerfiles/conversion-webhook/Dockerfile
@@ -1,18 +1,33 @@
-FROM eclipse-temurin:17-jdk AS builder
+FROM eclipse-temurin:21-jdk AS builder
+# Accept APT_HTTP_PROXY build-arg for caching
+ARG APT_HTTP_PROXY
+# Configure apt proxy if provided (hybrid: works on ARC and GitHub runners)
+RUN if [ -n "$APT_HTTP_PROXY" ]; then \
+ echo "Acquire::http::Proxy \"$APT_HTTP_PROXY\";" > /etc/apt/apt.conf.d/01proxy; \
+ # Force HTTP repositories when using proxy (Noble defaults to HTTPS which apt-cacher-ng cannot cache)
+ sed -i 's|https://|http://|g' /etc/apt/sources.list.d/*.sources 2>/dev/null || true; \
+ sed -i 's|https://|http://|g' /etc/apt/sources.list 2>/dev/null || true; \
+ echo "Using APT Proxy: $APT_HTTP_PROXY with HTTP repositories"; \
+ else \
+ echo "No APT Proxy set, using direct connection"; \
+ fi
RUN apt-get update && apt-get install -y maven
WORKDIR /conversion
COPY java/common ./common
COPY java/conversion ./conversion
-RUN cd /conversion/common/maven-conf && \
+RUN --mount=type=cache,target=/root/.m2 \
+ cd /conversion/common/maven-conf && \
mvn clean install --no-transfer-progress && \
cd /conversion/common/org.eclipse.theia.cloud.common && \
- mvn clean install --no-transfer-progress&& \
+ mvn clean install --no-transfer-progress && \
cd /conversion/conversion/org.eclipse.theia.cloud.conversion && \
mvn clean package -Dmaven.test.skip=true -Dquarkus.package.type=uber-jar --no-transfer-progress
-FROM eclipse-temurin:17-jre-alpine
+FROM eclipse-temurin:21-jre-alpine
WORKDIR /conversion
-COPY --from=builder /conversion/conversion/org.eclipse.theia.cloud.conversion/target/conversion-webhook-0.12.0-SNAPSHOT-runner.jar .
+COPY --from=builder /conversion/conversion/org.eclipse.theia.cloud.conversion/target/conversion-webhook-1.2.0-SNAPSHOT-runner.jar .
+
+ENV CERT_RELOAD_PERIOD=604800
-ENTRYPOINT java -jar ./conversion-webhook-0.12.0-SNAPSHOT-runner.jar
-CMD [ "" ]
\ No newline at end of file
+ENTRYPOINT ["java", "-Dquarkus.http.ssl.certificate.reload-period=${CERT_RELOAD_PERIOD}", "-jar", "./conversion-webhook-1.2.0-SNAPSHOT-runner.jar"]
+CMD [ "" ]
diff --git a/dockerfiles/landing-page/.project b/dockerfiles/landing-page/.project
deleted file mode 100644
index 163ddcb4f..000000000
--- a/dockerfiles/landing-page/.project
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- landing-page-docker
-
-
-
-
-
-
-
-
diff --git a/dockerfiles/landing-page/Dockerfile b/dockerfiles/landing-page/Dockerfile
deleted file mode 100644
index 004ba1dcb..000000000
--- a/dockerfiles/landing-page/Dockerfile
+++ /dev/null
@@ -1,21 +0,0 @@
-# This docker file build uses the local common package
-
-# build stage
-FROM node:20-alpine as build-stage
-WORKDIR /app
-COPY node/common/src ./common/src
-COPY node/common/package.json node/common/tsconfig*.json ./common/
-COPY node/configs ./configs/
-COPY node/tsconfig.json node/package*.json ./
-COPY node/landing-page/. ./landing-page/
-
-RUN npm ci && \
- npm run build -w common && \
- npm run build -w landing-page && \
- chmod 644 /app/landing-page/dist/favicon.ico
-
-# production stage
-FROM nginx:stable-alpine as production-stage
-COPY --from=build-stage /app/landing-page/dist /usr/share/nginx/html
-EXPOSE 80
-CMD ["nginx", "-g", "daemon off;"]
diff --git a/dockerfiles/operator/Dockerfile b/dockerfiles/operator/Dockerfile
index 56f6fdb9b..5190e4a3f 100644
--- a/dockerfiles/operator/Dockerfile
+++ b/dockerfiles/operator/Dockerfile
@@ -1,23 +1,75 @@
-FROM eclipse-temurin:17-jdk AS builder
+FROM eclipse-temurin:21-jdk AS builder
+# Accept APT_HTTP_PROXY build-arg for caching
+ARG APT_HTTP_PROXY
+# Configure apt proxy if provided (hybrid: works on ARC and GitHub runners)
+RUN if [ -n "$APT_HTTP_PROXY" ]; then \
+ echo "Acquire::http::Proxy \"$APT_HTTP_PROXY\";" > /etc/apt/apt.conf.d/01proxy; \
+ # Force HTTP repositories when using proxy (Noble defaults to HTTPS which apt-cacher-ng cannot cache)
+ sed -i 's|https://|http://|g' /etc/apt/sources.list.d/*.sources 2>/dev/null || true; \
+ sed -i 's|https://|http://|g' /etc/apt/sources.list 2>/dev/null || true; \
+ echo "Using APT Proxy: $APT_HTTP_PROXY with HTTP repositories"; \
+ else \
+ echo "No APT Proxy set, using direct connection"; \
+ fi
RUN apt-get update && apt-get install -y maven
WORKDIR /operator
COPY java/common ./common
COPY java/operator ./operator
-RUN cd /operator/common/maven-conf && \
- mvn clean install --no-transfer-progress && \
+RUN --mount=type=secret,id=SENTRY_AUTH_TOKEN \
+ --mount=type=cache,target=/root/.m2 \
+ SENTRY_AUTH_TOKEN_VALUE="$(cat /run/secrets/SENTRY_AUTH_TOKEN 2>/dev/null || true)" && \
+ MAVEN_SENTRY_ARGS="" && \
+ if [ -n "$SENTRY_AUTH_TOKEN_VALUE" ]; then \
+ export SENTRY_AUTH_TOKEN="$SENTRY_AUTH_TOKEN_VALUE" && \
+ MAVEN_SENTRY_ARGS="-Dsentry=true" && \
+ echo "Sentry build integration enabled"; \
+ else \
+ echo "No SENTRY_AUTH_TOKEN provided, skipping Sentry build integration"; \
+ fi && \
+ cd /operator/common/maven-conf && \
+ mvn clean install --no-transfer-progress $MAVEN_SENTRY_ARGS && \
cd /operator/common/org.eclipse.theia.cloud.common && \
- mvn clean install --no-transfer-progress && \
+ mvn clean install --no-transfer-progress $MAVEN_SENTRY_ARGS && \
cd /operator/operator/org.eclipse.theia.cloud.operator && \
- mvn clean install --no-transfer-progress && \
+ mvn clean install --no-transfer-progress $MAVEN_SENTRY_ARGS && \
cd /operator/operator/org.eclipse.theia.cloud.defaultoperator && \
- mvn clean verify --no-transfer-progress
+ mvn clean verify --no-transfer-progress $MAVEN_SENTRY_ARGS
-FROM eclipse-temurin:17-jre-alpine
+FROM eclipse-temurin:21-jre-alpine
+RUN apk add --no-cache curl unzip
RUN mkdir /templates
WORKDIR /log-config
COPY java/operator/org.eclipse.theia.cloud.defaultoperator/log4j2.xml .
WORKDIR /operator
-COPY --from=builder /operator/operator/org.eclipse.theia.cloud.defaultoperator/target/defaultoperator-0.12.0-SNAPSHOT-jar-with-dependencies.jar .
+RUN curl -fsSL -o sentry-agent.zip https://github.com/getsentry/sentry-java/releases/download/8.29.0/sentry-opentelemetry-agent-8.29.0.zip && \
+ unzip -j sentry-agent.zip -d . && \
+ rm sentry-agent.zip
+COPY --from=builder /operator/operator/org.eclipse.theia.cloud.defaultoperator/target/defaultoperator-1.2.0-SNAPSHOT-jar-with-dependencies.jar .
+COPY --from=builder /operator/operator/org.eclipse.theia.cloud.defaultoperator/sentry.properties ./sentry.properties
+
+# If set to true, enables Sentry Java agent flags.
+ENV SENTRY_ENABLE=false
+# Default path for the baked-in Sentry properties file.
+# This can still be overridden with a mounted file or env-based config if needed.
+ENV SENTRY_PROPERTIES_FILE=/operator/sentry.properties
+
+RUN cat > /entrypoint.sh <<'EOF_ENTRYPOINT' && chmod +x /entrypoint.sh
+#!/bin/sh
+set -eu
+
+JAVA_OPTS=""
+if [ "${SENTRY_ENABLE:-false}" = "true" ]; then
+ JAVA_OPTS="$JAVA_OPTS -javaagent:/operator/sentry-opentelemetry-agent-8.29.0.jar"
+ if [ -n "${SENTRY_PROPERTIES_FILE:-}" ] && [ -f "${SENTRY_PROPERTIES_FILE}" ]; then
+ JAVA_OPTS="$JAVA_OPTS -Dsentry.properties.file=${SENTRY_PROPERTIES_FILE}"
+ fi
+fi
+
+exec java $JAVA_OPTS \
+ -Dlog4j2.configurationFile=/log-config/log4j2.xml \
+ -jar ./defaultoperator-1.2.0-SNAPSHOT-jar-with-dependencies.jar \
+ "$@"
+EOF_ENTRYPOINT
+
# to get more debug information from the kubernetes client itself, add -Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG below
-ENTRYPOINT [ "java", "-Dlog4j2.configurationFile=/log-config/log4j2.xml", "-jar", "./defaultoperator-0.12.0-SNAPSHOT-jar-with-dependencies.jar" ]
-CMD [ "" ]
+ENTRYPOINT ["/entrypoint.sh"]
diff --git a/dockerfiles/operator/Dockerfile.withcache b/dockerfiles/operator/Dockerfile.withcache
index df9323799..0c8249bb4 100644
--- a/dockerfiles/operator/Dockerfile.withcache
+++ b/dockerfiles/operator/Dockerfile.withcache
@@ -1,4 +1,4 @@
-FROM eclipse-temurin:17-jdk AS builder
+FROM eclipse-temurin:21-jdk AS builder
RUN apt-get update && apt-get install -y maven
WORKDIR /operator
COPY java/common ./common
@@ -13,12 +13,12 @@ RUN --mount=type=cache,target=/root/.m2 \
cd /operator/operator/org.eclipse.theia.cloud.defaultoperator && \
mvn clean verify
-FROM eclipse-temurin:17-jre-alpine
+FROM eclipse-temurin:21-jre-alpine
RUN mkdir /templates
WORKDIR /log-config
COPY java/operator/org.eclipse.theia.cloud.defaultoperator/log4j2.xml .
WORKDIR /operator
-COPY --from=builder /operator/operator/org.eclipse.theia.cloud.defaultoperator/target/defaultoperator-0.12.0-SNAPSHOT-jar-with-dependencies.jar .
+COPY --from=builder /operator/operator/org.eclipse.theia.cloud.defaultoperator/target/defaultoperator-1.2.0-SNAPSHOT-jar-with-dependencies.jar .
# to get more debug information from the kubernetes client itself, add -Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG below
-ENTRYPOINT [ "java", "-Dlog4j2.configurationFile=/log-config/log4j2.xml", "-jar", "./defaultoperator-0.12.0-SNAPSHOT-jar-with-dependencies.jar" ]
+ENTRYPOINT [ "java", "-Dlog4j2.configurationFile=/log-config/log4j2.xml", "-jar", "./defaultoperator-1.2.0-SNAPSHOT-jar-with-dependencies.jar" ]
CMD [ "" ]
diff --git a/dockerfiles/service/Dockerfile b/dockerfiles/service/Dockerfile
index b9f6c549a..b4530792d 100644
--- a/dockerfiles/service/Dockerfile
+++ b/dockerfiles/service/Dockerfile
@@ -1,33 +1,91 @@
-FROM eclipse-temurin:17-jdk AS builder
+FROM eclipse-temurin:21-jdk AS builder
+# Accept APT_HTTP_PROXY build-arg for caching
+ARG APT_HTTP_PROXY
+# Configure apt proxy if provided (hybrid: works on ARC and GitHub runners)
+RUN if [ -n "$APT_HTTP_PROXY" ]; then \
+ echo "Acquire::http::Proxy \"$APT_HTTP_PROXY\";" > /etc/apt/apt.conf.d/01proxy; \
+ # Force HTTP repositories when using proxy (Noble defaults to HTTPS which apt-cacher-ng cannot cache)
+ sed -i 's|https://|http://|g' /etc/apt/sources.list.d/*.sources 2>/dev/null || true; \
+ sed -i 's|https://|http://|g' /etc/apt/sources.list 2>/dev/null || true; \
+ echo "Using APT Proxy: $APT_HTTP_PROXY with HTTP repositories"; \
+ else \
+ echo "No APT Proxy set, using direct connection"; \
+ fi
RUN apt-get update && apt-get install -y maven
WORKDIR /service
COPY java/common ./common
COPY java/service ./service
-RUN cd /service/common/maven-conf && \
- mvn clean install --no-transfer-progress && \
+RUN --mount=type=secret,id=SENTRY_AUTH_TOKEN \
+ --mount=type=cache,target=/root/.m2 \
+ SENTRY_AUTH_TOKEN_VALUE="$(cat /run/secrets/SENTRY_AUTH_TOKEN 2>/dev/null || true)" && \
+ MAVEN_SENTRY_ARGS="" && \
+ if [ -n "$SENTRY_AUTH_TOKEN_VALUE" ]; then \
+ export SENTRY_AUTH_TOKEN="$SENTRY_AUTH_TOKEN_VALUE" && \
+ MAVEN_SENTRY_ARGS="-Dsentry=true" && \
+ echo "Sentry build integration enabled"; \
+ else \
+ echo "No SENTRY_AUTH_TOKEN provided, skipping Sentry build integration"; \
+ fi && \
+ cd /service/common/maven-conf && \
+ mvn clean install --no-transfer-progress $MAVEN_SENTRY_ARGS && \
cd /service/common/org.eclipse.theia.cloud.common && \
- mvn clean install --no-transfer-progress&& \
+ mvn clean install --no-transfer-progress $MAVEN_SENTRY_ARGS && \
cd /service/service/org.eclipse.theia.cloud.service && \
- mvn clean package -Dmaven.test.skip=true -Dquarkus.package.type=uber-jar --no-transfer-progress
+ mvn clean package -Dmaven.test.skip=true -Dquarkus.package.type=uber-jar --no-transfer-progress $MAVEN_SENTRY_ARGS
-FROM eclipse-temurin:17-jre-alpine
+FROM eclipse-temurin:21-jre-alpine
+RUN apk add --no-cache curl unzip
WORKDIR /service
-COPY --from=builder /service/service/org.eclipse.theia.cloud.service/target/service-0.12.0-SNAPSHOT-runner.jar .
-ENV APPID default-app-id
-ENV SERVICE_PORT 8081
+RUN curl -fsSL -o sentry-agent.zip https://github.com/getsentry/sentry-java/releases/download/8.29.0/sentry-opentelemetry-agent-8.29.0.zip && \
+ unzip -j sentry-agent.zip -d . && \
+ rm sentry-agent.zip
+COPY --from=builder /service/service/org.eclipse.theia.cloud.service/target/service-1.2.0-SNAPSHOT-runner.jar .
+COPY --from=builder /service/service/org.eclipse.theia.cloud.service/sentry.properties ./sentry.properties
+ENV SERVICE_PORT=8081
-ENV KEYCLOAK_ENABLE true
+ENV APPID=default-app-id
+
+ENV KEYCLOAK_ENABLE=true
# Keycloak config
-ENV KEYCLOAK_SERVERURL https://keycloak.url/auth/realms/TheiaCloud
-ENV KEYCLOAK_CLIENTID theia-cloud
-ENV KEYCLOAK_CLIENTSECRET publicbutoauth2proxywantsasecret
-
-ENTRYPOINT java -Dtheia.cloud.app.id=${APPID} \
- -Dquarkus.http.port=${SERVICE_PORT} \
- -Dtheia.cloud.use.keycloak=${KEYCLOAK_ENABLE} \
- -Dquarkus.oidc.auth-server-url=${KEYCLOAK_SERVERURL} \
- -Dquarkus.oidc.client-id=${KEYCLOAK_CLIENTID} \
- -Dquarkus.oidc.credentials.secret=${KEYCLOAK_CLIENTSECRET} \
- -jar ./service-0.12.0-SNAPSHOT-runner.jar
-CMD [ "" ]
\ No newline at end of file
+ENV KEYCLOAK_SERVERURL=https://keycloak.url/auth/realms/TheiaCloud
+ENV KEYCLOAK_CLIENTID=theia-cloud
+ENV KEYCLOAK_CLIENTSECRET=publicbutoauth2proxywantsasecret
+ENV KEYCLOAK_ADMIN_GROUP=theia-cloud/admin
+ENV ADMIN_API_TOKEN=
+
+# If set to true, enables Sentry Java agent flags.
+ENV SENTRY_ENABLE=false
+# Default path for the baked-in Sentry properties file.
+# This can still be overridden with a mounted file or env-based config if needed.
+ENV SENTRY_PROPERTIES_FILE=/service/sentry.properties
+
+# Use SERVICE_AUTH_TOKEN if set, otherwise fall back to APPID for backwards compatibility
+RUN cat > /entrypoint.sh <<'EOF_ENTRYPOINT' && chmod +x /entrypoint.sh
+#!/bin/sh
+set -eu
+
+JAVA_OPTS=""
+if [ "${SENTRY_ENABLE:-false}" = "true" ]; then
+ JAVA_OPTS="$JAVA_OPTS -javaagent:/service/sentry-opentelemetry-agent-8.29.0.jar"
+ if [ -n "${SENTRY_PROPERTIES_FILE:-}" ] && [ -f "${SENTRY_PROPERTIES_FILE}" ]; then
+ JAVA_OPTS="$JAVA_OPTS -Dsentry.properties.file=${SENTRY_PROPERTIES_FILE}"
+ fi
+fi
+
+SERVICE_AUTH_TOKEN_EFFECTIVE="${SERVICE_AUTH_TOKEN:-${APPID}}"
+
+exec java $JAVA_OPTS \
+ -Dtheia.cloud.service.auth.token="${SERVICE_AUTH_TOKEN_EFFECTIVE}" \
+ -Dquarkus.http.port="${SERVICE_PORT}" \
+ -Dtheia.cloud.auth.admin.group="${KEYCLOAK_ADMIN_GROUP}" \
+ -Dtheia.cloud.admin.api.token="${ADMIN_API_TOKEN}" \
+ -Dtheia.cloud.use.keycloak="${KEYCLOAK_ENABLE}" \
+ -Dquarkus.oidc.auth-server-url="${KEYCLOAK_SERVERURL}" \
+ -Dquarkus.oidc.client-id="${KEYCLOAK_CLIENTID}" \
+ -Dquarkus.oidc.credentials.secret="${KEYCLOAK_CLIENTSECRET}" \
+ -jar ./service-1.2.0-SNAPSHOT-runner.jar \
+ "$@"
+EOF_ENTRYPOINT
+
+ENTRYPOINT ["/entrypoint.sh"]
diff --git a/dockerfiles/service/Dockerfile.withcache b/dockerfiles/service/Dockerfile.withcache
index e468153d8..ba30b17b7 100644
--- a/dockerfiles/service/Dockerfile.withcache
+++ b/dockerfiles/service/Dockerfile.withcache
@@ -1,4 +1,4 @@
-FROM eclipse-temurin:17-jdk AS builder
+FROM eclipse-temurin:21-jdk AS builder
RUN apt-get update && apt-get install -y maven
WORKDIR /service
COPY java/common ./common
@@ -11,24 +11,28 @@ RUN --mount=type=cache,target=/root/.m2 \
cd /service/service/org.eclipse.theia.cloud.service && \
mvn clean package -Dmaven.test.skip=true -Dquarkus.package.type=uber-jar
-FROM eclipse-temurin:17-jre-alpine
+FROM eclipse-temurin:21-jre-alpine
WORKDIR /service
-COPY --from=builder /service/service/org.eclipse.theia.cloud.service/target/service-0.12.0-SNAPSHOT-runner.jar .
-ENV APPID default-app-id
+COPY --from=builder /service/service/org.eclipse.theia.cloud.service/target/service-1.2.0-SNAPSHOT-runner.jar .
+
ENV SERVICE_PORT 8081
+ENV APPID default-app-id
ENV KEYCLOAK_ENABLE true
# Keycloak config
ENV KEYCLOAK_SERVERURL https://keycloak.url/auth/realms/TheiaCloud
ENV KEYCLOAK_CLIENTID theia-cloud
ENV KEYCLOAK_CLIENTSECRET publicbutoauth2proxywantsasecret
+ENV ADMIN_API_TOKEN ""
-ENTRYPOINT java -Dtheia.cloud.app.id=${APPID} \
+# Use SERVICE_AUTH_TOKEN if set, otherwise fall back to APPID for backwards compatibility
+ENTRYPOINT java -Dtheia.cloud.service.auth.token=${SERVICE_AUTH_TOKEN:-${APPID}} \
-Dquarkus.http.port=${SERVICE_PORT} \
+ -Dtheia.cloud.admin.api.token=${ADMIN_API_TOKEN} \
-Dtheia.cloud.use.keycloak=${KEYCLOAK_ENABLE} \
-Dquarkus.oidc.auth-server-url=${KEYCLOAK_SERVERURL} \
-Dquarkus.oidc.client-id=${KEYCLOAK_CLIENTID} \
-Dquarkus.oidc.credentials.secret=${KEYCLOAK_CLIENTSECRET} \
- -jar ./service-0.12.0-SNAPSHOT-runner.jar
-CMD [ "" ]
\ No newline at end of file
+ -jar ./service-1.2.0-SNAPSHOT-runner.jar
+CMD [ "" ]
diff --git a/dockerfiles/wondershaper/Dockerfile b/dockerfiles/wondershaper/Dockerfile
index e34e18b7c..edf2fe5ce 100644
--- a/dockerfiles/wondershaper/Dockerfile
+++ b/dockerfiles/wondershaper/Dockerfile
@@ -1,4 +1,16 @@
FROM debian:bookworm-slim
+# Accept APT_HTTP_PROXY build-arg for caching
+ARG APT_HTTP_PROXY
+# Configure apt proxy if provided (hybrid: works on ARC and GitHub runners)
+RUN if [ -n "$APT_HTTP_PROXY" ]; then \
+ echo "Acquire::http::Proxy \"$APT_HTTP_PROXY\";" > /etc/apt/apt.conf.d/01proxy; \
+ # Force HTTP repositories when using proxy (Bookworm uses HTTPS which apt-cacher-ng cannot cache)
+ sed -i 's|https://|http://|g' /etc/apt/sources.list.d/*.sources 2>/dev/null || true; \
+ sed -i 's|https://|http://|g' /etc/apt/sources.list 2>/dev/null || true; \
+ echo "Using APT Proxy: $APT_HTTP_PROXY with HTTP repositories"; \
+ else \
+ echo "No APT Proxy set, using direct connection"; \
+ fi
RUN apt-get update && apt-get install -y iproute2 wondershaper && \
apt-get clean
# kilobits per second
diff --git a/documentation/Architecture.md b/documentation/Architecture.md
index 12eb59266..8ee2dc1a8 100644
--- a/documentation/Architecture.md
+++ b/documentation/Architecture.md
@@ -2,7 +2,9 @@
The Theia Cloud Operator listens for changes to custom resources inside the cluster. With those custom resources clients may trigger the creation/deletion/handling of workspaces and sessions. The Operator is responsible for handling all things that are related to the Kubernetes-Resources for workspaces and sessions. All access is authenticated via an Authenticator.
-
+External systems (product UI or integration) launch sessions by calling the REST service. See [External Session Launch Integration](./ExternalIntegration.md) for the launch contract and how launch data reaches the session container.
+
+
## Operator Java Overview
diff --git a/documentation/Building.md b/documentation/Building.md
index 83cea270a..e91635a82 100644
--- a/documentation/Building.md
+++ b/documentation/Building.md
@@ -1,6 +1,6 @@
# Building
-Besides the Operator, REST-Service, and the landing page, Theia Cloud also provides a few simple Theia-based demo applications and an init-container running wondershaper which may enable limiting the network bandwidth of a running application.
+Besides the Operator and REST-Service, Theia Cloud also provides a few simple Theia-based demo applications and an init-container running wondershaper which may enable limiting the network bandwidth of a running application.
**Note:** all commands are from the root of the repository.
@@ -16,12 +16,6 @@ docker build --no-cache -t theia-cloud-service -f dockerfiles/service/Dockerfile
docker build --no-cache -t theia-cloud-operator -f dockerfiles/operator/Dockerfile .
```
-## Landing page
-
-```bash
-docker build -t theia-cloud-landing-page -f dockerfiles/landing-page/Dockerfile .
-```
-
## Wondershaper
```bash
diff --git a/documentation/ExternalIntegration.md b/documentation/ExternalIntegration.md
new file mode 100644
index 000000000..c6401dac0
--- /dev/null
+++ b/documentation/ExternalIntegration.md
@@ -0,0 +1,112 @@
+# External Session Launch Integration
+
+External systems (primarily the EduIDE landing page / Artemis) launch IDE sessions by calling the Quarkus service. This page documents the direct REST contract and how the launch data flows through to the session container. For the auto-generated endpoint and model reference, see [`./api/README.md`](./api/README.md); those files are generated from [`openapi.json`](./openapi.json) (see [`./OpenAPI.md`](./OpenAPI.md)) and must not be edited by hand.
+
+## The launch endpoint
+
+`POST /service` accepts a `LaunchRequest` and returns a plain-string session URL. It creates a workspace if required.
+
+`GET /service/{appId}` is an OPTIONAL health/readiness probe that returns a boolean. It is not a launch prerequisite.
+
+The following example first pings the service (optional) and then launches a session:
+
+```bash
+# Optional readiness probe. Returns "true" when the service is available.
+curl "https:///service/"
+
+# Launch a session. Blocks until the session URL is ready.
+curl -X POST "https:///service" \
+ -H "Content-Type: application/json" \
+ -H "Accept: text/plain" \
+ -d '{
+ "appId": "",
+ "user": "student@example.org",
+ "appDefinition": "eduide",
+ "workspaceName": "student-exercise-1",
+ "env": {
+ "fromMap": {
+ "THEIA": "true",
+ "GIT_URI": "https://artemis.example.org/git/course/exercise.git",
+ "GIT_USER": "student",
+ "GIT_MAIL": "student@example.org",
+ "ARTEMIS_TOKEN": "",
+ "ARTEMIS_URL": "https://artemis.example.org",
+ "TEMPLATE": "java-maven"
+ }
+ }
+ }'
+```
+
+The response body is the session URL as plain text.
+
+## LaunchRequest and EnvironmentVars payload
+
+`LaunchRequest` carries the following fields:
+
+| Field | Type | Notes |
+| --------------- | ----------------- | --------------------------------------------------------------------------- |
+| `appId` | string | The service auth token. Required. |
+| `user` | string | The user identification, usually the email address. |
+| `appDefinition` | string | The app to launch. |
+| `workspaceName` | string | Optional. Name of the workspace to mount or create. |
+| `label` | string | Optional. Human-readable workspace label. A default is generated if absent. |
+| `ephemeral` | boolean | Optional. If true, no workspace is created for the session. |
+| `timeout` | integer | Minutes to wait for session launch. Default is 3 minutes. |
+| `env` | `EnvironmentVars` | Environment variables to deliver to the session. |
+
+`EnvironmentVars` bundles three sources of environment variables:
+
+| Field | Type | Notes |
+| ---------------- | ------------------- | ------------------------------------------------------------------------- |
+| `fromMap` | map of key/value | Environment variables passed directly to the deployment. |
+| `fromConfigMaps` | list of names | Names of PRE-EXISTING in-cluster ConfigMaps to source variables from. |
+| `fromSecrets` | list of names | Names of PRE-EXISTING in-cluster Secrets to source variables from. |
+
+External callers outside the cluster pass values via `fromMap`. Prefer `fromSecrets` for sensitive values that are already held as Kubernetes Secrets in the cluster.
+
+The EduIDE env-var keys the landing page sends via `fromMap` are `THEIA`, `GIT_URI`, `GIT_USER`, `GIT_MAIL`, `ARTEMIS_TOKEN`, `ARTEMIS_URL`, and `TEMPLATE`. Example body:
+
+```json
+{
+ "appId": "",
+ "user": "student@example.org",
+ "appDefinition": "eduide",
+ "workspaceName": "student-exercise-1",
+ "env": {
+ "fromMap": {
+ "THEIA": "true",
+ "GIT_URI": "https://artemis.example.org/git/course/exercise.git",
+ "GIT_USER": "student",
+ "GIT_MAIL": "student@example.org",
+ "ARTEMIS_TOKEN": "",
+ "ARTEMIS_URL": "https://artemis.example.org",
+ "TEMPLATE": "java-maven"
+ }
+ }
+}
+```
+
+## End-to-end flow
+
+The service (`RootResource.launch`) validates the request, then `K8sUtil` writes a `Session` custom resource that carries the env map (`spec.envVars`, `spec.envVarsFromConfigMaps`, and `spec.envVarsFromSecrets`). The operator then delivers the variables to the running IDE in one of two ways:
+
+- Lazy path: the operator injects the variables directly into the IDE container's environment in the pod spec (`AddedHandlerUtil.addCustomEnvVarsToDeploymentFromSession`).
+- Eager / prewarmed path: the deployment already exists, so the operator collects the variables (`SessionEnvCollector`) and pushes them asynchronously over HTTP `POST /data` (`AsyncDataInjector`) to the data-bridge extension inside the container.
+
+The environment variables are opaque to EduIDE-Cloud itself. The session container image (Scorpio) consumes them to configure git, authenticate with Artemis, and clone `GIT_URI`.
+
+For the broader system context, see [`./Architecture.md`](./Architecture.md).
+
+## Authentication
+
+`appId` must equal the server's configured service auth token. It is validated in `BaseResource`, and a mismatch returns HTTP 470.
+
+OIDC (Keycloak) authentication runs via Quarkus. Send `Authorization: Bearer `. The `user` field defaults to the JWT `email` claim if omitted, and must match the token email if supplied (otherwise HTTP 403).
+
+In anonymous mode (Keycloak disabled), `user` is mandatory. A blank `user` returns HTTP 400.
+
+## Behaviors integrators must know
+
+- `POST /service` blocks until the session URL is ready (up to `timeout`, default 3 minutes). Set the client HTTP timeout above 3 minutes.
+- Omitting `workspaceName` (with `ephemeral: false`) creates a new persistent volume and a fresh clone on every call. Pass a stable `workspaceName` to reuse a persistent workspace.
+- `ephemeral: true` is rejected with HTTP 400 for app definitions that have workspace-mounting sidecars. Use a workspace-backed launch there.
diff --git a/documentation/Install.md b/documentation/Install.md
index 5c098c11f..eec746e6d 100644
--- a/documentation/Install.md
+++ b/documentation/Install.md
@@ -20,37 +20,42 @@ Please check fo
As of writing this guide the installation command looks like this:\
`kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml`
-### NginX Ingress Controller
+### Gateway API + Envoy Gateway (required)
-Follow the installation guide for your platform:\
-
+Theia Cloud routes traffic through Gateway API (`HTTPRoute`) and requires Envoy Gateway as controller.
-### Global certificate (Only when using paths)
+The operator configures `X-Forwarded-Uri` using Envoy runtime syntax (`%REQ(:PATH)%`) in HTTPRoute rules.
+With non-Envoy Gateway controllers this may be interpreted as a literal value and break path forwarding.
-If Theia Cloud is used with paths instead of subdomains, the global HTTPS certificate should exist and be configured as the NginX Ingress Controller's default certificate.
+Install Gateway API CRDs and Envoy Gateway first by following:
-You can either provide the default certificate yourself or use a dummy service to initially generate the certificate.
-In [global-certificate.yaml](./platforms/global-certificate.yaml), replace `example.com` with your own host and `letsencrypt-prod` with your certificate issuer of choice.
-Apply the configuration with `kubectl apply -f ./documentation/platforms/global-certificate.yaml`.
+- Gateway API:
+- Envoy Gateway:
-Configure the nginx controllers deployment to use the certificate `default-tls` in the default namespace as its default certificate.
-For this, add argument `"--default-ssl-certificate=default/default-tls"` to the container by patching the controller's deployment.
+After installation, verify the controller is available (default class name usually `envoy`):
```bash
-kubectl patch deploy ingress-nginx-controller --type='json' -n ingress-nginx \
--p '[{ "op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--default-ssl-certificate=default/default-tls" }]'
+kubectl get gatewayclass
+kubectl get gateway -A
```
+### Global certificate (only when using paths)
+
+If Theia Cloud is configured to use paths instead of subdomains, ensure the configured Gateway listeners cover your base host and use TLS certificates that match that host.
+
+When using the Helm chart-managed Gateway (`gateway.create=true`), listener + certificate references are rendered automatically from chart values.
+When using a shared external Gateway (`gateway.create=false`), configure matching listeners and TLS certificateRefs on that shared Gateway before deploying Theia Cloud.
+
## Install
For this demo we will use nip.io hostnames.
-Obtain the external IP address of the `ingress-nginx-controller`:
+Obtain the external IP address of your Envoy Gateway service:
```bash
$ kubectl get svc --all-namespaces
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
-ingress-nginx ingress-nginx-controller LoadBalancer 10.52.4.129 34.141.62.32 80:32507/TCP,443:32114/TCP 11m
+envoy-gateway-system envoy-gateway LoadBalancer 10.52.4.129 34.141.62.32 80:32507/TCP,443:32114/TCP 11m
```
Open `./terraform/values/valuesDemo.yaml` and update the host section to use `subdomain.34.141.62.32.nip.io`:
@@ -67,7 +72,7 @@ hosts:
### Add & update Theia Cloud helm repo
```bash
-helm repo add theia-cloud-remote https://github.eclipsesource.com/theia-cloud-helm
+helm repo add theia-cloud-remote https://eclipse-theia.github.io/theia-cloud-helm
helm repo update
```
@@ -76,9 +81,9 @@ helm repo update
```bash
helm install theia-cloud-base theia-cloud-remote/theia-cloud-base --set issuer.email=your-mail@example.com
-helm install theia-cloud-crds theia-cloud-remote/theia-cloud-crds --namespace theiacloud --create-namespace
+helm install theia-cloud-crds theia-cloud-remote/theia-cloud-crds --namespace theia-cloud --create-namespace
-helm install theia-cloud theia-cloud-remote/theia-cloud --namespace theiacloud
+helm install theia-cloud theia-cloud-remote/theia-cloud --namespace theia-cloud
```
### Install the current next version
@@ -88,31 +93,31 @@ Simply add the `--devel` flag:
```bash
helm install theia-cloud-base theia-cloud-remote/theia-cloud-base --set issuer.email=your-mail@example.com --devel
-helm install theia-cloud-crds theia-cloud-remote/theia-cloud-crds --namespace theiacloud --create-namespace --devel
+helm install theia-cloud-crds theia-cloud-remote/theia-cloud-crds --namespace theia-cloud --create-namespace --devel
-helm install theia-cloud theia-cloud-remote/theia-cloud --namespace theiacloud --devel
+helm install theia-cloud theia-cloud-remote/theia-cloud --namespace theia-cloud --devel
```
### Optional: switch to the newly created namespace
```bash
-kubectl config set-context --current --namespace=theiacloud
+kubectl config set-context --current --namespace=theia-cloud
```
### Trouble shooting
Recreate tls secret for sessions:
-`kubectl delete secret ws-cert-secret -n theiacloud`
+`kubectl delete secret ws-cert-secret -n theia-cloud`
## Check
```bash
-helm get manifest theia-cloud -n theiacloud
+helm get manifest theia-cloud -n theia-cloud
```
## Uninstall
```bash
-helm uninstall theia-cloud -n theiacloud
+helm uninstall theia-cloud -n theia-cloud
```
diff --git a/documentation/Naming.md b/documentation/Naming.md
new file mode 100644
index 000000000..f4418202a
--- /dev/null
+++ b/documentation/Naming.md
@@ -0,0 +1,48 @@
+# Naming Conventions
+
+The project name is **Theia Cloud** (with a space), which is a subproject of Eclipse Theia.
+
+Since various languages and use cases follow different naming conventions, we aim to align with these conventions as closely as possible.
+
+Below are guidelines for commonly used naming conventions specific to this project.
+
+## `theia.cloud`
+
+Use `theia.cloud` in hierarchical structures and identifiers. Examples include:
+
+- **Java package/jar names**: `org.eclipse.theia.cloud`
+- **Java system properties**: `-Dtheia.cloud.service.auth.token=yourServiceAuthToken`
+- **Kubernetes group names/metadata**: `theia.cloud`, `theia.cloud/v1beta9`, `sessions.theia.cloud`
+- **Theia command identifiers**: `theia.cloud.monitor.activity.report`
+
+## `TheiaCloud` / `theiaCloud`
+
+Use **PascalCase** or **camelCase** for Java/TypeScript class, method, and property names, such as `DefaultTheiaCloudOperatorLauncher` or `window.theiaCloudConfig`.
+
+This format is also appropriate in contexts where spaces are avoided for technical reasons, such as in **Keycloak Realm** names (e.g., `TheiaCloud`).
+
+## `theia-cloud`
+
+This format serves as a preferred technical abbreviation. Use it when no other conventions apply, including:
+
+- **NPM Package names**
+- **File names**
+- **k8s namespaces**
+
+## `THEIACLOUD`
+
+Use **all uppercase** with underscores for **environment variables**, such as `THEIACLOUD_SESSION_NAME`.
+
+We can't use `THEIA_CLOUD` because `THEIA_` env variables may get special treatment by Theia itself.
+
+## Theia Cloud
+
+In general documentation and free text, use the standard project name **Theia Cloud** (with a space). Avoid spaces in technical contexts.
+
+## `theia-cloud.io`
+
+For **Kubernetes labels**, use the project domain as a prefix to custom labels, such as `theia-cloud.io/workspace-name`.
+
+## Avoid `theiacloud` in Code
+
+Avoid using the project name without separators or spaces, especially without PascalCase or camelCase. This format is reserved for usernames, such as those on Docker or npm.
diff --git a/documentation/OpenAPI.md b/documentation/OpenAPI.md
index 35194223b..7af37e305 100644
--- a/documentation/OpenAPI.md
+++ b/documentation/OpenAPI.md
@@ -30,7 +30,9 @@ docker pull swaggerapi/swagger-ui
docker run -p 80:8080 swaggerapi/swagger-ui
```
-Then browse to and explore the spec from here:
+Then browse to and explore the spec from here:
+
+Or explore it from the locally started service: .
## Generate Typescript API
@@ -48,3 +50,24 @@ Use the `openapi-generator-cli` from the root of this repository:
```bash
openapi-generator-cli generate -g typescript-axios -i ./documentation/openapi.json -o node/common/src/client/ --additional-properties=supportsES6=true,typescriptThreePlus=true --skip-validate-spec
```
+
+## Generate API Documentation
+
+Documentation for the service API can be generated as markdown files.
+
+If not already done for generating the typescript API, install the `openapi-generator-cli`:
+
+```bash
+npm install @openapitools/openapi-generator-cli -g
+openapi-generator-cli version-manager set 7.8.0
+```
+
+Use the `openapi-generator-cli` from the root of this repository:
+
+```bash
+openapi-generator-cli generate -g markdown -i ./documentation/openapi.json -o ./documentation/api --skip-validate-spec
+```
+
+> [!TIP]
+> The Open API generator supports generating code and documentation in various languages.
+> Execute `openapi-generator-cli list` to see all of them.
diff --git a/documentation/api/Apis/AppDefinitionAdminResourceApi.md b/documentation/api/Apis/AppDefinitionAdminResourceApi.md
new file mode 100644
index 000000000..51637912b
--- /dev/null
+++ b/documentation/api/Apis/AppDefinitionAdminResourceApi.md
@@ -0,0 +1,37 @@
+# AppDefinitionAdminResourceApi
+
+All URIs are relative to *http://localhost*
+
+| Method | HTTP request | Description |
+|------------- | ------------- | -------------|
+| [**serviceAdminAppdefinitionAppDefinitionNamePatch**](AppDefinitionAdminResourceApi.md#serviceAdminAppdefinitionAppDefinitionNamePatch) | **PATCH** /service/admin/appdefinition/{appDefinitionName} | Updates an app definition |
+
+
+
+# **serviceAdminAppdefinitionAppDefinitionNamePatch**
+> AppDefinition serviceAdminAppdefinitionAppDefinitionNamePatch(appDefinitionName, AppDefinitionUpdateRequest)
+
+Updates an app definition
+
+ Updates an app definition's properties. Allowed properties to update are defined by AppDefinitionUpdateRequest.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **appDefinitionName** | **String**| The K8S resource name of the app definition to update. | [default to null] |
+| **AppDefinitionUpdateRequest** | [**AppDefinitionUpdateRequest**](../Models/AppDefinitionUpdateRequest.md)| | |
+
+### Return type
+
+[**AppDefinition**](../Models/AppDefinition.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+- **Content-Type**: application/json
+- **Accept**: application/json
+
diff --git a/documentation/api/Apis/AppDefinitionResourceApi.md b/documentation/api/Apis/AppDefinitionResourceApi.md
new file mode 100644
index 000000000..0b1a888d8
--- /dev/null
+++ b/documentation/api/Apis/AppDefinitionResourceApi.md
@@ -0,0 +1,36 @@
+# AppDefinitionResourceApi
+
+All URIs are relative to *http://localhost*
+
+| Method | HTTP request | Description |
+|------------- | ------------- | -------------|
+| [**serviceAppdefinitionAppIdGet**](AppDefinitionResourceApi.md#serviceAppdefinitionAppIdGet) | **GET** /service/appdefinition/{appId} | List app definitions |
+
+
+
+# **serviceAppdefinitionAppIdGet**
+> List serviceAppdefinitionAppIdGet(appId)
+
+List app definitions
+
+ List available app definitions.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **appId** | **String**| | [default to null] |
+
+### Return type
+
+[**List**](../Models/AppDefinitionSpec.md)
+
+### Authorization
+
+[SecurityScheme](../README.md#SecurityScheme)
+
+### HTTP request headers
+
+- **Content-Type**: Not defined
+- **Accept**: application/json
+
diff --git a/documentation/api/Apis/RootAdminResourceApi.md b/documentation/api/Apis/RootAdminResourceApi.md
new file mode 100644
index 000000000..867ce134f
--- /dev/null
+++ b/documentation/api/Apis/RootAdminResourceApi.md
@@ -0,0 +1,36 @@
+# RootAdminResourceApi
+
+All URIs are relative to *http://localhost*
+
+| Method | HTTP request | Description |
+|------------- | ------------- | -------------|
+| [**serviceAdminAppIdGet**](RootAdminResourceApi.md#serviceAdminAppIdGet) | **GET** /service/admin/{appId} | Admin Ping |
+
+
+
+# **serviceAdminAppIdGet**
+> Boolean serviceAdminAppIdGet(appId)
+
+Admin Ping
+
+ Replies with success if the service is available and the user an admin.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **appId** | **String**| | [default to null] |
+
+### Return type
+
+**Boolean**
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+- **Content-Type**: Not defined
+- **Accept**: text/plain
+
diff --git a/documentation/api/Apis/RootResourceApi.md b/documentation/api/Apis/RootResourceApi.md
new file mode 100644
index 000000000..e7736523d
--- /dev/null
+++ b/documentation/api/Apis/RootResourceApi.md
@@ -0,0 +1,64 @@
+# RootResourceApi
+
+All URIs are relative to *http://localhost*
+
+| Method | HTTP request | Description |
+|------------- | ------------- | -------------|
+| [**serviceAppIdGet**](RootResourceApi.md#serviceAppIdGet) | **GET** /service/{appId} | Ping |
+| [**servicePost**](RootResourceApi.md#servicePost) | **POST** /service | Launch Session |
+
+
+
+# **serviceAppIdGet**
+> Boolean serviceAppIdGet(appId)
+
+Ping
+
+ Replies if the service is available.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **appId** | **String**| | [default to null] |
+
+### Return type
+
+**Boolean**
+
+### Authorization
+
+[SecurityScheme](../README.md#SecurityScheme)
+
+### HTTP request headers
+
+- **Content-Type**: Not defined
+- **Accept**: text/plain
+
+
+# **servicePost**
+> String servicePost(LaunchRequest)
+
+Launch Session
+
+ Launches a session and creates a workspace if required. Responds with the URL of the launched session.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **LaunchRequest** | [**LaunchRequest**](../Models/LaunchRequest.md)| | |
+
+### Return type
+
+**String**
+
+### Authorization
+
+[SecurityScheme](../README.md#SecurityScheme)
+
+### HTTP request headers
+
+- **Content-Type**: application/json
+- **Accept**: text/plain
+
diff --git a/documentation/api/Apis/SessionResourceApi.md b/documentation/api/Apis/SessionResourceApi.md
new file mode 100644
index 000000000..95f34d642
--- /dev/null
+++ b/documentation/api/Apis/SessionResourceApi.md
@@ -0,0 +1,150 @@
+# SessionResourceApi
+
+All URIs are relative to *http://localhost*
+
+| Method | HTTP request | Description |
+|------------- | ------------- | -------------|
+| [**serviceSessionAppIdUserGet**](SessionResourceApi.md#serviceSessionAppIdUserGet) | **GET** /service/session/{appId}/{user} | List sessions |
+| [**serviceSessionDelete**](SessionResourceApi.md#serviceSessionDelete) | **DELETE** /service/session | Stop session |
+| [**serviceSessionPatch**](SessionResourceApi.md#serviceSessionPatch) | **PATCH** /service/session | Report session activity |
+| [**serviceSessionPerformanceAppIdSessionNameGet**](SessionResourceApi.md#serviceSessionPerformanceAppIdSessionNameGet) | **GET** /service/session/performance/{appId}/{sessionName} | Get performance metrics |
+| [**serviceSessionPost**](SessionResourceApi.md#serviceSessionPost) | **POST** /service/session | Start a new session |
+
+
+
+# **serviceSessionAppIdUserGet**
+> List serviceSessionAppIdUserGet(appId, user)
+
+List sessions
+
+ List sessions of a user.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **appId** | **String**| | [default to null] |
+| **user** | **String**| | [default to null] |
+
+### Return type
+
+[**List**](../Models/SessionSpec.md)
+
+### Authorization
+
+[SecurityScheme](../README.md#SecurityScheme)
+
+### HTTP request headers
+
+- **Content-Type**: Not defined
+- **Accept**: application/json
+
+
+# **serviceSessionDelete**
+> Boolean serviceSessionDelete(SessionStopRequest)
+
+Stop session
+
+ Stops a session.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **SessionStopRequest** | [**SessionStopRequest**](../Models/SessionStopRequest.md)| | |
+
+### Return type
+
+**Boolean**
+
+### Authorization
+
+[SecurityScheme](../README.md#SecurityScheme)
+
+### HTTP request headers
+
+- **Content-Type**: application/json
+- **Accept**: text/plain
+
+
+# **serviceSessionPatch**
+> Boolean serviceSessionPatch(SessionActivityRequest)
+
+Report session activity
+
+ Updates the last activity timestamp for a session to monitor activity.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **SessionActivityRequest** | [**SessionActivityRequest**](../Models/SessionActivityRequest.md)| | |
+
+### Return type
+
+**Boolean**
+
+### Authorization
+
+[SecurityScheme](../README.md#SecurityScheme)
+
+### HTTP request headers
+
+- **Content-Type**: application/json
+- **Accept**: text/plain
+
+
+# **serviceSessionPerformanceAppIdSessionNameGet**
+> SessionPerformance serviceSessionPerformanceAppIdSessionNameGet(appId, sessionName)
+
+Get performance metrics
+
+ Returns the current CPU and memory usage of the session's pod.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **appId** | **String**| | [default to null] |
+| **sessionName** | **String**| | [default to null] |
+
+### Return type
+
+[**SessionPerformance**](../Models/SessionPerformance.md)
+
+### Authorization
+
+[SecurityScheme](../README.md#SecurityScheme)
+
+### HTTP request headers
+
+- **Content-Type**: Not defined
+- **Accept**: application/json
+
+
+# **serviceSessionPost**
+> String serviceSessionPost(SessionStartRequest)
+
+Start a new session
+
+ Starts a new session for an existing workspace and responds with the URL of the started session.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **SessionStartRequest** | [**SessionStartRequest**](../Models/SessionStartRequest.md)| | |
+
+### Return type
+
+**String**
+
+### Authorization
+
+[SecurityScheme](../README.md#SecurityScheme)
+
+### HTTP request headers
+
+- **Content-Type**: application/json
+- **Accept**: text/plain
+
diff --git a/documentation/api/Apis/WorkspaceResourceApi.md b/documentation/api/Apis/WorkspaceResourceApi.md
new file mode 100644
index 000000000..e30a57aac
--- /dev/null
+++ b/documentation/api/Apis/WorkspaceResourceApi.md
@@ -0,0 +1,93 @@
+# WorkspaceResourceApi
+
+All URIs are relative to *http://localhost*
+
+| Method | HTTP request | Description |
+|------------- | ------------- | -------------|
+| [**serviceWorkspaceAppIdUserGet**](WorkspaceResourceApi.md#serviceWorkspaceAppIdUserGet) | **GET** /service/workspace/{appId}/{user} | List workspaces |
+| [**serviceWorkspaceDelete**](WorkspaceResourceApi.md#serviceWorkspaceDelete) | **DELETE** /service/workspace | Delete workspace |
+| [**serviceWorkspacePost**](WorkspaceResourceApi.md#serviceWorkspacePost) | **POST** /service/workspace | Create workspace |
+
+
+
+# **serviceWorkspaceAppIdUserGet**
+> List serviceWorkspaceAppIdUserGet(appId, user)
+
+List workspaces
+
+ Lists the workspaces of a user.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **appId** | **String**| | [default to null] |
+| **user** | **String**| | [default to null] |
+
+### Return type
+
+[**List**](../Models/UserWorkspace.md)
+
+### Authorization
+
+[SecurityScheme](../README.md#SecurityScheme)
+
+### HTTP request headers
+
+- **Content-Type**: Not defined
+- **Accept**: application/json
+
+
+# **serviceWorkspaceDelete**
+> Boolean serviceWorkspaceDelete(WorkspaceDeletionRequest)
+
+Delete workspace
+
+ Deletes a workspace.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **WorkspaceDeletionRequest** | [**WorkspaceDeletionRequest**](../Models/WorkspaceDeletionRequest.md)| | |
+
+### Return type
+
+**Boolean**
+
+### Authorization
+
+[SecurityScheme](../README.md#SecurityScheme)
+
+### HTTP request headers
+
+- **Content-Type**: application/json
+- **Accept**: text/plain
+
+
+# **serviceWorkspacePost**
+> UserWorkspace serviceWorkspacePost(WorkspaceCreationRequest)
+
+Create workspace
+
+ Creates a new workspace for a user.
+
+### Parameters
+
+|Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|
+| **WorkspaceCreationRequest** | [**WorkspaceCreationRequest**](../Models/WorkspaceCreationRequest.md)| | |
+
+### Return type
+
+[**UserWorkspace**](../Models/UserWorkspace.md)
+
+### Authorization
+
+[SecurityScheme](../README.md#SecurityScheme)
+
+### HTTP request headers
+
+- **Content-Type**: application/json
+- **Accept**: application/json
+
diff --git a/documentation/api/Models/ActivityTracker.md b/documentation/api/Models/ActivityTracker.md
new file mode 100644
index 000000000..1bb91339e
--- /dev/null
+++ b/documentation/api/Models/ActivityTracker.md
@@ -0,0 +1,10 @@
+# ActivityTracker
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **timeoutAfter** | **Integer** | | [optional] [default to null] |
+| **notifyAfter** | **Integer** | | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/AppDefinition.md b/documentation/api/Models/AppDefinition.md
new file mode 100644
index 000000000..65349c560
--- /dev/null
+++ b/documentation/api/Models/AppDefinition.md
@@ -0,0 +1,21 @@
+# AppDefinition
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **apiVersion** | **String** | | [optional] [default to null] |
+| **kind** | **String** | | [optional] [default to null] |
+| **metadata** | [**ObjectMeta**](ObjectMeta.md) | | [optional] [default to null] |
+| **spec** | [**AppDefinitionSpec**](AppDefinitionSpec.md) | | [optional] [default to null] |
+| **status** | [**AppDefinitionStatus**](AppDefinitionStatus.md) | | [optional] [default to null] |
+| **singular** | **String** | | [optional] [default to null] |
+| **crdName** | **String** | | [optional] [default to null] |
+| **scope** | **String** | | [optional] [default to null] |
+| **plural** | **String** | | [optional] [default to null] |
+| **served** | **Boolean** | | [optional] [default to null] |
+| **storage** | **Boolean** | | [optional] [default to null] |
+| **deprecated** | **Boolean** | | [optional] [default to null] |
+| **deprecationWarning** | **String** | | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/AppDefinitionListRequest.md b/documentation/api/Models/AppDefinitionListRequest.md
new file mode 100644
index 000000000..ecb02b0c1
--- /dev/null
+++ b/documentation/api/Models/AppDefinitionListRequest.md
@@ -0,0 +1,9 @@
+# AppDefinitionListRequest
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **appId** | **String** | The App Id of this Theia Cloud instance. Request without a matching Id will be denied. | [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/AppDefinitionSpec.md b/documentation/api/Models/AppDefinitionSpec.md
new file mode 100644
index 000000000..96f551d78
--- /dev/null
+++ b/documentation/api/Models/AppDefinitionSpec.md
@@ -0,0 +1,28 @@
+# AppDefinitionSpec
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **name** | **String** | | [optional] [default to null] |
+| **image** | **String** | | [optional] [default to null] |
+| **imagePullPolicy** | **String** | | [optional] [default to null] |
+| **pullSecret** | **String** | | [optional] [default to null] |
+| **uid** | **Integer** | | [optional] [default to null] |
+| **port** | **Integer** | | [optional] [default to null] |
+| **ingressname** | **String** | | [optional] [default to null] |
+| **minInstances** | **Integer** | | [optional] [default to null] |
+| **maxInstances** | **Integer** | | [optional] [default to null] |
+| **timeout** | **Integer** | | [optional] [default to null] |
+| **requestsMemory** | **String** | | [optional] [default to null] |
+| **requestsCpu** | **String** | | [optional] [default to null] |
+| **limitsMemory** | **String** | | [optional] [default to null] |
+| **limitsCpu** | **String** | | [optional] [default to null] |
+| **downlinkLimit** | **Integer** | | [optional] [default to null] |
+| **uplinkLimit** | **Integer** | | [optional] [default to null] |
+| **mountPath** | **String** | | [optional] [default to null] |
+| **monitor** | [**Monitor**](Monitor.md) | | [optional] [default to null] |
+| **options** | **Map** | | [optional] [default to null] |
+| **ingressHostnamePrefixes** | **List** | | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/AppDefinitionStatus.md b/documentation/api/Models/AppDefinitionStatus.md
new file mode 100644
index 000000000..ee1873466
--- /dev/null
+++ b/documentation/api/Models/AppDefinitionStatus.md
@@ -0,0 +1,10 @@
+# AppDefinitionStatus
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **operatorStatus** | **String** | | [optional] [default to null] |
+| **operatorMessage** | **String** | | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/AppDefinitionUpdateRequest.md b/documentation/api/Models/AppDefinitionUpdateRequest.md
new file mode 100644
index 000000000..960e157bd
--- /dev/null
+++ b/documentation/api/Models/AppDefinitionUpdateRequest.md
@@ -0,0 +1,11 @@
+# AppDefinitionUpdateRequest
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **appId** | **String** | The App Id of this Theia Cloud instance. Request without a matching Id will be denied. | [default to null] |
+| **minInstances** | **Integer** | The minimum number of instances to run. | [optional] [default to null] |
+| **maxInstances** | **Integer** | The maximum number of instances to run. | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/EnvironmentVars.md b/documentation/api/Models/EnvironmentVars.md
new file mode 100644
index 000000000..10f2a6810
--- /dev/null
+++ b/documentation/api/Models/EnvironmentVars.md
@@ -0,0 +1,11 @@
+# EnvironmentVars
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **fromMap** | **Map** | Map of environment variables to be passed to Deployment. Ignored if Theia applications are started eagerly. Empty by default. | [optional] [default to null] |
+| **fromConfigMaps** | **List** | List of ConfigMaps (by name) containing environment variables to be passed to Deployment as envFrom.configMapRef. Ignored if Theia applications are started eagerly. Empty by default. | [optional] [default to null] |
+| **fromSecrets** | **List** | List of Secrets (by name) containing environment variables to be passed to Deployment as envFrom.secretRef. Ignored if Theia applications are started eagerly. Empty by default. | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/LaunchRequest.md b/documentation/api/Models/LaunchRequest.md
new file mode 100644
index 000000000..650226e43
--- /dev/null
+++ b/documentation/api/Models/LaunchRequest.md
@@ -0,0 +1,16 @@
+# LaunchRequest
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **appId** | **String** | The App Id of this Theia Cloud instance. Request without a matching Id will be denied. | [default to null] |
+| **user** | **String** | The user identification, usually the email address. | [default to null] |
+| **appDefinition** | **String** | The app to launch. Needs to be set if a new or ephemeral session should be launched. For an existing workspace the last app definition will be used if none is given. | [optional] [default to null] |
+| **workspaceName** | **String** | The name of the workspace to mount/create. Needs to be set if an existing workspace should be launched. | [optional] [default to null] |
+| **label** | **String** | The label of the workspace to mount/create. If no label is given, a default label will be generated. | [optional] [default to null] |
+| **ephemeral** | **Boolean** | If true no workspace will be created for the session. | [optional] [default to null] |
+| **timeout** | **Integer** | Number of minutes to wait for session launch. Default is 3 Minutes. | [optional] [default to null] |
+| **env** | [**EnvironmentVars**](EnvironmentVars.md) | Environment variables | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/ManagedFieldsEntry.md b/documentation/api/Models/ManagedFieldsEntry.md
new file mode 100644
index 000000000..b82a31d14
--- /dev/null
+++ b/documentation/api/Models/ManagedFieldsEntry.md
@@ -0,0 +1,15 @@
+# ManagedFieldsEntry
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **apiVersion** | **String** | | [optional] [default to null] |
+| **fieldsType** | **String** | | [optional] [default to null] |
+| **fieldsV1** | [**Object**](.md) | | [optional] [default to null] |
+| **manager** | **String** | | [optional] [default to null] |
+| **operation** | **String** | | [optional] [default to null] |
+| **subresource** | **String** | | [optional] [default to null] |
+| **time** | **String** | | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/Monitor.md b/documentation/api/Models/Monitor.md
new file mode 100644
index 000000000..5aec98fab
--- /dev/null
+++ b/documentation/api/Models/Monitor.md
@@ -0,0 +1,10 @@
+# Monitor
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **port** | **Integer** | | [optional] [default to null] |
+| **activityTracker** | [**ActivityTracker**](ActivityTracker.md) | | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/ObjectMeta.md b/documentation/api/Models/ObjectMeta.md
new file mode 100644
index 000000000..7a744a143
--- /dev/null
+++ b/documentation/api/Models/ObjectMeta.md
@@ -0,0 +1,23 @@
+# ObjectMeta
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **annotations** | **Map** | | [optional] [default to null] |
+| **creationTimestamp** | **String** | | [optional] [default to null] |
+| **deletionGracePeriodSeconds** | **Long** | | [optional] [default to null] |
+| **deletionTimestamp** | **String** | | [optional] [default to null] |
+| **finalizers** | **List** | | [optional] [default to null] |
+| **generateName** | **String** | | [optional] [default to null] |
+| **generation** | **Long** | | [optional] [default to null] |
+| **labels** | **Map** | | [optional] [default to null] |
+| **managedFields** | [**List**](ManagedFieldsEntry.md) | | [optional] [default to null] |
+| **name** | **String** | | [optional] [default to null] |
+| **namespace** | **String** | | [optional] [default to null] |
+| **ownerReferences** | [**List**](OwnerReference.md) | | [optional] [default to null] |
+| **resourceVersion** | **String** | | [optional] [default to null] |
+| **selfLink** | **String** | | [optional] [default to null] |
+| **uid** | **String** | | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/OwnerReference.md b/documentation/api/Models/OwnerReference.md
new file mode 100644
index 000000000..3837f2cd4
--- /dev/null
+++ b/documentation/api/Models/OwnerReference.md
@@ -0,0 +1,14 @@
+# OwnerReference
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **apiVersion** | **String** | | [optional] [default to null] |
+| **kind** | **String** | | [optional] [default to null] |
+| **blockOwnerDeletion** | **Boolean** | | [optional] [default to null] |
+| **controller** | **Boolean** | | [optional] [default to null] |
+| **name** | **String** | | [optional] [default to null] |
+| **uid** | **String** | | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/PingRequest.md b/documentation/api/Models/PingRequest.md
new file mode 100644
index 000000000..747a68927
--- /dev/null
+++ b/documentation/api/Models/PingRequest.md
@@ -0,0 +1,9 @@
+# PingRequest
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **appId** | **String** | The App Id of this Theia Cloud instance. Request without a matching Id will be denied. | [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/SessionActivityRequest.md b/documentation/api/Models/SessionActivityRequest.md
new file mode 100644
index 000000000..ff357d548
--- /dev/null
+++ b/documentation/api/Models/SessionActivityRequest.md
@@ -0,0 +1,10 @@
+# SessionActivityRequest
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **appId** | **String** | The App Id of this Theia Cloud instance. Request without a matching Id will be denied. | [default to null] |
+| **sessionName** | **String** | The name of the session for which activity is reported. | [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/SessionListRequest.md b/documentation/api/Models/SessionListRequest.md
new file mode 100644
index 000000000..570cf84b1
--- /dev/null
+++ b/documentation/api/Models/SessionListRequest.md
@@ -0,0 +1,10 @@
+# SessionListRequest
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **appId** | **String** | The App Id of this Theia Cloud instance. Request without a matching Id will be denied. | [default to null] |
+| **user** | **String** | The user identification, usually the email address. | [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/SessionPerformance.md b/documentation/api/Models/SessionPerformance.md
new file mode 100644
index 000000000..f552aa781
--- /dev/null
+++ b/documentation/api/Models/SessionPerformance.md
@@ -0,0 +1,12 @@
+# SessionPerformance
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **cpuAmount** | **String** | Used CPU amount of the workspace | [default to null] |
+| **cpuFormat** | **String** | Used CPU format of the workspace | [default to null] |
+| **memoryAmount** | **String** | Used memory amount of the workspace | [default to null] |
+| **memoryFormat** | **String** | Used memory format of the workspace | [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/SessionPerformanceRequest.md b/documentation/api/Models/SessionPerformanceRequest.md
new file mode 100644
index 000000000..5b94e6473
--- /dev/null
+++ b/documentation/api/Models/SessionPerformanceRequest.md
@@ -0,0 +1,10 @@
+# SessionPerformanceRequest
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **appId** | **String** | The App Id of this Theia Cloud instance. Request without a matching Id will be denied. | [default to null] |
+| **sessionName** | **String** | The name of the session | [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/SessionSpec.md b/documentation/api/Models/SessionSpec.md
new file mode 100644
index 000000000..cee3e0416
--- /dev/null
+++ b/documentation/api/Models/SessionSpec.md
@@ -0,0 +1,17 @@
+# SessionSpec
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **name** | **String** | | [optional] [default to null] |
+| **appDefinition** | **String** | | [optional] [default to null] |
+| **user** | **String** | | [optional] [default to null] |
+| **workspace** | **String** | | [optional] [default to null] |
+| **sessionSecret** | **String** | | [optional] [default to null] |
+| **options** | **Map** | | [optional] [default to null] |
+| **envVars** | **Map** | | [optional] [default to null] |
+| **envVarsFromConfigMaps** | **List** | | [optional] [default to null] |
+| **envVarsFromSecrets** | **List** | | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/SessionStartRequest.md b/documentation/api/Models/SessionStartRequest.md
new file mode 100644
index 000000000..4e9d4dce8
--- /dev/null
+++ b/documentation/api/Models/SessionStartRequest.md
@@ -0,0 +1,14 @@
+# SessionStartRequest
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **appId** | **String** | The App Id of this Theia Cloud instance. Request without a matching Id will be denied. | [default to null] |
+| **user** | **String** | The user identification, usually the email address. | [default to null] |
+| **appDefinition** | **String** | The app to launch. | [default to null] |
+| **workspaceName** | **String** | The name of the workspace to mount/create. | [optional] [default to null] |
+| **timeout** | **Integer** | Number of minutes to wait for session launch. Default is 3 Minutes. | [optional] [default to null] |
+| **env** | [**EnvironmentVars**](EnvironmentVars.md) | Environment variables | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/SessionStopRequest.md b/documentation/api/Models/SessionStopRequest.md
new file mode 100644
index 000000000..c2b78d25f
--- /dev/null
+++ b/documentation/api/Models/SessionStopRequest.md
@@ -0,0 +1,11 @@
+# SessionStopRequest
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **appId** | **String** | The App Id of this Theia Cloud instance. Request without a matching Id will be denied. | [default to null] |
+| **user** | **String** | The user identification, usually the email address. | [default to null] |
+| **sessionName** | **String** | The name of the session to stop. | [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/UserWorkspace.md b/documentation/api/Models/UserWorkspace.md
new file mode 100644
index 000000000..cacae143d
--- /dev/null
+++ b/documentation/api/Models/UserWorkspace.md
@@ -0,0 +1,13 @@
+# UserWorkspace
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **name** | **String** | The name of the workspace | [default to null] |
+| **label** | **String** | The label of the workspace | [default to null] |
+| **appDefinition** | **String** | The app this workspace was used with. | [optional] [default to null] |
+| **user** | **String** | The user identification, usually the email address. | [default to null] |
+| **active** | **Boolean** | Whether the workspace is in use at the moment. | [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/WorkspaceCreationRequest.md b/documentation/api/Models/WorkspaceCreationRequest.md
new file mode 100644
index 000000000..91e6e4140
--- /dev/null
+++ b/documentation/api/Models/WorkspaceCreationRequest.md
@@ -0,0 +1,12 @@
+# WorkspaceCreationRequest
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **appId** | **String** | The App Id of this Theia Cloud instance. Request without a matching Id will be denied. | [default to null] |
+| **user** | **String** | The user identification, usually the email address. | [default to null] |
+| **appDefinition** | **String** | The app this workspace will be used with. | [optional] [default to null] |
+| **label** | **String** | The label of the workspace | [optional] [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/WorkspaceDeletionRequest.md b/documentation/api/Models/WorkspaceDeletionRequest.md
new file mode 100644
index 000000000..74f32c236
--- /dev/null
+++ b/documentation/api/Models/WorkspaceDeletionRequest.md
@@ -0,0 +1,11 @@
+# WorkspaceDeletionRequest
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **appId** | **String** | The App Id of this Theia Cloud instance. Request without a matching Id will be denied. | [default to null] |
+| **user** | **String** | The user identification, usually the email address. | [default to null] |
+| **workspaceName** | **String** | The name of the workspace to delete. | [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/Models/WorkspaceListRequest.md b/documentation/api/Models/WorkspaceListRequest.md
new file mode 100644
index 000000000..a90d69f0d
--- /dev/null
+++ b/documentation/api/Models/WorkspaceListRequest.md
@@ -0,0 +1,10 @@
+# WorkspaceListRequest
+## Properties
+
+| Name | Type | Description | Notes |
+|------------ | ------------- | ------------- | -------------|
+| **appId** | **String** | The App Id of this Theia Cloud instance. Request without a matching Id will be denied. | [default to null] |
+| **user** | **String** | The user identification, usually the email address. | [default to null] |
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
diff --git a/documentation/api/README.md b/documentation/api/README.md
new file mode 100644
index 000000000..74bc08977
--- /dev/null
+++ b/documentation/api/README.md
@@ -0,0 +1,64 @@
+# Documentation for Theia Cloud API
+
+
+## Documentation for API Endpoints
+
+All URIs are relative to *http://localhost*
+
+| Class | Method | HTTP request | Description |
+|------------ | ------------- | ------------- | -------------|
+| *AppDefinitionAdminResourceApi* | [**serviceAdminAppdefinitionAppDefinitionNamePatch**](Apis/AppDefinitionAdminResourceApi.md#serviceadminappdefinitionappdefinitionnamepatch) | **PATCH** /service/admin/appdefinition/{appDefinitionName} | Updates an app definition |
+| *AppDefinitionResourceApi* | [**serviceAppdefinitionAppIdGet**](Apis/AppDefinitionResourceApi.md#serviceappdefinitionappidget) | **GET** /service/appdefinition/{appId} | List app definitions |
+| *RootAdminResourceApi* | [**serviceAdminAppIdGet**](Apis/RootAdminResourceApi.md#serviceadminappidget) | **GET** /service/admin/{appId} | Admin Ping |
+| *RootResourceApi* | [**serviceAppIdGet**](Apis/RootResourceApi.md#serviceappidget) | **GET** /service/{appId} | Ping |
+*RootResourceApi* | [**servicePost**](Apis/RootResourceApi.md#servicepost) | **POST** /service | Launch Session |
+| *SessionResourceApi* | [**serviceSessionAppIdUserGet**](Apis/SessionResourceApi.md#servicesessionappiduserget) | **GET** /service/session/{appId}/{user} | List sessions |
+*SessionResourceApi* | [**serviceSessionDelete**](Apis/SessionResourceApi.md#servicesessiondelete) | **DELETE** /service/session | Stop session |
+*SessionResourceApi* | [**serviceSessionPatch**](Apis/SessionResourceApi.md#servicesessionpatch) | **PATCH** /service/session | Report session activity |
+*SessionResourceApi* | [**serviceSessionPerformanceAppIdSessionNameGet**](Apis/SessionResourceApi.md#servicesessionperformanceappidsessionnameget) | **GET** /service/session/performance/{appId}/{sessionName} | Get performance metrics |
+*SessionResourceApi* | [**serviceSessionPost**](Apis/SessionResourceApi.md#servicesessionpost) | **POST** /service/session | Start a new session |
+| *WorkspaceResourceApi* | [**serviceWorkspaceAppIdUserGet**](Apis/WorkspaceResourceApi.md#serviceworkspaceappiduserget) | **GET** /service/workspace/{appId}/{user} | List workspaces |
+*WorkspaceResourceApi* | [**serviceWorkspaceDelete**](Apis/WorkspaceResourceApi.md#serviceworkspacedelete) | **DELETE** /service/workspace | Delete workspace |
+*WorkspaceResourceApi* | [**serviceWorkspacePost**](Apis/WorkspaceResourceApi.md#serviceworkspacepost) | **POST** /service/workspace | Create workspace |
+
+
+
+## Documentation for Models
+
+ - [ActivityTracker](./Models/ActivityTracker.md)
+ - [AppDefinition](./Models/AppDefinition.md)
+ - [AppDefinitionListRequest](./Models/AppDefinitionListRequest.md)
+ - [AppDefinitionSpec](./Models/AppDefinitionSpec.md)
+ - [AppDefinitionStatus](./Models/AppDefinitionStatus.md)
+ - [AppDefinitionUpdateRequest](./Models/AppDefinitionUpdateRequest.md)
+ - [EnvironmentVars](./Models/EnvironmentVars.md)
+ - [LaunchRequest](./Models/LaunchRequest.md)
+ - [ManagedFieldsEntry](./Models/ManagedFieldsEntry.md)
+ - [Monitor](./Models/Monitor.md)
+ - [ObjectMeta](./Models/ObjectMeta.md)
+ - [OwnerReference](./Models/OwnerReference.md)
+ - [PingRequest](./Models/PingRequest.md)
+ - [SessionActivityRequest](./Models/SessionActivityRequest.md)
+ - [SessionListRequest](./Models/SessionListRequest.md)
+ - [SessionPerformance](./Models/SessionPerformance.md)
+ - [SessionPerformanceRequest](./Models/SessionPerformanceRequest.md)
+ - [SessionSpec](./Models/SessionSpec.md)
+ - [SessionStartRequest](./Models/SessionStartRequest.md)
+ - [SessionStopRequest](./Models/SessionStopRequest.md)
+ - [UserWorkspace](./Models/UserWorkspace.md)
+ - [WorkspaceCreationRequest](./Models/WorkspaceCreationRequest.md)
+ - [WorkspaceDeletionRequest](./Models/WorkspaceDeletionRequest.md)
+ - [WorkspaceListRequest](./Models/WorkspaceListRequest.md)
+
+
+
+## Documentation for Authorization
+
+
+### SecurityScheme
+
+- **Type**: OAuth
+- **Flow**: implicit
+- **Authorization URL**:
+- **Scopes**: N/A
+
diff --git a/documentation/openapi.json b/documentation/openapi.json
index f64d016ab..6f92f03fe 100644
--- a/documentation/openapi.json
+++ b/documentation/openapi.json
@@ -1,13 +1,687 @@
{
- "openapi": "3.0.3",
+ "openapi": "3.1.0",
+ "components": {
+ "schemas": {
+ "ActivityTracker": {
+ "type": "object",
+ "properties": {
+ "timeoutAfter": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "notifyAfter": {
+ "type": "integer",
+ "format": "int32"
+ }
+ }
+ },
+ "AppDefinition": {
+ "type": "object",
+ "properties": {
+ "apiVersion": {
+ "type": "string",
+ "readOnly": true
+ },
+ "kind": {
+ "type": "string",
+ "readOnly": true
+ },
+ "metadata": {
+ "$ref": "#/components/schemas/ObjectMeta"
+ },
+ "spec": {
+ "$ref": "#/components/schemas/AppDefinitionSpec"
+ },
+ "status": {
+ "$ref": "#/components/schemas/AppDefinitionStatus"
+ },
+ "singular": {
+ "type": "string",
+ "writeOnly": true
+ },
+ "crdName": {
+ "type": "string"
+ },
+ "scope": {
+ "type": "string",
+ "writeOnly": true
+ },
+ "plural": {
+ "type": "string",
+ "writeOnly": true
+ },
+ "served": {
+ "type": "boolean",
+ "writeOnly": true
+ },
+ "storage": {
+ "type": "boolean",
+ "writeOnly": true
+ },
+ "deprecated": {
+ "type": "boolean",
+ "writeOnly": true
+ },
+ "deprecationWarning": {
+ "type": "string",
+ "writeOnly": true
+ }
+ }
+ },
+ "AppDefinitionListRequest": {
+ "type": "object",
+ "required": ["appId"],
+ "description": "A request to list available app definitions.",
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ }
+ }
+ },
+ "AppDefinitionSpec": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "image": {
+ "type": "string"
+ },
+ "imagePullPolicy": {
+ "type": "string"
+ },
+ "pullSecret": {
+ "type": "string"
+ },
+ "uid": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "port": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "ingressname": {
+ "type": "string"
+ },
+ "minInstances": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "maxInstances": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "timeout": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "requestsMemory": {
+ "type": "string"
+ },
+ "requestsCpu": {
+ "type": "string"
+ },
+ "limitsMemory": {
+ "type": "string"
+ },
+ "limitsCpu": {
+ "type": "string"
+ },
+ "downlinkLimit": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "uplinkLimit": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "mountPath": {
+ "type": "string"
+ },
+ "monitor": {
+ "$ref": "#/components/schemas/Monitor"
+ },
+ "options": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "ingressHostnamePrefixes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "AppDefinitionStatus": {
+ "type": "object",
+ "properties": {
+ "operatorStatus": {
+ "type": "string"
+ },
+ "operatorMessage": {
+ "type": "string"
+ }
+ }
+ },
+ "AppDefinitionUpdateRequest": {
+ "type": "object",
+ "required": ["appId"],
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ },
+ "minInstances": {
+ "type": "integer",
+ "format": "int32",
+ "description": "The minimum number of instances to run."
+ },
+ "maxInstances": {
+ "type": "integer",
+ "format": "int32",
+ "description": "The maximum number of instances to run."
+ }
+ }
+ },
+ "EnvironmentVars": {
+ "type": "object",
+ "description": "An object to hold all the ways environment variables can be passed. Not to be used by itself.",
+ "properties": {
+ "fromMap": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Map of environment variables to be passed to Deployment. Ignored if Theia applications are started eagerly. Empty by default."
+ },
+ "fromConfigMaps": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "List of ConfigMaps (by name) containing environment variables to be passed to Deployment as envFrom.configMapRef. Ignored if Theia applications are started eagerly. Empty by default."
+ },
+ "fromSecrets": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "List of Secrets (by name) containing environment variables to be passed to Deployment as envFrom.secretRef. Ignored if Theia applications are started eagerly. Empty by default."
+ }
+ }
+ },
+ "FieldsV1": {
+ "type": "object"
+ },
+ "LaunchRequest": {
+ "type": "object",
+ "required": ["appId", "user"],
+ "description": "A request to launch a new session.",
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ },
+ "user": {
+ "type": "string",
+ "description": "The user identification, usually the email address."
+ },
+ "appDefinition": {
+ "type": "string",
+ "description": "The app to launch. Needs to be set if a new or ephemeral session should be launched. For an existing workspace the last app definition will be used if none is given."
+ },
+ "workspaceName": {
+ "type": "string",
+ "description": "The name of the workspace to mount/create. Needs to be set if an existing workspace should be launched."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label of the workspace to mount/create. If no label is given, a default label will be generated."
+ },
+ "ephemeral": {
+ "type": "boolean",
+ "description": "If true no workspace will be created for the session."
+ },
+ "timeout": {
+ "type": "integer",
+ "format": "int32",
+ "description": "Number of minutes to wait for session launch. Default is 3 Minutes."
+ },
+ "env": {
+ "$ref": "#/components/schemas/EnvironmentVars",
+ "type": "object",
+ "description": "Environment variables"
+ }
+ }
+ },
+ "ManagedFieldsEntry": {
+ "type": "object",
+ "properties": {
+ "apiVersion": {
+ "type": "string"
+ },
+ "fieldsType": {
+ "type": "string"
+ },
+ "fieldsV1": {
+ "$ref": "#/components/schemas/FieldsV1"
+ },
+ "manager": {
+ "type": "string"
+ },
+ "operation": {
+ "type": "string"
+ },
+ "subresource": {
+ "type": "string"
+ },
+ "time": {
+ "type": "string"
+ }
+ }
+ },
+ "Monitor": {
+ "type": "object",
+ "properties": {
+ "port": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "activityTracker": {
+ "$ref": "#/components/schemas/ActivityTracker"
+ }
+ }
+ },
+ "ObjectMeta": {
+ "type": "object",
+ "properties": {
+ "annotations": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "creationTimestamp": {
+ "type": "string"
+ },
+ "deletionGracePeriodSeconds": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "deletionTimestamp": {
+ "type": "string"
+ },
+ "finalizers": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "generateName": {
+ "type": "string"
+ },
+ "generation": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "labels": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "managedFields": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ManagedFieldsEntry"
+ }
+ },
+ "name": {
+ "type": "string"
+ },
+ "namespace": {
+ "type": "string"
+ },
+ "ownerReferences": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OwnerReference"
+ }
+ },
+ "resourceVersion": {
+ "type": "string"
+ },
+ "selfLink": {
+ "type": "string"
+ },
+ "uid": {
+ "type": "string"
+ }
+ }
+ },
+ "OwnerReference": {
+ "type": "object",
+ "properties": {
+ "apiVersion": {
+ "type": "string"
+ },
+ "kind": {
+ "type": "string"
+ },
+ "blockOwnerDeletion": {
+ "type": "boolean"
+ },
+ "controller": {
+ "type": "boolean"
+ },
+ "name": {
+ "type": "string"
+ },
+ "uid": {
+ "type": "string"
+ }
+ }
+ },
+ "PingRequest": {
+ "type": "object",
+ "required": ["appId"],
+ "description": "Request to ping the availability of the service.",
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ }
+ }
+ },
+ "SessionActivityRequest": {
+ "type": "object",
+ "required": ["appId", "sessionName"],
+ "description": "A request to report activity for a running session.",
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ },
+ "sessionName": {
+ "type": "string",
+ "description": "The name of the session for which activity is reported."
+ }
+ }
+ },
+ "SessionListRequest": {
+ "type": "object",
+ "required": ["appId", "user"],
+ "description": "A request to list the sessions of a user.",
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ },
+ "user": {
+ "type": "string",
+ "description": "The user identification, usually the email address."
+ }
+ }
+ },
+ "SessionPerformance": {
+ "type": "object",
+ "required": ["cpuAmount", "cpuFormat", "memoryAmount", "memoryFormat"],
+ "description": "Description of the performance of a session",
+ "properties": {
+ "cpuAmount": {
+ "type": "string",
+ "description": "Used CPU amount of the workspace"
+ },
+ "cpuFormat": {
+ "type": "string",
+ "description": "Used CPU format of the workspace"
+ },
+ "memoryAmount": {
+ "type": "string",
+ "description": "Used memory amount of the workspace"
+ },
+ "memoryFormat": {
+ "type": "string",
+ "description": "Used memory format of the workspace"
+ }
+ }
+ },
+ "SessionPerformanceRequest": {
+ "type": "object",
+ "required": ["appId", "sessionName"],
+ "description": "A request to list the sessions of a user.",
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ },
+ "sessionName": {
+ "type": "string",
+ "description": "The name of the session"
+ }
+ }
+ },
+ "SessionSetConfigValueRequest": {
+ "type": "object",
+ "required": ["appId", "key", "value"],
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ },
+ "key": {
+ "type": "string",
+ "description": "The configuration key"
+ },
+ "value": {
+ "type": ["string", "null"],
+ "description": "The configuration value"
+ }
+ }
+ },
+ "SessionSpec": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "appDefinition": {
+ "type": "string"
+ },
+ "user": {
+ "type": "string"
+ },
+ "workspace": {
+ "type": "string"
+ },
+ "sessionSecret": {
+ "type": "string"
+ },
+ "options": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "envVars": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "envVarsFromConfigMaps": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "envVarsFromSecrets": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "SessionStartRequest": {
+ "type": "object",
+ "required": ["appId", "user", "appDefinition"],
+ "description": "A request to start a session",
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ },
+ "user": {
+ "type": "string",
+ "description": "The user identification, usually the email address."
+ },
+ "appDefinition": {
+ "type": "string",
+ "description": "The app to launch."
+ },
+ "workspaceName": {
+ "type": "string",
+ "description": "The name of the workspace to mount/create."
+ },
+ "timeout": {
+ "type": "integer",
+ "format": "int32",
+ "description": "Number of minutes to wait for session launch. Default is 3 Minutes."
+ },
+ "env": {
+ "$ref": "#/components/schemas/EnvironmentVars",
+ "type": "object",
+ "description": "Environment variables"
+ }
+ }
+ },
+ "SessionStopRequest": {
+ "type": "object",
+ "required": ["appId", "user", "sessionName"],
+ "description": "A request to stop a session",
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ },
+ "user": {
+ "type": "string",
+ "description": "The user identification, usually the email address."
+ },
+ "sessionName": {
+ "type": "string",
+ "description": "The name of the session to stop."
+ }
+ }
+ },
+ "UserWorkspace": {
+ "type": "object",
+ "required": ["name", "label", "user", "active"],
+ "description": "Description of a user workspace",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the workspace"
+ },
+ "label": {
+ "type": "string",
+ "description": "The label of the workspace"
+ },
+ "appDefinition": {
+ "type": "string",
+ "description": "The app this workspace was used with."
+ },
+ "user": {
+ "type": "string",
+ "description": "The user identification, usually the email address."
+ },
+ "active": {
+ "type": "boolean",
+ "description": "Whether the workspace is in use at the moment."
+ }
+ }
+ },
+ "WorkspaceCreationRequest": {
+ "type": "object",
+ "required": ["appId", "user"],
+ "description": "Request to create a new workspace.",
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ },
+ "user": {
+ "type": "string",
+ "description": "The user identification, usually the email address."
+ },
+ "appDefinition": {
+ "type": "string",
+ "description": "The app this workspace will be used with."
+ },
+ "label": {
+ "type": "string",
+ "description": "The label of the workspace"
+ }
+ }
+ },
+ "WorkspaceDeletionRequest": {
+ "type": "object",
+ "required": ["appId", "user", "workspaceName"],
+ "description": "Request to delete a workspace",
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ },
+ "user": {
+ "type": "string",
+ "description": "The user identification, usually the email address."
+ },
+ "workspaceName": {
+ "type": "string",
+ "description": "The name of the workspace to delete."
+ }
+ }
+ },
+ "WorkspaceListRequest": {
+ "type": "object",
+ "required": ["appId", "user"],
+ "description": "Request to list workspaces of a user.",
+ "properties": {
+ "appId": {
+ "type": "string",
+ "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied."
+ },
+ "user": {
+ "type": "string",
+ "description": "The user identification, usually the email address."
+ }
+ }
+ }
+ },
+ "securitySchemes": {
+ "SecurityScheme": {
+ "type": "oauth2",
+ "description": "Authentication",
+ "flows": {
+ "implicit": {}
+ }
+ }
+ }
+ },
"info": {
"title": "Theia Cloud API",
- "version": "0.12.0"
+ "version": "1.2.0"
},
"paths": {
"/service": {
"post": {
- "tags": ["Root Resource"],
"summary": "Launch Session",
"description": "Launches a session and creates a workspace if required. Responds with the URL of the launched session.",
"requestBody": {
@@ -17,7 +691,8 @@
"$ref": "#/components/schemas/LaunchRequest"
}
}
- }
+ },
+ "required": true
},
"responses": {
"200": {
@@ -30,13 +705,14 @@
}
}
},
- "403": {
- "description": "Not Allowed"
- },
"401": {
"description": "Not Authorized"
+ },
+ "403": {
+ "description": "Not Allowed"
}
},
+ "tags": ["Root Resource"],
"security": [
{
"SecurityScheme": []
@@ -44,94 +720,60 @@
]
}
},
- "/service/session": {
- "post": {
- "tags": ["Session Resource"],
- "summary": "Start a new session",
- "description": "Starts a new session for an existing workspace and responds with the URL of the started session.",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SessionStartRequest"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "text/plain": {
- "schema": {
- "type": "string"
- }
- }
- }
- },
- "403": {
- "description": "Not Allowed"
- },
- "401": {
- "description": "Not Authorized"
- }
- },
- "security": [
+ "/service/admin/appdefinition/{appDefinitionName}": {
+ "patch": {
+ "summary": "Updates an app definition",
+ "description": "Updates an app definition's properties. Allowed properties to update are defined by AppDefinitionUpdateRequest.",
+ "parameters": [
{
- "SecurityScheme": []
+ "description": "The K8S resource name of the app definition to update.",
+ "name": "appDefinitionName",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
}
- ]
- },
- "delete": {
- "tags": ["Session Resource"],
- "summary": "Stop session",
- "description": "Stops a session.",
+ ],
"requestBody": {
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/SessionStopRequest"
+ "$ref": "#/components/schemas/AppDefinitionUpdateRequest"
}
}
- }
+ },
+ "required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
- "text/plain": {
+ "application/json": {
"schema": {
- "type": "boolean"
+ "$ref": "#/components/schemas/AppDefinition"
}
}
}
- },
- "403": {
- "description": "Not Allowed"
- },
- "401": {
- "description": "Not Authorized"
}
},
- "security": [
+ "tags": ["App Definition Admin Resource"]
+ }
+ },
+ "/service/admin/{appId}": {
+ "get": {
+ "summary": "Admin Ping",
+ "description": "Replies with success if the service is available and the user an admin.",
+ "parameters": [
{
- "SecurityScheme": []
- }
- ]
- },
- "patch": {
- "tags": ["Session Resource"],
- "summary": "Report session activity",
- "description": "Updates the last activity timestamp for a session to monitor activity.",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SessionActivityRequest"
- }
+ "name": "appId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
}
}
- },
+ ],
"responses": {
"200": {
"description": "OK",
@@ -142,26 +784,15 @@
}
}
}
- },
- "403": {
- "description": "Not Allowed"
- },
- "401": {
- "description": "Not Authorized"
}
},
- "security": [
- {
- "SecurityScheme": []
- }
- ]
+ "tags": ["Root Admin Resource"]
}
},
- "/service/session/performance/{appId}/{sessionName}": {
+ "/service/appdefinition/{appId}": {
"get": {
- "tags": ["Session Resource"],
- "summary": "Get performance metrics",
- "description": "Returns the current CPU and memory usage of the session's pod.",
+ "summary": "List app definitions",
+ "description": "List available app definitions.",
"parameters": [
{
"name": "appId",
@@ -170,14 +801,6 @@
"schema": {
"type": "string"
}
- },
- {
- "name": "sessionName",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
}
],
"responses": {
@@ -186,18 +809,22 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/SessionPerformance"
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AppDefinitionSpec"
+ }
}
}
}
},
- "403": {
- "description": "Not Allowed"
- },
"401": {
"description": "Not Authorized"
+ },
+ "403": {
+ "description": "Not Allowed"
}
},
+ "tags": ["App Definition Resource"],
"security": [
{
"SecurityScheme": []
@@ -205,107 +832,95 @@
]
}
},
- "/service/session/{appId}/{user}": {
- "get": {
- "tags": ["Session Resource"],
- "summary": "List sessions",
- "description": "List sessions of a user.",
- "parameters": [
- {
- "name": "appId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
+ "/service/session": {
+ "delete": {
+ "summary": "Stop session",
+ "description": "Stops a session.",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SessionStopRequest"
+ }
}
},
- {
- "name": "user",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
+ "required": true
+ },
"responses": {
"200": {
"description": "OK",
"content": {
- "application/json": {
+ "text/plain": {
"schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/SessionSpec"
- }
+ "type": "boolean"
}
}
}
},
- "403": {
- "description": "Not Allowed"
- },
"401": {
"description": "Not Authorized"
+ },
+ "403": {
+ "description": "Not Allowed"
}
},
+ "tags": ["Session Resource"],
"security": [
{
"SecurityScheme": []
}
]
- }
- },
- "/service/workspace": {
+ },
"post": {
- "tags": ["Workspace Resource"],
- "summary": "Create workspace",
- "description": "Creates a new workspace for a user.",
+ "summary": "Start a new session",
+ "description": "Starts a new session for an existing workspace and responds with the URL of the started session.",
"requestBody": {
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/WorkspaceCreationRequest"
+ "$ref": "#/components/schemas/SessionStartRequest"
}
}
- }
+ },
+ "required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
- "application/json": {
+ "text/plain": {
"schema": {
- "$ref": "#/components/schemas/UserWorkspace"
+ "type": "string"
}
}
}
},
- "403": {
- "description": "Not Allowed"
- },
"401": {
"description": "Not Authorized"
+ },
+ "403": {
+ "description": "Not Allowed"
}
},
+ "tags": ["Session Resource"],
"security": [
{
"SecurityScheme": []
}
]
},
- "delete": {
- "tags": ["Workspace Resource"],
- "summary": "Delete workspace",
- "description": "Deletes a workspace.",
+ "patch": {
+ "summary": "Report session activity",
+ "description": "Updates the last activity timestamp for a session to monitor activity.",
"requestBody": {
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/WorkspaceDeletionRequest"
+ "$ref": "#/components/schemas/SessionActivityRequest"
}
}
- }
+ },
+ "required": true
},
"responses": {
"200": {
@@ -318,13 +933,14 @@
}
}
},
- "403": {
- "description": "Not Allowed"
- },
"401": {
"description": "Not Authorized"
+ },
+ "403": {
+ "description": "Not Allowed"
}
},
+ "tags": ["Session Resource"],
"security": [
{
"SecurityScheme": []
@@ -332,11 +948,10 @@
]
}
},
- "/service/workspace/{appId}/{user}": {
+ "/service/session/performance/{appId}/{sessionName}": {
"get": {
- "tags": ["Workspace Resource"],
- "summary": "List workspaces",
- "description": "Lists the workspaces of a user.",
+ "summary": "Get performance metrics",
+ "description": "Returns the current CPU and memory usage of the session's pod.",
"parameters": [
{
"name": "appId",
@@ -347,7 +962,7 @@
}
},
{
- "name": "user",
+ "name": "sessionName",
"in": "path",
"required": true,
"schema": {
@@ -361,21 +976,19 @@
"content": {
"application/json": {
"schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/UserWorkspace"
- }
+ "$ref": "#/components/schemas/SessionPerformance"
}
}
}
},
- "403": {
- "description": "Not Allowed"
- },
"401": {
"description": "Not Authorized"
+ },
+ "403": {
+ "description": "Not Allowed"
}
},
+ "tags": ["Session Resource"],
"security": [
{
"SecurityScheme": []
@@ -383,11 +996,10 @@
]
}
},
- "/service/{appId}": {
+ "/service/session/{appId}/{user}": {
"get": {
- "tags": ["Root Resource"],
- "summary": "Ping",
- "description": "Replies if the service is available.",
+ "summary": "List sessions",
+ "description": "List sessions of a user.",
"parameters": [
{
"name": "appId",
@@ -396,379 +1008,268 @@
"schema": {
"type": "string"
}
+ },
+ {
+ "name": "user",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
}
],
"responses": {
"200": {
"description": "OK",
"content": {
- "text/plain": {
+ "application/json": {
"schema": {
- "type": "boolean"
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/SessionSpec"
+ }
}
}
}
},
- "403": {
- "description": "Not Allowed"
- },
"401": {
"description": "Not Authorized"
+ },
+ "403": {
+ "description": "Not Allowed"
}
},
+ "tags": ["Session Resource"],
"security": [
{
"SecurityScheme": []
}
]
}
- }
- },
- "components": {
- "schemas": {
- "EnvironmentVars": {
- "description": "An object to hold all the ways environment variables can be passed. Not to be used by itself.",
- "type": "object",
- "properties": {
- "fromMap": {
- "description": "Map of environment variables to be passed to Deployment. Ignored if Theia applications are started eagerly. Empty by default.",
- "type": "object",
- "additionalProperties": {
- "type": "string"
- }
- },
- "fromConfigMaps": {
- "description": "List of ConfigMaps (by name) containing environment variables to be passed to Deployment as envFrom.configMapRef. Ignored if Theia applications are started eagerly. Empty by default.",
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "fromSecrets": {
- "description": "List of Secrets (by name) containing environment variables to be passed to Deployment as envFrom.secretRef. Ignored if Theia applications are started eagerly. Empty by default.",
- "type": "array",
- "items": {
+ },
+ "/service/session/{session}/config": {
+ "post": {
+ "summary": "Set config value",
+ "description": "Sets a config value in the config store if it is available. This requires the @eclipse-theiacloud/config-store Theia extension to be present in the application.",
+ "parameters": [
+ {
+ "description": "The name of the session",
+ "name": "session",
+ "in": "path",
+ "required": true,
+ "schema": {
"type": "string"
}
}
- }
- },
- "LaunchRequest": {
- "description": "A request to launch a new session.",
- "required": ["appId", "user"],
- "type": "object",
- "properties": {
- "appId": {
- "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied.",
- "type": "string"
- },
- "user": {
- "description": "The user identification, usually the email address.",
- "type": "string"
- },
- "appDefinition": {
- "description": "The app to launch. Needs to be set if a new or ephemeral session should be launched. For an existing workspace the last app definition will be used if none is given.",
- "type": "string"
- },
- "workspaceName": {
- "description": "The name of the workspace to mount/create. Needs to be set if an existing workspace should be launched.",
- "type": "string"
- },
- "label": {
- "description": "The label of the workspace to mount/create. If no label is given, a default label will be generated.",
- "type": "string"
- },
- "ephemeral": {
- "description": "If true no workspace will be created for the session.",
- "type": "boolean"
- },
- "timeout": {
- "format": "int32",
- "description": "Number of minutes to wait for session launch. Default is 3 Minutes.",
- "type": "integer"
- },
- "env": {
- "description": "Environment variables",
- "type": "object",
- "allOf": [
- {
- "$ref": "#/components/schemas/EnvironmentVars"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SessionSetConfigValueRequest"
}
- ]
- }
- }
- },
- "PingRequest": {
- "description": "Request to ping the availability of the service.",
- "required": ["appId"],
- "type": "object",
- "properties": {
- "appId": {
- "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied.",
- "type": "string"
- }
- }
- },
- "SessionActivityRequest": {
- "description": "A request to report activity for a running session.",
- "required": ["appId", "sessionName"],
- "type": "object",
- "properties": {
- "appId": {
- "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied.",
- "type": "string"
- },
- "sessionName": {
- "description": "The name of the session for which activity is reported.",
- "type": "string"
- }
- }
- },
- "SessionListRequest": {
- "description": "A request to list the sessions of a user.",
- "required": ["appId", "user"],
- "type": "object",
- "properties": {
- "appId": {
- "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied.",
- "type": "string"
- },
- "user": {
- "description": "The user identification, usually the email address.",
- "type": "string"
- }
- }
- },
- "SessionPerformance": {
- "description": "Description of the performance of a session",
- "required": ["cpuAmount", "cpuFormat", "memoryAmount", "memoryFormat"],
- "type": "object",
- "properties": {
- "cpuAmount": {
- "description": "Used CPU amount of the workspace",
- "type": "string"
- },
- "cpuFormat": {
- "description": "Used CPU format of the workspace",
- "type": "string"
- },
- "memoryAmount": {
- "description": "Used memory amount of the workspace",
- "type": "string"
- },
- "memoryFormat": {
- "description": "Used memory format of the workspace",
- "type": "string"
- }
- }
- },
- "SessionPerformanceRequest": {
- "description": "A request to list the sessions of a user.",
- "required": ["appId", "sessionName"],
- "type": "object",
- "properties": {
- "appId": {
- "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied.",
- "type": "string"
- },
- "sessionName": {
- "description": "The name of the session",
- "type": "string"
- }
- }
- },
- "SessionSpec": {
- "type": "object",
- "properties": {
- "name": {
- "type": "string"
- },
- "appDefinition": {
- "type": "string"
- },
- "user": {
- "type": "string"
- },
- "workspace": {
- "type": "string"
- },
- "sessionSecret": {
- "type": "string"
- },
- "options": {
- "type": "object",
- "additionalProperties": {
- "type": "string"
- }
- },
- "envVars": {
- "type": "object",
- "additionalProperties": {
- "type": "string"
- }
- },
- "envVarsFromConfigMaps": {
- "type": "array",
- "items": {
- "type": "string"
}
},
- "envVarsFromSecrets": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- }
- },
- "SessionStartRequest": {
- "description": "A request to start a session",
- "required": ["appId", "user", "appDefinition"],
- "type": "object",
- "properties": {
- "appId": {
- "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied.",
- "type": "string"
- },
- "user": {
- "description": "The user identification, usually the email address.",
- "type": "string"
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "Config value set successfully."
},
- "appDefinition": {
- "description": "The app to launch.",
- "type": "string"
+ "403": {
+ "description": "User does not own the session."
},
- "workspaceName": {
- "description": "The name of the workspace to mount/create.",
- "type": "string"
+ "474": {
+ "description": "Session not found."
},
- "timeout": {
- "format": "int32",
- "description": "Number of minutes to wait for session launch. Default is 3 Minutes.",
- "type": "integer"
+ "500": {
+ "description": "Internal server error while setting config value or other unexpected errors."
},
- "env": {
- "description": "Environment variables",
- "type": "object",
- "allOf": [
- {
- "$ref": "#/components/schemas/EnvironmentVars"
- }
- ]
- }
- }
- },
- "SessionStopRequest": {
- "description": "A request to stop a session",
- "required": ["appId", "user", "sessionName"],
- "type": "object",
- "properties": {
- "appId": {
- "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied.",
- "type": "string"
+ "580": {
+ "description": "Config store not available."
},
- "user": {
- "description": "The user identification, usually the email address.",
- "type": "string"
+ "401": {
+ "description": "Not Authorized"
},
- "sessionName": {
- "description": "The name of the session to stop.",
- "type": "string"
+ "403": {
+ "description": "Not Allowed"
}
- }
- },
- "UserWorkspace": {
- "description": "Description of a user workspace",
- "required": ["name", "label", "user", "active"],
- "type": "object",
- "properties": {
- "name": {
- "description": "The name of the workspace",
- "type": "string"
- },
- "label": {
- "description": "The label of the workspace",
- "type": "string"
+ },
+ "tags": ["Session Resource"],
+ "security": [
+ {
+ "SecurityScheme": []
+ }
+ ]
+ }
+ },
+ "/service/workspace": {
+ "delete": {
+ "summary": "Delete workspace",
+ "description": "Deletes a workspace.",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WorkspaceDeletionRequest"
+ }
+ }
},
- "appDefinition": {
- "description": "The app this workspace was used with.",
- "type": "string"
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UserWorkspace"
+ }
+ }
+ }
},
- "user": {
- "description": "The user identification, usually the email address.",
- "type": "string"
+ "401": {
+ "description": "Not Authorized"
},
- "active": {
- "description": "Whether the workspace is in use at the moment.",
- "type": "boolean"
+ "403": {
+ "description": "Not Allowed"
}
- }
+ },
+ "tags": ["Workspace Resource"],
+ "security": [
+ {
+ "SecurityScheme": []
+ }
+ ]
},
- "WorkspaceCreationRequest": {
- "description": "Request to create a new workspace.",
- "required": ["appId", "user"],
- "type": "object",
- "properties": {
- "appId": {
- "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied.",
- "type": "string"
+ "post": {
+ "summary": "Create workspace",
+ "description": "Creates a new workspace for a user.",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WorkspaceCreationRequest"
+ }
+ }
},
- "user": {
- "description": "The user identification, usually the email address.",
- "type": "string"
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UserWorkspace"
+ }
+ }
+ }
},
- "appDefinition": {
- "description": "The app this workspace will be used with.",
- "type": "string"
+ "401": {
+ "description": "Not Authorized"
},
- "label": {
- "description": "The label of the workspace",
- "type": "string"
+ "403": {
+ "description": "Not Allowed"
}
- }
- },
- "WorkspaceDeletionRequest": {
- "description": "Request to delete a workspace",
- "required": ["appId", "user", "workspaceName"],
- "type": "object",
- "properties": {
- "appId": {
- "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied.",
- "type": "string"
- },
- "user": {
- "description": "The user identification, usually the email address.",
- "type": "string"
+ },
+ "tags": ["Workspace Resource"],
+ "security": [
+ {
+ "SecurityScheme": []
+ }
+ ]
+ }
+ },
+ "/service/workspace/{appId}/{user}": {
+ "get": {
+ "summary": "List workspaces",
+ "description": "Lists the workspaces of a user.",
+ "parameters": [
+ {
+ "name": "appId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
},
- "workspaceName": {
- "description": "The name of the workspace to delete.",
- "type": "string"
+ {
+ "name": "user",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
}
- }
- },
- "WorkspaceListRequest": {
- "description": "Request to list workspaces of a user.",
- "required": ["appId", "user"],
- "type": "object",
- "properties": {
- "appId": {
- "description": "The App Id of this Theia Cloud instance. Request without a matching Id will be denied.",
- "type": "string"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/UserWorkspace"
+ }
+ }
+ }
+ }
},
- "user": {
- "description": "The user identification, usually the email address.",
- "type": "string"
+ "401": {
+ "description": "Not Authorized"
+ },
+ "403": {
+ "description": "Not Allowed"
}
- }
+ },
+ "tags": ["Workspace Resource"],
+ "security": [
+ {
+ "SecurityScheme": []
+ }
+ ]
}
},
- "securitySchemes": {
- "SecurityScheme": {
- "type": "oauth2",
- "description": "Authentication",
- "flows": {
- "implicit": {}
- }
+ "/service/{appId}": {
+ "get": {
+ "summary": "Ping",
+ "description": "Replies if the service is available.",
+ "parameters": [
+ {
+ "name": "appId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "text/plain": {
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Not Authorized"
+ },
+ "403": {
+ "description": "Not Allowed"
+ }
+ },
+ "tags": ["Root Resource"],
+ "security": [
+ {
+ "SecurityScheme": []
+ }
+ ]
}
}
}
diff --git a/documentation/operator.graphml b/documentation/operator.graphml
index 51eb645d4..c3c656e57 100644
--- a/documentation/operator.graphml
+++ b/documentation/operator.graphml
@@ -21,7 +21,7 @@
- TheiaCloud
+ Theia Cloud
@@ -67,7 +67,7 @@ AppDefinitionAddedHandler
- <<interface>>
+ <<interface>>
SessionAddedHandler
@@ -98,7 +98,7 @@ SessionAddedHandler
- LazyStartSessionAddedHandler
+ LazyStartSessionAddedHandler
@@ -113,7 +113,7 @@ SessionAddedHandler
- EagerStartSessionAddedHandler
+ EagerStartSessionAddedHandler
@@ -143,7 +143,7 @@ SessionAddedHandler
- We may have different kinds of implementations of the handlers.
+ We may have different kinds of implementations of the handlers.
E.g. we might have handlers that start up unassigned pods already and assign the running pods to a user on session added (EagerStart)
Or we could do it Che-like and spin up the pod when a new session is added
@@ -174,7 +174,7 @@ IngressCreationService
- Listens for app definition/sessions changes
+ Listens for app definition/sessions changes
and delegates to handlers
diff --git a/documentation/platforms/Minikube.md b/documentation/platforms/Minikube.md
index e72675159..602ff8521 100644
--- a/documentation/platforms/Minikube.md
+++ b/documentation/platforms/Minikube.md
@@ -1,7 +1,7 @@
# Getting started with Minikube
Minikube is a local Kubernetes that allows you to test and develop for Kubernetes.\
-In this guide we will show you how to install minikube and helm 3 as well as cert-manager, the cloud native certificate management, the NginX Ingress Controller and Keycloak. We will use existing installation methods for all of the Theia Cloud preqrequisites.\
+In this guide we will show you how to install minikube and helm 3 as well as cert-manager, Gateway API + Envoy Gateway, and Keycloak. We will use existing installation methods for all of the Theia Cloud prerequisites.\
Finally we will install and try Theia Cloud using helm.
If you have no experience with Kubernetes/Minikube yet, we encourage you to check out some basic tutorials first, e.g. and
@@ -38,7 +38,7 @@ Build the docker image as usual.
### Operator cannot set the workspace URL to the Session
This usually happens when using custom host names that are mapped in a hosts definition file (e.g. `/etc/hosts` on Linux) to the local IP of Minikube.
-One [known case](https://github.com/eclipsesource/theia-cloud/issues/150) occurred on MacOS with Docker Desktop:
+One [known case](https://github.com/eclipse-theia/theia-cloud/issues/150) occurred on MacOS with Docker Desktop:
The VM running Kubernetes does not know the hosts settings of the host operating system.
Thus, the custom hostname could not be resolved in the cluster and, thus, the operator could not resolve the session pods' addresses.
diff --git a/documentation/theiacloud.png b/documentation/theia-cloud.png
similarity index 100%
rename from documentation/theiacloud.png
rename to documentation/theia-cloud.png
diff --git a/java/common/maven-conf/pom.xml b/java/common/maven-conf/pom.xml
index f7beecee8..8944bcae2 100644
--- a/java/common/maven-conf/pom.xml
+++ b/java/common/maven-conf/pom.xml
@@ -5,7 +5,7 @@
4.0.0org.eclipse.theia.cloudconf
- 0.12.0-SNAPSHOT
+ 1.2.0-SNAPSHOTpomTheia Cloud Maven ConfigurationCommon properties and configuration
@@ -17,20 +17,31 @@
quarkus-bomio.quarkus.platform7.0.0
- 2.23.1
+ 2.25.13.7.1
- 3.13.0
- 3.4.0
- 2.2.2
- 5.11.0
- 20240303
- 4.7.6
- 1.1.4
- 1.7.36
- 6.12.1
- 3.11.3
+ 3.14.0
+ 3.5.3
+ 2.2.5
+ 5.13.4
+ 5.18.0
+ 20250517
+ 4.7.7
+ 3.0.1
+ 2.0.17
+ 7.3.1
+ 3.25.1
+
+
+ dash-licenses-snapshots
+ https://repo.eclipse.org/content/repositories/dash-licenses-snapshots/
+
+ true
+
+
+
+
github
@@ -38,7 +49,7 @@
githubGitHub Packages
- https://maven.pkg.github.com/eclipsesource/theia-cloud
+ https://maven.pkg.github.com/eclipse-theia/theia-cloud
@@ -84,4 +95,4 @@
-
\ No newline at end of file
+
diff --git a/java/common/org.eclipse.theia.cloud.common/pom.xml b/java/common/org.eclipse.theia.cloud.common/pom.xml
index 22adab06f..3af633dc6 100644
--- a/java/common/org.eclipse.theia.cloud.common/pom.xml
+++ b/java/common/org.eclipse.theia.cloud.common/pom.xml
@@ -11,7 +11,7 @@
org.eclipse.theia.cloudconf
- 0.12.0-SNAPSHOT
+ 1.2.0-SNAPSHOT../../common/maven-conf/
@@ -21,6 +21,16 @@
kubernetes-client${kubernetes-client.version}
+
+ io.fabric8
+ kubernetes-httpclient-okhttp
+ ${kubernetes-client.version}
+
+
+ org.json
+ json
+ ${org.json.version}
+ org.apache.logging.log4jlog4j-api
@@ -37,6 +47,23 @@
${junit-jupiter.version}test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ io.sentry
+ sentry
+ 8.0.0
+
+
+ io.sentry
+ sentry-okhttp
+ 8.0.0
+
@@ -45,8 +72,8 @@
maven-compiler-plugin${maven-compiler-plugin.version}
- 17
- 17
+ 21
+ 21
@@ -64,4 +91,4 @@
-
\ No newline at end of file
+
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/DataBridgeClient.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/DataBridgeClient.java
new file mode 100644
index 000000000..7b050561c
--- /dev/null
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/DataBridgeClient.java
@@ -0,0 +1,48 @@
+package org.eclipse.theia.cloud.common.k8s.client;
+
+import org.eclipse.theia.cloud.common.k8s.resource.session.DataInjectionResponse;
+import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Client for interacting with the data bridge service running in session
+ * pods.
+ */
+public interface DataBridgeClient {
+
+ /**
+ * Injects data into a session's data bridge.
+ *
+ * @param sessionName The name of the session
+ * @param data Map of environment variable data to inject
+ * @param correlationId For logging/tracing
+ * @return Response indicating success or failure, or empty if the session or
+ * bridge is unreachable
+ */
+ Optional injectData(String sessionName, Map data,
+ String correlationId);
+
+ /**
+ * Injects data into a session using the Session object.
+ *
+ * @param session The session object
+ * @param data Map of environment variable data to inject
+ * @param correlationId For logging/tracing
+ * @return Response indicating success or failure, or empty if the bridge is
+ * unreachable
+ */
+ Optional injectData(Session session, Map data,
+ String correlationId);
+
+ /**
+ * Checks if the data bridge is healthy/reachable.
+ *
+ * @param sessionName The name of the session
+ * @param correlationId For logging/tracing
+ * @return true if health check succeeds
+ */
+ boolean healthCheck(String sessionName, String correlationId);
+}
+
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/DefaultDataBridgeClient.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/DefaultDataBridgeClient.java
new file mode 100644
index 000000000..73be6479a
--- /dev/null
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/DefaultDataBridgeClient.java
@@ -0,0 +1,203 @@
+package org.eclipse.theia.cloud.common.k8s.client;
+
+import static org.eclipse.theia.cloud.common.util.LogMessageUtil.formatLogMessage;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.session.DataInjectionResponse;
+import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+import org.eclipse.theia.cloud.common.util.DataBridgeUtil;
+import org.json.JSONObject;
+
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+import io.sentry.okhttp.SentryOkHttpEventListener;
+import io.sentry.okhttp.SentryOkHttpInterceptor;
+
+/**
+ * Default implementation of {@link DataBridgeClient} using OkHttp.
+ */
+public class DefaultDataBridgeClient implements DataBridgeClient {
+
+ private static final Logger LOGGER = LogManager.getLogger(DefaultDataBridgeClient.class);
+
+ private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
+
+ private final OkHttpClient httpClient;
+ private final TheiaCloudClient theiaCloudClient;
+
+ /**
+ * Creates a client with default timeout settings.
+ *
+ * @param theiaCloudClient The Theia Cloud client for accessing K8s resources
+ */
+ public DefaultDataBridgeClient(TheiaCloudClient theiaCloudClient) {
+ this(theiaCloudClient, createDefaultHttpClient());
+ }
+
+ /**
+ * Creates a client with custom HTTP client.
+ *
+ * @param theiaCloudClient The Theia Cloud client for accessing K8s resources
+ * @param httpClient Custom HTTP client
+ */
+ public DefaultDataBridgeClient(TheiaCloudClient theiaCloudClient, OkHttpClient httpClient) {
+ this.theiaCloudClient = theiaCloudClient;
+ this.httpClient = httpClient;
+ }
+
+ private static OkHttpClient createDefaultHttpClient() {
+ return new OkHttpClient.Builder()
+ .connectTimeout(10, TimeUnit.SECONDS)
+ .writeTimeout(10, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS)
+ .addInterceptor(new SentryOkHttpInterceptor())
+ .eventListener(new SentryOkHttpEventListener())
+ .build();
+ }
+
+ @Override
+ public Optional injectData(String sessionName, Map data,
+ String correlationId) {
+ Optional session = theiaCloudClient.sessions().get(sessionName);
+ if (session.isEmpty()) {
+ LOGGER.warn(formatLogMessage(correlationId, "Session not found: " + sessionName));
+ return Optional.empty();
+ }
+ return injectData(session.get(), data, correlationId);
+ }
+
+ @Override
+ public Optional injectData(Session session, Map data,
+ String correlationId) {
+ String sessionName = session.getSpec().getName();
+
+ // Get the session's service IP
+ Optional serviceIP = theiaCloudClient.getClusterIPFromSessionName(sessionName);
+
+ if (serviceIP.isEmpty()) {
+ LOGGER.warn(
+ formatLogMessage(correlationId, "Could not resolve service IP for session: " + sessionName));
+ return Optional.empty();
+ }
+
+ // Get the app definition to find the data bridge port
+ String appDefName = session.getSpec().getAppDefinition();
+ Optional appDef = theiaCloudClient.appDefinitions().get(appDefName);
+
+ if (appDef.isEmpty()) {
+ LOGGER.warn(formatLogMessage(correlationId, "App definition not found: " + appDefName));
+ return Optional.empty();
+ }
+
+ if (!DataBridgeUtil.isDataBridgeEnabled(appDef.get().getSpec())) {
+ LOGGER.debug(formatLogMessage(correlationId,
+ "Data bridge disabled for app definition: " + appDefName + ", skipping injection"));
+ return Optional.empty();
+ }
+
+ int port = DataBridgeUtil.getDataBridgePort(appDef.get().getSpec());
+
+ // Build the URL
+ Optional url = DataBridgeUtil.getDataInjectionURL(serviceIP.get(), port);
+
+ if (url.isEmpty()) {
+ LOGGER.warn(formatLogMessage(correlationId, "Could not construct data injection URL"));
+ return Optional.empty();
+ }
+
+ return performInjection(url.get(), data, correlationId, sessionName);
+ }
+
+ private Optional performInjection(String url, Map data,
+ String correlationId, String sessionName) {
+ LOGGER.info(formatLogMessage(correlationId, "Injecting data to session: " + sessionName + " at " + url));
+
+ try {
+ // Build request body matching the TypeScript schema
+ JSONObject requestBody = new JSONObject();
+ JSONObject environment = new JSONObject(data);
+ requestBody.put("environment", environment);
+
+ RequestBody body = RequestBody.create(requestBody.toString(), JSON_MEDIA_TYPE);
+
+ // Build and execute request
+ Request request = new Request.Builder().url(url).post(body).build();
+
+ try (Response response = httpClient.newCall(request).execute()) {
+ if (response.isSuccessful()) {
+ LOGGER.info(formatLogMessage(correlationId,
+ "Successfully injected data to: " + sessionName));
+ return Optional
+ .of(DataInjectionResponse.success("Data injected successfully"));
+ } else {
+ String errorBody = response.body() != null ? response.body().string() : "Unknown error";
+ LOGGER.warn(formatLogMessage(correlationId, "Failed to inject data. Status: "
+ + response.code() + ", Body: " + errorBody));
+ return Optional.of(DataInjectionResponse
+ .failure("HTTP " + response.code() + ": " + errorBody));
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.error(
+ formatLogMessage(correlationId, "IOException while injecting data to: " + sessionName), e);
+ return Optional.of(DataInjectionResponse.failure("Network error: " + e.getMessage()));
+ }
+ }
+
+ @Override
+ public boolean healthCheck(String sessionName, String correlationId) {
+ Optional session = theiaCloudClient.sessions().get(sessionName);
+ if (session.isEmpty()) {
+ return false;
+ }
+
+ Optional serviceIP = theiaCloudClient.getClusterIPFromSessionName(sessionName);
+
+ if (serviceIP.isEmpty()) {
+ return false;
+ }
+
+ // Get port from app definition
+ String appDefName = session.get().getSpec().getAppDefinition();
+ Optional appDef = theiaCloudClient.appDefinitions().get(appDefName);
+
+ if (appDef.isEmpty() || !DataBridgeUtil.isDataBridgeEnabled(appDef.get().getSpec())) {
+ return false;
+ }
+
+ int port = DataBridgeUtil.getDataBridgePort(appDef.get().getSpec());
+
+ Optional url = DataBridgeUtil.getHealthCheckURL(serviceIP.get(), port);
+
+ if (url.isEmpty()) {
+ return false;
+ }
+
+ try {
+ Request request = new Request.Builder().url(url.get()).get().build();
+
+ try (Response response = httpClient.newCall(request).execute()) {
+ boolean healthy = response.isSuccessful();
+ if (healthy) {
+ LOGGER.debug(formatLogMessage(correlationId, "Health check passed for session: " + sessionName));
+ }
+ return healthy;
+ }
+ } catch (IOException e) {
+ LOGGER.debug(formatLogMessage(correlationId, "Health check failed for session: " + sessionName), e);
+ return false;
+ }
+ }
+}
+
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/DefaultSessionResourceClient.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/DefaultSessionResourceClient.java
index 517fdef4b..d75779c49 100644
--- a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/DefaultSessionResourceClient.java
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/DefaultSessionResourceClient.java
@@ -16,6 +16,8 @@
package org.eclipse.theia.cloud.common.k8s.client;
import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@@ -23,6 +25,7 @@
import org.eclipse.theia.cloud.common.k8s.resource.session.SessionSpec;
import org.eclipse.theia.cloud.common.k8s.resource.session.SessionSpecResourceList;
import org.eclipse.theia.cloud.common.k8s.resource.session.SessionStatus;
+import org.eclipse.theia.cloud.common.tracing.TraceContext;
import org.eclipse.theia.cloud.common.util.TheiaCloudError;
import io.fabric8.kubernetes.api.model.ObjectMeta;
@@ -46,10 +49,16 @@ public Session create(String correlationId, SessionSpec spec) {
ObjectMeta metadata = new ObjectMeta();
metadata.setName(spec.getName());
+
+ // Inject trace context into annotations for operator to continue trace
+ Map annotations = new HashMap<>();
+ TraceContext.fromCurrent().ifPresent(ctx -> ctx.mergeInto(annotations));
+ if (!annotations.isEmpty()) {
+ metadata.setAnnotations(annotations);
+ }
+
session.setMetadata(metadata);
- updateStatus(correlationId, session, status -> status.setLastActivity(Instant.now().toEpochMilli()));
-
info(correlationId, "Create Session " + session.getSpec());
return operation().resource(session).create();
}
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/ResourceClient.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/ResourceClient.java
index d470ef5b9..dc1fa7e12 100644
--- a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/ResourceClient.java
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/ResourceClient.java
@@ -58,6 +58,8 @@ default T edit(String correlationId, String name, Consumer consumer) {
info(correlationId, "Edit " + name);
Resource resource = resource(name);
if (resource.get() == null) {
+ warn(correlationId, "Resource not found! Could not edit " + name
+ + ". Was this called before the resource has been created?");
return null;
}
return resource.edit(JavaUtil.toUnary(consumer));
@@ -67,6 +69,8 @@ default T editStatus(String correlationId, String name, Consumer consumer) {
trace(correlationId, "Edit status of " + name);
Resource resource = resource(name);
if (resource.get() == null) {
+ warn(correlationId, "Resource " + name
+ + " not found. Could not update the status. Note that the status of a resource cannot be changed before it is created on the cluster.");
return null;
}
return resource.editStatus(JavaUtil.toUnary(consumer));
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/TheiaCloudClient.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/TheiaCloudClient.java
index 6bc70eb60..aeeedca12 100644
--- a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/TheiaCloudClient.java
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/client/TheiaCloudClient.java
@@ -15,6 +15,8 @@
********************************************************************************/
package org.eclipse.theia.cloud.common.k8s.client;
+import org.eclipse.theia.cloud.common.util.CustomResourceUtil;
+
import java.util.Optional;
import io.fabric8.kubernetes.api.model.HasMetadata;
@@ -26,10 +28,11 @@
import io.fabric8.kubernetes.api.model.PersistentVolumeList;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceList;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRoute;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRouteList;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.api.model.networking.v1.IngressList;
import io.fabric8.kubernetes.client.KubernetesClient;
-import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
@@ -61,6 +64,10 @@ default ResourceClient ingresses() {
return client(kubernetes().network().v1().ingresses(), Ingress.class);
}
+ default ResourceClient httpRoutes() {
+ return client(HTTPRoute.class, HTTPRouteList.class);
+ }
+
@SuppressWarnings("unchecked")
default ResourceClient> client(Class typeClass) {
return client(typeClass,
@@ -78,9 +85,29 @@ default > ResourceCli
}
default Optional getClusterIPFromSessionName(String sessionName) {
- try (final KubernetesClient client = new KubernetesClientBuilder().build()) {
+ try (final KubernetesClient client = CustomResourceUtil.createClient()) {
ServiceList svcList = client.services().inNamespace(namespace()).list();
for (Service svc : svcList.getItems()) {
+ if (svc.getMetadata().getName().endsWith("-int")) {
+ continue;
+ }
+ for (OwnerReference ownerReference : svc.getMetadata().getOwnerReferences()) {
+ if (sessionName.equals(ownerReference.getName()) && "Session".equals(ownerReference.getKind())) {
+ return Optional.of(svc.getSpec().getClusterIP());
+ }
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ default Optional getInternalClusterIPFromSessionName(String sessionName) {
+ try (final KubernetesClient client = CustomResourceUtil.createClient()) {
+ ServiceList svcList = client.services().inNamespace(namespace()).list();
+ for (Service svc : svcList.getItems()) {
+ if (!svc.getMetadata().getName().endsWith("-int")) {
+ continue;
+ }
for (OwnerReference ownerReference : svc.getMetadata().getOwnerReferences()) {
if (sessionName.equals(ownerReference.getName()) && "Session".equals(ownerReference.getKind())) {
return Optional.of(svc.getSpec().getClusterIP());
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/AppDefinition.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/AppDefinition.java
index 65ef5bc88..c1c8769d0 100644
--- a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/AppDefinition.java
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/AppDefinition.java
@@ -29,7 +29,7 @@
import io.fabric8.kubernetes.model.annotation.Singular;
import io.fabric8.kubernetes.model.annotation.Version;
-@Version("v1beta10")
+@Version("v1beta11")
@Group("theia.cloud")
@Kind("AppDefinition")
@Singular("appdefinition")
@@ -37,7 +37,7 @@
public class AppDefinition extends CustomResource implements Namespaced {
private static final long serialVersionUID = 8749670583218521755L;
- public static final String API = "theia.cloud/v1beta10";
+ public static final String API = "theia.cloud/v1beta11";
public static final String KIND = "AppDefinition";
public static final String CRD_NAME = "appdefinitions.theia.cloud";
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/AppDefinitionSpec.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/AppDefinitionSpec.java
index 7478d9cbf..bae2269ba 100644
--- a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/AppDefinitionSpec.java
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/AppDefinitionSpec.java
@@ -20,6 +20,7 @@
import java.util.Map;
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.hub.AppDefinitionHub;
+import org.eclipse.theia.cloud.common.serialization.SensitiveData;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@@ -37,6 +38,7 @@ public class AppDefinitionSpec {
private String imagePullPolicy;
@JsonProperty("pullSecret")
+ @SensitiveData
private String pullSecret;
@JsonProperty("uid")
@@ -87,6 +89,9 @@ public class AppDefinitionSpec {
@JsonProperty("ingressHostnamePrefixes")
private List ingressHostnamePrefixes;
+ @JsonProperty("sidecars")
+ private List sidecars;
+
/**
* Default constructor.
*/
@@ -115,6 +120,7 @@ public AppDefinitionSpec(AppDefinitionHub fromHub) {
this.options = fromHub.getOptions().orElse(null);
this.ingressHostnamePrefixes = fromHub.getIngressHostnamePrefixes().orElse(null);
+ this.sidecars = fromHub.getSidecars().orElse(null);
int monitorPort = fromHub.getMonitorPort().orElse(0);
if (monitorPort > 0) {
@@ -162,10 +168,18 @@ public int getMinInstances() {
return minInstances;
}
+ public void setMinInstances(int minInstances) {
+ this.minInstances = minInstances;
+ }
+
public Integer getMaxInstances() {
return maxInstances;
}
+ public void setMaxInstances(Integer maxInstances) {
+ this.maxInstances = maxInstances;
+ }
+
public Integer getTimeout() {
return timeout;
}
@@ -210,12 +224,35 @@ public List getIngressHostnamePrefixes() {
return ingressHostnamePrefixes;
}
+ public List getSidecars() {
+ return sidecars;
+ }
+
+ public boolean hasSidecars() {
+ return sidecars != null && !sidecars.isEmpty();
+ }
+
+ public boolean requiresSharedWorkspace() {
+ if (sidecars == null) {
+ return false;
+ }
+
+ for (SidecarSpec sidecar : sidecars) {
+ if (sidecar != null && sidecar.isMountWorkspace()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
@Override
public String toString() {
+ final String redactedPullSecret = "***";
return "AppDefinitionSpec [name=" + name + ", image=" + image + ", imagePullPolicy=" + imagePullPolicy
- + ", pullSecret=" + pullSecret + ", uid=" + uid + ", port=" + port + ", ingressname=" + ingressname
- + ", minInstances=" + minInstances + ", maxInstances=" + maxInstances + ", timeout=" + timeout
- + ", requestsMemory=" + requestsMemory + ", requestsCpu=" + requestsCpu + ", limitsMemory="
+ + ", pullSecret=" + redactedPullSecret + ", uid=" + uid + ", port=" + port + ", ingressname="
+ + ingressname + ", minInstances=" + minInstances + ", maxInstances=" + maxInstances + ", timeout="
+ + timeout + ", requestsMemory=" + requestsMemory + ", requestsCpu=" + requestsCpu + ", limitsMemory="
+ limitsMemory + ", limitsCpu=" + limitsCpu + ", downlinkLimit=" + downlinkLimit + ", uplinkLimit="
+ uplinkLimit + ", mountPath=" + mountPath + "]";
}
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/SidecarSpec.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/SidecarSpec.java
new file mode 100644
index 000000000..f04c7a746
--- /dev/null
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/SidecarSpec.java
@@ -0,0 +1,119 @@
+/********************************************************************************
+ * Copyright (C) 2026 EclipseSource and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+package org.eclipse.theia.cloud.common.k8s.resource.appdefinition;
+
+import java.util.List;
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+@JsonDeserialize()
+public class SidecarSpec {
+
+ @JsonProperty("name")
+ private String name;
+
+ @JsonProperty("image")
+ private String image;
+
+ @JsonProperty("port")
+ private int port;
+
+ @JsonProperty("languages")
+ private List languages;
+
+ @JsonProperty("cpuLimit")
+ private String cpuLimit;
+
+ @JsonProperty("memoryLimit")
+ private String memoryLimit;
+
+ @JsonProperty("cpuRequest")
+ private String cpuRequest;
+
+ @JsonProperty("memoryRequest")
+ private String memoryRequest;
+
+ @JsonProperty("mountWorkspace")
+ private Boolean mountWorkspace;
+
+ /**
+ * Default constructor.
+ */
+ public SidecarSpec() {
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getImage() {
+ return image;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public List getLanguages() {
+ return languages;
+ }
+
+ public String getCpuLimit() {
+ return cpuLimit;
+ }
+
+ public String getMemoryLimit() {
+ return memoryLimit;
+ }
+
+ public String getCpuRequest() {
+ return cpuRequest;
+ }
+
+ public String getMemoryRequest() {
+ return memoryRequest;
+ }
+
+ public boolean isMountWorkspace() {
+ return mountWorkspace == null ? false : mountWorkspace;
+ }
+
+ @Override
+ public String toString() {
+ return "SidecarSpec [name=" + name + ", image=" + image + ", port=" + port + ", languages=" + languages
+ + ", cpuLimit=" + cpuLimit + ", memoryLimit=" + memoryLimit + ", cpuRequest=" + cpuRequest
+ + ", memoryRequest=" + memoryRequest + ", mountWorkspace=" + mountWorkspace + "]";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SidecarSpec that = (SidecarSpec) o;
+ return port == that.port
+ && Objects.equals(mountWorkspace, that.mountWorkspace)
+ && Objects.equals(name, that.name)
+ && Objects.equals(image, that.image)
+ && Objects.equals(languages, that.languages)
+ && Objects.equals(cpuLimit, that.cpuLimit)
+ && Objects.equals(memoryLimit, that.memoryLimit)
+ && Objects.equals(cpuRequest, that.cpuRequest)
+ && Objects.equals(memoryRequest, that.memoryRequest);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, image, port, languages, cpuLimit, memoryLimit, cpuRequest, memoryRequest,
+ mountWorkspace);
+ }
+
+}
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/hub/AppDefinitionHub.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/hub/AppDefinitionHub.java
index 32731a2bb..6b22a6247 100644
--- a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/hub/AppDefinitionHub.java
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/k8s/resource/appdefinition/hub/AppDefinitionHub.java
@@ -21,6 +21,7 @@
import java.util.OptionalInt;
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.SidecarSpec;
import io.fabric8.kubernetes.api.model.ObjectMeta;
@@ -58,6 +59,8 @@ public class AppDefinitionHub {
final Optional
*
- * The created name contains a "session" prefix, the session's user and app definition, the identifier (if given),
- * and the last segment of the Session's UID. User, app definition and identifier are shortened to keep the name
- * within Kubernetes' character limit (63) minus 6 characters. The latter allows Kubernetes to add 6 characters at
- * the end of deployment pod names while the pod names pod names will still contain the whole name of the deployment
+ * The created name contains a "instance-" prefix, instance number, the identifier (if given), and the last segment
+ * of the App Definition's UID. User, app definition and identifier are shortened to keep the name within
+ * Kubernetes' character limit (63) minus 6 characters. The latter allows Kubernetes to add 6 characters at the end
+ * of deployment pod names while the pod names pod names will still contain the whole name of the deployment
*
*
* @param appDefinition the {@link AppDefinition}
@@ -69,7 +90,7 @@ public static String createName(AppDefinition appDefinition, int instance) {
* @return the name
*/
public static String createName(AppDefinition appDefinition, int instance, String identifier) {
- String prefix = "instance-" + instance;
+ String prefix = APP_DEFINITION_INSTANCE_PREFIX + instance;
return createName(prefix, identifier, null, appDefinition.getSpec().getName(),
appDefinition.getMetadata().getUid());
}
@@ -82,6 +103,19 @@ public static String createName(Session session) {
session.getMetadata().getUid());
}
+ /**
+ * Creates a string that can be used as a name for a Kubernetes object with a suffix. The suffix is always preserved
+ * in full and other parts are shortened accordingly to stay within length limits.
+ *
+ * @param session the {@link Session}
+ * @param suffix the suffix to append to the name. Must not be null or blank.
+ * @return the name with suffix
+ */
+ public static String createNameWithSuffix(Session session, String suffix) {
+ return createNameWithSuffix("session", session.getSpec().getUser(), session.getSpec().getAppDefinition(),
+ session.getMetadata().getUid(), suffix);
+ }
+
/**
* Creates a string that can be used as a name for a Kubernetes object. When the same arguments are passed to this
* method again, the resulting name will be the same. For different arguments the resulting name will be unique in
@@ -150,6 +184,54 @@ public static String createName(Workspace workspace, String identifier) {
workspace.getMetadata().getUid());
}
+ /**
+ * Builds a valid Kubernetes object names with a suffix. The suffix is always preserved in full and other parts are
+ * shortened accordingly to stay within length limits.
+ *
+ * @param prefix The prefix to start the object name with. Should start with a letter and be at most 13
+ * characters long. Longer prefixes are possible but might lead to other info being cut short.
+ * @param user The user, may be null
+ * @param appDefinition The app definition name, may be null
+ * @param uid a unique Kubernetes object id that the name relates to. I.e. the UID of a session when
+ * creating the name of a session deployment.
+ * @param suffix the suffix to append to the name. Must not be null or blank.
+ * @return the joined and valid Kubernetes name with suffix
+ */
+ private static String createNameWithSuffix(String prefix, String user, String appDefinition, String uid,
+ String suffix) {
+ /*
+ * Kubenertes UIDs are standardized UUIDs/GUIDs. This means the uid string will have a length of 36. We take the
+ * last segment with length 12 to generate unique names for each Session even if user and app definition are the
+ * same.
+ */
+ String shortUid = trimUid(uid);
+
+ // If the user is an email address, only take the part before the @ sign because
+ // this is usually sufficient to identify the user.
+ String userName = user != null ? user.split("@")[0] : null;
+
+ // Calculate available space considering the suffix
+ String validSuffix = suffix != null && !suffix.isBlank() ? suffix : null;
+ if (validSuffix == null) {
+ throw new IllegalArgumentException("Suffix must not be null or blank");
+ }
+
+ int suffixLength = validSuffix.length() + 1; // +1 for the "-" separator
+ int availableLength = VALID_NAME_LIMIT - suffixLength;
+
+ // Calculate lengths for the variable parts, ensuring we reserve space for prefix, uid, and separators
+ int fixedPartsLength = prefix.length() + 1 + shortUid.length(); // +1 for separator before uid
+ int availableForVariableParts = Math.max(0, availableLength - fixedPartsLength);
+
+ // Divide remaining space between user and app definition
+ int infoSegmentLength = availableForVariableParts > 0 ? Math.max(1, availableForVariableParts / 2) : 0;
+
+ String shortUserName = trimLength(userName, infoSegmentLength);
+ String shortAppDef = trimLength(appDefinition, infoSegmentLength);
+
+ return asValidName(prefix, shortUserName, shortAppDef, shortUid, validSuffix);
+ }
+
/**
* Builds a valid Kubernetes object names. Except for the prefix, all segments are limited to a fixed number of
* characters to ensure that the resulting name includes information from all parameters.
@@ -174,7 +256,7 @@ private static String createName(String prefix, String identifier, String user,
// If the user is an email address, only take the part before the @ sign because
// this is usually sufficient to identify the user.
- String userName = user.split("@")[0];
+ String userName = user != null ? user.split("@")[0] : null;
int infoSegmentLength;
String shortenedIdentifier = null;
@@ -200,8 +282,8 @@ private static String trimUid(String uid) {
}
private static String trimLength(String text, int maxLength) {
- if (text == null) {
- return text;
+ if (text == null || maxLength <= 0) {
+ return null;
}
return text.substring(0, Math.min(text.length(), maxLength));
}
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/util/SessionUtil.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/util/SessionUtil.java
index be64a2271..59c8debfa 100644
--- a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/util/SessionUtil.java
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/util/SessionUtil.java
@@ -15,10 +15,99 @@
********************************************************************************/
package org.eclipse.theia.cloud.common.util;
+import java.util.Optional;
+
+import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinitionSpec;
+import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+
public final class SessionUtil {
private SessionUtil() {
// util
}
+ /**
+ * Get the cluster URL of the session pod, if available. This is the cluster internal URL of the pod.
+ *
+ * @param client The Theia Cloud K8s client to use for the request.
+ * @param session The session to get the cluster IP for.
+ * @return The cluster IP of the session pod, if available.
+ */
+ public static Optional getClusterURL(TheiaCloudClient client, Session session) {
+ Optional ip = getClusterIP(client, session);
+ Optional port = getPort(client, session);
+ if (ip.isPresent() && port.isPresent()) {
+ return Optional.of("http://" + ip.get() + ":" + port.get());
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Get the internal cluster URL of the session pod, if available. This is the cluster internal URL of the internal
+ * service that bypasses OAuth2 proxy for service-to-service communication.
+ *
+ * @param client The Theia Cloud K8s client to use for the request.
+ * @param session The session to get the internal cluster IP for.
+ * @return The internal cluster URL of the session pod, if available.
+ */
+ public static Optional getInternalClusterURL(TheiaCloudClient client, Session session) {
+ Optional ip = getInternalClusterIP(client, session);
+ Optional port = getInternalPort();
+ if (ip.isPresent() && port.isPresent()) {
+ return Optional.of("http://" + ip.get() + ":" + port.get());
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Get the cluster IP of the session pod, if available. The cluster IP is the internal IP address of the pod.
+ *
+ * @param client The Theia Cloud K8s client to use for the request.
+ * @param session The session to get the cluster IP for.
+ * @return The cluster IP of the session pod, if available.
+ */
+ public static Optional getClusterIP(TheiaCloudClient client, Session session) {
+ Optional sessionIP = client.getClusterIPFromSessionName(session.getSpec().getName());
+ return sessionIP;
+ }
+
+ /**
+ * Get the internal cluster IP of the session pod, if available. The internal cluster IP is the IP address of the
+ * internal service that bypasses OAuth2 proxy for service-to-service communication.
+ *
+ * @param client The Theia Cloud K8s client to use for the request.
+ * @param session The session to get the internal cluster IP for.
+ * @return The internal cluster IP of the session pod, if available.
+ */
+ public static Optional getInternalClusterIP(TheiaCloudClient client, Session session) {
+ Optional sessionIP = client.getInternalClusterIPFromSessionName(session.getSpec().getName());
+ return sessionIP;
+ }
+
+ /**
+ * Get the port that the session's Theia application is running on. This does not consider the port that the OAuth
+ * proxy runs on which is target by external connections.
+ *
+ * @param client The Theia Cloud K8s client to use for the request.
+ * @param session The session to get the port for.
+ * @return The port of the session pod, if available.
+ */
+ public static Optional getPort(TheiaCloudClient client, Session session) {
+ String appDefinitionId = session.getSpec().getAppDefinition();
+ return client.appDefinitions().get(appDefinitionId)//
+ .map(AppDefinition::getSpec)//
+ .map(AppDefinitionSpec::getPort);
+ }
+
+ /**
+ * Get the internal service port for service-to-service communication. This is a fixed port (3001) that the internal
+ * services use to bypass OAuth2 proxy.
+ *
+ * @return The internal service port (3001).
+ */
+ public static Optional getInternalPort() {
+ return Optional.of(3001);
+ }
}
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/util/TheiaCloudError.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/util/TheiaCloudError.java
index 297fe346f..a25600f4f 100644
--- a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/util/TheiaCloudError.java
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/util/TheiaCloudError.java
@@ -47,6 +47,10 @@ public class TheiaCloudError {
public static final TheiaCloudError SESSION_USER_NO_SESSIONS = new TheiaCloudError(554,
"No sessions allowed for this user.");
+ // server errors: 58x optional features
+ public static final TheiaCloudError CONFIG_STORE_NOT_AVAILABLE = new TheiaCloudError(580,
+ "The Theia Cloud Config Store is not available. It needs to be installed in the application.");
+
private final int code;
private final String reason;
diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/resources/META-INF/services/io.fabric8.kubernetes.api.model.KubernetesResource b/java/common/org.eclipse.theia.cloud.common/src/main/resources/META-INF/services/io.fabric8.kubernetes.api.model.KubernetesResource
index 1ea803012..8588ba6ab 100644
--- a/java/common/org.eclipse.theia.cloud.common/src/main/resources/META-INF/services/io.fabric8.kubernetes.api.model.KubernetesResource
+++ b/java/common/org.eclipse.theia.cloud.common/src/main/resources/META-INF/services/io.fabric8.kubernetes.api.model.KubernetesResource
@@ -1,4 +1,5 @@
org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition
+org.eclipse.theia.cloud.common.k8s.resource.appdefinition.v1beta10.AppDefinitionV1beta10
org.eclipse.theia.cloud.common.k8s.resource.appdefinition.v1beta9.AppDefinitionV1beta9
org.eclipse.theia.cloud.common.k8s.resource.appdefinition.v1beta8.AppDefinitionV1beta8
org.eclipse.theia.cloud.common.k8s.resource.session.Session
diff --git a/java/common/org.eclipse.theia.cloud.common/src/test/java/org/eclipse/theia/cloud/common/serialization/SensitiveDataSerializerTests.java b/java/common/org.eclipse.theia.cloud.common/src/test/java/org/eclipse/theia/cloud/common/serialization/SensitiveDataSerializerTests.java
new file mode 100644
index 000000000..a93a2528d
--- /dev/null
+++ b/java/common/org.eclipse.theia.cloud.common/src/test/java/org/eclipse/theia/cloud/common/serialization/SensitiveDataSerializerTests.java
@@ -0,0 +1,189 @@
+package org.eclipse.theia.cloud.common.serialization;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Unit tests for {@link SensitiveDataSerializer}.
+ */
+class SensitiveDataSerializerTest {
+
+ private JsonGenerator jsonGenerator;
+ private SerializerProvider serializerProvider;
+ private TypeFactory typeFactory;
+
+ @BeforeEach
+ void setUp() {
+ jsonGenerator = mock(JsonGenerator.class);
+ serializerProvider = mock(SerializerProvider.class);
+ typeFactory = TypeFactory.defaultInstance();
+ }
+
+ @Test
+ void serialize_StringType() throws IOException {
+ JavaType stringType = typeFactory.constructType(String.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(stringType);
+
+ serializer.serialize("test", jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeString(SensitiveDataSerializer.REDACTED_STRING);
+ }
+
+ @Test
+ void serialize_StringType_Null() throws IOException {
+ JavaType stringType = typeFactory.constructType(String.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(stringType);
+
+ serializer.serialize(null, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeString(SensitiveDataSerializer.REDACTED_STRING);
+ }
+
+ @Test
+ void serialize_MapType() throws IOException {
+ JavaType mapType = typeFactory.constructMapType(HashMap.class, String.class, Object.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(mapType);
+
+ serializer.serialize(new HashMap<>(), jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeStartObject();
+ verify(jsonGenerator).writeEndObject();
+ }
+
+ @Test
+ void serialize_MapType_Null() throws IOException {
+ JavaType mapType = typeFactory.constructMapType(HashMap.class, String.class, Object.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(mapType);
+
+ serializer.serialize(null, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeStartObject();
+ verify(jsonGenerator).writeEndObject();
+ }
+
+ @Test
+ void serialize_ArrayType() throws IOException {
+ JavaType arrayType = typeFactory.constructArrayType(String.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(arrayType);
+
+ serializer.serialize(new String[] { "test" }, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeStartArray();
+ verify(jsonGenerator).writeEndArray();
+ }
+
+ @Test
+ void serialize_ArrayType_Null() throws IOException {
+ JavaType arrayType = typeFactory.constructArrayType(String.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(arrayType);
+
+ serializer.serialize(null, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeStartArray();
+ verify(jsonGenerator).writeEndArray();
+ }
+
+ @Test
+ void serialize_NumberType() throws IOException {
+ JavaType numberType = typeFactory.constructType(Integer.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(numberType);
+
+ serializer.serialize(123, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeNumber(SensitiveDataSerializer.REDACTED_NUMBER);
+ }
+
+ @Test
+ void serialize_NumberType_Null() throws IOException {
+ JavaType numberType = typeFactory.constructType(Integer.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(numberType);
+
+ serializer.serialize(null, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeNumber(SensitiveDataSerializer.REDACTED_NUMBER);
+ }
+
+ @Test
+ void serialize_PrimitiveIntType() throws IOException {
+ JavaType intType = typeFactory.constructType(int.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(intType);
+
+ serializer.serialize(123, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeNumber(SensitiveDataSerializer.REDACTED_NUMBER);
+ }
+
+ @Test
+ void serialize_PrimitiveLongType() throws IOException {
+ JavaType longType = typeFactory.constructType(long.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(longType);
+
+ serializer.serialize(123L, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeNumber(SensitiveDataSerializer.REDACTED_NUMBER);
+ }
+
+ @Test
+ void serialize_PrimitiveFloatType() throws IOException {
+ JavaType floatType = typeFactory.constructType(float.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(floatType);
+
+ serializer.serialize(123.45f, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeNumber(SensitiveDataSerializer.REDACTED_NUMBER);
+ }
+
+ @Test
+ void serialize_PrimitiveDoubleType() throws IOException {
+ JavaType doubleType = typeFactory.constructType(double.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(doubleType);
+
+ serializer.serialize(123.45, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeNumber(SensitiveDataSerializer.REDACTED_NUMBER);
+ }
+
+ @Test
+ void serialize_BooleanType() throws IOException {
+ JavaType booleanType = typeFactory.constructType(Boolean.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(booleanType);
+
+ serializer.serialize(true, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeBoolean(SensitiveDataSerializer.REDACTED_BOOLEAN);
+ }
+
+ @Test
+ void serialize_BooleanType_Null() throws IOException {
+ JavaType booleanType = typeFactory.constructType(Boolean.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(booleanType);
+
+ serializer.serialize(null, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeBoolean(SensitiveDataSerializer.REDACTED_BOOLEAN);
+ }
+
+ @Test
+ void serialize_PrimitiveBooleanType() throws IOException {
+ JavaType booleanType = typeFactory.constructType(boolean.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(booleanType);
+
+ serializer.serialize(true, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeBoolean(SensitiveDataSerializer.REDACTED_BOOLEAN);
+ }
+
+ @Test
+ void serialize_UnsupportedType() throws IOException {
+ JavaType unsupportedType = typeFactory.constructType(Object.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(unsupportedType);
+
+ serializer.serialize(new Object(), jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeNull();
+ }
+
+ @Test
+ void serialize_UnsupportedType_Null() throws IOException {
+ JavaType unsupportedType = typeFactory.constructType(Object.class);
+ SensitiveDataSerializer serializer = new SensitiveDataSerializer(unsupportedType);
+
+ serializer.serialize(null, jsonGenerator, serializerProvider);
+ verify(jsonGenerator).writeNull();
+ }
+}
\ No newline at end of file
diff --git a/java/common/org.eclipse.theia.cloud.common/src/test/java/org/eclipse/theia/cloud/common/util/NamingUtilTests.java b/java/common/org.eclipse.theia.cloud.common/src/test/java/org/eclipse/theia/cloud/common/util/NamingUtilTests.java
index d68f2d0a3..d0284d8f8 100644
--- a/java/common/org.eclipse.theia.cloud.common/src/test/java/org/eclipse/theia/cloud/common/util/NamingUtilTests.java
+++ b/java/common/org.eclipse.theia.cloud.common/src/test/java/org/eclipse/theia/cloud/common/util/NamingUtilTests.java
@@ -16,7 +16,10 @@
package org.eclipse.theia.cloud.common.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinitionSpec;
import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
import org.eclipse.theia.cloud.common.k8s.resource.session.SessionSpec;
import org.eclipse.theia.cloud.common.k8s.resource.workspace.Workspace;
@@ -30,6 +33,22 @@
*/
class NamingUtilTests {
+ @Test
+ void createName_AppDefinitionAndInstace() {
+ AppDefinition appDefinition = createAppDefinition();
+
+ String result = NamingUtil.createName(appDefinition, 1);
+ assertEquals("instance-1-some-app-definiti-381261d79c23", result);
+ }
+
+ @Test
+ void createName_AppDefinitionAndInstaceAndIdentifier() {
+ AppDefinition appDefinition = createAppDefinition();
+
+ String result = NamingUtil.createName(appDefinition, 1, "longidentifier");
+ assertEquals("instance-1-longidentif-some-app-de-381261d79c23", result);
+ }
+
@Test
void createName_SessionAndNullIdentifier() {
Session session = createSession();
@@ -94,6 +113,74 @@ void createName_WorkspaceAndIdentifier() {
assertEquals("ws-longidentif-some-userna-test-app-de-381261d79c23", result);
}
+ @Test
+ void createNameWithSuffix_AppDefinitionAndInstance() {
+ AppDefinition appDefinition = createAppDefinition();
+
+ String result = NamingUtil.createNameWithSuffix(appDefinition, 1, "internal");
+ assertEquals("instance-1-some-app-defini-381261d79c23-internal", result);
+ }
+
+ @Test
+ void createNameWithSuffix_Session() {
+ Session session = createSession();
+
+ String result = NamingUtil.createNameWithSuffix(session, "internal");
+ assertEquals("session-some-username-test-app-definit-426930ea37d7-internal", result);
+ }
+
+ @Test
+ void createNameWithSuffix_AppDefinitionWithLongSuffix() {
+ AppDefinition appDefinition = createAppDefinition();
+
+ String result = NamingUtil.createNameWithSuffix(appDefinition, 1, "very-long-suffix-for-testing");
+ // Ensure the suffix is preserved and the total length doesn't exceed limit
+ assertTrue(result.endsWith("-very-long-suffix-for-testing"));
+ assertTrue(result.length() <= NamingUtil.VALID_NAME_LIMIT);
+ assertTrue(result.startsWith("instance-1"));
+ }
+
+ @Test
+ void createNameWithSuffix_VerifyLengthAndSuffixPreservation() {
+ Session session = createSession();
+ AppDefinition appDefinition = createAppDefinition();
+
+ // Test with regular suffix
+ String sessionResult = NamingUtil.createNameWithSuffix(session, "internal");
+ String appDefResult = NamingUtil.createNameWithSuffix(appDefinition, 1, "internal");
+
+ // Verify all results end with the suffix
+ assertTrue(sessionResult.endsWith("-internal"));
+ assertTrue(appDefResult.endsWith("-internal"));
+
+ // Verify all results are within length limit
+ assertTrue(sessionResult.length() <= NamingUtil.VALID_NAME_LIMIT);
+ assertTrue(appDefResult.length() <= NamingUtil.VALID_NAME_LIMIT);
+
+ // Verify all results start with expected prefixes
+ assertTrue(sessionResult.startsWith("session-"));
+ assertTrue(appDefResult.startsWith("instance-1-"));
+
+ // Verify the results are still valid Kubernetes names (no invalid characters)
+ assertTrue(sessionResult.matches("[a-z0-9-]+"));
+ assertTrue(appDefResult.matches("[a-z0-9-]+"));
+ }
+
+ private AppDefinition createAppDefinition() {
+ AppDefinition appDefinition = new AppDefinition();
+ ObjectMeta objectMeta = new ObjectMeta();
+ objectMeta.setUid("6f1a8966-4d5a-41dc-82ba-381261d79c23");
+ appDefinition.setMetadata(objectMeta);
+ AppDefinitionSpec spec = new AppDefinitionSpec() {
+ @Override
+ public String getName() {
+ return "some-app-definition";
+ }
+ };
+ appDefinition.setSpec(spec);
+ return appDefinition;
+ }
+
private Session createSession() {
Session session = new Session();
ObjectMeta objectMeta = new ObjectMeta();
diff --git a/java/conversion/org.eclipse.theia.cloud.conversion/README.md b/java/conversion/org.eclipse.theia.cloud.conversion/README.md
index 38d3514c8..7e6014674 100644
--- a/java/conversion/org.eclipse.theia.cloud.conversion/README.md
+++ b/java/conversion/org.eclipse.theia.cloud.conversion/README.md
@@ -49,7 +49,7 @@ Or, if you don't have GraalVM installed, you can run the native executable build
./mvnw package -Pnative -Dquarkus.native.container-build=true
```
-You can then execute your native executable with: `./target/conversion-webhook-1.0.0-SNAPSHOT-runner`
+You can then execute your native executable with: `./target/conversion-webhook-1.2.0-SNAPSHOT-runner`
If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling.
diff --git a/java/conversion/org.eclipse.theia.cloud.conversion/pom.xml b/java/conversion/org.eclipse.theia.cloud.conversion/pom.xml
index 5bfd29a66..07d136338 100644
--- a/java/conversion/org.eclipse.theia.cloud.conversion/pom.xml
+++ b/java/conversion/org.eclipse.theia.cloud.conversion/pom.xml
@@ -9,7 +9,7 @@
org.eclipse.theia.cloudconf
- 0.12.0-SNAPSHOT
+ 1.2.0-SNAPSHOT../../common/maven-conf/
@@ -33,7 +33,7 @@
io.quarkus
- quarkus-resteasy-reactive-jackson
+ quarkus-rest-jackson
@@ -45,7 +45,7 @@
org.eclipse.theia.cloudcommon
- 0.12.0-SNAPSHOT
+ 1.2.0-SNAPSHOT
@@ -81,8 +81,8 @@
maven-compiler-plugin${maven-compiler-plugin.version}
- 17
- 17
+ 21
+ 21-parameters
@@ -135,4 +135,4 @@
-
\ No newline at end of file
+
diff --git a/java/conversion/org.eclipse.theia.cloud.conversion/src/main/java/org/eclipse/theia/cloud/conversion/ConversionEndpoint.java b/java/conversion/org.eclipse.theia.cloud.conversion/src/main/java/org/eclipse/theia/cloud/conversion/ConversionEndpoint.java
index cb7149ee6..1b46fe7d2 100644
--- a/java/conversion/org.eclipse.theia.cloud.conversion/src/main/java/org/eclipse/theia/cloud/conversion/ConversionEndpoint.java
+++ b/java/conversion/org.eclipse.theia.cloud.conversion/src/main/java/org/eclipse/theia/cloud/conversion/ConversionEndpoint.java
@@ -16,6 +16,7 @@
package org.eclipse.theia.cloud.conversion;
import org.eclipse.theia.cloud.conversion.mappers.appdefinition.AppDefinitionV1beta10Mapper;
+import org.eclipse.theia.cloud.conversion.mappers.appdefinition.AppDefinitionV1beta11Mapper;
import org.eclipse.theia.cloud.conversion.mappers.appdefinition.AppDefinitionV1beta8Mapper;
import org.eclipse.theia.cloud.conversion.mappers.appdefinition.AppDefinitionV1beta9Mapper;
import org.eclipse.theia.cloud.conversion.mappers.session.SessionV1beta6Mapper;
@@ -48,6 +49,7 @@ public ConversionEndpoint() {
appDefinitionController.registerMapper(new AppDefinitionV1beta8Mapper());
appDefinitionController.registerMapper(new AppDefinitionV1beta9Mapper());
appDefinitionController.registerMapper(new AppDefinitionV1beta10Mapper());
+ appDefinitionController.registerMapper(new AppDefinitionV1beta11Mapper());
this.workspaceController = new ConversionController();
workspaceController.registerMapper(new WorkspaceV1beta3Mapper());
diff --git a/java/conversion/org.eclipse.theia.cloud.conversion/src/main/java/org/eclipse/theia/cloud/conversion/mappers/appdefinition/AppDefinitionV1beta10Mapper.java b/java/conversion/org.eclipse.theia.cloud.conversion/src/main/java/org/eclipse/theia/cloud/conversion/mappers/appdefinition/AppDefinitionV1beta10Mapper.java
index 196517686..4f43669b2 100644
--- a/java/conversion/org.eclipse.theia.cloud.conversion/src/main/java/org/eclipse/theia/cloud/conversion/mappers/appdefinition/AppDefinitionV1beta10Mapper.java
+++ b/java/conversion/org.eclipse.theia.cloud.conversion/src/main/java/org/eclipse/theia/cloud/conversion/mappers/appdefinition/AppDefinitionV1beta10Mapper.java
@@ -15,23 +15,24 @@
********************************************************************************/
package org.eclipse.theia.cloud.conversion.mappers.appdefinition;
-import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.hub.AppDefinitionHub;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.v1beta10.AppDefinitionV1beta10;
import io.javaoperatorsdk.webhook.conversion.Mapper;
import io.javaoperatorsdk.webhook.conversion.TargetVersion;
+@SuppressWarnings("deprecation")
@TargetVersion("v1beta10")
-public class AppDefinitionV1beta10Mapper implements Mapper {
+public class AppDefinitionV1beta10Mapper implements Mapper {
@Override
- public AppDefinitionHub toHub(AppDefinition resource) {
+ public AppDefinitionHub toHub(AppDefinitionV1beta10 resource) {
return new AppDefinitionHub(resource);
}
@Override
- public AppDefinition fromHub(AppDefinitionHub hub) {
- return new AppDefinition(hub);
+ public AppDefinitionV1beta10 fromHub(AppDefinitionHub hub) {
+ return new AppDefinitionV1beta10(hub);
}
}
diff --git a/java/conversion/org.eclipse.theia.cloud.conversion/src/main/java/org/eclipse/theia/cloud/conversion/mappers/appdefinition/AppDefinitionV1beta11Mapper.java b/java/conversion/org.eclipse.theia.cloud.conversion/src/main/java/org/eclipse/theia/cloud/conversion/mappers/appdefinition/AppDefinitionV1beta11Mapper.java
new file mode 100644
index 000000000..815c19b15
--- /dev/null
+++ b/java/conversion/org.eclipse.theia.cloud.conversion/src/main/java/org/eclipse/theia/cloud/conversion/mappers/appdefinition/AppDefinitionV1beta11Mapper.java
@@ -0,0 +1,37 @@
+/********************************************************************************
+ * Copyright (C) 2023 EclipseSource and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+package org.eclipse.theia.cloud.conversion.mappers.appdefinition;
+
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.hub.AppDefinitionHub;
+
+import io.javaoperatorsdk.webhook.conversion.Mapper;
+import io.javaoperatorsdk.webhook.conversion.TargetVersion;
+
+@TargetVersion("v1beta11")
+public class AppDefinitionV1beta11Mapper implements Mapper {
+
+ @Override
+ public AppDefinitionHub toHub(AppDefinition resource) {
+ return new AppDefinitionHub(resource);
+ }
+
+ @Override
+ public AppDefinition fromHub(AppDefinitionHub hub) {
+ return new AppDefinition(hub);
+ }
+
+}
diff --git a/java/conversion/org.eclipse.theia.cloud.conversion/src/main/resources/application.properties b/java/conversion/org.eclipse.theia.cloud.conversion/src/main/resources/application.properties
index a269f8d04..eed059354 100644
--- a/java/conversion/org.eclipse.theia.cloud.conversion/src/main/resources/application.properties
+++ b/java/conversion/org.eclipse.theia.cloud.conversion/src/main/resources/application.properties
@@ -1,2 +1,3 @@
quarkus.http.ssl.certificate.files=/etc/webhook/certs/tls.crt
-quarkus.http.ssl.certificate.key-files=/etc/webhook/certs/tls.key
\ No newline at end of file
+quarkus.http.ssl.certificate.key-files=/etc/webhook/certs/tls.key
+quarkus.http.ssl.certificate.reload-period=604800
\ No newline at end of file
diff --git a/java/operator/README.md b/java/operator/README.md
new file mode 100644
index 000000000..40aca29fb
--- /dev/null
+++ b/java/operator/README.md
@@ -0,0 +1,43 @@
+# Theia Cloud Operator
+
+This folder contains the Java projects for the Theia Cloud operator, including:
+
+- **Default Theia Cloud Operator**: Provides a default implementation of the operator.
+- **Theia Cloud Operator Library**: The key functionality for the Theia Cloud Operator, to be used in your implementation.
+
+## Projects
+
+### Default Theia Cloud Operator
+
+This project implements the default Kubernetes Operator for Theia Cloud.
+
+#### Build and Run
+
+```sh
+mvn clean install
+java -jar target/defaultoperator-1.2.0-SNAPSHOT-jar-with-dependencies.jar
+```
+
+#### Debugging the Default Theia Cloud Operator
+
+To debug the Default Theia Cloud Operator, follow these steps:
+
+- **Set the Replicas**:
+
+ - Set the replicas of the operator in your Theia Cloud installation to 0.
+
+- **Start the Debug Config**:
+
+ - Launch the debug configuration with the appropriate flags for debugging.
+ - There are specific configurations for the [terraform test-configurations](../../terraform/test-configurations/) available.
+ - The configurations will prompt you for your Minikube IP address. To find out the IP address, run the following command in your terminal:
+
+```sh
+ minikube ip
+```
+
+_Note_: Ensure your local `kubectl` is set to the correct namespace (e.g., `theia-cloud`), as the debuggable operator will interact with your local Kubernetes cluster.
+
+### Theia Cloud Operator Library
+
+A shared library for building Theia Cloud Kubernetes operators.
diff --git a/java/operator/org.eclipse.theia.cloud.defaultoperator/pom.xml b/java/operator/org.eclipse.theia.cloud.defaultoperator/pom.xml
index 1a7d3eabc..b1ecdece2 100644
--- a/java/operator/org.eclipse.theia.cloud.defaultoperator/pom.xml
+++ b/java/operator/org.eclipse.theia.cloud.defaultoperator/pom.xml
@@ -11,7 +11,7 @@
org.eclipse.theia.cloudconf
- 0.12.0-SNAPSHOT
+ 1.2.0-SNAPSHOT../../common/maven-conf/
@@ -19,12 +19,12 @@
org.eclipse.theia.cloudoperator
- 0.12.0-SNAPSHOT
+ 1.2.0-SNAPSHOTorg.eclipse.theia.cloudcommon
- 0.12.0-SNAPSHOT
+ 1.2.0-SNAPSHOTorg.json
@@ -77,8 +77,8 @@
maven-compiler-plugin${maven-compiler-plugin.version}
- 17
- 17
+ 21
+ 21
@@ -109,4 +109,41 @@
-
\ No newline at end of file
+
+
+ sentry
+
+
+ sentry
+ true
+
+
+
+
+
+ io.sentry
+ sentry-maven-plugin
+ 0.10.0
+ true
+
+ aet
+ theia
+ ${env.SENTRY_AUTH_TOKEN}
+ https://sentry.aet.cit.tum.de
+ true
+
+
+
+
+ uploadSourceBundle
+ validateSdkDependencyVersions
+
+
+
+
+
+
+
+
+
+
diff --git a/java/operator/org.eclipse.theia.cloud.defaultoperator/sentry.properties b/java/operator/org.eclipse.theia.cloud.defaultoperator/sentry.properties
new file mode 100644
index 000000000..4524a9fc1
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.defaultoperator/sentry.properties
@@ -0,0 +1,6 @@
+# Add your Sentry DSN here
+dsn=https://a3851baf2ece0c6bec13e1c99fd3111f@sentry.aet.cit.tum.de/13
+# Add data like request headers and IP for users,
+# see https://docs.sentry.io/platforms/java/data-management/data-collected/ for more info
+send-default-pii=false
+traces-sample-rate=1.0
\ No newline at end of file
diff --git a/java/operator/org.eclipse.theia.cloud.defaultoperator/src/main/java/org/eclipse/theia/cloud/defaultoperator/DefaultTheiaCloudOperatorLauncher.java b/java/operator/org.eclipse.theia.cloud.defaultoperator/src/main/java/org/eclipse/theia/cloud/defaultoperator/DefaultTheiaCloudOperatorLauncher.java
index c07b8736d..da51aa634 100644
--- a/java/operator/org.eclipse.theia.cloud.defaultoperator/src/main/java/org/eclipse/theia/cloud/defaultoperator/DefaultTheiaCloudOperatorLauncher.java
+++ b/java/operator/org.eclipse.theia.cloud.defaultoperator/src/main/java/org/eclipse/theia/cloud/defaultoperator/DefaultTheiaCloudOperatorLauncher.java
@@ -15,14 +15,33 @@
********************************************************************************/
package org.eclipse.theia.cloud.defaultoperator;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.eclipse.theia.cloud.operator.LeaderElectionTheiaCloudOperatorLauncher;
import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
import org.eclipse.theia.cloud.operator.di.AbstractTheiaCloudOperatorModule;
+import io.sentry.Sentry;
+
public class DefaultTheiaCloudOperatorLauncher extends LeaderElectionTheiaCloudOperatorLauncher {
- public static void main(String[] args) throws InterruptedException {
- new DefaultTheiaCloudOperatorLauncher().runMain(args);
+ private static final Logger LOGGER = LogManager.getLogger(DefaultTheiaCloudOperatorLauncher.class);
+
+ public static void main(String[] args) {
+ try {
+ Sentry.configureScope(scope -> {
+ scope.setTag("component", "operator");
+ });
+ new DefaultTheiaCloudOperatorLauncher().runMain(args);
+ } catch (InterruptedException e) {
+ LOGGER.error("Operator interrupted", e);
+ Thread.currentThread().interrupt();
+ System.exit(1);
+ } catch (Throwable t) {
+ LOGGER.error("Fatal error starting operator", t);
+ t.printStackTrace();
+ System.exit(1);
+ }
}
@Override
diff --git a/java/operator/org.eclipse.theia.cloud.operator/pom.xml b/java/operator/org.eclipse.theia.cloud.operator/pom.xml
index 8e3e081c7..c49a34589 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/pom.xml
+++ b/java/operator/org.eclipse.theia.cloud.operator/pom.xml
@@ -11,7 +11,7 @@
org.eclipse.theia.cloudconf
- 0.12.0-SNAPSHOT
+ 1.2.0-SNAPSHOT../../common/maven-conf/
@@ -19,7 +19,7 @@
org.eclipse.theia.cloudcommon
- 0.12.0-SNAPSHOT
+ 1.2.0-SNAPSHOTorg.json
@@ -51,19 +51,38 @@
log4j-core${log4j.version}
-
info.picoclipicocli${picocli.version}
-
org.slf4jslf4j-simple${slf4j.version}
-
+
+ io.sentry
+ sentry
+ 8.0.0
+
+
+ io.sentry
+ sentry-okhttp
+ 8.0.0
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit-jupiter.version}
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
@@ -72,11 +91,47 @@
maven-compiler-plugin${maven-compiler-plugin.version}
- 17
- 17
+ 21
+ 21
-
\ No newline at end of file
+
+
+ sentry
+
+
+ sentry
+ true
+
+
+
+
+
+ io.sentry
+ sentry-maven-plugin
+ 0.10.0
+ true
+
+ aet
+ theia
+ ${env.SENTRY_AUTH_TOKEN}
+ https://sentry.aet.cit.tum.de
+
+
+
+
+ uploadSourceBundle
+ validateSdkDependencyVersions
+
+
+
+
+
+
+
+
+
+
diff --git a/java/operator/org.eclipse.theia.cloud.operator/sentry.properties b/java/operator/org.eclipse.theia.cloud.operator/sentry.properties
new file mode 120000
index 000000000..535aae8f0
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/sentry.properties
@@ -0,0 +1 @@
+../org.eclipse.theia.cloud.defaultoperator/sentry.properties
\ No newline at end of file
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/BasicTheiaCloudOperator.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/BasicTheiaCloudOperator.java
index 2b8d1125b..56023f51b 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/BasicTheiaCloudOperator.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/BasicTheiaCloudOperator.java
@@ -20,13 +20,18 @@
import java.time.Instant;
import java.time.temporal.ChronoUnit;
+import java.util.ArrayDeque;
+import java.util.Deque;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.LogManager;
@@ -35,20 +40,30 @@
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
import org.eclipse.theia.cloud.common.k8s.resource.workspace.Workspace;
+import org.eclipse.theia.cloud.common.tracing.TraceContext;
+import org.eclipse.theia.cloud.common.tracing.Tracing;
import org.eclipse.theia.cloud.operator.handler.appdef.AppDefinitionHandler;
import org.eclipse.theia.cloud.operator.handler.session.SessionHandler;
import org.eclipse.theia.cloud.operator.handler.ws.WorkspaceHandler;
import org.eclipse.theia.cloud.operator.plugins.OperatorPlugin;
import org.eclipse.theia.cloud.operator.util.SpecWatch;
+import io.sentry.ISpan;
+import io.sentry.Sentry;
+
import com.google.inject.Inject;
import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.client.KubernetesClientException;
public class BasicTheiaCloudOperator implements TheiaCloudOperator {
private static final ScheduledExecutorService STOP_EXECUTOR = Executors.newSingleThreadScheduledExecutor();
private static final ScheduledExecutorService WATCH_EXECUTOR = Executors.newSingleThreadScheduledExecutor();
+ private static final int SESSION_EVENT_QUEUE_SIZE = 1000;
+ private static final long SESSION_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS = 30L;
+ private static final int APPDEF_INIT_MAX_RETRIES = 10;
+ private static final long APPDEF_INIT_RETRY_DELAY_MS = 1000L;
private static final Logger LOGGER = LogManager.getLogger(BasicTheiaCloudOperator.class);
@@ -80,9 +95,15 @@ public class BasicTheiaCloudOperator implements TheiaCloudOperator {
private final Map sessionCache = new ConcurrentHashMap<>();
private final Set> watches = new LinkedHashSet<>();
+ // Session event scheduling: per-session serialization, cross-session
+ // parallelism.
+ private ExecutorService sessionExecutor;
+ private final Map sessionStates = new ConcurrentHashMap<>();
+
@Override
public void start() {
this.operatorPlugins.forEach(plugin -> plugin.start());
+ initSessionExecutor();
watches.add(initAppDefinitionsAndWatchForChanges());
watches.add(initWorkspacesAndWatchForChanges());
watches.add(initSessionsAndWatchForChanges());
@@ -91,18 +112,71 @@ public void start() {
WATCH_EXECUTOR.scheduleWithFixedDelay(this::lookForIdleWatches, 1, 1, TimeUnit.MINUTES);
}
+ @Override
+ public void stop() {
+ if (sessionExecutor == null) {
+ return;
+ }
+ sessionExecutor.shutdown();
+ try {
+ if (!sessionExecutor.awaitTermination(SESSION_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
+ sessionExecutor.shutdownNow();
+ }
+ } catch (InterruptedException e) {
+ sessionExecutor.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
+ }
+
protected SpecWatch initAppDefinitionsAndWatchForChanges() {
+ for (int attempt = 1; attempt <= APPDEF_INIT_MAX_RETRIES; attempt++) {
+ try {
+ resourceClient.appDefinitions().list().forEach(this::initAppDefinition);
+ SpecWatch watcher = new SpecWatch<>(appDefinitionCache, this::handleAppDefnitionEvent,
+ "App Definition", COR_ID_APPDEFINITIONPREFIX);
+ resourceClient.appDefinitions().operation().watch(watcher);
+ return watcher;
+ } catch (Exception e) {
+ boolean retryable = isRetryableAppDefinitionInitError(e);
+ if (retryable && attempt < APPDEF_INIT_MAX_RETRIES) {
+ LOGGER.warn(formatLogMessage(TheiaCloudOperatorLauncher.COR_ID_INIT,
+ "AppDefinition watch init failed with retryable error (attempt " + attempt
+ + "/" + APPDEF_INIT_MAX_RETRIES + "), retrying in "
+ + APPDEF_INIT_RETRY_DELAY_MS + "ms"),
+ e);
+ sleepQuietly(APPDEF_INIT_RETRY_DELAY_MS);
+ continue;
+ }
+
+ LOGGER.error(formatLogMessage(TheiaCloudOperatorLauncher.COR_ID_INIT,
+ "Error while initializing app definitions watch"), e);
+ System.exit(-1);
+ throw new IllegalStateException();
+ }
+ }
+
+ throw new IllegalStateException("Unreachable");
+ }
+
+ private boolean isRetryableAppDefinitionInitError(Exception e) {
+ Throwable current = e;
+ while (current != null) {
+ if (current instanceof KubernetesClientException kce) {
+ if (kce.getCode() == 429 && kce.getMessage() != null
+ && kce.getMessage().contains("storage is (re)initializing")) {
+ return true;
+ }
+ }
+ current = current.getCause();
+ }
+ return false;
+ }
+
+ private void sleepQuietly(long millis) {
try {
- resourceClient.appDefinitions().list().forEach(this::initAppDefinition);
- SpecWatch watcher = new SpecWatch<>(appDefinitionCache, this::handleAppDefnitionEvent,
- "App Definition", COR_ID_APPDEFINITIONPREFIX);
- resourceClient.appDefinitions().operation().watch(watcher);
- return watcher;
- } catch (Exception e) {
- LOGGER.error(formatLogMessage(TheiaCloudOperatorLauncher.COR_ID_INIT,
- "Error while initializing app definitions watch"), e);
- System.exit(-1);
- throw new IllegalStateException();
+ Thread.sleep(millis);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
}
}
@@ -124,7 +198,7 @@ protected SpecWatch initWorkspacesAndWatchForChanges() {
protected SpecWatch initSessionsAndWatchForChanges() {
try {
resourceClient.sessions().list().forEach(this::initSession);
- SpecWatch watcher = new SpecWatch<>(sessionCache, this::handleSessionEvent, "Session",
+ SpecWatch watcher = new SpecWatch<>(sessionCache, this::scheduleSessionEvent, "Session",
COR_ID_SESSIONPREFIX);
resourceClient.sessions().operation().watch(watcher);
return watcher;
@@ -152,28 +226,35 @@ protected void initWorkspace(Workspace resource) {
protected void initSession(Session resource) {
sessionCache.put(resource.getMetadata().getUid(), resource);
String uid = resource.getMetadata().getUid();
- handleSessionEvent(Watcher.Action.ADDED, uid, TheiaCloudOperatorLauncher.COR_ID_INIT);
+ scheduleSessionEvent(Watcher.Action.ADDED, uid, TheiaCloudOperatorLauncher.COR_ID_INIT);
+ }
+
+ protected void scheduleSessionEvent(Watcher.Action action, String uid, String correlationId) {
+ Session session = sessionCache.get(uid);
+ SessionState state = sessionStates.computeIfAbsent(uid, k -> new SessionState());
+ boolean shouldSchedule = false;
+ synchronized (state.lock) {
+ state.queue.add(new SessionEvent(action, correlationId, session));
+ // Ensure only one runner per session UID.
+ if (!state.running) {
+ state.running = true;
+ shouldSchedule = true;
+ }
+ }
+ if (shouldSchedule) {
+ sessionExecutor.execute(() -> drainSessionEvents(uid, state));
+ }
}
protected void handleAppDefnitionEvent(Watcher.Action action, String uid, String correlationId) {
try {
AppDefinition appDefinition = appDefinitionCache.get(uid);
switch (action) {
- case ADDED:
- appDefinitionAddedHandler.appDefinitionAdded(appDefinition, correlationId);
- break;
- case DELETED:
- appDefinitionAddedHandler.appDefinitionDeleted(appDefinition, correlationId);
- break;
- case MODIFIED:
- appDefinitionAddedHandler.appDefinitionModified(appDefinition, correlationId);
- break;
- case ERROR:
- appDefinitionAddedHandler.appDefinitionErrored(appDefinition, correlationId);
- break;
- case BOOKMARK:
- appDefinitionAddedHandler.appDefinitionBookmarked(appDefinition, correlationId);
- break;
+ case ADDED -> appDefinitionAddedHandler.appDefinitionAdded(appDefinition, correlationId);
+ case DELETED -> appDefinitionAddedHandler.appDefinitionDeleted(appDefinition, correlationId);
+ case MODIFIED -> appDefinitionAddedHandler.appDefinitionModified(appDefinition, correlationId);
+ case ERROR -> appDefinitionAddedHandler.appDefinitionErrored(appDefinition, correlationId);
+ case BOOKMARK -> appDefinitionAddedHandler.appDefinitionBookmarked(appDefinition, correlationId);
}
} catch (Exception e) {
LOGGER.error(formatLogMessage(correlationId, "Error while handling app definitions"), e);
@@ -181,52 +262,153 @@ protected void handleAppDefnitionEvent(Watcher.Action action, String uid, String
}
protected void handleSessionEvent(Watcher.Action action, String uid, String correlationId) {
+ Session session = sessionCache.get(uid);
+ if (session == null) {
+ return;
+ }
+
+ // Extract trace context from Session annotations (if propagated from service)
+ Optional traceContext = TraceContext.fromMetadata(session.getMetadata());
+
+ ISpan span = null;
try {
- Session session = sessionCache.get(uid);
+ String name = "session." + action.name().toLowerCase();
+ String operation = action.name() + " session " + session.getSpec().getName();
+ span = Tracing.continueTraceAsync(traceContext, name, operation);
+ span.setTag("session.name", session.getSpec().getName());
+ span.setTag("app_definition", session.getSpec().getAppDefinition());
+ span.setTag("user", session.getSpec().getUser());
+
+ span.setTag("action", action.name());
+ span.setData("correlation_id", correlationId);
+
switch (action) {
- case ADDED:
- sessionHandler.sessionAdded(session, correlationId);
- break;
- case DELETED:
- sessionHandler.sessionDeleted(session, correlationId);
- break;
- case MODIFIED:
- sessionHandler.sessionModified(session, correlationId);
- break;
- case ERROR:
- sessionHandler.sessionErrored(session, correlationId);
- break;
- case BOOKMARK:
- sessionHandler.sessionBookmarked(session, correlationId);
- break;
+ case ADDED -> sessionHandler.sessionAdded(session, correlationId, span);
+ case DELETED -> sessionHandler.sessionDeleted(session, correlationId, span);
+ case MODIFIED -> sessionHandler.sessionModified(session, correlationId, span);
+ case ERROR -> sessionHandler.sessionErrored(session, correlationId, span);
+ case BOOKMARK -> sessionHandler.sessionBookmarked(session, correlationId, span);
}
+
+ Tracing.finishSuccess(span);
} catch (Exception e) {
LOGGER.error(formatLogMessage(correlationId, "Error while handling sessions"), e);
+ if (span != null) {
+ Tracing.finishError(span, e);
+ }
+ if (!arguments.isContinueOnException()) {
+ System.exit(-1);
+ }
+ }
+ }
+
+ private void handleSessionEvent(SessionEvent event) {
+ if (event.session == null) {
+ throw new IllegalArgumentException("Session must not be null");
+ }
+ // Extract trace context from Session annotations (if propagated from service)
+ Optional traceContext = TraceContext.fromMetadata(event.session.getMetadata());
+
+ ISpan span = null;
+ try {
+ String name = "session." + event.action.name().toLowerCase();
+ String operation = event.action.name() + " session " + event.session.getSpec().getName();
+ span = Tracing.continueTraceAsync(traceContext, name, operation);
+ span.setTag("session.name", event.session.getSpec().getName());
+ span.setTag("app_definition", event.session.getSpec().getAppDefinition());
+ span.setTag("user", event.session.getSpec().getUser());
+
+ span.setTag("action", event.action.name());
+ span.setData("correlation_id", event.correlationId);
+
+ switch (event.action) {
+ case ADDED -> sessionHandler.sessionAdded(event.session, event.correlationId, span);
+ case DELETED -> sessionHandler.sessionDeleted(event.session, event.correlationId, span);
+ case MODIFIED -> sessionHandler.sessionModified(event.session, event.correlationId, span);
+ case ERROR -> sessionHandler.sessionErrored(event.session, event.correlationId, span);
+ case BOOKMARK -> sessionHandler.sessionBookmarked(event.session, event.correlationId, span);
+ }
+ Tracing.finishSuccess(span);
+ } catch (Exception e) {
+ LOGGER.error(formatLogMessage(event.correlationId, "Error while handling sessions"), e);
+ if (span != null) {
+ Tracing.finishError(span, e);
+ }
if (!arguments.isContinueOnException()) {
System.exit(-1);
}
}
}
+ private void initSessionExecutor() {
+ int threads = arguments.getSessionHandlerThreads();
+ sessionExecutor = new ThreadPoolExecutor(threads, threads, 0L, TimeUnit.MILLISECONDS,
+ new ArrayBlockingQueue<>(SESSION_EVENT_QUEUE_SIZE), new ThreadPoolExecutor.CallerRunsPolicy());
+ }
+
+ private void drainSessionEvents(String uid, SessionState state) {
+ while (true) {
+ SessionEvent event;
+ synchronized (state.lock) {
+ event = state.queue.poll();
+ if (event == null) {
+ state.running = false;
+ break;
+ }
+ }
+ try {
+ handleSessionEvent(event);
+ } catch (IllegalArgumentException e) {
+ LOGGER.error(formatLogMessage(event.correlationId, "Error while handling sessions"), e);
+ Sentry.captureException(e);
+ }
+ }
+ tryCleanupSessionState(uid, state);
+ }
+
+ private void tryCleanupSessionState(String uid, SessionState state) {
+ SessionState currentState = sessionStates.get(uid);
+ if (currentState == null) {
+ return;
+ }
+ synchronized (currentState.lock) {
+ if (currentState != state) {
+ return;
+ }
+ if (!currentState.queue.isEmpty() || currentState.running) {
+ return;
+ }
+ sessionStates.remove(uid, currentState);
+ }
+ }
+
+ private static final class SessionState {
+ private final Deque queue = new ArrayDeque<>();
+ private final Object lock = new Object();
+ private boolean running;
+ }
+
+ private static final class SessionEvent {
+ private final Watcher.Action action;
+ private final String correlationId;
+ private final Session session;
+
+ private SessionEvent(Watcher.Action action, String correlationId, Session session) {
+ this.action = action;
+ this.correlationId = correlationId;
+ this.session = session;
+ }
+ }
+
protected void handleWorkspaceEvent(Watcher.Action action, String uid, String correlationId) {
try {
Workspace workspace = workspaceCache.get(uid);
switch (action) {
- case ADDED:
- workspaceHandler.workspaceAdded(workspace, correlationId);
- break;
- case DELETED:
- workspaceHandler.workspaceDeleted(workspace, correlationId);
- break;
- case MODIFIED:
- workspaceHandler.workspaceModified(workspace, correlationId);
- break;
- case ERROR:
- workspaceHandler.workspaceErrored(workspace, correlationId);
- break;
- case BOOKMARK:
- workspaceHandler.workspaceBookmarked(workspace, correlationId);
- break;
+ case ADDED -> workspaceHandler.workspaceAdded(workspace, correlationId);
+ case DELETED -> workspaceHandler.workspaceDeleted(workspace, correlationId);
+ case MODIFIED -> workspaceHandler.workspaceModified(workspace, correlationId);
+ case ERROR -> workspaceHandler.workspaceErrored(workspace, correlationId);
+ case BOOKMARK -> workspaceHandler.workspaceBookmarked(workspace, correlationId);
}
} catch (Exception e) {
LOGGER.error(formatLogMessage(correlationId, "Error while handling workspaces"), e);
@@ -260,8 +442,10 @@ protected void stopTimedOutSessions() {
}
/**
- * If watches have not been called at all (neither reconnecting calls or actual actions), this might mean that the
- * watch can't communicate with the kube API anymore. In this case we want to hand over to a different operator
+ * If watches have not been called at all (neither reconnecting calls or actual
+ * actions), this might mean that the
+ * watch can't communicate with the kube API anymore. In this case we want to
+ * hand over to a different operator
* which will start up fresh watches.
*/
protected void lookForIdleWatches() {
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/LeaderElectionTheiaCloudOperatorLauncher.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/LeaderElectionTheiaCloudOperatorLauncher.java
index 8fbd61092..3e9d6d895 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/LeaderElectionTheiaCloudOperatorLauncher.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/LeaderElectionTheiaCloudOperatorLauncher.java
@@ -36,6 +36,10 @@
import io.fabric8.kubernetes.client.extended.leaderelection.LeaderElectionConfigBuilder;
import io.fabric8.kubernetes.client.extended.leaderelection.LeaderElector;
import io.fabric8.kubernetes.client.extended.leaderelection.resourcelock.LeaseLock;
+import io.fabric8.kubernetes.client.okhttp.OkHttpClientFactory;
+import io.sentry.okhttp.SentryOkHttpEventListener;
+import io.sentry.okhttp.SentryOkHttpInterceptor;
+import okhttp3.OkHttpClient;
public abstract class LeaderElectionTheiaCloudOperatorLauncher extends TheiaCloudOperatorLauncher {
@@ -64,8 +68,18 @@ protected void runLeaderElection(TheiaCloudOperatorArguments args) {
Config k8sConfig = new ConfigBuilder().build();
- try (KubernetesClient k8sClient = new KubernetesClientBuilder().withConfig(k8sConfig).build()) {
+ try (KubernetesClient k8sClient = new KubernetesClientBuilder()
+ .withConfig(k8sConfig)
+ .withHttpClientFactory(new OkHttpClientFactory() {
+ @Override
+ protected void additionalConfig(OkHttpClient.Builder builder) {
+ builder.addInterceptor(new SentryOkHttpInterceptor());
+ builder.eventListener(new SentryOkHttpEventListener());
+ }
+ })
+ .build()) {
String leaseLockNamespace = k8sClient.getNamespace();
+ LOGGER.info(formatLogMessage(COR_ID_INIT, "Using namespace: " + leaseLockNamespace));
LeaderElectionConfig leaderElectionConfig = new LeaderElectionConfigBuilder()//
.withReleaseOnCancel(true)//
@@ -88,12 +102,20 @@ protected void runLeaderElection(TheiaCloudOperatorArguments args) {
.build();
LeaderElector leaderElector = k8sClient.leaderElector().withConfig(leaderElectionConfig).build();
leaderElector.run();
+ } catch (Exception e) {
+ LOGGER.error(formatLogMessage(COR_ID_INIT, "Error during leader election"), e);
+ throw new RuntimeException("Failed to start leader election", e);
}
}
protected void onStartLeading() {
LOGGER.info(formatLogMessage(COR_ID_INIT, "Elected as new leader!"));
- startOperatorAsLeader(args);
+ try {
+ startOperatorAsLeader(args);
+ } catch (Exception e) {
+ LOGGER.error(formatLogMessage(COR_ID_INIT, "Error starting operator as leader"), e);
+ throw new RuntimeException("Failed to start operator as leader", e);
+ }
}
protected void onStopLeading() {
@@ -106,14 +128,19 @@ protected void onNewLeader(String newLeader) {
}
protected void startOperatorAsLeader(TheiaCloudOperatorArguments arguments) {
- AbstractTheiaCloudOperatorModule module = createModule(arguments);
- LOGGER.info(formatLogMessage(COR_ID_INIT, "Using " + module.getClass().getName() + " as DI module"));
-
- Injector injector = Guice.createInjector(module);
- TheiaCloudOperator theiaCloud = injector.getInstance(TheiaCloudOperator.class);
-
- LOGGER.info(formatLogMessage(COR_ID_INIT, "Launching Theia Cloud Now"));
- theiaCloud.start();
+ try {
+ AbstractTheiaCloudOperatorModule module = createModule(arguments);
+ LOGGER.info(formatLogMessage(COR_ID_INIT, "Using " + module.getClass().getName() + " as DI module"));
+
+ Injector injector = Guice.createInjector(module);
+ TheiaCloudOperator theiaCloud = injector.getInstance(TheiaCloudOperator.class);
+
+ LOGGER.info(formatLogMessage(COR_ID_INIT, "Launching Theia Cloud Now"));
+ theiaCloud.start();
+ } catch (Exception e) {
+ LOGGER.error(formatLogMessage(COR_ID_INIT, "Error during operator initialization"), e);
+ throw e;
+ }
}
@Override
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/TheiaCloudOperator.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/TheiaCloudOperator.java
index eb32d73f9..aafb1fc58 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/TheiaCloudOperator.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/TheiaCloudOperator.java
@@ -20,4 +20,5 @@ public interface TheiaCloudOperator {
void start();
+ void stop();
}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/TheiaCloudOperatorArguments.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/TheiaCloudOperatorArguments.java
index 35ef3b8a6..b7c27361e 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/TheiaCloudOperatorArguments.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/TheiaCloudOperatorArguments.java
@@ -17,6 +17,7 @@
package org.eclipse.theia.cloud.operator;
import picocli.CommandLine.Option;
+import java.util.logging.Logger;
public class TheiaCloudOperatorArguments {
@@ -64,7 +65,10 @@ public enum BandwidthLimiter {
"--sessionsPerUser" }, description = "Number of active sessions a single user is allowed to start.", required = false)
private Integer sessionsPerUser;
- @Option(names = { "--appId" }, description = "Application ID necessary for service calls", required = false)
+ @Option(names = { "--serviceAuthToken" }, description = "Service authentication token necessary for service calls", required = false)
+ private String serviceAuthToken;
+
+ @Option(names = { "--appId" }, description = "(Deprecated) Use --serviceAuthToken instead. Application ID necessary for service calls", required = false, hidden = true)
private String appId;
@Option(names = {
@@ -114,6 +118,10 @@ public enum BandwidthLimiter {
"--maxWatchIdleTime" }, description = "When a kubernetes watcher is idle for more than this time (in milliseconds) we assume that there is a problem and restart.", required = false)
private long maxWatchIdleTime = 1000 * 60 * 60; // 1 Hour
+ @Option(names = {
+ "--sessionHandlerThreads" }, description = "Number of threads used to handle session events.", required = false)
+ private Integer sessionHandlerThreads;
+
@Option(names = {
"--continueOnException" }, description = "Whether the operator will continue to run in case of unexpected exceptions.", required = false)
private boolean continueOnException;
@@ -122,6 +130,27 @@ public enum BandwidthLimiter {
"--oAuth2ProxyVersion" }, description = "The version to use of the quay.io/oauth2-proxy/oauth2-proxy image.", required = false, defaultValue = "latest")
private String oAuth2ProxyVersion;
+ @Option(names = {
+ "--enableBuildCaching" }, description = "Whether to enable caching of Theia application builds.", required = false)
+ private boolean enableBuildCaching = false;
+
+ @Option(names = { "--buildCacheUrl", "--gradleBuildCacheUrl" }, description = "The URL of the remote Gradle build cache server.", required = false)
+ private String buildCacheUrl;
+
+ @Option(names = { "--bazelBuildCacheUrl" }, description = "The URL of the remote Bazel build cache server.", required = false)
+ private String bazelBuildCacheUrl;
+
+ @Option(names = {
+ "--enableBuildCachePush" }, description = "Whether sessions are allowed to push to the build cache.", required = false)
+ private boolean enableBuildCachePush = false;
+
+ @Option(names = {
+ "--enableDependencyCaching" }, description = "Whether to enable the dependency cache (Reposilite).", required = false)
+ private boolean enableDependencyCaching = false;
+
+ @Option(names = { "--dependencyCacheUrl" }, description = "The URL of the dependency cache server (Reposilite).", required = false)
+ private String dependencyCacheUrl;
+
public boolean isUseKeycloak() {
return useKeycloak;
}
@@ -162,8 +191,20 @@ public Integer getMonitorInterval() {
return monitorInterval;
}
+ /**
+ * @return the configured service auth token
+ */
+ public String getServiceAuthToken() {
+ return getServiceAuthTokenWithFallback();
+ }
+
+ /**
+ * @deprecated Use {@link #getServiceAuthToken()} instead. This method is maintained for backwards compatibility.
+ * @return the configured service auth token
+ */
+ @Deprecated(since = "1.2.0", forRemoval = true)
public String getAppId() {
- return appId;
+ return getServiceAuthTokenWithFallback();
}
public String getInstancesHost() {
@@ -213,6 +254,24 @@ public int getLeaderRetryPeriod() {
public long getMaxWatchIdleTime() {
return maxWatchIdleTime;
}
+ /**
+ * Gets the number of threads to use for session event handling.
+ *
+ * If a specific thread count is configured and greater than zero, returns that value.
+ * Otherwise, calculates a default based on the number of available CPU cores,
+ * with a formula of (cores * 2) bounded within the range [4, 32].
+ *
+ * @return the number of session handler threads
+ */
+ public int getSessionHandlerThreads() {
+ if (sessionHandlerThreads != null && sessionHandlerThreads > 0) {
+ return sessionHandlerThreads;
+ }
+ int cores = Runtime.getRuntime().availableProcessors();
+ // Number of cores * 2
+ // Bounded within [4, 32]
+ return Math.min(32, Math.max(4, cores * 2));
+ }
public boolean isContinueOnException() {
return continueOnException;
@@ -221,11 +280,74 @@ public boolean isContinueOnException() {
public String getOAuth2ProxyVersion() {
return oAuth2ProxyVersion;
}
+
+ /**
+ * Get the service auth token with fallback to deprecated app id argument.
+ * Logs a deprecation warning if the old argument is used.
+ */
+ private String getServiceAuthTokenWithFallback() {
+ if (serviceAuthToken != null) {
+ return serviceAuthToken;
+ }
+
+ if (appId != null) {
+ Logger logger = Logger.getLogger(TheiaCloudOperatorArguments.class.getName());
+ logger.warning("Using deprecated command line argument '--appId'. " +
+ "Please migrate to '--serviceAuthToken' in your configuration.");
+ return appId;
+ }
+
+ return null;
+ }
+
+ /**
+ * Validates that required argument combinations are satisfied.
+ * Must be called after argument parsing.
+ *
+ * @throws IllegalArgumentException if a caching flag is enabled but its URL is missing
+ */
+ public void validate() {
+ if (enableBuildCaching
+ && (buildCacheUrl == null || buildCacheUrl.trim().isEmpty())
+ && (bazelBuildCacheUrl == null || bazelBuildCacheUrl.trim().isEmpty())) {
+ throw new IllegalArgumentException(
+ "At least one of --buildCacheUrl or --bazelBuildCacheUrl is required when --enableBuildCaching is set");
+ }
+ if (enableDependencyCaching && (dependencyCacheUrl == null || dependencyCacheUrl.trim().isEmpty())) {
+ throw new IllegalArgumentException(
+ "--dependencyCacheUrl is required when --enableDependencyCaching is set");
+ }
+ }
+
+ public boolean isEnableBuildCaching() {
+ return enableBuildCaching;
+ }
+
+ public String getBuildCacheUrl() {
+ return buildCacheUrl;
+ }
+
+ public String getBazelBuildCacheUrl() {
+ return bazelBuildCacheUrl;
+ }
+
+ public boolean isEnableBuildCachePush() {
+ return enableBuildCachePush;
+ }
+
+ public boolean isEnableDependencyCaching() {
+ return enableDependencyCaching;
+ }
+
+ public String getDependencyCacheUrl() {
+ return dependencyCacheUrl;
+ }
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
+ result = prime * result + ((serviceAuthToken == null) ? 0 : serviceAuthToken.hashCode());
result = prime * result + ((appId == null) ? 0 : appId.hashCode());
result = prime * result + ((bandwidthLimiter == null) ? 0 : bandwidthLimiter.hashCode());
result = prime * result + ((cloudProvider == null) ? 0 : cloudProvider.hashCode());
@@ -244,6 +366,7 @@ public int hashCode() {
result = prime * result + (int) (maxWatchIdleTime ^ (maxWatchIdleTime >>> 32));
result = prime * result + ((monitorInterval == null) ? 0 : monitorInterval.hashCode());
result = prime * result + ((requestedStorage == null) ? 0 : requestedStorage.hashCode());
+ result = prime * result + ((sessionHandlerThreads == null) ? 0 : sessionHandlerThreads.hashCode());
result = prime * result + ((serviceUrl == null) ? 0 : serviceUrl.hashCode());
result = prime * result + ((sessionsPerUser == null) ? 0 : sessionsPerUser.hashCode());
result = prime * result + ((storageClassName == null) ? 0 : storageClassName.hashCode());
@@ -251,6 +374,12 @@ public int hashCode() {
result = prime * result + (usePaths ? 1231 : 1237);
result = prime * result + ((wondershaperImage == null) ? 0 : wondershaperImage.hashCode());
result = prime * result + ((oAuth2ProxyVersion == null) ? 0 : oAuth2ProxyVersion.hashCode());
+ result = prime * result + (enableBuildCaching ? 1231 : 1237);
+ result = prime * result + ((buildCacheUrl == null) ? 0 : buildCacheUrl.hashCode());
+ result = prime * result + ((bazelBuildCacheUrl == null) ? 0 : bazelBuildCacheUrl.hashCode());
+ result = prime * result + (enableBuildCachePush ? 1231 : 1237);
+ result = prime * result + (enableDependencyCaching ? 1231 : 1237);
+ result = prime * result + ((dependencyCacheUrl == null) ? 0 : dependencyCacheUrl.hashCode());
return result;
}
@@ -263,6 +392,11 @@ public boolean equals(Object obj) {
if (getClass() != obj.getClass())
return false;
TheiaCloudOperatorArguments other = (TheiaCloudOperatorArguments) obj;
+ if (serviceAuthToken == null) {
+ if (other.serviceAuthToken != null)
+ return false;
+ } else if (!serviceAuthToken.equals(other.serviceAuthToken))
+ return false;
if (appId == null) {
if (other.appId != null)
return false;
@@ -318,6 +452,11 @@ public boolean equals(Object obj) {
return false;
} else if (!monitorInterval.equals(other.monitorInterval))
return false;
+ if (sessionHandlerThreads == null) {
+ if (other.sessionHandlerThreads != null)
+ return false;
+ } else if (!sessionHandlerThreads.equals(other.sessionHandlerThreads))
+ return false;
if (requestedStorage == null) {
if (other.requestedStorage != null)
return false;
@@ -352,6 +491,27 @@ public boolean equals(Object obj) {
return false;
} else if (!oAuth2ProxyVersion.equals(other.oAuth2ProxyVersion))
return false;
+ if (enableBuildCaching != other.enableBuildCaching)
+ return false;
+ if (buildCacheUrl == null) {
+ if (other.buildCacheUrl != null)
+ return false;
+ } else if (!buildCacheUrl.equals(other.buildCacheUrl))
+ return false;
+ if (bazelBuildCacheUrl == null) {
+ if (other.bazelBuildCacheUrl != null)
+ return false;
+ } else if (!bazelBuildCacheUrl.equals(other.bazelBuildCacheUrl))
+ return false;
+ if (enableBuildCachePush != other.enableBuildCachePush)
+ return false;
+ if (enableDependencyCaching != other.enableDependencyCaching)
+ return false;
+ if (dependencyCacheUrl == null) {
+ if (other.dependencyCacheUrl != null)
+ return false;
+ } else if (!dependencyCacheUrl.equals(other.dependencyCacheUrl))
+ return false;
return true;
}
@@ -361,13 +521,18 @@ public String toString() {
+ enableMonitor + ", enableActivityTracker=" + enableActivityTracker + ", monitorInterval="
+ monitorInterval + ", cloudProvider=" + cloudProvider + ", bandwidthLimiter=" + bandwidthLimiter
+ ", wondershaperImage=" + wondershaperImage + ", serviceUrl=" + serviceUrl + ", sessionsPerUser="
- + sessionsPerUser + ", appId=" + appId + ", instancesHost=" + instancesHost + ", usePaths=" + usePaths
- + ", instancesPath=" + instancesPath + ", storageClassName=" + storageClassName + ", requestedStorage="
- + requestedStorage + ", keycloakURL=" + keycloakURL + ", keycloakRealm=" + keycloakRealm
+ + sessionsPerUser + ", serviceAuthToken=" + serviceAuthToken + ", appId=" + appId + ", instancesHost="
+ + instancesHost + ", usePaths=" + usePaths + ", instancesPath=" + instancesPath + ", storageClassName="
+ + storageClassName + ", requestedStorage=" + requestedStorage + ", sessionHandlerThreads="
+ + sessionHandlerThreads + ", keycloakURL=" + keycloakURL + ", keycloakRealm=" + keycloakRealm
+ ", keycloakClientId=" + keycloakClientId + ", leaderLeaseDuration=" + leaderLeaseDuration
+ ", leaderRenewDeadline=" + leaderRenewDeadline + ", leaderRetryPeriod=" + leaderRetryPeriod
+ ", maxWatchIdleTime=" + maxWatchIdleTime + ", continueOnException=" + continueOnException
- + ", oAuth2ProxyVersion=" + oAuth2ProxyVersion + "]";
+ + ", oAuth2ProxyVersion=" + oAuth2ProxyVersion + ", enableBuildCaching=" + enableBuildCaching
+ + ", buildCacheUrl=" + buildCacheUrl + ", bazelBuildCacheUrl=" + bazelBuildCacheUrl
+ + ", enableBuildCachePush=" + enableBuildCachePush
+ + ", enableDependencyCaching=" + enableDependencyCaching + ", dependencyCacheUrl="
+ + dependencyCacheUrl + "]";
}
}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/TheiaCloudOperatorLauncher.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/TheiaCloudOperatorLauncher.java
index 0d3fc893b..6ac0829bf 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/TheiaCloudOperatorLauncher.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/TheiaCloudOperatorLauncher.java
@@ -26,6 +26,7 @@
import com.google.inject.Injector;
import picocli.CommandLine;
+import picocli.CommandLine.ParameterException;
public abstract class TheiaCloudOperatorLauncher {
@@ -36,22 +37,43 @@ public abstract class TheiaCloudOperatorLauncher {
protected TheiaCloudOperatorArguments args;
public void runMain(String[] args) throws InterruptedException {
- this.args = createArguments(args);
- AbstractTheiaCloudOperatorModule module = createModule(this.args);
- LOGGER.info(formatLogMessage(COR_ID_INIT, "Using " + module.getClass().getName() + " as DI module"));
-
- Injector injector = Guice.createInjector(module);
- TheiaCloudOperator theiaCloud = injector.getInstance(TheiaCloudOperator.class);
- LOGGER.info(formatLogMessage(COR_ID_INIT, "Launching Theia Cloud Now"));
- theiaCloud.start();
+ try {
+ this.args = createArguments(args);
+ AbstractTheiaCloudOperatorModule module = createModule(this.args);
+ LOGGER.info(formatLogMessage(COR_ID_INIT, "Using " + module.getClass().getName() + " as DI module"));
+
+ Injector injector = Guice.createInjector(module);
+ TheiaCloudOperator theiaCloud = injector.getInstance(TheiaCloudOperator.class);
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ try {
+ theiaCloud.stop();
+ } catch (Exception e) {
+ LOGGER.error(formatLogMessage(COR_ID_INIT, "Error during operator shutdown"), e);
+ }
+ }, "theia-cloud-operator-shutdown"));
+ LOGGER.info(formatLogMessage(COR_ID_INIT, "Launching Theia Cloud Now"));
+ theiaCloud.start();
+ } catch (Exception e) {
+ LOGGER.error(formatLogMessage(COR_ID_INIT, "Error during operator startup"), e);
+ throw e;
+ }
}
public TheiaCloudOperatorArguments createArguments(String[] args) {
- TheiaCloudOperatorArguments arguments = new TheiaCloudOperatorArguments();
- CommandLine commandLine = new CommandLine(arguments).setTrimQuotes(true);
- commandLine.parseArgs(args);
- LOGGER.info(formatLogMessage(COR_ID_INIT, "Parsed args: " + arguments));
- return arguments;
+ try {
+ TheiaCloudOperatorArguments arguments = new TheiaCloudOperatorArguments();
+ CommandLine commandLine = new CommandLine(arguments).setTrimQuotes(true);
+ commandLine.parseArgs(args);
+ arguments.validate();
+ LOGGER.info(formatLogMessage(COR_ID_INIT, "Parsed args: " + arguments));
+ return arguments;
+ } catch (ParameterException e) {
+ LOGGER.error(formatLogMessage(COR_ID_INIT, "Failed to parse command line arguments"), e);
+ throw e;
+ } catch (Exception e) {
+ LOGGER.error(formatLogMessage(COR_ID_INIT, "Unexpected error parsing arguments"), e);
+ throw e;
+ }
}
abstract AbstractTheiaCloudOperatorModule createModule(TheiaCloudOperatorArguments arguments);
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/databridge/AsyncDataInjector.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/databridge/AsyncDataInjector.java
new file mode 100644
index 000000000..d6b8d4d61
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/databridge/AsyncDataInjector.java
@@ -0,0 +1,191 @@
+package org.eclipse.theia.cloud.operator.databridge;
+
+import static org.eclipse.theia.cloud.common.util.LogMessageUtil.formatLogMessage;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eclipse.theia.cloud.common.k8s.client.DataBridgeClient;
+import org.eclipse.theia.cloud.common.k8s.resource.session.DataInjectionResponse;
+import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import io.sentry.ISpan;
+import io.sentry.SpanStatus;
+import org.eclipse.theia.cloud.common.tracing.TraceContext;
+import org.eclipse.theia.cloud.common.tracing.Tracing;
+
+/**
+ * Handles asynchronous data injection into sessions via the data bridge. Polls
+ * the health endpoint until the data
+ * bridge is ready, then injects data once.
+ */
+@Singleton
+public class AsyncDataInjector {
+
+ private static final Logger LOGGER = LogManager.getLogger(AsyncDataInjector.class);
+
+ /** Maximum number of health check attempts (1 per second for 60 seconds) */
+ private static final int MAX_HEALTH_CHECKS = 60;
+
+ /** Interval between health checks in milliseconds */
+ private static final int HEALTH_CHECK_INTERVAL_MS = 1000;
+
+ private final ScheduledExecutorService scheduler;
+
+ @Inject
+ private DataBridgeClient dataBridgeClient;
+
+ public AsyncDataInjector() {
+ this.scheduler = Executors.newScheduledThreadPool(4);
+ }
+
+ /**
+ * Schedules asynchronous data injection for a session. Polls the health
+ * endpoint until ready, then injects data
+ * once.
+ *
+ * @param parentSpan The parent span to create a child span from for tracing
+ * @param session The session to inject data into
+ * @param envVars The environment variables to inject
+ * @param correlationId For logging/tracing
+ */
+ public void scheduleInjection(ISpan parentSpan, Session session, Map envVars,
+ String correlationId) {
+ String sessionName = session.getSpec().getName();
+ String appDef = session.getSpec().getAppDefinition();
+ int envVarCount = envVars.size();
+
+ LOGGER.info(formatLogMessage(correlationId, "Scheduling async data injection for session: " + sessionName));
+
+ // Extract trace context BEFORE scheduling - the parent span will be finished by
+ // the time scheduler runs
+ Optional traceContext = TraceContext.fromSpan(parentSpan);
+
+ scheduler.submit(() -> {
+ // Create a new transaction linked to the same trace (parent span is already
+ // finished)
+ ISpan span = Tracing.continueTraceAsync(traceContext, "databridge.inject", "Data bridge injection");
+ span.setTag("session.name", sessionName);
+ span.setTag("app_definition", appDef);
+ span.setData("correlation_id", correlationId);
+ span.setData("env_var_count", envVarCount);
+
+ pollHealthThenInject(span, session, envVars, correlationId, 0);
+ });
+ }
+
+ /**
+ * Polls the health endpoint and injects data when ready. Recursively schedules
+ * itself for retries. Aggregate metrics (health check attempts, outcome) are tracked
+ * on the parent span rather than creating individual spans for each health check.
+ *
+ * @param span The parent span from scheduleInjection - aggregate metrics are tracked here
+ * @param session The session to inject data into
+ * @param envVars The environment variables to inject
+ * @param correlationId For logging/tracing
+ * @param attempt The current attempt number (0-indexed)
+ */
+ private void pollHealthThenInject(ISpan span, Session session, Map envVars,
+ String correlationId, int attempt) {
+ String sessionName = session.getSpec().getName();
+
+ if (attempt >= MAX_HEALTH_CHECKS) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "Data bridge not ready after " + MAX_HEALTH_CHECKS + "s for session: " + sessionName));
+
+ span.setTag("outcome", "timeout");
+ span.setData("health_check_attempts", attempt);
+ Tracing.finish(span, SpanStatus.DEADLINE_EXCEEDED);
+ return;
+ }
+
+ // Check if data bridge is healthy (no individual span, just aggregate metrics)
+ boolean healthy = dataBridgeClient.healthCheck(sessionName, correlationId);
+
+ if (healthy) {
+ // Ready - inject data once
+ LOGGER.info(formatLogMessage(correlationId,
+ "Data bridge ready for session: " + sessionName + " (attempt " + (attempt + 1) + ")"));
+
+ // Track aggregate metrics on the span
+ span.setData("health_check_attempts", attempt + 1);
+ span.setData("time_to_ready_ms", (attempt + 1) * HEALTH_CHECK_INTERVAL_MS);
+
+ ISpan injectSpan = Tracing.childSpan(span, "databridge.inject_data", "Inject environment data");
+ injectSpan.setData("env_var_count", envVars.size());
+
+ try {
+ Optional response = dataBridgeClient.injectData(session, envVars, correlationId);
+
+ if (response.isEmpty()) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "Data injection failed - no response for session: " + sessionName));
+ injectSpan.setTag("outcome", "no_response");
+ Tracing.finish(injectSpan, SpanStatus.INTERNAL_ERROR);
+ span.setTag("outcome", "injection_failed");
+ span.setTag("failure.reason", "no_response");
+ Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ } else if (!response.get().isSuccess()) {
+ String error = response.get().getError();
+ LOGGER.error(formatLogMessage(correlationId,
+ "Data injection failed for session: " + sessionName + " - " + error));
+ injectSpan.setTag("outcome", "error");
+ injectSpan.setData("error_message", error);
+ Tracing.finish(injectSpan, SpanStatus.INTERNAL_ERROR);
+ span.setTag("outcome", "injection_failed");
+ span.setTag("failure.reason", "api_error");
+ span.setData("error_message", error);
+ Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ } else {
+ LOGGER.info(
+ formatLogMessage(correlationId, "Data injected successfully for session: " + sessionName));
+ injectSpan.setTag("outcome", "success");
+ Tracing.finishSuccess(injectSpan);
+ span.setTag("outcome", "success");
+ Tracing.finishSuccess(span);
+ }
+ } catch (Exception e) {
+ LOGGER.error(formatLogMessage(correlationId, "Exception during data injection"), e);
+ injectSpan.setTag("outcome", "exception");
+ Tracing.finishError(injectSpan, e);
+ span.setTag("outcome", "injection_failed");
+ span.setTag("failure.reason", "exception");
+ Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ }
+ return;
+ }
+
+ // Not ready - schedule next health check
+ if (attempt == 0) {
+ LOGGER.debug(formatLogMessage(correlationId,
+ "Data bridge not ready yet for session: " + sessionName + ", polling..."));
+ }
+
+ scheduler.schedule(() -> pollHealthThenInject(span, session, envVars, correlationId, attempt + 1),
+ HEALTH_CHECK_INTERVAL_MS, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Shuts down the scheduler. Should be called when the operator is shutting
+ * down.
+ */
+ public void shutdown() {
+ scheduler.shutdown();
+ try {
+ if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
+ scheduler.shutdownNow();
+ }
+ } catch (InterruptedException e) {
+ scheduler.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
+ }
+}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/di/AbstractTheiaCloudOperatorModule.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/di/AbstractTheiaCloudOperatorModule.java
index d6bdcfd66..8865de5ec 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/di/AbstractTheiaCloudOperatorModule.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/di/AbstractTheiaCloudOperatorModule.java
@@ -18,6 +18,7 @@
import java.util.function.Consumer;
+import org.eclipse.theia.cloud.common.k8s.client.DataBridgeClient;
import org.eclipse.theia.cloud.common.k8s.client.DefaultTheiaCloudClient;
import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
@@ -31,7 +32,7 @@
import org.eclipse.theia.cloud.operator.handler.appdef.AppDefinitionHandler;
import org.eclipse.theia.cloud.operator.handler.appdef.EagerStartAppDefinitionAddedHandler;
import org.eclipse.theia.cloud.operator.handler.appdef.LazyStartAppDefinitionHandler;
-import org.eclipse.theia.cloud.operator.handler.session.EagerStartSessionHandler;
+import org.eclipse.theia.cloud.operator.handler.session.EagerWithLazyFallbackSessionHandler;
import org.eclipse.theia.cloud.operator.handler.session.LazySessionHandler;
import org.eclipse.theia.cloud.operator.handler.session.SessionHandler;
import org.eclipse.theia.cloud.operator.handler.ws.LazyWorkspaceHandler;
@@ -46,9 +47,13 @@
import org.eclipse.theia.cloud.operator.pv.MinikubePersistentVolumeCreator;
import org.eclipse.theia.cloud.operator.pv.PersistentVolumeCreator;
import org.eclipse.theia.cloud.operator.replacements.DefaultDeploymentTemplateReplacements;
+import org.eclipse.theia.cloud.operator.databridge.AsyncDataInjector;
+import org.eclipse.theia.cloud.operator.sidecar.SidecarManager;
+import org.eclipse.theia.cloud.operator.sidecar.SidecarResourceFactory;
import org.eclipse.theia.cloud.operator.replacements.DefaultPersistentVolumeTemplateReplacements;
import org.eclipse.theia.cloud.operator.replacements.DeploymentTemplateReplacements;
import org.eclipse.theia.cloud.operator.replacements.PersistentVolumeTemplateReplacements;
+import org.eclipse.theia.cloud.operator.util.SessionEnvCollector;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
@@ -81,6 +86,11 @@ protected void configure() {
configure(MultiBinding.create(OperatorPlugin.class), this::bindOperatorPlugins);
bind(MonitorMessagingService.class).to(bindMonitorMessagingService()).in(Singleton.class);
+ bind(DataBridgeClient.class).toProvider(DataBridgeClientProvider.class).in(Singleton.class);
+ bind(SidecarResourceFactory.class).in(Singleton.class);
+ bind(SidecarManager.class).in(Singleton.class);
+ bind(SessionEnvCollector.class).in(Singleton.class);
+ bind(AsyncDataInjector.class).in(Singleton.class);
bind(TheiaCloudOperatorArguments.class).toInstance(arguments);
}
@@ -143,7 +153,7 @@ protected Class extends WorkspaceHandler> bindWorkspaceHandler() {
protected Class extends SessionHandler> bindSessionHandler() {
if (arguments.isEagerStart()) {
- return EagerStartSessionHandler.class;
+ return EagerWithLazyFallbackSessionHandler.class;
} else {
return LazySessionHandler.class;
}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/di/DataBridgeClientProvider.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/di/DataBridgeClientProvider.java
new file mode 100644
index 000000000..a641215dc
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/di/DataBridgeClientProvider.java
@@ -0,0 +1,25 @@
+package org.eclipse.theia.cloud.operator.di;
+
+import org.eclipse.theia.cloud.common.k8s.client.DataBridgeClient;
+import org.eclipse.theia.cloud.common.k8s.client.DefaultDataBridgeClient;
+import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+/**
+ * Guice provider for {@link DataBridgeClient}.
+ */
+@Singleton
+public class DataBridgeClientProvider implements Provider {
+
+ @Inject
+ private TheiaCloudClient theiaCloudClient;
+
+ @Override
+ public DataBridgeClient get() {
+ return new DefaultDataBridgeClient(theiaCloudClient);
+ }
+}
+
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/AddedHandlerUtil.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/AddedHandlerUtil.java
index 377696e4f..f2f838fe7 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/AddedHandlerUtil.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/AddedHandlerUtil.java
@@ -63,6 +63,11 @@
import io.fabric8.kubernetes.api.model.SecretEnvSource;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
+import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
+import io.sentry.ISpan;
+import io.sentry.SpanStatus;
+import org.eclipse.theia.cloud.common.tracing.Tracing;
+import org.eclipse.theia.cloud.common.tracing.TraceContext;
public final class AddedHandlerUtil {
@@ -72,6 +77,7 @@ public final class AddedHandlerUtil {
public static final String TEMPLATE_SERVICE_YAML = "/templateService.yaml";
public static final String TEMPLATE_SERVICE_WITHOUT_AOUTH2_PROXY_YAML = "/templateServiceWithoutOAuthProxy.yaml";
+ public static final String TEMPLATE_INTERNAL_SERVICE_YAML = "/templateInternalService.yaml";
public static final String TEMPLATE_CONFIGMAP_EMAILS_YAML = "/templateConfigmapEmails.yaml";
public static final String TEMPLATE_CONFIGMAP_YAML = "/templateConfigmap.yaml";
public static final String TEMPLATE_DEPLOYMENT_YAML = "/templateDeployment.yaml";
@@ -87,6 +93,7 @@ public final class AddedHandlerUtil {
public static final String FILENAME_AUTHENTICATED_EMAILS_LIST = "authenticated-emails-list";
public static final String INGRESS_REWRITE_PATH = "(/|$)(.*)";
+ public static final String INGRESS_PATH_TYPE = "ImplementationSpecific";
private static final HostnameVerifier ALL_GOOD_HOSTNAME_VERIFIER = new HostnameVerifier() {
@Override
@@ -129,83 +136,144 @@ public static void updateProxyConfigMap(NamespacedKubernetesClient client, Strin
}
public static void updateSessionURLAsync(SessionResourceClient sessions, Session session, String namespace,
- String url, String correlationId) {
+ String url, String correlationId, ISpan parentSpan) {
+ String sessionName = session.getSpec().getName();
+ String appDef = session.getSpec().getAppDefinition();
+
+ // Extract trace context BEFORE scheduling - the parent span will be finished by the time executor runs
+ Optional traceContext =
+ TraceContext.fromSpan(parentSpan);
+
EXECUTOR.execute(() -> {
+ // Create a new transaction linked to the same trace (parent span is already finished)
+ ISpan span = Tracing.continueTraceAsync(traceContext, "session.url_availability", "URL availability check");
+ span.setTag("session.name", sessionName);
+ span.setTag("app_definition", appDef);
+ span.setData("correlation_id", correlationId);
+ span.setData("url", url);
+
boolean updateURL = false;
- for (int i = 1; i <= 100; i++) {
- try {
+ int lastResponseCode = -1;
+ String failureReason = null;
+
+ try {
+ for (int i = 1; i <= 309; i++) {
/*
- * On the first 15 loops we will check every 2.5s whether URL is available. This will take at least
- * 37.5s. On the second 15 loops we will check every 5s. This will take at least 75s. On the next 15
- * loops we will check every 10s. This will take at least further 150s. If the pod has not started
- * within the first 4-5 minutes, we will continue to check every minute. We give up after an hour.
+ * On the first 60 loops we will check every 500ms whether URL is available. This will take at
+ * least 30s. On the next 60 loops we will check every 1000ms. This will take at least 60s. On
+ * the next 24 loops we will check every 2500ms. This will take at least further 60s. On the
+ * next 60 loops we will check every 5000ms. This will take at least further 5 minutes. If the
+ * pod has not started within the first ~7 minutes, we will continue to check every 30 seconds.
+ * We give up after an hour.
*/
- if (i <= 15) {
- Thread.sleep(2500);
- } else if (i <= 30) {
- Thread.sleep(5000);
- } else if (i <= 45) {
- Thread.sleep(10000);
+ long sleepDuration;
+ if (i <= 60) {
+ sleepDuration = 500;
+ } else if (i <= 120) {
+ sleepDuration = 1000;
+ } else if (i <= 144) {
+ sleepDuration = 2500;
+ } else if (i <= 204) {
+ sleepDuration = 5000;
} else {
- Thread.sleep(60000);
+ sleepDuration = 30000;
+ }
+
+ try {
+ Thread.sleep(sleepDuration);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ span.setTag("outcome", "cancelled");
+ Tracing.finishError(span, e);
+ return;
}
- } catch (InterruptedException e) {
- /* silent */
- }
- HttpsURLConnection connection;
- try {
- connection = (HttpsURLConnection) new URL(HOST_PROTOCOL + url).openConnection();
- } catch (IOException e) {
- LOGGER.error(formatLogMessage(correlationId, "Error while checking session availability."), e);
- continue;
- }
- int code;
-
- try {
- connection.setHostnameVerifier(ALL_GOOD_HOSTNAME_VERIFIER);
- SSLContext sc = SSLContext.getInstance("SSL");
- sc.init(null, new TrustManager[] { TRUST_ALL_MANAGER }, new java.security.SecureRandom());
- HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
- connection.setSSLSocketFactory(sc.getSocketFactory());
- connection.connect();
- code = connection.getResponseCode();
- } catch (IOException e) {
- LOGGER.error(formatLogMessage(correlationId, url + " is NOT available yet."), e);
- continue;
- } catch (NoSuchAlgorithmException | KeyManagementException e) {
- LOGGER.error(formatLogMessage(correlationId,
- "Error while checking session availability with SSL ignore."), e);
- continue;
- }
+ HttpsURLConnection connection;
+ try {
+ connection = (HttpsURLConnection) new URL(HOST_PROTOCOL + url).openConnection();
+ } catch (IOException e) {
+ LOGGER.error(formatLogMessage(correlationId, "Error while checking session availability."), e);
+ failureReason = "connection_error";
+ continue;
+ }
+ int code;
+
+ try {
+ connection.setHostnameVerifier(ALL_GOOD_HOSTNAME_VERIFIER);
+ SSLContext sc = SSLContext.getInstance("SSL");
+ sc.init(null, new TrustManager[] { TRUST_ALL_MANAGER }, new java.security.SecureRandom());
+ HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+ connection.setSSLSocketFactory(sc.getSocketFactory());
+ connection.connect();
+ code = connection.getResponseCode();
+ lastResponseCode = code;
+ } catch (IOException e) {
+ LOGGER.error(formatLogMessage(correlationId, url + " is NOT available yet."), e);
+ failureReason = "io_error";
+ continue;
+ } catch (NoSuchAlgorithmException | KeyManagementException e) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "Error while checking session availability with SSL ignore."), e);
+ failureReason = "ssl_error";
+ continue;
+ }
- LOGGER.trace(formatLogMessage(correlationId, url + " has response code " + code));
+ LOGGER.trace(formatLogMessage(correlationId, url + " has response code " + code));
+
+ if (code == 200) {
+ updateURL = true;
+ } else if (code != 404 && code != 503 && code != 500 && !updateURL) {
+ /*
+ * we don't get a 404, 500 or 503, so something is available. Try accessing the URL once more then
+ * update URL anyway
+ */
+ updateURL = true;
+ continue;
+ }
- if (code == 200) {
- updateURL = true;
- } else if (code != 404 && code != 503 && !updateURL) {
- /*
- * we don't get a 404 or 503, so something is available. Try accessing the URL once more then update
- * URL anyway
- */
- updateURL = true;
- continue;
+ if (updateURL) {
+ LOGGER.info(formatLogMessage(correlationId, url + " is available."));
+
+ ISpan updateSpan = Tracing.childSpan(span, "session.update_status", "Update session URL status");
+ try {
+ sessions.updateStatus(correlationId, session, status -> status.setUrl(url));
+ Tracing.finishSuccess(updateSpan);
+ } catch (Exception e) {
+ Tracing.finishError(updateSpan, e);
+ // Propagate failure to outer span
+ span.setTag("outcome", "error");
+ span.setData("attempts", i);
+ span.setData("final_response_code", code);
+ Tracing.finishError(span, e);
+ return;
+ }
+
+ LOGGER.info(formatMetric(correlationId, "Running session for " + appDef));
+
+ span.setTag("outcome", "success");
+ span.setData("attempts", i);
+ span.setData("final_response_code", code);
+ Tracing.finishSuccess(span);
+ return;
+ } else {
+ LOGGER.trace(formatLogMessage(correlationId, url + " is NOT available yet."));
+ }
}
- if (updateURL) {
- LOGGER.info(formatLogMessage(correlationId, url + " is available."));
- sessions.updateStatus(correlationId, session, status -> status.setUrl(url));
- LOGGER.info(
- formatMetric(correlationId, "Running session for " + session.getSpec().getAppDefinition()));
- break;
- } else {
- LOGGER.trace(formatLogMessage(correlationId, url + " is NOT available yet."));
+ // Exhausted all attempts
+ span.setTag("outcome", "timeout");
+ span.setData("attempts", 309);
+ span.setData("last_response_code", lastResponseCode);
+ if (failureReason != null) {
+ span.setTag("failure.reason", failureReason);
}
+ Tracing.finish(span, SpanStatus.DEADLINE_EXCEEDED);
+ } catch (Exception e) {
+ span.setTag("outcome", "error");
+ Tracing.finishError(span, e);
}
-
});
-
}
public static void removeEmptyResources(Deployment deployment) {
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/EagerStartAppDefinitionAddedHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/EagerStartAppDefinitionAddedHandler.java
index c0e7efef6..c57728ff4 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/EagerStartAppDefinitionAddedHandler.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/EagerStartAppDefinitionAddedHandler.java
@@ -1,250 +1,202 @@
-/********************************************************************************
- * Copyright (C) 2022 EclipseSource, Lockular, Ericsson, STMicroelectronics and
- * others.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the Eclipse
- * Public License v. 2.0 are satisfied: GNU General Public License, version 2
- * with the GNU Classpath Exception which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- ********************************************************************************/
package org.eclipse.theia.cloud.operator.handler.appdef;
import static org.eclipse.theia.cloud.common.util.LogMessageUtil.formatLogMessage;
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinitionSpec;
-import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
-import org.eclipse.theia.cloud.operator.bandwidth.BandwidthLimiter;
-import org.eclipse.theia.cloud.operator.handler.AddedHandlerUtil;
-import org.eclipse.theia.cloud.operator.ingress.IngressPathProvider;
-import org.eclipse.theia.cloud.operator.replacements.DeploymentTemplateReplacements;
-import org.eclipse.theia.cloud.operator.util.JavaResourceUtil;
-import org.eclipse.theia.cloud.operator.util.K8sUtil;
-import org.eclipse.theia.cloud.operator.util.TheiaCloudConfigMapUtil;
-import org.eclipse.theia.cloud.operator.util.TheiaCloudDeploymentUtil;
+import org.eclipse.theia.cloud.operator.sidecar.SidecarManager;
+import org.eclipse.theia.cloud.operator.pool.PrewarmedResourcePool;
+import org.eclipse.theia.cloud.common.tracing.Tracing;
+import io.sentry.Sentry;
import org.eclipse.theia.cloud.operator.util.TheiaCloudIngressUtil;
-import org.eclipse.theia.cloud.operator.util.TheiaCloudServiceUtil;
import com.google.inject.Inject;
-import io.fabric8.kubernetes.api.model.ConfigMap;
-import io.fabric8.kubernetes.api.model.Service;
-import io.fabric8.kubernetes.api.model.apps.Deployment;
-import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
+import io.sentry.ISpan;
+import io.sentry.ITransaction;
+import io.sentry.SpanStatus;
/**
- * A {@link AppDefinitionHandler} that will eagerly start up deployments ahead of usage time which will later be used as
- * sessions.
+ * A {@link AppDefinitionHandler} that manages a pool of prewarmed deployments for eager start sessions. This handler
+ * delegates pool management to {@link PrewarmedResourcePool}.
*/
public class EagerStartAppDefinitionAddedHandler implements AppDefinitionHandler {
private static final Logger LOGGER = LogManager.getLogger(EagerStartAppDefinitionAddedHandler.class);
- public static final String LABEL_KEY = "theiacloud";
- public static final String LABEL_VALUE_PROXY = "proxy";
- public static final String LABEL_VALUE_EMAILS = "emails";
-
- @Inject
- protected TheiaCloudClient client;
-
- @Inject
- protected TheiaCloudOperatorArguments arguments;
-
@Inject
- protected IngressPathProvider ingressPathProvider;
+ private TheiaCloudClient client;
@Inject
- protected BandwidthLimiter bandwidthLimiter;
+ private PrewarmedResourcePool pool;
@Inject
- protected DeploymentTemplateReplacements deploymentReplacements;
+ private SidecarManager sidecarManager;
@Override
public boolean appDefinitionAdded(AppDefinition appDefinition, String correlationId) {
AppDefinitionSpec spec = appDefinition.getSpec();
- LOGGER.info(formatLogMessage(correlationId, "Handling " + spec));
-
- String appDefinitionResourceName = appDefinition.getMetadata().getName();
- String appDefinitionResourceUID = appDefinition.getMetadata().getUid();
- int instances = spec.getMinInstances();
-
- /* Create ingress if not existing */
- if (!TheiaCloudIngressUtil.checkForExistingIngressAndAddOwnerReferencesIfMissing(client.kubernetes(),
- client.namespace(), appDefinition, correlationId)) {
- LOGGER.error(formatLogMessage(correlationId,
- "Expected ingress '" + spec.getIngressname() + "' for app definition '" + appDefinitionResourceName
- + "' does not exist. Abort handling app definition."));
- return false;
- } else {
- LOGGER.trace(formatLogMessage(correlationId, "Ingress available already"));
- }
+ String appDefName = appDefinition.getMetadata().getName();
+ int minInstances = spec.getMinInstances();
- /* Get existing services for this app definition */
- List existingServices = K8sUtil.getExistingServices(client.kubernetes(), client.namespace(),
- appDefinitionResourceName, appDefinitionResourceUID);
+ ITransaction tx = Tracing.startTransaction("appdef.added", "appdef");
+ tx.setTag("app_definition", appDefName);
+ tx.setData("min_instances", minInstances);
+ tx.setData("correlation_id", correlationId);
+ tx.setTag("appdef.action", "added");
- /* Compute missing services */
- Set missingServiceIds = TheiaCloudServiceUtil.computeIdsOfMissingServices(appDefinition, correlationId,
- instances, existingServices);
-
- /* Create missing services for this app definition */
- for (int instance : missingServiceIds) {
- createAndApplyService(client.kubernetes(), client.namespace(), correlationId, appDefinitionResourceName,
- appDefinitionResourceUID, instance, appDefinition, arguments.isUseKeycloak());
- }
+ LOGGER.info(formatLogMessage(correlationId, "Handling " + spec));
- if (arguments.isUseKeycloak()) {
- /* Get existing configmaps for this app definition */
- List existingConfigMaps = K8sUtil.getExistingConfigMaps(client.kubernetes(), client.namespace(),
- appDefinitionResourceName, appDefinitionResourceUID);
- List existingProxyConfigMaps = existingConfigMaps.stream()//
- .filter(configmap -> LABEL_VALUE_PROXY.equals(configmap.getMetadata().getLabels().get(LABEL_KEY)))//
- .collect(Collectors.toList());
- List existingEmailsConfigMaps = existingConfigMaps.stream()//
- .filter(configmap -> LABEL_VALUE_EMAILS.equals(configmap.getMetadata().getLabels().get(LABEL_KEY)))//
- .collect(Collectors.toList());
-
- /* Compute missing configmaps */
- Set missingProxyIds = TheiaCloudConfigMapUtil.computeIdsOfMissingProxyConfigMaps(appDefinition,
- correlationId, instances, existingProxyConfigMaps);
- Set missingEmailIds = TheiaCloudConfigMapUtil.computeIdsOfMissingEmailConfigMaps(appDefinition,
- correlationId, instances, existingEmailsConfigMaps);
-
- /* Create missing configmaps for this app definition */
- for (int instance : missingProxyIds) {
- createAndApplyProxyConfigMap(client.kubernetes(), client.namespace(), correlationId,
- appDefinitionResourceName, appDefinitionResourceUID, instance, appDefinition);
+ try {
+ // Verify ingress exists
+ ISpan ingressSpan = Tracing.childSpan(tx, "appdef.verify_ingress", "Verify ingress exists");
+ ingressSpan.setData("route_name", spec.getIngressname());
+
+ if (!TheiaCloudIngressUtil.checkForExistingIngressAndAddOwnerReferencesIfMissing(client.kubernetes(),
+ client.namespace(), appDefinition, correlationId)) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "Expected HTTPRoute '" + spec.getIngressname() + "' for app definition '" + appDefName
+ + "' does not exist. Abort handling app definition."));
+ ingressSpan.setTag("outcome", "not_found");
+ Tracing.finish(ingressSpan, SpanStatus.NOT_FOUND);
+ tx.setTag("error.reason", "ingress_not_found");
+ tx.setTag("outcome", "failure");
+ Tracing.finish(tx, SpanStatus.NOT_FOUND);
+ return false;
}
- for (int instance : missingEmailIds) {
- createAndApplyEmailConfigMap(client.kubernetes(), client.namespace(), correlationId,
- appDefinitionResourceName, appDefinitionResourceUID, instance, appDefinition);
+ Tracing.finishSuccess(ingressSpan);
+
+ ISpan sidecarValidationSpan = Tracing.childSpan(tx, "appdef.validate_sidecars",
+ "Validate sidecar configuration");
+ if (!sidecarManager.validateUniqueSidecarNames(appDefinition, correlationId)) {
+ sidecarValidationSpan.setTag("outcome", "invalid");
+ Tracing.finish(sidecarValidationSpan, SpanStatus.INVALID_ARGUMENT);
+ tx.setTag("error.reason", "duplicate_sidecar_names");
+ tx.setTag("outcome", "failure");
+ Tracing.finish(tx, SpanStatus.INVALID_ARGUMENT);
+ return false;
}
- }
+ Tracing.finishSuccess(sidecarValidationSpan);
- /* Get existing deployments for this app definition */
- List existingDeployments = K8sUtil.getExistingDeployments(client.kubernetes(), client.namespace(),
- appDefinitionResourceName, appDefinitionResourceUID);
+ LOGGER.trace(formatLogMessage(correlationId, "HTTPRoute available"));
- /* Compute missing deployments */
- Set missingDeploymentIds = TheiaCloudDeploymentUtil.computeIdsOfMissingDeployments(appDefinition,
- correlationId, instances, existingDeployments);
+ // Ensure pool has minimum capacity
+ ISpan poolSpan = Tracing.childSpan(tx, "appdef.ensure_capacity", "Ensure pool capacity");
+ poolSpan.setData("min_instances", minInstances);
- /* Create missing deployments for this app definition */
- for (int instance : missingDeploymentIds) {
- createAndApplyDeployment(client.kubernetes(), client.namespace(), correlationId, appDefinitionResourceName,
- appDefinitionResourceUID, instance, appDefinition, arguments.isUseKeycloak());
- }
- return true;
- }
+ boolean success = pool.ensureCapacity(appDefinition, minInstances, correlationId);
- protected void createAndApplyService(NamespacedKubernetesClient client, String namespace, String correlationId,
- String appDefinitionResourceName, String appDefinitionResourceUID, int instance,
- AppDefinition appDefinition, boolean useOAuth2Proxy) {
- Map replacements = TheiaCloudServiceUtil.getServiceReplacements(namespace, appDefinition,
- instance);
- String templateYaml = useOAuth2Proxy ? AddedHandlerUtil.TEMPLATE_SERVICE_YAML
- : AddedHandlerUtil.TEMPLATE_SERVICE_WITHOUT_AOUTH2_PROXY_YAML;
- String serviceYaml;
- try {
- serviceYaml = JavaResourceUtil.readResourceAndReplacePlaceholders(templateYaml, replacements,
- correlationId);
- } catch (IOException | URISyntaxException e) {
- LOGGER.error(
- formatLogMessage(correlationId, "Error while adjusting template for instance number " + instance),
- e);
- return;
- }
- K8sUtil.loadAndCreateServiceWithOwnerReference(client, namespace, correlationId, serviceYaml, AppDefinition.API,
- AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0);
- }
+ poolSpan.setTag("pool.operation", "ensure_capacity");
+ poolSpan.setTag("outcome", success ? "success" : "failure");
+ Tracing.finish(poolSpan, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
- protected void createAndApplyDeployment(NamespacedKubernetesClient client, String namespace, String correlationId,
- String appDefinitionResourceName, String appDefinitionResourceUID, int instance,
- AppDefinition appDefinition, boolean useOAuth2Proxy) {
- Map replacements = deploymentReplacements.getReplacements(namespace, appDefinition, instance);
- String templateYaml = useOAuth2Proxy ? AddedHandlerUtil.TEMPLATE_DEPLOYMENT_YAML
- : AddedHandlerUtil.TEMPLATE_DEPLOYMENT_WITHOUT_AOUTH2_PROXY_YAML;
- String deploymentYaml;
- try {
- deploymentYaml = JavaResourceUtil.readResourceAndReplacePlaceholders(templateYaml, replacements,
- correlationId);
- } catch (IOException | URISyntaxException e) {
- LOGGER.error(
- formatLogMessage(correlationId, "Error while adjusting template for instance number " + instance),
- e);
- return;
+ tx.setData("min_instances", minInstances);
+ tx.setTag("outcome", success ? "success" : "failure");
+ Tracing.finish(tx, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ return success;
+
+ } catch (Exception e) {
+ Tracing.finishError(tx, e);
+ throw e;
}
- K8sUtil.loadAndCreateDeploymentWithOwnerReference(client, namespace, correlationId, deploymentYaml,
- AppDefinition.API, AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0,
- deployment -> {
- bandwidthLimiter.limit(deployment, appDefinition.getSpec().getDownlinkLimit(),
- appDefinition.getSpec().getUplinkLimit(), correlationId);
- AddedHandlerUtil.removeEmptyResources(deployment);
- if (appDefinition.getSpec().getPullSecret() != null
- && !appDefinition.getSpec().getPullSecret().isEmpty()) {
- AddedHandlerUtil.addImagePullSecret(deployment, appDefinition.getSpec().getPullSecret());
- }
- });
}
- protected void createAndApplyProxyConfigMap(NamespacedKubernetesClient client, String namespace,
- String correlationId, String appDefinitionResourceName, String appDefinitionResourceUID, int instance,
- AppDefinition appDefinition) {
- Map replacements = TheiaCloudConfigMapUtil.getProxyConfigMapReplacements(namespace,
- appDefinition, instance);
- String configMapYaml;
+ @Override
+ public boolean appDefinitionDeleted(AppDefinition appDefinition, String correlationId) {
+ AppDefinitionSpec spec = appDefinition.getSpec();
+ String appDefName = appDefinition.getMetadata().getName();
+
+ ITransaction tx = Tracing.startTransaction("appdef.deleted", "appdef");
+ tx.setTag("app_definition", appDefName);
+ tx.setData("min_instances", spec.getMinInstances());
+ tx.setData("correlation_id", correlationId);
+ tx.setTag("appdef.action", "deleted");
+
+ LOGGER.info(formatLogMessage(correlationId, "Deleting resources for " + spec));
+
try {
- configMapYaml = JavaResourceUtil.readResourceAndReplacePlaceholders(
- AddedHandlerUtil.TEMPLATE_CONFIGMAP_YAML, replacements, correlationId);
- } catch (IOException | URISyntaxException e) {
- LOGGER.error(
- formatLogMessage(correlationId, "Error while adjusting template for instance number " + instance),
- e);
- return;
+ // Release all pool resources
+ ISpan releaseSpan = Tracing.childSpan(tx, "appdef.release_all", "Release all pool resources");
+ releaseSpan.setTag("pool.operation", "release_all");
+
+ boolean success = pool.releaseAll(appDefinition, correlationId);
+
+ releaseSpan.setTag("outcome", success ? "success" : "failure");
+ Tracing.finish(releaseSpan, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ tx.setTag("outcome", success ? "success" : "failure");
+ Tracing.finish(tx, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ return success;
+
+ } catch (Exception e) {
+ Tracing.finishError(tx, e);
+ throw e;
}
- K8sUtil.loadAndCreateConfigMapWithOwnerReference(client, namespace, correlationId, configMapYaml,
- AppDefinition.API, AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0,
- configMap -> {
- String host = arguments.getInstancesHost() + ingressPathProvider.getPath(appDefinition, instance);
- int port = appDefinition.getSpec().getPort();
- AddedHandlerUtil.updateProxyConfigMap(client, namespace, configMap, host, port);
- });
}
- protected void createAndApplyEmailConfigMap(NamespacedKubernetesClient client, String namespace,
- String correlationId, String appDefinitionResourceName, String appDefinitionResourceUID, int instance,
- AppDefinition appDefinition) {
- Map replacements = TheiaCloudConfigMapUtil.getEmailConfigMapReplacements(namespace,
- appDefinition, instance);
- String configMapYaml;
+ @Override
+ public boolean appDefinitionModified(AppDefinition appDefinition, String correlationId) {
+ AppDefinitionSpec spec = appDefinition.getSpec();
+ String appDefName = appDefinition.getMetadata().getName();
+ int minInstances = spec.getMinInstances();
+
+ ITransaction tx = Tracing.startTransaction("appdef.modified", "appdef");
+ tx.setTag("app_definition", appDefName);
+ tx.setData("min_instances", minInstances);
+ tx.setData("correlation_id", correlationId);
+ tx.setTag("appdef.action", "modified");
+ tx.setData("generation", appDefinition.getMetadata().getGeneration());
+
+ LOGGER.info(formatLogMessage(correlationId, "Reconciling " + spec));
+
try {
- configMapYaml = JavaResourceUtil.readResourceAndReplacePlaceholders(
- AddedHandlerUtil.TEMPLATE_CONFIGMAP_EMAILS_YAML, replacements, correlationId);
- } catch (IOException | URISyntaxException e) {
- LOGGER.error(
- formatLogMessage(correlationId, "Error while adjusting template for instance number " + instance),
- e);
- return;
+ // Verify ingress exists
+ ISpan ingressSpan = Tracing.childSpan(tx, "appdef.verify_ingress", "Verify ingress exists");
+ ingressSpan.setData("route_name", spec.getIngressname());
+
+ if (!TheiaCloudIngressUtil.checkForExistingIngressAndAddOwnerReferencesIfMissing(client.kubernetes(),
+ client.namespace(), appDefinition, correlationId)) {
+ LOGGER.error(formatLogMessage(correlationId, "Expected HTTPRoute '" + spec.getIngressname()
+ + "' for app definition '" + appDefName + "' does not exist. Abort handling."));
+ ingressSpan.setTag("outcome", "not_found");
+ Tracing.finish(ingressSpan, SpanStatus.NOT_FOUND);
+ tx.setTag("error.reason", "ingress_not_found");
+ tx.setTag("outcome", "failure");
+ Tracing.finish(tx, SpanStatus.NOT_FOUND);
+ return false;
+ }
+ Tracing.finishSuccess(ingressSpan);
+
+ ISpan sidecarValidationSpan = Tracing.childSpan(tx, "appdef.validate_sidecars",
+ "Validate sidecar configuration");
+ if (!sidecarManager.validateUniqueSidecarNames(appDefinition, correlationId)) {
+ sidecarValidationSpan.setTag("outcome", "invalid");
+ Tracing.finish(sidecarValidationSpan, SpanStatus.INVALID_ARGUMENT);
+ tx.setTag("error.reason", "duplicate_sidecar_names");
+ tx.setTag("outcome", "failure");
+ Tracing.finish(tx, SpanStatus.INVALID_ARGUMENT);
+ return false;
+ }
+ Tracing.finishSuccess(sidecarValidationSpan);
+
+ // Reconcile pool to target instance count
+ ISpan reconcileSpan = Tracing.childSpan(tx, "appdef.reconcile_pool", "Reconcile pool to target");
+ reconcileSpan.setData("target_instances", minInstances);
+ reconcileSpan.setTag("pool.operation", "reconcile");
+
+ boolean success = pool.reconcile(appDefinition, minInstances, correlationId);
+
+ reconcileSpan.setTag("outcome", success ? "success" : "failure");
+ Tracing.finish(reconcileSpan, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ tx.setTag("outcome", success ? "success" : "failure");
+ Tracing.finish(tx, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ return success;
+
+ } catch (Exception e) {
+ Tracing.finishError(tx, e);
+ throw e;
}
- K8sUtil.loadAndCreateConfigMapWithOwnerReference(client, namespace, correlationId, configMapYaml,
- AppDefinition.API, AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0);
}
-
}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/LazyStartAppDefinitionHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/LazyStartAppDefinitionHandler.java
index 1597d0e4e..abdd18e2d 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/LazyStartAppDefinitionHandler.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/LazyStartAppDefinitionHandler.java
@@ -29,6 +29,7 @@
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinitionSpec;
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinitionStatus;
import org.eclipse.theia.cloud.operator.ingress.IngressPathProvider;
+import org.eclipse.theia.cloud.operator.sidecar.SidecarManager;
import org.eclipse.theia.cloud.operator.util.TheiaCloudIngressUtil;
import com.google.inject.Inject;
@@ -43,6 +44,9 @@ public class LazyStartAppDefinitionHandler implements AppDefinitionHandler {
@Inject
protected IngressPathProvider ingressPathProvider;
+ @Inject
+ protected SidecarManager sidecarManager;
+
@Override
public boolean appDefinitionAdded(AppDefinition appDefinition, String correlationId) {
try {
@@ -99,15 +103,23 @@ protected boolean doAppDefinitionAdded(AppDefinition appDefinition, String corre
if (!TheiaCloudIngressUtil.checkForExistingIngressAndAddOwnerReferencesIfMissing(client.kubernetes(),
client.namespace(), appDefinition, correlationId)) {
LOGGER.error(formatLogMessage(correlationId,
- "Expected ingress '" + spec.getIngressname() + "' for app definition '" + appDefinitionResourceName
+ "Expected HTTPRoute '" + spec.getIngressname() + "' for app definition '" + appDefinitionResourceName
+ "' does not exist. Abort handling app definition."));
client.appDefinitions().updateStatus(correlationId, appDefinition, s -> {
s.setOperatorStatus(OperatorStatus.ERROR);
- s.setOperatorMessage("Ingress does not exist.");
+ s.setOperatorMessage("HTTPRoute does not exist.");
});
return false;
} else {
- LOGGER.trace(formatLogMessage(correlationId, "Ingress available already"));
+ LOGGER.trace(formatLogMessage(correlationId, "HTTPRoute available already"));
+ }
+
+ if (!sidecarManager.validateUniqueSidecarNames(appDefinition, correlationId)) {
+ client.appDefinitions().updateStatus(correlationId, appDefinition, s -> {
+ s.setOperatorStatus(OperatorStatus.ERROR);
+ s.setOperatorMessage("Duplicate sidecar names found in AppDefinition; sidecar names must be unique.");
+ });
+ return false;
}
client.appDefinitions().updateStatus(correlationId, appDefinition, s -> {
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerSessionHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerSessionHandler.java
new file mode 100644
index 000000000..7e6909309
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerSessionHandler.java
@@ -0,0 +1,368 @@
+/********************************************************************************
+ * Copyright (C) 2022-2025 EclipseSource, Lockular, Ericsson, STMicroelectronics and
+ * others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+package org.eclipse.theia.cloud.operator.handler.session;
+
+import static org.eclipse.theia.cloud.common.util.LogMessageUtil.formatLogMessage;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+import org.eclipse.theia.cloud.common.k8s.resource.session.SessionSpec;
+import org.eclipse.theia.cloud.common.util.DataBridgeUtil;
+import org.eclipse.theia.cloud.operator.databridge.AsyncDataInjector;
+import org.eclipse.theia.cloud.operator.handler.AddedHandlerUtil;
+import org.eclipse.theia.cloud.operator.ingress.IngressManager;
+import org.eclipse.theia.cloud.operator.sidecar.SidecarManager;
+import org.eclipse.theia.cloud.operator.pool.PrewarmedResourcePool;
+import org.eclipse.theia.cloud.common.tracing.Tracing;
+import org.eclipse.theia.cloud.operator.util.SessionEnvCollector;
+import org.eclipse.theia.cloud.operator.pool.PrewarmedResourcePool.PoolInstance;
+import org.eclipse.theia.cloud.operator.pool.PrewarmedResourcePool.ReservationOutcome;
+import org.eclipse.theia.cloud.operator.pool.PrewarmedResourcePool.ReservationResult;
+
+import com.google.inject.Inject;
+
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRoute;
+import io.fabric8.kubernetes.client.KubernetesClientException;
+import io.sentry.ISpan;
+import io.sentry.Sentry;
+import io.sentry.SpanStatus;
+
+/**
+ * A {@link SessionHandler} that uses prewarmed instances from the pool. This handler delegates to
+ * {@link PrewarmedResourcePool} for instance management and {@link IngressManager} for ingress operations.
+ */
+public class EagerSessionHandler implements SessionHandler {
+
+ private static final Logger LOGGER = LogManager.getLogger(EagerSessionHandler.class);
+
+ public static final String SESSION_START_STRATEGY_ANNOTATION = "theia-cloud.io/session-start-strategy";
+ public static final String SESSION_START_STRATEGY_EAGER = "eager";
+ public static final String SESSION_INSTANCE_ID_ANNOTATION = "theia-cloud.io/instance-id";
+
+ /**
+ * Outcome of trying to handle a session with eager start.
+ */
+ public enum EagerSessionAddedOutcome {
+ HANDLED, NO_CAPACITY, ERROR
+ }
+
+ @Inject
+ private TheiaCloudClient client;
+
+ @Inject
+ private PrewarmedResourcePool pool;
+
+ @Inject
+ private IngressManager ingressManager;
+
+ @Inject
+ private SidecarManager sidecarManager;
+
+ @Inject
+ private SessionEnvCollector sessionEnvCollector;
+
+ @Inject
+ private AsyncDataInjector asyncDataInjector;
+
+ @Override
+ public boolean sessionAdded(Session session, String correlationId, ISpan span) {
+ return trySessionAdded(session, correlationId, span) == EagerSessionAddedOutcome.HANDLED;
+ }
+
+ /**
+ * Tries to handle a session using eager start. Returns the outcome so callers can fall back to lazy start if
+ * needed.
+ */
+ public EagerSessionAddedOutcome trySessionAdded(Session session, String correlationId, ISpan parentSpan) {
+ SessionSpec spec = session.getSpec();
+ String sessionName = spec.getName();
+ String appDefinitionID = spec.getAppDefinition();
+
+ // Create child span from parent span
+ ISpan span = Tracing.childSpan(parentSpan, "eager.setup", "Eager session setup");
+
+ span.setData("session_name", sessionName);
+ span.setData("app_definition", appDefinitionID);
+ span.setData("user", spec.getUser());
+
+ LOGGER.info(formatLogMessage(correlationId, "Handling sessionAdded " + spec));
+
+ try {
+ SessionStatusUtil.PreHandleResult preHandle = SessionStatusUtil.evaluateStatus(session, client,
+ correlationId, LOGGER);
+ if (preHandle == SessionStatusUtil.PreHandleResult.ALREADY_HANDLED) {
+ span.setTag("outcome", "already_handled");
+ Tracing.finish(span, SpanStatus.OK);
+ return EagerSessionAddedOutcome.HANDLED;
+ }
+ if (preHandle == SessionStatusUtil.PreHandleResult.INTERRUPTED
+ || preHandle == SessionStatusUtil.PreHandleResult.PREVIOUS_ERROR) {
+ span.setTag("outcome", "previous_error");
+ Tracing.finish(span, SpanStatus.ABORTED);
+ return EagerSessionAddedOutcome.ERROR;
+ }
+
+ boolean markedHandling = true;
+ try {
+ SessionStatusUtil.markHandling(client, session, correlationId);
+ } catch (RuntimeException ex) {
+ LOGGER.warn(formatLogMessage(correlationId,
+ "Unable to mark session as HANDLING. Treating as interrupted/race."), ex);
+ markedHandling = false;
+ }
+ // Check if the session was already handled by another instance
+ if (!markedHandling) {
+ Optional latestSession = client.sessions().get(session.getMetadata().getName());
+ if (latestSession.isPresent()) {
+ SessionStatusUtil.PreHandleResult latestPreHandle = SessionStatusUtil.evaluateStatus(
+ latestSession.get(), client, correlationId, LOGGER);
+ if (latestPreHandle == SessionStatusUtil.PreHandleResult.ALREADY_HANDLED) {
+ span.setTag("outcome", "already_handled");
+ Tracing.finish(span, SpanStatus.OK);
+ return EagerSessionAddedOutcome.HANDLED;
+ }
+ if (latestPreHandle == SessionStatusUtil.PreHandleResult.INTERRUPTED
+ || latestPreHandle == SessionStatusUtil.PreHandleResult.PREVIOUS_ERROR) {
+ span.setTag("outcome", "interrupted");
+ Tracing.finish(span, SpanStatus.ABORTED);
+ return EagerSessionAddedOutcome.ERROR;
+ }
+ }
+ span.setTag("outcome", "interrupted");
+ Tracing.finish(span, SpanStatus.ABORTED);
+ return EagerSessionAddedOutcome.ERROR;
+ }
+
+ // Find app definition
+ ISpan appDefSpan = Tracing.childSpan(span, "eager.find_appdef", "Find app definition");
+ Optional appDefOpt = client.appDefinitions().get(appDefinitionID);
+ if (appDefOpt.isEmpty()) {
+ LOGGER.error(
+ formatLogMessage(correlationId, "No App Definition with name " + appDefinitionID + " found."));
+ SessionStatusUtil.markError(client, session, correlationId, "App Definition not found.");
+ appDefSpan.setTag("outcome", "not_found");
+ Tracing.finish(appDefSpan, SpanStatus.NOT_FOUND);
+ span.setTag("outcome", "error");
+ Tracing.finish(span, SpanStatus.NOT_FOUND);
+ return EagerSessionAddedOutcome.ERROR;
+ }
+ AppDefinition appDef = appDefOpt.get();
+ Tracing.finishSuccess(appDefSpan);
+
+ // Find HTTPRoute
+ ISpan ingressSpan = Tracing.childSpan(span, "eager.find_route", "Find HTTPRoute");
+ Optional routeOpt = ingressManager.getIngress(appDef, correlationId);
+ if (routeOpt.isEmpty()) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "No HTTPRoute for app definition " + appDefinitionID + " found."));
+ SessionStatusUtil.markError(client, session, correlationId, "HTTPRoute not available.");
+ ingressSpan.setTag("outcome", "not_found");
+ Tracing.finish(ingressSpan, SpanStatus.NOT_FOUND);
+ span.setTag("outcome", "error");
+ Tracing.finish(span, SpanStatus.NOT_FOUND);
+ return EagerSessionAddedOutcome.ERROR;
+ }
+ HTTPRoute route = routeOpt.get();
+ Tracing.finishSuccess(ingressSpan);
+
+ // Reserve an instance from the pool
+ ISpan reserveSpan = Tracing.childSpan(span, "eager.reserve_instance", "Reserve pool instance");
+ ReservationResult reservation = pool.reserveInstance(session, appDef, correlationId);
+ reserveSpan.setTag("pool.outcome", reservation.getOutcome().name().toLowerCase());
+
+ if (reservation.getOutcome() == ReservationOutcome.NO_CAPACITY) {
+ reserveSpan.setTag("outcome", "no_capacity");
+ Tracing.finish(reserveSpan, SpanStatus.RESOURCE_EXHAUSTED);
+ span.setTag("outcome", "no_capacity");
+ Tracing.finish(span, SpanStatus.RESOURCE_EXHAUSTED);
+ return EagerSessionAddedOutcome.NO_CAPACITY;
+ }
+ if (reservation.getOutcome() == ReservationOutcome.ERROR) {
+ SessionStatusUtil.markError(client, session, correlationId, "Failed to reserve prewarmed instance.");
+ reserveSpan.setTag("outcome", "error");
+ Tracing.finish(reserveSpan, SpanStatus.INTERNAL_ERROR);
+ span.setTag("outcome", "error");
+ Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return EagerSessionAddedOutcome.ERROR;
+ }
+
+ PoolInstance instance = reservation.getInstance().get();
+ reserveSpan.setData("instance_id", instance.getInstanceId());
+ Tracing.finishSuccess(reserveSpan);
+
+ // Annotate session with start strategy and instance ID
+ ISpan annotateSpan = Tracing.childSpan(span, "eager.annotate_session", "Annotate session");
+ annotateSession(session, correlationId, instance.getInstanceId());
+ Tracing.finishSuccess(annotateSpan);
+
+ // Complete session setup (labels, deployment ownership, email config)
+ ISpan setupSpan = Tracing.childSpan(span, "eager.complete_setup", "Complete session setup");
+ setupSpan.setData("instance_id", instance.getInstanceId());
+ if (!pool.completeSessionSetup(session, appDef, instance, correlationId)) {
+ SessionStatusUtil.markError(client, session, correlationId, "Failed to complete session setup.");
+ setupSpan.setTag("outcome", "failure");
+ Tracing.finish(setupSpan, SpanStatus.INTERNAL_ERROR);
+ span.setTag("outcome", "error");
+ Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return EagerSessionAddedOutcome.ERROR;
+ }
+ Tracing.finishSuccess(setupSpan);
+
+ // Schedule async credential injection via data bridge (tracked in separate transaction)
+ if (DataBridgeUtil.isDataBridgeEnabled(appDef.getSpec())) {
+ Map envVars = sessionEnvCollector.collect(session, correlationId);
+ if (!envVars.isEmpty()) {
+ Sentry.addBreadcrumb("Scheduling data bridge injection with " + envVars.size() + " env vars",
+ "databridge");
+ asyncDataInjector.scheduleInjection(span, session, envVars, correlationId);
+ }
+ }
+
+ // Create the dedicated session HTTPRoute after the pool instance is fully assigned.
+ ISpan ingressRuleSpan = Tracing.childSpan(span, "eager.add_route_rule", "Add HTTPRoute rule");
+ String host;
+ try {
+ host = ingressManager.addRuleForSession(route, instance.getExternalService(), appDef,
+ session, instance.getInstanceId(), correlationId);
+ ingressRuleSpan.setData("host", host);
+ Tracing.finishSuccess(ingressRuleSpan);
+ } catch (KubernetesClientException e) {
+ LOGGER.error(formatLogMessage(correlationId, "Error while editing HTTPRoute"), e);
+ SessionStatusUtil.markError(client, session, correlationId, "Failed to edit HTTPRoute.");
+ Tracing.finishError(ingressRuleSpan, e);
+ span.setTag("outcome", "error");
+ Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return EagerSessionAddedOutcome.ERROR;
+ }
+
+ Sentry.addBreadcrumb("Scheduling URL availability check for " + host, "session");
+ AddedHandlerUtil.updateSessionURLAsync(client.sessions(), session, client.namespace(), host, correlationId, span);
+
+ span.setData("instance_id", instance.getInstanceId());
+ SessionStatusUtil.markHandled(client, session, correlationId, null);
+ Tracing.finishSuccess(span);
+ return EagerSessionAddedOutcome.HANDLED;
+
+ } catch (Exception e) {
+ SessionStatusUtil.markError(client, session, correlationId,
+ "Unexpected error. Please check the logs for correlationId: " + correlationId);
+ Tracing.finishError(span, e);
+ throw e;
+ }
+ }
+
+ @Override
+ public boolean sessionDeleted(Session session, String correlationId, ISpan parentSpan) {
+ SessionSpec spec = session.getSpec();
+ String sessionName = spec.getName();
+ String appDefinitionID = spec.getAppDefinition();
+
+ ISpan span = Tracing.childSpan(parentSpan, "eager.cleanup", "Eager session cleanup");
+
+ span.setData("session_name", sessionName);
+ span.setData("app_definition", appDefinitionID);
+
+ LOGGER.info(formatLogMessage(correlationId, "Handling sessionDeleted " + spec));
+
+ try {
+ // Find app definition
+ ISpan appDefSpan = Tracing.childSpan(span, "eager.find_appdef", "Find app definition");
+ Optional appDefOpt = client.appDefinitions().get(appDefinitionID);
+ if (appDefOpt.isEmpty()) {
+ LOGGER.info(formatLogMessage(correlationId,
+ "No App Definition found. Resources will be cleaned up by K8s garbage collection."));
+ appDefSpan.setTag("outcome", "not_found");
+ Tracing.finish(appDefSpan, SpanStatus.NOT_FOUND);
+ Tracing.finishSuccess(span); // This is OK - K8s GC handles it
+ return true;
+ }
+ AppDefinition appDef = appDefOpt.get();
+ Tracing.finishSuccess(appDefSpan);
+
+ // Get instance ID from session annotation
+ Integer instanceId = getInstanceIdFromAnnotation(session);
+ if (instanceId == null) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "Session missing instance-id annotation. Cannot determine which instance to release."));
+ span.setTag("error.reason", "missing_instance_id_annotation");
+ span.setTag("outcome", "failure");
+ Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return false;
+ }
+ span.setData("instance_id", instanceId);
+
+ sidecarManager.restartPrewarmedSidecarPods(appDef, instanceId, correlationId);
+
+ // Release instance back to pool
+ ISpan releaseSpan = Tracing.childSpan(span, "eager.release_instance", "Release pool instance");
+ releaseSpan.setData("instance_id", instanceId);
+ boolean success = pool.releaseInstance(session, appDef, correlationId);
+ releaseSpan.setTag("outcome", success ? "success" : "failure");
+ Tracing.finish(releaseSpan, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+
+ // Reconcile the specific instance
+ ISpan reconcileSpan = Tracing.childSpan(span, "eager.reconcile_instance", "Reconcile released instance");
+ reconcileSpan.setData("instance_id", instanceId);
+ pool.reconcileInstance(appDef, instanceId, correlationId);
+ Tracing.finishSuccess(reconcileSpan);
+
+ span.setTag("outcome", success ? "success" : "failure");
+ Tracing.finish(span, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ return success;
+
+ } catch (Exception e) {
+ Tracing.finishError(span, e);
+ throw e;
+ }
+ }
+
+ private void annotateSession(Session session, String correlationId, int instanceId) {
+ String name = session.getMetadata().getName();
+ client.sessions().edit(correlationId, name, s -> {
+ Map annotations = s.getMetadata().getAnnotations();
+ if (annotations == null) {
+ annotations = new HashMap<>();
+ s.getMetadata().setAnnotations(annotations);
+ }
+ annotations.put(SESSION_START_STRATEGY_ANNOTATION, SESSION_START_STRATEGY_EAGER);
+ annotations.put(SESSION_INSTANCE_ID_ANNOTATION, String.valueOf(instanceId));
+ });
+ }
+
+ private Integer getInstanceIdFromAnnotation(Session session) {
+ Map annotations = session.getMetadata().getAnnotations();
+ if (annotations == null) {
+ return null;
+ }
+ String idStr = annotations.get(SESSION_INSTANCE_ID_ANNOTATION);
+ if (idStr == null) {
+ return null;
+ }
+ try {
+ return Integer.parseInt(idStr);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerStartSessionHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerStartSessionHandler.java
deleted file mode 100644
index c9ddd2055..000000000
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerStartSessionHandler.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/********************************************************************************
- * Copyright (C) 2022 EclipseSource, Lockular, Ericsson, STMicroelectronics and
- * others.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the Eclipse
- * Public License v. 2.0 are satisfied: GNU General Public License, version 2
- * with the GNU Classpath Exception which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- ********************************************************************************/
-package org.eclipse.theia.cloud.operator.handler.session;
-
-import static org.eclipse.theia.cloud.common.util.LogMessageUtil.formatLogMessage;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Optional;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
-import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
-import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
-import org.eclipse.theia.cloud.common.k8s.resource.session.SessionSpec;
-import org.eclipse.theia.cloud.common.util.JavaUtil;
-import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
-import org.eclipse.theia.cloud.operator.handler.AddedHandlerUtil;
-import org.eclipse.theia.cloud.operator.ingress.IngressPathProvider;
-import org.eclipse.theia.cloud.operator.util.K8sUtil;
-import org.eclipse.theia.cloud.operator.util.TheiaCloudConfigMapUtil;
-import org.eclipse.theia.cloud.operator.util.TheiaCloudDeploymentUtil;
-import org.eclipse.theia.cloud.operator.util.TheiaCloudHandlerUtil;
-import org.eclipse.theia.cloud.operator.util.TheiaCloudServiceUtil;
-
-import com.google.inject.Inject;
-
-import io.fabric8.kubernetes.api.model.Service;
-import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPath;
-import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressRuleValue;
-import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
-import io.fabric8.kubernetes.api.model.networking.v1.IngressBackend;
-import io.fabric8.kubernetes.api.model.networking.v1.IngressRule;
-import io.fabric8.kubernetes.api.model.networking.v1.IngressServiceBackend;
-import io.fabric8.kubernetes.api.model.networking.v1.ServiceBackendPort;
-import io.fabric8.kubernetes.client.KubernetesClientException;
-import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
-
-/**
- * A {@link SessionAddedHandler} that relies on the fact that the app definition handler created spare deployments to
- * use.
- */
-public class EagerStartSessionHandler implements SessionHandler {
-
- private static final Logger LOGGER = LogManager.getLogger(EagerStartSessionHandler.class);
-
- @Inject
- private TheiaCloudClient client;
-
- @Inject
- protected IngressPathProvider ingressPathProvider;
-
- @Inject
- protected TheiaCloudOperatorArguments arguments;
-
- @Override
- public boolean sessionAdded(Session session, String correlationId) {
- SessionSpec spec = session.getSpec();
- LOGGER.info(formatLogMessage(correlationId, "Handling " + spec));
-
- String sessionResourceName = session.getMetadata().getName();
- String sessionResourceUID = session.getMetadata().getUid();
-
- String appDefinitionID = spec.getAppDefinition();
- String userEmail = spec.getUser();
-
- /* find app definition for session */
- Optional appDefinition = client.appDefinitions().get(appDefinitionID);
- if (appDefinition.isEmpty()) {
- LOGGER.error(formatLogMessage(correlationId, "No App Definition with name " + appDefinitionID + " found."));
- return false;
- }
-
- String appDefinitionResourceName = appDefinition.get().getMetadata().getName();
- String appDefinitionResourceUID = appDefinition.get().getMetadata().getUid();
- int port = appDefinition.get().getSpec().getPort();
-
- /* find ingress */
- Optional ingress = K8sUtil.getExistingIngress(client.kubernetes(), client.namespace(),
- appDefinitionResourceName, appDefinitionResourceUID);
- if (ingress.isEmpty()) {
- LOGGER.error(
- formatLogMessage(correlationId, "No Ingress for app definition " + appDefinitionID + " found."));
- return false;
- }
-
- /* get a service to use */
- Entry, Boolean> reserveServiceResult = reserveService(client.kubernetes(), client.namespace(),
- appDefinitionResourceName, appDefinitionResourceUID, appDefinitionID, sessionResourceName,
- sessionResourceUID, correlationId);
- if (reserveServiceResult.getValue()) {
- LOGGER.info(formatLogMessage(correlationId, "Found an already reserved service"));
- return true;
- }
- Optional serviceToUse = reserveServiceResult.getKey();
- if (serviceToUse.isEmpty()) {
- LOGGER.error(
- formatLogMessage(correlationId, "No Service for app definition " + appDefinitionID + " found."));
- return false;
- }
-
- /* get the deployment for the service and add as owner */
- Integer instance = TheiaCloudServiceUtil.getId(correlationId, appDefinition.get(), serviceToUse.get());
- if (instance == null) {
- LOGGER.error(formatLogMessage(correlationId, "Error while getting instance from Service"));
- return false;
- }
-
- try {
- client.kubernetes().apps().deployments()
- .withName(TheiaCloudDeploymentUtil.getDeploymentName(appDefinition.get(), instance))
- .edit(deployment -> TheiaCloudHandlerUtil.addOwnerReferenceToItem(correlationId,
- sessionResourceName, sessionResourceUID, deployment));
- } catch (KubernetesClientException e) {
- LOGGER.error(formatLogMessage(correlationId, "Error while editing deployment "
- + (appDefinitionID + TheiaCloudDeploymentUtil.DEPLOYMENT_NAME + instance)), e);
- return false;
- }
-
- if (arguments.isUseKeycloak()) {
- /* add user to allowed emails */
- try {
- client.kubernetes().configMaps()
- .withName(TheiaCloudConfigMapUtil.getEmailConfigName(appDefinition.get(), instance))
- .edit(configmap -> {
- configmap.setData(Collections
- .singletonMap(AddedHandlerUtil.FILENAME_AUTHENTICATED_EMAILS_LIST, userEmail));
- return configmap;
- });
- } catch (KubernetesClientException e) {
- LOGGER.error(
- formatLogMessage(correlationId,
- "Error while editing email configmap "
- + (appDefinitionID + TheiaCloudConfigMapUtil.CONFIGMAP_EMAIL_NAME + instance)),
- e);
- return false;
- }
- }
-
- /* adjust the ingress */
- String host;
- try {
- host = updateIngress(ingress, serviceToUse, appDefinitionID, instance, port, appDefinition.get(),
- correlationId);
- } catch (KubernetesClientException e) {
- LOGGER.error(formatLogMessage(correlationId,
- "Error while editing ingress " + ingress.get().getMetadata().getName()), e);
- return false;
- }
-
- /* Update session resource */
- try {
- AddedHandlerUtil.updateSessionURLAsync(client.sessions(), session, client.namespace(), host, correlationId);
- } catch (KubernetesClientException e) {
- LOGGER.error(
- formatLogMessage(correlationId, "Error while editing session " + session.getMetadata().getName()),
- e);
- return false;
- }
-
- return true;
- }
-
- protected synchronized Entry, Boolean> reserveService(NamespacedKubernetesClient client,
- String namespace, String appDefinitionResourceName, String appDefinitionResourceUID, String appDefinitionID,
- String sessionResourceName, String sessionResourceUID, String correlationId) {
- List existingServices = K8sUtil.getExistingServices(client, namespace, appDefinitionResourceName,
- appDefinitionResourceUID);
-
- Optional alreadyReservedService = TheiaCloudServiceUtil.getServiceOwnedBySession(sessionResourceName,
- sessionResourceUID, existingServices);
- if (alreadyReservedService.isPresent()) {
- return JavaUtil.tuple(alreadyReservedService, true);
- }
-
- Optional serviceToUse = TheiaCloudServiceUtil.getUnusedService(existingServices);
- if (serviceToUse.isEmpty()) {
- return JavaUtil.tuple(serviceToUse, false);
- }
-
- /* add our session as owner to the service */
- try {
- client.services().inNamespace(namespace).withName(serviceToUse.get().getMetadata().getName())
- .edit(service -> TheiaCloudHandlerUtil.addOwnerReferenceToItem(correlationId, sessionResourceName,
- sessionResourceUID, service));
- } catch (KubernetesClientException e) {
- LOGGER.error(formatLogMessage(correlationId,
- "Error while editing service " + (serviceToUse.get().getMetadata().getName())), e);
- return JavaUtil.tuple(Optional.empty(), false);
- }
- return JavaUtil.tuple(serviceToUse, false);
- }
-
- protected synchronized String updateIngress(Optional ingress, Optional serviceToUse,
- String appDefinitionID, int instance, int port, AppDefinition appDefinition, String correlationId) {
- final String host = arguments.getInstancesHost();
- String path = ingressPathProvider.getPath(appDefinition, instance);
- client.ingresses().edit(correlationId, ingress.get().getMetadata().getName(),
- ingressToUpdate -> addIngressRule(ingressToUpdate, serviceToUse.get(), host, port, path));
- return host + path + "/";
- }
-
- protected Ingress addIngressRule(Ingress ingress, Service serviceToUse, String host, int port, String path) {
- IngressRule ingressRule = new IngressRule();
- ingress.getSpec().getRules().add(ingressRule);
-
- ingressRule.setHost(host);
-
- HTTPIngressRuleValue http = new HTTPIngressRuleValue();
- ingressRule.setHttp(http);
-
- HTTPIngressPath httpIngressPath = new HTTPIngressPath();
- http.getPaths().add(httpIngressPath);
- httpIngressPath.setPath(path + AddedHandlerUtil.INGRESS_REWRITE_PATH);
- httpIngressPath.setPathType("Prefix");
-
- IngressBackend ingressBackend = new IngressBackend();
- httpIngressPath.setBackend(ingressBackend);
-
- IngressServiceBackend ingressServiceBackend = new IngressServiceBackend();
- ingressBackend.setService(ingressServiceBackend);
- ingressServiceBackend.setName(serviceToUse.getMetadata().getName());
-
- ServiceBackendPort serviceBackendPort = new ServiceBackendPort();
- ingressServiceBackend.setPort(serviceBackendPort);
- serviceBackendPort.setNumber(port);
-
- return ingress;
- }
-
-}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerWithLazyFallbackSessionHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerWithLazyFallbackSessionHandler.java
new file mode 100644
index 000000000..903f68fe0
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerWithLazyFallbackSessionHandler.java
@@ -0,0 +1,131 @@
+package org.eclipse.theia.cloud.operator.handler.session;
+
+import static org.eclipse.theia.cloud.common.util.LogMessageUtil.formatLogMessage;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
+import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+import org.eclipse.theia.cloud.common.tracing.Tracing;
+
+import com.google.inject.Inject;
+
+import io.sentry.ISpan;
+import io.sentry.SpanStatus;
+
+/**
+ * Tries to handle a session with {@link EagerSessionHandler} first. If there is
+ * no prewarmed capacity left, falls back
+ * to {@link LazySessionHandler}.
+ */
+public class EagerWithLazyFallbackSessionHandler implements SessionHandler {
+
+ /**
+ * Value indicating session was started lazily after eager capacity was
+ * exhausted.
+ */
+ public static final String SESSION_START_STRATEGY_LAZY_FALLBACK = "lazy-fallback";
+
+ private static final Logger LOGGER = LogManager.getLogger(EagerWithLazyFallbackSessionHandler.class);
+
+ @Inject
+ private EagerSessionHandler eager;
+
+ @Inject
+ private LazySessionHandler lazy;
+
+ @Inject
+ private TheiaCloudClient client;
+
+ @Override
+ public boolean sessionAdded(Session session, String correlationId, ISpan span) {
+ // Try eager start first
+ ISpan eagerSpan = Tracing.childSpan(span, "session.eager_attempt", "Attempt eager session start");
+ eagerSpan.setTag("eagersession.strategy", "eager");
+
+ EagerSessionHandler.EagerSessionAddedOutcome eagerOutcome = eager.trySessionAdded(session, correlationId,
+ eagerSpan);
+ eagerSpan.setData("eager_outcome", eagerOutcome.name());
+
+ if (eagerOutcome == EagerSessionHandler.EagerSessionAddedOutcome.HANDLED) {
+ Tracing.finishSuccess(eagerSpan);
+ span.setTag("session.strategy", "eager");
+ return true;
+ }
+
+ if (eagerOutcome == EagerSessionHandler.EagerSessionAddedOutcome.ERROR) {
+ eagerSpan.setTag("outcome", "error");
+ Tracing.finish(eagerSpan, SpanStatus.INTERNAL_ERROR);
+ span.setTag("session.strategy", "eager");
+ return false;
+ }
+
+ // NO_CAPACITY - fall back to lazy
+ eagerSpan.setTag("outcome", "no_capacity");
+ Tracing.finish(eagerSpan, SpanStatus.RESOURCE_EXHAUSTED);
+ span.setTag("fallback", "true");
+ span.setTag("fallback.reason", "no_prewarmed_capacity");
+
+ LOGGER.info(formatLogMessage(correlationId,
+ "No prewarmed capacity left. Falling back to lazy session handling."));
+
+ ISpan lazySpan = Tracing.childSpan(span, "session.lazy_fallback", "Fallback to lazy session start");
+ lazySpan.setTag("session.strategy", "lazy-fallback");
+ lazySpan.setTag("fallback.reason", "no_prewarmed_capacity");
+
+ boolean lazyResult = lazy.sessionAdded(session, correlationId, lazySpan);
+
+ if (lazyResult) {
+ annotateSessionStrategy(session, correlationId, SESSION_START_STRATEGY_LAZY_FALLBACK);
+ Tracing.finishSuccess(lazySpan);
+ span.setTag("session.strategy", "lazy-fallback");
+ } else {
+ lazySpan.setTag("outcome", "failure");
+ Tracing.finish(lazySpan, SpanStatus.INTERNAL_ERROR);
+ span.setTag("session.strategy", "lazy-fallback");
+ }
+
+ return lazyResult;
+ }
+
+ @Override
+ public boolean sessionDeleted(Session session, String correlationId, ISpan parentSpan) {
+ String strategy = Optional.ofNullable(session.getMetadata()).map(m -> m.getAnnotations())
+ .map(a -> a.get(EagerSessionHandler.SESSION_START_STRATEGY_ANNOTATION)).orElse("unknown");
+
+ parentSpan.setTag("session.start_strategy", strategy);
+
+ boolean result;
+ if (EagerSessionHandler.SESSION_START_STRATEGY_EAGER.equals(strategy)) {
+ ISpan span = Tracing.childSpan(parentSpan, "session.eager_cleanup", "Eager session cleanup");
+ span.setTag("cleanup.type", "eager");
+ result = eager.sessionDeleted(session, correlationId, span);
+ span.setTag("outcome", result ? "success" : "failure");
+ Tracing.finish(span, result ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ } else {
+ ISpan span = Tracing.childSpan(parentSpan, "session.lazy_cleanup", "Lazy session cleanup");
+ span.setTag("cleanup.type", "lazy");
+ result = lazy.sessionDeleted(session, correlationId, span);
+ span.setTag("outcome", result ? "success" : "failure");
+ Tracing.finish(span, result ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ }
+
+ return result;
+ }
+
+ private void annotateSessionStrategy(Session session, String correlationId, String strategy) {
+ String name = session.getMetadata().getName();
+ client.sessions().edit(correlationId, name, s -> {
+ Map annotations = s.getMetadata().getAnnotations();
+ if (annotations == null) {
+ annotations = new HashMap<>();
+ s.getMetadata().setAnnotations(annotations);
+ }
+ annotations.put(EagerSessionHandler.SESSION_START_STRATEGY_ANNOTATION, strategy);
+ });
+ }
+}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/LazySessionHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/LazySessionHandler.java
index 88100f956..1c8e00835 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/LazySessionHandler.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/LazySessionHandler.java
@@ -1,5 +1,5 @@
/********************************************************************************
- * Copyright (C) 2022-2023 EclipseSource, Lockular, Ericsson, STMicroelectronics and
+ * Copyright (C) 2022-2025 EclipseSource, Lockular, Ericsson, STMicroelectronics and
* others.
*
* This program and the accompanying materials are made available under the
@@ -19,10 +19,6 @@
import static org.eclipse.theia.cloud.common.util.LogMessageUtil.formatLogMessage;
import static org.eclipse.theia.cloud.common.util.LogMessageUtil.formatMetric;
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -30,31 +26,30 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
-import org.eclipse.theia.cloud.common.k8s.resource.OperatorStatus;
-import org.eclipse.theia.cloud.common.k8s.resource.ResourceStatus;
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinitionSpec;
import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
import org.eclipse.theia.cloud.common.k8s.resource.session.SessionSpec;
-import org.eclipse.theia.cloud.common.k8s.resource.session.SessionStatus;
import org.eclipse.theia.cloud.common.k8s.resource.workspace.Workspace;
+import org.eclipse.theia.cloud.common.util.LabelsUtil;
import org.eclipse.theia.cloud.common.util.TheiaCloudError;
import org.eclipse.theia.cloud.common.util.WorkspaceUtil;
import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
-import org.eclipse.theia.cloud.operator.bandwidth.BandwidthLimiter;
import org.eclipse.theia.cloud.operator.handler.AddedHandlerUtil;
-import org.eclipse.theia.cloud.operator.ingress.IngressPathProvider;
-import org.eclipse.theia.cloud.operator.replacements.DeploymentTemplateReplacements;
-import org.eclipse.theia.cloud.operator.util.JavaResourceUtil;
+import org.eclipse.theia.cloud.operator.ingress.IngressManager;
+import org.eclipse.theia.cloud.operator.sidecar.SidecarManager;
+import org.eclipse.theia.cloud.operator.util.K8sResourceFactory;
import org.eclipse.theia.cloud.operator.util.K8sUtil;
-import org.eclipse.theia.cloud.operator.util.TheiaCloudConfigMapUtil;
-import org.eclipse.theia.cloud.operator.util.TheiaCloudIngressUtil;
import org.eclipse.theia.cloud.operator.util.TheiaCloudK8sUtil;
+import org.eclipse.theia.cloud.common.tracing.Tracing;
import org.eclipse.theia.cloud.operator.util.TheiaCloudPersistentVolumeUtil;
-import org.eclipse.theia.cloud.operator.util.TheiaCloudServiceUtil;
import com.google.inject.Inject;
+import io.sentry.ISpan;
+import io.sentry.Sentry;
+import io.sentry.SpanStatus;
+
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSource;
@@ -63,235 +58,305 @@
import io.fabric8.kubernetes.api.model.Volume;
import io.fabric8.kubernetes.api.model.VolumeMount;
import io.fabric8.kubernetes.api.model.apps.Deployment;
-import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPath;
-import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressRuleValue;
-import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
-import io.fabric8.kubernetes.api.model.networking.v1.IngressBackend;
-import io.fabric8.kubernetes.api.model.networking.v1.IngressRule;
-import io.fabric8.kubernetes.api.model.networking.v1.IngressServiceBackend;
-import io.fabric8.kubernetes.api.model.networking.v1.ServiceBackendPort;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRoute;
import io.fabric8.kubernetes.client.KubernetesClientException;
+/**
+ * A {@link SessionHandler} that creates resources on-demand (lazy start).
+ *
+ * Uses {@link K8sResourceFactory} for resource creation and
+ * {@link IngressManager} for ingress operations.
+ */
public class LazySessionHandler implements SessionHandler {
private static final Logger LOGGER = LogManager.getLogger(LazySessionHandler.class);
protected static final String USER_DATA = "user-data";
- @Inject
- protected IngressPathProvider ingressPathProvider;
@Inject
protected TheiaCloudOperatorArguments arguments;
+
+ @Inject
+ protected TheiaCloudClient client;
+
@Inject
- protected BandwidthLimiter bandwidthLimiter;
+ protected K8sResourceFactory resourceFactory;
+
@Inject
- protected DeploymentTemplateReplacements deploymentReplacements;
+ protected IngressManager ingressManager;
@Inject
- protected TheiaCloudClient client;
+ protected SidecarManager sidecarManager;
@Override
- public boolean sessionAdded(Session session, String correlationId) {
+ public boolean sessionAdded(Session session, String correlationId, ISpan parentSpan) {
+ ISpan span = Tracing.childSpan(parentSpan, "lazy.setup", "Lazy session setup");
try {
- return doSessionAdded(session, correlationId);
+ return doSessionAdded(session, correlationId, span);
} catch (Throwable ex) {
LOGGER.error(formatLogMessage(correlationId,
"An unexpected exception occurred while adding Session: " + session), ex);
- client.sessions().updateStatus(correlationId, session, status -> {
- status.setOperatorStatus(OperatorStatus.ERROR);
- status.setOperatorMessage(
- "Unexpected error. Please check the logs for correlationId: " + correlationId);
- });
+ Tracing.finishError(span, ex);
+ SessionStatusUtil.markError(client, session, correlationId,
+ "Unexpected error. Please check the logs for correlationId: " + correlationId);
return false;
}
}
- protected boolean doSessionAdded(Session session, String correlationId) {
- /* session information */
+ protected boolean doSessionAdded(Session session, String correlationId, ISpan span) {
+ // Session information
String sessionResourceName = session.getMetadata().getName();
String sessionResourceUID = session.getMetadata().getUid();
+ // Use provided span
+ span.setTag("session.name", sessionResourceName);
+ span.setTag("session.strategy", "lazy");
+ span.setData("correlation_id", correlationId);
+
// Check current session status and ignore if handling failed or finished before
- Optional status = Optional.ofNullable(session.getStatus());
- String operatorStatus = status.map(ResourceStatus::getOperatorStatus).orElse(OperatorStatus.NEW);
- if (OperatorStatus.HANDLED.equals(operatorStatus)) {
- LOGGER.trace(formatLogMessage(correlationId,
- "Session was successfully handled before and is skipped now. Session: " + session));
+ SessionStatusUtil.PreHandleResult preHandle = SessionStatusUtil.evaluateStatus(session, client, correlationId,
+ LOGGER);
+ if (preHandle == SessionStatusUtil.PreHandleResult.ALREADY_HANDLED) {
+ span.setTag("outcome", "already_handled");
+ Tracing.finish(span, SpanStatus.OK);
return true;
}
- if (OperatorStatus.HANDLING.equals(operatorStatus)) {
- // TODO We should not return but continue where we left off.
- LOGGER.warn(formatLogMessage(correlationId,
- "Session handling was unexpectedly interrupted before. Session is skipped now and its status is set to ERROR. Session: "
- + session));
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.ERROR);
- s.setOperatorMessage("Handling was unexpectedly interrupted before. CorrelationId: " + correlationId);
- });
+ if (preHandle == SessionStatusUtil.PreHandleResult.INTERRUPTED) {
+ span.setTag("outcome", "interrupted");
+ Tracing.finish(span, SpanStatus.ABORTED);
return false;
}
- if (OperatorStatus.ERROR.equals(operatorStatus)) {
- LOGGER.warn(formatLogMessage(correlationId,
- "Session could not be handled before and is skipped now. Session: " + session));
+ if (preHandle == SessionStatusUtil.PreHandleResult.PREVIOUS_ERROR) {
+ span.setTag("outcome", "previous_error");
+ Tracing.finish(span, SpanStatus.ABORTED);
return false;
}
- // Set session status to being handled
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.HANDLING);
- });
+ // Set status to handling
+ SessionStatusUtil.markHandling(client, session, correlationId);
SessionSpec sessionSpec = session.getSpec();
- /* find app definition for session */
+ // Find app definition
+ ISpan appDefSpan = Tracing.childSpan(span, "lazy.find_appdef", "Find app definition");
String appDefinitionID = sessionSpec.getAppDefinition();
- Optional optionalAppDefinition = client.appDefinitions().get(appDefinitionID);
- if (optionalAppDefinition.isEmpty()) {
- LOGGER.error(formatLogMessage(correlationId, "No App Definition with name " + appDefinitionID + " found."));
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.ERROR);
- s.setOperatorMessage("App Definition not found.");
- });
+ span.setTag("app_definition", appDefinitionID);
+ Optional appDefOpt = client.appDefinitions().get(appDefinitionID);
+ if (appDefOpt.isEmpty()) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "No App Definition with name " + appDefinitionID + " found."));
+ SessionStatusUtil.markError(client, session, correlationId, "App Definition not found.");
+ appDefSpan.setTag("outcome", "not_found");
+ Tracing.finish(appDefSpan, SpanStatus.NOT_FOUND);
+ span.setTag("outcome", "appdef_not_found");
+ Tracing.finish(span, SpanStatus.NOT_FOUND);
return false;
}
- AppDefinition appDefinition = optionalAppDefinition.get();
-
- if (hasMaxInstancesReached(appDefinition, session, correlationId)) {
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.ERROR);
- s.setOperatorMessage("Max instances reached.");
- });
+ AppDefinition appDef = appDefOpt.get();
+ AppDefinitionSpec appDefSpec = appDef.getSpec();
+ Tracing.finishSuccess(appDefSpan);
+
+ // Create labels
+ Map labels = LabelsUtil.createSessionLabels(session, appDef);
+
+ // Check limits
+ ISpan limitsSpan = Tracing.childSpan(span, "lazy.check_limits", "Check instance and session limits");
+ if (hasMaxInstancesReached(appDef, session, correlationId)) {
+ SessionStatusUtil.markError(client, session, correlationId, "Max instances reached.");
+ limitsSpan.setTag("outcome", "max_instances");
+ Tracing.finish(limitsSpan, SpanStatus.RESOURCE_EXHAUSTED);
+ span.setTag("outcome", "max_instances_reached");
+ Tracing.finish(span, SpanStatus.RESOURCE_EXHAUSTED);
return false;
}
-
if (hasMaxSessionsReached(session, correlationId)) {
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.ERROR);
- s.setOperatorMessage("Max sessions reached.");
- });
+ SessionStatusUtil.markError(client, session, correlationId, "Max sessions reached.");
+ limitsSpan.setTag("outcome", "max_sessions");
+ Tracing.finish(limitsSpan, SpanStatus.RESOURCE_EXHAUSTED);
+ span.setTag("outcome", "max_sessions_reached");
+ Tracing.finish(span, SpanStatus.RESOURCE_EXHAUSTED);
return false;
}
-
- Optional ingress = getIngress(appDefinition, correlationId);
- if (ingress.isEmpty()) {
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.ERROR);
- s.setOperatorMessage("Ingress not available.");
- });
+ Tracing.finishSuccess(limitsSpan);
+
+ // Get shared HTTPRoute template
+ ISpan ingressSpan = Tracing.childSpan(span, "lazy.get_route", "Get HTTPRoute for app definition");
+ Optional routeOpt = ingressManager.getIngress(appDef, correlationId);
+ if (routeOpt.isEmpty()) {
+ SessionStatusUtil.markError(client, session, correlationId, "HTTPRoute not available.");
+ ingressSpan.setTag("outcome", "not_found");
+ Tracing.finish(ingressSpan, SpanStatus.NOT_FOUND);
+ span.setTag("outcome", "route_not_found");
+ Tracing.finish(span, SpanStatus.NOT_FOUND);
return false;
}
+ Tracing.finishSuccess(ingressSpan);
syncSessionDataToWorkspace(session, correlationId);
- /* Create service for this session */
- List existingServices = K8sUtil.getExistingServices(client.kubernetes(), client.namespace(),
- sessionResourceName, sessionResourceUID);
+ // Check for existing service (idempotency)
+ List existingServices = K8sUtil.getExistingServices(
+ client.kubernetes(), client.namespace(), sessionResourceName, sessionResourceUID);
if (!existingServices.isEmpty()) {
- LOGGER.warn(formatLogMessage(correlationId,
- "Existing service for " + sessionSpec + ". Session already running?"));
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.HANDLED);
- s.setOperatorMessage("Service already exists.");
- });
- // TODO do not return true if the sessions was in handling state at the start of
- // this handler
+ LOGGER.warn(formatLogMessage(correlationId, "Service already exists for session."));
+ SessionStatusUtil.markHandled(client, session, correlationId, "Service already exists.");
+ span.setTag("outcome", "idempotent_service_exists");
+ Tracing.finish(span, SpanStatus.OK);
return true;
}
- Optional serviceToUse = createAndApplyService(correlationId, sessionResourceName, sessionResourceUID,
- session, appDefinition.getSpec(), arguments.isUseKeycloak());
- if (serviceToUse.isEmpty()) {
- LOGGER.error(formatLogMessage(correlationId, "Unable to create service for session " + sessionSpec));
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.ERROR);
- s.setOperatorMessage("Failed to create service.");
- });
+ // Create service
+ ISpan serviceSpan = Tracing.childSpan(span, "lazy.create_service", "Create service");
+ Optional serviceOpt = resourceFactory.createServiceForLazySession(
+ session, appDef, labels, correlationId);
+ if (serviceOpt.isEmpty()) {
+ LOGGER.error(formatLogMessage(correlationId, "Unable to create service for session."));
+ SessionStatusUtil.markError(client, session, correlationId, "Failed to create service.");
+ serviceSpan.setTag("outcome", "failed");
+ Tracing.finish(serviceSpan, SpanStatus.INTERNAL_ERROR);
+ span.setTag("outcome", "service_creation_failed");
+ Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return false;
+ }
+ Tracing.finishSuccess(serviceSpan);
+
+ // Create internal service
+ ISpan internalServiceSpan = Tracing.childSpan(span, "lazy.create_internal_service", "Create internal service");
+ Optional internalServiceOpt = resourceFactory.createInternalServiceForLazySession(
+ session, appDef, labels, correlationId);
+ if (internalServiceOpt.isEmpty()) {
+ LOGGER.error(formatLogMessage(correlationId, "Unable to create internal service."));
+ SessionStatusUtil.markError(client, session, correlationId, "Failed to create internal service.");
+ internalServiceSpan.setTag("outcome", "failed");
+ Tracing.finish(internalServiceSpan, SpanStatus.INTERNAL_ERROR);
+ span.setTag("outcome", "internal_service_failed");
+ Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
return false;
}
+ Tracing.finishSuccess(internalServiceSpan);
+ // Create configmaps (if using Keycloak)
if (arguments.isUseKeycloak()) {
- /* Create config maps for this session */
- List existingConfigMaps = K8sUtil.getExistingConfigMaps(client.kubernetes(), client.namespace(),
- sessionResourceName, sessionResourceUID);
+ ISpan configMapSpan = Tracing.childSpan(span, "lazy.create_configmaps", "Create OAuth2 configmaps");
+ List existingConfigMaps = K8sUtil.getExistingConfigMaps(
+ client.kubernetes(), client.namespace(), sessionResourceName, sessionResourceUID);
if (!existingConfigMaps.isEmpty()) {
- LOGGER.warn(formatLogMessage(correlationId,
- "Existing configmaps for " + sessionSpec + ". Session already running?"));
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.HANDLED);
- s.setOperatorMessage("Configmaps already exist.");
- });
- // TODO do not return true if the sessions was in handling state at the start of
- // this handler
+ LOGGER.warn(formatLogMessage(correlationId, "ConfigMaps already exist for session."));
+ SessionStatusUtil.markHandled(client, session, correlationId, "ConfigMaps already exist.");
+ configMapSpan.setTag("outcome", "already_exists");
+ Tracing.finish(configMapSpan, SpanStatus.OK);
+ span.setTag("outcome", "idempotent_configmaps_exist");
+ Tracing.finish(span, SpanStatus.OK);
return true;
}
- createAndApplyEmailConfigMap(correlationId, sessionResourceName, sessionResourceUID, session);
- createAndApplyProxyConfigMap(correlationId, sessionResourceName, sessionResourceUID, session,
- appDefinition);
+ resourceFactory.createEmailConfigMapForLazySession(session, labels, correlationId);
+ resourceFactory.createProxyConfigMapForLazySession(session, appDef, labels, correlationId);
+ Tracing.finishSuccess(configMapSpan);
}
- /* Create deployment for this session */
- List existingDeployments = K8sUtil.getExistingDeployments(client.kubernetes(), client.namespace(),
- sessionResourceName, sessionResourceUID);
+ // Check for existing deployment (idempotency)
+ List existingDeployments = K8sUtil.getExistingDeployments(
+ client.kubernetes(), client.namespace(), sessionResourceName, sessionResourceUID);
if (!existingDeployments.isEmpty()) {
- LOGGER.warn(formatLogMessage(correlationId,
- "Existing deployments for " + sessionSpec + ". Session already running?"));
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.HANDLED);
- s.setOperatorMessage("Deployment already exists.");
- });
+ LOGGER.warn(formatLogMessage(correlationId, "Deployment already exists for session."));
+ SessionStatusUtil.markHandled(client, session, correlationId, "Deployment already exists.");
+ span.setTag("outcome", "idempotent_deployment_exists");
+ Tracing.finish(span, SpanStatus.OK);
return true;
}
+ // Create deployment
+ ISpan deploymentSpan = Tracing.childSpan(span, "lazy.create_deployment", "Create deployment");
Optional storageName = getStorageName(session, correlationId);
- createAndApplyDeployment(correlationId, sessionResourceName, sessionResourceUID, session, appDefinition,
- storageName, arguments.isUseKeycloak());
+ deploymentSpan.setData("has_storage", storageName.isPresent());
+ resourceFactory.createDeploymentForLazySession(
+ session, appDef, storageName, labels,
+ deployment -> {
+ storageName.ifPresent(name -> addVolumeClaim(deployment, name, appDefSpec));
+ sidecarManager.injectSidecarEnvVars(session, deployment, appDef, correlationId);
+ },
+ correlationId);
+ Tracing.finishSuccess(deploymentSpan);
- /* adjust the ingress */
+ // Sidecar setup is best-effort: the session remains usable even if sidecar creation fails.
+ if (!sidecarManager.createSidecars(session, appDef, storageName, correlationId)) {
+ LOGGER.warn(formatLogMessage(correlationId,
+ "Sidecar creation failed for session " + session.getMetadata().getName()
+ + "; session will continue without sidecar support."));
+ }
+
+ // Create the dedicated session HTTPRoute.
+ ISpan ingressRuleSpan = Tracing.childSpan(span, "lazy.add_route_rule", "Add HTTPRoute rule");
String host;
try {
- host = updateIngress(ingress, serviceToUse, session, appDefinition, correlationId);
+ host = ingressManager.addRuleForSession(
+ routeOpt.get(), serviceOpt.get(), appDef, session, correlationId);
+ ingressRuleSpan.setData("host", host);
+ Tracing.finishSuccess(ingressRuleSpan);
} catch (KubernetesClientException e) {
- LOGGER.error(formatLogMessage(correlationId,
- "Error while editing ingress " + ingress.get().getMetadata().getName()), e);
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.ERROR);
- s.setOperatorMessage("Failed to edit ingress");
- });
+ LOGGER.error(formatLogMessage(correlationId, "Error while editing HTTPRoute"), e);
+ SessionStatusUtil.markError(client, session, correlationId, "Failed to edit HTTPRoute.");
+ Tracing.finishError(ingressRuleSpan, e);
+ span.setTag("outcome", "route_rule_failed");
+ Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
return false;
}
- /* Update session resource */
+ Sentry.addBreadcrumb("Scheduling URL availability check for " + host, "session");
+ AddedHandlerUtil.updateSessionURLAsync(client.sessions(), session, client.namespace(), host, correlationId,
+ span);
+
+ SessionStatusUtil.markHandled(client, session, correlationId, null);
+ span.setTag("outcome", "success");
+ Tracing.finishSuccess(span);
+ return true;
+ }
+
+ @Override
+ public synchronized boolean sessionDeleted(Session session, String correlationId, ISpan parentSpan) {
+ ISpan span = Tracing.childSpan(parentSpan, "lazy.cleanup", "Lazy session cleanup");
try {
- AddedHandlerUtil.updateSessionURLAsync(client.sessions(), session, client.namespace(), host, correlationId);
- } catch (KubernetesClientException e) {
- LOGGER.error(
- formatLogMessage(correlationId, "Error while editing session " + session.getMetadata().getName()),
- e);
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.ERROR);
- s.setOperatorMessage("Failed to set session URL.");
- });
- return false;
+ boolean success = doSessionDeleted(session, correlationId, span);
+ if (success) {
+ Tracing.finishSuccess(span);
+ }
+ return success;
+ } catch (Exception e) {
+ Tracing.finishError(span, e);
+ throw e;
}
+ }
- client.sessions().updateStatus(correlationId, session, s -> {
- s.setOperatorStatus(OperatorStatus.HANDLED);
- });
+ protected boolean doSessionDeleted(Session session, String correlationId, ISpan span) {
+ SessionSpec sessionSpec = session.getSpec();
+ String sessionName = session.getMetadata().getName();
+ String appDefinitionID = sessionSpec.getAppDefinition();
+
+ // Use provided span
+ span.setTag("session.name", sessionName);
+ span.setTag("session.strategy", "lazy");
+ span.setTag("app_definition", appDefinitionID);
+ span.setData("correlation_id", correlationId);
+
+ sidecarManager.deleteSidecars(session, correlationId);
+
+ span.setTag("outcome", "success");
return true;
}
+ // ========== Helper Methods ==========
+
protected void syncSessionDataToWorkspace(Session session, String correlationId) {
if (!session.getSpec().isEphemeral() && session.getSpec().hasAppDefinition()) {
- // update last used workspace
client.workspaces().edit(correlationId, session.getSpec().getWorkspace(), workspace -> {
workspace.getSpec().setAppDefinition(session.getSpec().getAppDefinition());
});
}
}
- protected boolean hasMaxInstancesReached(AppDefinition appDefinition, Session session, String correlationId) {
- if (TheiaCloudK8sUtil.checkIfMaxInstancesReached(client.kubernetes(), client.namespace(), session.getSpec(),
- appDefinition.getSpec(), correlationId)) {
- LOGGER.info(formatMetric(correlationId, "Max instances reached for " + appDefinition.getSpec().getName()));
+ protected boolean hasMaxInstancesReached(AppDefinition appDef, Session session, String correlationId) {
+ if (TheiaCloudK8sUtil.checkIfMaxInstancesReached(
+ client.kubernetes(), client.namespace(), session.getSpec(), appDef.getSpec(), correlationId)) {
+ LOGGER.info(formatMetric(correlationId, "Max instances reached for " + appDef.getSpec().getName()));
client.sessions().updateStatus(correlationId, session, status -> {
status.setError(TheiaCloudError.SESSION_SERVER_LIMIT_REACHED);
});
@@ -301,40 +366,26 @@ protected boolean hasMaxInstancesReached(AppDefinition appDefinition, Session se
}
protected boolean hasMaxSessionsReached(Session session, String correlationId) {
- /* check if max sessions reached already */
- if (arguments.getSessionsPerUser() != null && arguments.getSessionsPerUser() >= 0) {
- if (arguments.getSessionsPerUser() == 0) {
- LOGGER.info(formatLogMessage(correlationId,
- "No sessions allowed for this user. Could not create session " + session.getSpec()));
- client.sessions().updateStatus(correlationId, session, status -> {
- status.setError(TheiaCloudError.SESSION_USER_NO_SESSIONS);
- });
- return true;
- }
-
- long userSessions = client.sessions().list(session.getSpec().getUser()).size();
- if (userSessions > arguments.getSessionsPerUser()) {
- LOGGER.info(formatLogMessage(correlationId,
- "No more sessions allowed for this user, limit is " + arguments.getSessionsPerUser()));
- client.sessions().updateStatus(correlationId, session, status -> {
- status.setError(TheiaCloudError.SESSION_USER_LIMIT_REACHED);
- });
- return true;
- }
+ if (arguments.getSessionsPerUser() == null || arguments.getSessionsPerUser() < 0) {
+ return false;
}
- return false;
- }
-
- protected Optional getIngress(AppDefinition appDefinition, String correlationId) {
- String appDefinitionResourceName = appDefinition.getMetadata().getName();
- String appDefinitionResourceUID = appDefinition.getMetadata().getUid();
- Optional ingress = K8sUtil.getExistingIngress(client.kubernetes(), client.namespace(),
- appDefinitionResourceName, appDefinitionResourceUID);
- if (ingress.isEmpty()) {
- LOGGER.error(formatLogMessage(correlationId,
- "No Ingress for app definition " + appDefinition.getSpec().getName() + " found."));
+ if (arguments.getSessionsPerUser() == 0) {
+ LOGGER.info(formatLogMessage(correlationId, "No sessions allowed for this user."));
+ client.sessions().updateStatus(correlationId, session, status -> {
+ status.setError(TheiaCloudError.SESSION_USER_NO_SESSIONS);
+ });
+ return true;
}
- return ingress;
+ long userSessions = client.sessions().list(session.getSpec().getUser()).size();
+ if (userSessions > arguments.getSessionsPerUser()) {
+ LOGGER.info(formatLogMessage(correlationId,
+ "Session limit reached for user: " + arguments.getSessionsPerUser()));
+ client.sessions().updateStatus(correlationId, session, status -> {
+ status.setError(TheiaCloudError.SESSION_USER_LIMIT_REACHED);
+ });
+ return true;
+ }
+ return false;
}
protected Optional getStorageName(Session session, String correlationId) {
@@ -342,210 +393,42 @@ protected Optional getStorageName(Session session, String correlationId)
return Optional.empty();
}
Optional workspace = client.workspaces().get(session.getSpec().getWorkspace());
- if (!workspace.isPresent()) {
- LOGGER.info(formatLogMessage(correlationId, "No workspace with name " + session.getSpec().getWorkspace()
- + " found for session " + session.getSpec().getName(), correlationId));
+ if (workspace.isEmpty()) {
+ LOGGER.info(formatLogMessage(correlationId,
+ "No workspace with name " + session.getSpec().getWorkspace() + " found for session "
+ + session.getSpec().getName()));
return Optional.empty();
-
}
if (!session.getSpec().getUser().equals(workspace.get().getSpec().getUser())) {
// the workspace is owned by a different user. do not mount and go ephemeral
// should get prevented by service, but we need to be sure to not expose data
- LOGGER.error(formatLogMessage(correlationId,
- "Workspace is owned by " + workspace.get().getSpec().getUser() + ", but requesting user is "
- + session.getSpec().getUser()));
+ LOGGER.error(formatLogMessage(correlationId, "Workspace is owned by " + workspace.get().getSpec().getUser()
+ + ", but requesting user is " + session.getSpec().getUser()));
return Optional.empty();
}
-
String storageName = WorkspaceUtil.getStorageName(workspace.get());
if (!client.persistentVolumeClaimsClient().has(storageName)) {
- LOGGER.info(formatLogMessage(correlationId,
- "No storage found for started session, will use ephemeral storage instead", correlationId));
+ LOGGER.info(formatLogMessage(correlationId, "No storage found. Using ephemeral storage."));
return Optional.empty();
}
return Optional.of(storageName);
}
- protected Optional createAndApplyService(String correlationId, String sessionResourceName,
- String sessionResourceUID, Session session, AppDefinitionSpec appDefinitionSpec, boolean useOAuth2Proxy) {
- Map replacements = TheiaCloudServiceUtil.getServiceReplacements(client.namespace(), session,
- appDefinitionSpec);
- String templateYaml = useOAuth2Proxy ? AddedHandlerUtil.TEMPLATE_SERVICE_YAML
- : AddedHandlerUtil.TEMPLATE_SERVICE_WITHOUT_AOUTH2_PROXY_YAML;
- String serviceYaml;
- try {
- serviceYaml = JavaResourceUtil.readResourceAndReplacePlaceholders(templateYaml, replacements,
- correlationId);
- } catch (IOException | URISyntaxException e) {
- LOGGER.error(formatLogMessage(correlationId, "Error while adjusting template for session " + session), e);
- return Optional.empty();
- }
- return K8sUtil.loadAndCreateServiceWithOwnerReference(client.kubernetes(), client.namespace(), correlationId,
- serviceYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0);
- }
-
- protected void createAndApplyEmailConfigMap(String correlationId, String sessionResourceName,
- String sessionResourceUID, Session session) {
- Map replacements = TheiaCloudConfigMapUtil.getEmailConfigMapReplacements(client.namespace(),
- session);
- String configMapYaml;
- try {
- configMapYaml = JavaResourceUtil.readResourceAndReplacePlaceholders(
- AddedHandlerUtil.TEMPLATE_CONFIGMAP_EMAILS_YAML, replacements, correlationId);
- } catch (IOException | URISyntaxException e) {
- LOGGER.error(formatLogMessage(correlationId, "Error while adjusting template for session " + session), e);
- return;
- }
- K8sUtil.loadAndCreateConfigMapWithOwnerReference(client.kubernetes(), client.namespace(), correlationId,
- configMapYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, configmap -> {
- configmap.setData(Collections.singletonMap(AddedHandlerUtil.FILENAME_AUTHENTICATED_EMAILS_LIST,
- session.getSpec().getUser()));
- });
- }
-
- protected void createAndApplyProxyConfigMap(String correlationId, String sessionResourceName,
- String sessionResourceUID, Session session, AppDefinition appDefinition) {
- Map replacements = TheiaCloudConfigMapUtil.getProxyConfigMapReplacements(client.namespace(),
- session);
- String configMapYaml;
- try {
- configMapYaml = JavaResourceUtil.readResourceAndReplacePlaceholders(
- AddedHandlerUtil.TEMPLATE_CONFIGMAP_YAML, replacements, correlationId);
- } catch (IOException | URISyntaxException e) {
- LOGGER.error(formatLogMessage(correlationId, "Error while adjusting template for session " + session), e);
- return;
- }
- K8sUtil.loadAndCreateConfigMapWithOwnerReference(client.kubernetes(), client.namespace(), correlationId,
- configMapYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, configMap -> {
- String host = arguments.getInstancesHost() + ingressPathProvider.getPath(appDefinition, session);
- int port = appDefinition.getSpec().getPort();
- AddedHandlerUtil.updateProxyConfigMap(client.kubernetes(), client.namespace(), configMap, host,
- port);
- });
- }
-
- protected void createAndApplyDeployment(String correlationId, String sessionResourceName, String sessionResourceUID,
- Session session, AppDefinition appDefinition, Optional pvName, boolean useOAuth2Proxy) {
- Map replacements = deploymentReplacements.getReplacements(client.namespace(), appDefinition,
- session);
- String templateYaml = useOAuth2Proxy ? AddedHandlerUtil.TEMPLATE_DEPLOYMENT_YAML
- : AddedHandlerUtil.TEMPLATE_DEPLOYMENT_WITHOUT_AOUTH2_PROXY_YAML;
- String deploymentYaml;
- try {
- deploymentYaml = JavaResourceUtil.readResourceAndReplacePlaceholders(templateYaml, replacements,
- correlationId);
- } catch (IOException | URISyntaxException e) {
- LOGGER.error(formatLogMessage(correlationId, "Error while adjusting template for session " + session), e);
- return;
- }
- K8sUtil.loadAndCreateDeploymentWithOwnerReference(client.kubernetes(), client.namespace(), correlationId,
- deploymentYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, deployment -> {
- pvName.ifPresent(name -> addVolumeClaim(deployment, name, appDefinition.getSpec()));
- bandwidthLimiter.limit(deployment, appDefinition.getSpec().getDownlinkLimit(),
- appDefinition.getSpec().getUplinkLimit(), correlationId);
- AddedHandlerUtil.removeEmptyResources(deployment);
-
- AddedHandlerUtil.addCustomEnvVarsToDeploymentFromSession(correlationId, deployment, session,
- appDefinition);
-
- if (appDefinition.getSpec().getPullSecret() != null
- && !appDefinition.getSpec().getPullSecret().isEmpty()) {
- AddedHandlerUtil.addImagePullSecret(deployment, appDefinition.getSpec().getPullSecret());
- }
- });
- }
-
- protected void addVolumeClaim(Deployment deployment, String pvcName, AppDefinitionSpec appDefinition) {
+ protected void addVolumeClaim(Deployment deployment, String pvcName, AppDefinitionSpec appDef) {
PodSpec podSpec = deployment.getSpec().getTemplate().getSpec();
Volume volume = new Volume();
podSpec.getVolumes().add(volume);
volume.setName(USER_DATA);
- PersistentVolumeClaimVolumeSource persistentVolumeClaim = new PersistentVolumeClaimVolumeSource();
- volume.setPersistentVolumeClaim(persistentVolumeClaim);
- persistentVolumeClaim.setClaimName(pvcName);
+ PersistentVolumeClaimVolumeSource pvc = new PersistentVolumeClaimVolumeSource();
+ volume.setPersistentVolumeClaim(pvc);
+ pvc.setClaimName(pvcName);
- Container theiaContainer = TheiaCloudPersistentVolumeUtil.getTheiaContainer(podSpec, appDefinition);
+ Container theiaContainer = TheiaCloudPersistentVolumeUtil.getTheiaContainer(podSpec, appDef);
VolumeMount volumeMount = new VolumeMount();
theiaContainer.getVolumeMounts().add(volumeMount);
volumeMount.setName(USER_DATA);
- volumeMount.setMountPath(TheiaCloudPersistentVolumeUtil.getMountPath(appDefinition));
- }
-
- protected synchronized String updateIngress(Optional ingress, Optional serviceToUse,
- Session session, AppDefinition appDefinition, String correlationId) {
- List hostsToAdd = new ArrayList<>();
- final String instancesHost = arguments.getInstancesHost();
- hostsToAdd.add(instancesHost);
- List ingressHostnamePrefixes = appDefinition.getSpec().getIngressHostnamePrefixes() != null
- ? appDefinition.getSpec().getIngressHostnamePrefixes()
- : Collections.emptyList();
- for (String prefix : ingressHostnamePrefixes) {
- hostsToAdd.add(prefix + instancesHost);
- }
- String path = ingressPathProvider.getPath(appDefinition, session);
- client.ingresses().edit(correlationId, ingress.get().getMetadata().getName(), ingressToUpdate -> {
- for (String host : hostsToAdd) {
- IngressRule ingressRule = new IngressRule();
- ingressToUpdate.getSpec().getRules().add(ingressRule);
-
- ingressRule.setHost(host);
-
- HTTPIngressRuleValue http = new HTTPIngressRuleValue();
- ingressRule.setHttp(http);
-
- HTTPIngressPath httpIngressPath = new HTTPIngressPath();
- http.getPaths().add(httpIngressPath);
- httpIngressPath.setPath(path + AddedHandlerUtil.INGRESS_REWRITE_PATH);
- httpIngressPath.setPathType("Prefix");
-
- IngressBackend ingressBackend = new IngressBackend();
- httpIngressPath.setBackend(ingressBackend);
-
- IngressServiceBackend ingressServiceBackend = new IngressServiceBackend();
- ingressBackend.setService(ingressServiceBackend);
- ingressServiceBackend.setName(serviceToUse.get().getMetadata().getName());
-
- ServiceBackendPort serviceBackendPort = new ServiceBackendPort();
- ingressServiceBackend.setPort(serviceBackendPort);
- serviceBackendPort.setNumber(appDefinition.getSpec().getPort());
- }
-
- });
- return instancesHost + path + "/";
- }
-
- @Override
- public boolean sessionDeleted(Session session, String correlationId) {
- /* session information */
- SessionSpec sessionSpec = session.getSpec();
-
- /* find appDefinition for session */
- String appDefinitionID = sessionSpec.getAppDefinition();
-
- Optional optionalAppDefinition = client.appDefinitions().get(appDefinitionID);
- if (optionalAppDefinition.isEmpty()) {
- LOGGER.error(formatLogMessage(correlationId, "No App Definition with name " + appDefinitionID + " found."));
- return false;
- }
-
- /* find ingress */
- String appDefinitionResourceName = optionalAppDefinition.get().getMetadata().getName();
- String appDefinitionResourceUID = optionalAppDefinition.get().getMetadata().getUid();
- Optional ingress = K8sUtil.getExistingIngress(client.kubernetes(), client.namespace(),
- appDefinitionResourceName, appDefinitionResourceUID);
- if (ingress.isEmpty()) {
- LOGGER.error(
- formatLogMessage(correlationId, "No Ingress for app definition " + appDefinitionID + " found."));
- return false;
- }
-
- String path = ingressPathProvider.getPath(optionalAppDefinition.get(), session);
- TheiaCloudIngressUtil.removeIngressRule(client.kubernetes(), client.namespace(), ingress.get(), path,
- correlationId);
-
- return true;
+ volumeMount.setMountPath(TheiaCloudPersistentVolumeUtil.getMountPath(appDef));
}
-
}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/README.md b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/README.md
new file mode 100644
index 000000000..99344c5f8
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/README.md
@@ -0,0 +1,31 @@
+# Session Handling
+
+## Approaches
+
+There are currently two approaches to session handling, represented by the `SessionHandler` interface.
+
+### Lazy Session Handler (default)
+
+Creates sessions on-demand, i.e. when a session is requested, the operator will create the necessary resources to run the session.
+
+The session is created with all environment variables and options set.
+
+### Eager Session Handler
+
+Creates generic sessions ahead of time, according to the `AppDefinition` spec.
+
+The session is created with all environment variables known ahead of time, like the data bridge port.
+
+#### Data Bridge component
+
+As the environment can't be personalized for prewarmed sessions, we need a way of injecting configuration at runtime.
+
+This is handled by the data bridge component, a VSCode extension exposing HTTP endpoints for injection data.
+
+#### Environment Configuration for prewarmed sessionsw
+
+Prewarmed sessions need to be configured in advance to allow successful data injection later.
+
+- `SCORPIO_THEIA_ENV_STRATEGY`: Sets the strategy to use for environment configuration. Must be set to `data-bridge` for prewarmed sessions.
+- `DATA_BRIDGE_PORT` (optional): Overrides the default port the data bridge component should listen on.
+- `DATA_BRIDGE_ENABLED`: Must be set to `1` or `true` to enable the data bridge component in prewarmed sessions.
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/SessionHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/SessionHandler.java
index 3f61d1896..9107dfeb6 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/SessionHandler.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/SessionHandler.java
@@ -17,24 +17,26 @@
import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+import io.sentry.ISpan;
+
public interface SessionHandler {
- default boolean sessionAdded(Session session, String correlationId) {
+ default boolean sessionAdded(Session session, String correlationId, ISpan span) {
return true;
}
- default boolean sessionDeleted(Session session, String correlationId) {
+ default boolean sessionDeleted(Session session, String correlationId, ISpan span) {
return true;
}
- default boolean sessionModified(Session session, String correlationId) {
+ default boolean sessionModified(Session session, String correlationId, ISpan span) {
return true;
}
- default boolean sessionErrored(Session session, String correlationId) {
+ default boolean sessionErrored(Session session, String correlationId, ISpan span) {
return true;
}
- default boolean sessionBookmarked(Session session, String correlationId) {
+ default boolean sessionBookmarked(Session session, String correlationId, ISpan span) {
return true;
}
}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/SessionStatusUtil.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/SessionStatusUtil.java
new file mode 100644
index 000000000..466930dc4
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/SessionStatusUtil.java
@@ -0,0 +1,117 @@
+package org.eclipse.theia.cloud.operator.handler.session;
+
+import java.time.Instant;
+import java.util.Optional;
+
+import org.apache.logging.log4j.Logger;
+import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
+import org.eclipse.theia.cloud.common.k8s.resource.OperatorStatus;
+import org.eclipse.theia.cloud.common.k8s.resource.ResourceStatus;
+import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+import org.eclipse.theia.cloud.common.k8s.resource.session.SessionStatus;
+
+public final class SessionStatusUtil {
+
+ public enum PreHandleResult {
+ PROCEED, ALREADY_HANDLED, INTERRUPTED, PREVIOUS_ERROR
+ }
+
+ private SessionStatusUtil() {
+ }
+
+ /**
+ * Evaluates the session status and returns the appropriate pre-handle result.
+ *
+ * @param session the session to evaluate
+ * @param client the Kubernetes client
+ * @param correlationId the correlation ID for tracking
+ * @param logger the logger instance
+ * @return the pre-handle result indicating how to proceed
+ */
+ public static PreHandleResult evaluateStatus(Session session, TheiaCloudClient client, String correlationId,
+ Logger logger) {
+ if (session == null) {
+ throw new IllegalArgumentException("Session must not be null");
+ }
+ if (client == null) {
+ throw new IllegalArgumentException("Client must not be null");
+ }
+ Optional status = Optional.ofNullable(session.getStatus());
+ String operatorStatus = status.map(ResourceStatus::getOperatorStatus).orElse(OperatorStatus.NEW);
+
+ if (OperatorStatus.HANDLED.equals(operatorStatus)) {
+ return PreHandleResult.ALREADY_HANDLED;
+ }
+ if (OperatorStatus.HANDLING.equals(operatorStatus)) {
+ markError(client, session, correlationId,
+ "Handling was unexpectedly interrupted. CorrelationId: " + correlationId);
+ return PreHandleResult.INTERRUPTED;
+ }
+ if (OperatorStatus.ERROR.equals(operatorStatus)) {
+ return PreHandleResult.PREVIOUS_ERROR;
+ }
+ return PreHandleResult.PROCEED;
+ }
+
+ /**
+ * Marks the session as currently being handled.
+ *
+ * @param client the Kubernetes client
+ * @param session the session to mark
+ * @param correlationId the correlation ID for tracking
+ */
+ public static void markHandling(TheiaCloudClient client, Session session, String correlationId) {
+ if (session == null) {
+ throw new IllegalArgumentException("Session must not be null");
+ }
+ if (client == null) {
+ throw new IllegalArgumentException("Client must not be null");
+ }
+ client.sessions().updateStatus(correlationId, session, s -> s.setOperatorStatus(OperatorStatus.HANDLING));
+ }
+
+ /**
+ * Marks the session as successfully handled.
+ *
+ * @param client the Kubernetes client
+ * @param session the session to mark
+ * @param correlationId the correlation ID for tracking
+ * @param message optional message to set on the session status
+ */
+ public static void markHandled(TheiaCloudClient client, Session session, String correlationId, String message) {
+ if (session == null) {
+ throw new IllegalArgumentException("Session must not be null");
+ }
+ if (client == null) {
+ throw new IllegalArgumentException("Client must not be null");
+ }
+ client.sessions().updateStatus(correlationId, session, s -> {
+ s.setOperatorStatus(OperatorStatus.HANDLED);
+ if (message != null) {
+ s.setOperatorMessage(message);
+ }
+ s.setLastActivity(Instant.now().toEpochMilli());
+ });
+ }
+
+ /**
+ * Marks the session with an error status.
+ *
+ * @param client the Kubernetes client
+ * @param session the session to mark
+ * @param correlationId the correlation ID for tracking
+ * @param message the error message to set
+ */
+ public static void markError(TheiaCloudClient client, Session session, String correlationId, String message) {
+ if (session == null) {
+ throw new IllegalArgumentException("Session must not be null");
+ }
+ if (client == null) {
+ throw new IllegalArgumentException("Client must not be null");
+ }
+ client.sessions().updateStatus(correlationId, session, s -> {
+ s.setOperatorStatus(OperatorStatus.ERROR);
+ s.setOperatorMessage(message);
+ });
+ }
+}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/ingress/IngressManager.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/ingress/IngressManager.java
new file mode 100644
index 000000000..76a7e12d8
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/ingress/IngressManager.java
@@ -0,0 +1,309 @@
+package org.eclipse.theia.cloud.operator.ingress;
+
+import static org.eclipse.theia.cloud.common.util.LogMessageUtil.formatLogMessage;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+import org.eclipse.theia.cloud.common.util.LabelsUtil;
+import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
+import org.eclipse.theia.cloud.operator.util.K8sUtil;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
+import io.fabric8.kubernetes.api.model.OwnerReference;
+import io.fabric8.kubernetes.api.model.OwnerReferenceBuilder;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPBackendRef;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPBackendRefBuilder;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPHeader;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPHeaderBuilder;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPHeaderFilterBuilder;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPPathMatch;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPPathMatchBuilder;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPPathModifierBuilder;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRequestRedirectFilterBuilder;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRoute;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRouteBuilder;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRouteFilter;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRouteFilterBuilder;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRouteMatch;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRouteMatchBuilder;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRouteRule;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRouteRuleBuilder;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRouteSpec;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPRouteSpecBuilder;
+import io.fabric8.kubernetes.api.model.gatewayapi.v1.HTTPURLRewriteFilterBuilder;
+import io.fabric8.kubernetes.client.KubernetesClientException;
+
+/**
+ * Centralized manager for HTTPRoute operations (Gateway API).
+ *
+ * Caller intent:
+ * - find the shared route template for an app definition
+ * - expose a session path via a dedicated session-owned HTTPRoute
+ */
+@Singleton
+public class IngressManager {
+
+ private static final Logger LOGGER = LogManager.getLogger(IngressManager.class);
+ /**
+ * Envoy Gateway runtime expression for the current request path.
+ * This value is interpreted by Envoy; other Gateway API implementations may
+ * treat it as a plain literal string.
+ */
+ private static final String ENVOY_REQUEST_PATH_EXPRESSION = "%REQ(:PATH)%";
+ private static final int HTTP_CONFLICT = 409;
+ private static final String SESSION_ROUTE_SUFFIX = "-route";
+
+ @Inject
+ private TheiaCloudClient client;
+
+ @Inject
+ private TheiaCloudOperatorArguments arguments;
+
+ @Inject
+ private IngressPathProvider pathProvider;
+
+ /**
+ * Gets the shared app-level HTTPRoute for an app definition.
+ *
+ * This route is treated as static configuration managed outside the operator:
+ * it provides the parentRefs and hostnames that every session-specific
+ * HTTPRoute should inherit.
+ */
+ public Optional getIngress(AppDefinition appDefinition, String correlationId) {
+ Optional route = K8sUtil.getExistingHttpRoute(
+ client.kubernetes(),
+ client.namespace(),
+ appDefinition.getMetadata().getName(),
+ appDefinition.getMetadata().getUid());
+ if (route.isEmpty()) {
+ LOGGER.debug(formatLogMessage(correlationId,
+ "No HTTPRoute found for app definition " + appDefinition.getMetadata().getName()));
+ }
+ return route;
+ }
+
+ /**
+ * Exposes an eager session through a dedicated session-owned HTTPRoute.
+ *
+ * @return the full URL for the session
+ */
+ public String addRuleForSession(
+ HTTPRoute routeTemplate,
+ Service service,
+ AppDefinition appDefinition,
+ Session session,
+ int instance,
+ String correlationId) {
+
+ String path = pathProvider.getPath(appDefinition, instance);
+ return createOrUpdateSessionRoute(routeTemplate, service, appDefinition, session, path, correlationId);
+ }
+
+ /**
+ * Exposes a lazy session through a dedicated session-owned HTTPRoute.
+ *
+ * @return the full URL for the session
+ */
+ public String addRuleForSession(
+ HTTPRoute routeTemplate,
+ Service service,
+ AppDefinition appDefinition,
+ Session session,
+ String correlationId) {
+
+ String path = pathProvider.getPath(appDefinition, session);
+ return createOrUpdateSessionRoute(routeTemplate, service, appDefinition, session, path, correlationId);
+ }
+
+ private String createOrUpdateSessionRoute(
+ HTTPRoute routeTemplate,
+ Service service,
+ AppDefinition appDefinition,
+ Session session,
+ String path,
+ String correlationId) {
+
+ String routeName = buildSessionRouteName(session);
+ String serviceName = service.getMetadata().getName();
+ int port = appDefinition.getSpec().getPort();
+ HTTPRoute desiredRoute = buildSessionRoute(routeTemplate, serviceName, port, path, session, appDefinition);
+
+ try {
+ client.httpRoutes().operation().resource(desiredRoute).create();
+ } catch (KubernetesClientException e) {
+ if (e.getCode() != HTTP_CONFLICT) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "Failed to create session HTTPRoute " + routeName + " for path " + path), e);
+ throw e;
+ }
+
+ // Retries for the same Session should converge on one stable route name.
+ HTTPRoute updatedRoute = client.httpRoutes().edit(correlationId, routeName, existingRoute -> {
+ existingRoute.setSpec(desiredRoute.getSpec());
+ existingRoute.getMetadata().setLabels(desiredRoute.getMetadata().getLabels());
+ existingRoute.getMetadata().setOwnerReferences(desiredRoute.getMetadata().getOwnerReferences());
+ });
+ if (updatedRoute == null) {
+ throw new KubernetesClientException("HTTPRoute " + routeName + " not found for update");
+ }
+ }
+
+ LOGGER.info(formatLogMessage(correlationId,
+ "Configured session HTTPRoute " + routeName + " for path " + path
+ + " and backend service " + serviceName));
+ return arguments.getInstancesHost() + ensureTrailingSlash(path);
+ }
+
+ private HTTPRoute buildSessionRoute(HTTPRoute routeTemplate, String serviceName, int port, String path,
+ Session session, AppDefinition appDefinition) {
+ HTTPRouteSpec templateSpec = getOrCreateSpec(routeTemplate);
+ List hosts = buildRouteHosts(appDefinition, templateSpec.getHostnames());
+
+ return new HTTPRouteBuilder()
+ .withMetadata(new ObjectMetaBuilder()
+ // Use the Session UID so retries and reconciliations keep targeting the same route while
+ // staying well within Kubernetes object name limits.
+ .withName(buildSessionRouteName(session))
+ .withNamespace(client.namespace())
+ .withLabels(LabelsUtil.createSessionLabels(session, appDefinition))
+ .withOwnerReferences(buildSessionOwnerReference(session))
+ .build())
+ .withSpec(new HTTPRouteSpecBuilder()
+ // Keep gateway attachment and hostname configuration in the shared app-level route so Helm
+ // remains the single source of truth for ingress topology.
+ .withParentRefs(templateSpec.getParentRefs())
+ .withHostnames(hosts)
+ .withRules(createRedirectRule(path), createRouteRule(serviceName, port, path))
+ .build())
+ .build();
+ }
+
+ private List buildRouteHosts(AppDefinition appDefinition, List templateHostnames) {
+ if (templateHostnames != null && !templateHostnames.isEmpty()) {
+ return new ArrayList<>(templateHostnames);
+ }
+
+ String instancesHost = arguments.getInstancesHost();
+ List hosts = new ArrayList<>();
+ hosts.add(instancesHost);
+
+ List prefixes = appDefinition.getSpec().getIngressHostnamePrefixes();
+ if (prefixes != null) {
+ for (String prefix : prefixes) {
+ hosts.add(prefix + instancesHost);
+ }
+ }
+
+ return hosts;
+ }
+
+ private OwnerReference buildSessionOwnerReference(Session session) {
+ return new OwnerReferenceBuilder()
+ .withApiVersion(Session.API)
+ .withKind(Session.KIND)
+ .withName(session.getMetadata().getName())
+ .withUid(session.getMetadata().getUid())
+ .build();
+ }
+
+ private String buildSessionRouteName(Session session) {
+ return session.getMetadata().getUid() + SESSION_ROUTE_SUFFIX;
+ }
+
+ private HTTPRouteSpec getOrCreateSpec(HTTPRoute route) {
+ HTTPRouteSpec spec = route.getSpec();
+ if (spec == null) {
+ spec = new HTTPRouteSpecBuilder().build();
+ route.setSpec(spec);
+ }
+ return spec;
+ }
+
+ /**
+ * Creates the backend routing rule for a session path.
+ *
+ * The X-Forwarded-Uri header uses an Envoy Gateway runtime expression and
+ * therefore requires Envoy Gateway for correct behavior.
+ */
+ private HTTPRouteRule createRouteRule(String serviceName, int port, String path) {
+ HTTPRouteMatch pathPrefixMatch = new HTTPRouteMatchBuilder()
+ .withPath(new HTTPPathMatchBuilder()
+ .withType("PathPrefix")
+ .withValue(ensureTrailingSlash(path))
+ .build())
+ .build();
+
+ HTTPHeader forwardedUriHeader = new HTTPHeaderBuilder()
+ .withName("X-Forwarded-Uri")
+ .withValue(ENVOY_REQUEST_PATH_EXPRESSION)
+ .build();
+
+ HTTPRouteFilter requestHeaderFilter = new HTTPRouteFilterBuilder()
+ .withType("RequestHeaderModifier")
+ .withRequestHeaderModifier(new HTTPHeaderFilterBuilder()
+ .withSet(forwardedUriHeader)
+ .build())
+ .build();
+
+ HTTPRouteFilter urlRewriteFilter = new HTTPRouteFilterBuilder()
+ .withType("URLRewrite")
+ .withUrlRewrite(new HTTPURLRewriteFilterBuilder()
+ .withPath(new HTTPPathModifierBuilder()
+ .withType("ReplacePrefixMatch")
+ .withReplacePrefixMatch("/")
+ .build())
+ .build())
+ .build();
+
+ HTTPBackendRef backendRef = new HTTPBackendRefBuilder()
+ .withName(serviceName)
+ .withPort(port)
+ .build();
+
+ return new HTTPRouteRuleBuilder()
+ .withMatches(pathPrefixMatch)
+ .withFilters(requestHeaderFilter, urlRewriteFilter)
+ .withBackendRefs(backendRef)
+ .build();
+ }
+
+ private HTTPRouteRule createRedirectRule(String path) {
+ HTTPRouteMatch exactMatch = new HTTPRouteMatchBuilder()
+ .withPath(new HTTPPathMatchBuilder()
+ .withType("Exact")
+ .withValue(path)
+ .build())
+ .build();
+
+ HTTPRouteFilter redirectFilter = new HTTPRouteFilterBuilder()
+ .withType("RequestRedirect")
+ .withRequestRedirect(new HTTPRequestRedirectFilterBuilder()
+ .withStatusCode(302)
+ .withPath(new HTTPPathModifierBuilder()
+ .withType("ReplaceFullPath")
+ .withReplaceFullPath(ensureTrailingSlash(path))
+ .build())
+ .build())
+ .build();
+
+ return new HTTPRouteRuleBuilder()
+ .withMatches(exactMatch)
+ .withFilters(redirectFilter)
+ .build();
+ }
+
+ private String ensureTrailingSlash(String value) {
+ return value.endsWith("/") ? value : value + "/";
+ }
+}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/messaging/MonitorMessagingServiceImpl.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/messaging/MonitorMessagingServiceImpl.java
index c05fa574d..f89573627 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/messaging/MonitorMessagingServiceImpl.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/messaging/MonitorMessagingServiceImpl.java
@@ -22,7 +22,9 @@
import org.apache.logging.log4j.Logger;
import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinitionSpec;
import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+import org.eclipse.theia.cloud.common.util.SessionUtil;
import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
import org.json.JSONObject;
@@ -33,6 +35,9 @@
import okhttp3.Request;
import okhttp3.RequestBody;
+import io.sentry.okhttp.SentryOkHttpEventListener;
+import io.sentry.okhttp.SentryOkHttpInterceptor;
+
public class MonitorMessagingServiceImpl implements MonitorMessagingService {
private static final String MONITOR_BASE_PATH = "/monitor";
@@ -46,6 +51,11 @@ public class MonitorMessagingServiceImpl implements MonitorMessagingService {
@Inject
private TheiaCloudOperatorArguments arguments;
+ private final OkHttpClient httpClient = new OkHttpClient.Builder()
+ .addInterceptor(new SentryOkHttpInterceptor())
+ .eventListener(new SentryOkHttpEventListener())
+ .build();
+
@Override
public void sendMessage(Session session, String level, String message) {
if (isEnabled()) {
@@ -70,20 +80,18 @@ public void sendTimeoutMessage(Session session, String detail) {
}
protected void postMessage(Session session, String level, String message, boolean fullscreen, String detail) {
- OkHttpClient client = new OkHttpClient().newBuilder().build();
-
Optional postMessageURL = getURL(session);
if (postMessageURL.isPresent()) {
MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
String json = new JSONObject().put("message", message).put("level", level).put("fullscreen", fullscreen)
.put("detail", detail).toString();
- RequestBody body = RequestBody.create(mediaType, json);
+ RequestBody body = RequestBody.create(json, mediaType);
Request postRequest = new Request.Builder().url(postMessageURL.get())
.addHeader("Authorization", "Bearer " + session.getSpec().getSessionSecret()).method("POST", body)
.build();
try {
- client.newCall(postRequest).execute();
+ httpClient.newCall(postRequest).execute();
} catch (IOException e) {
LOGGER.info("[" + session.getSpec().getName() + "] Could not send message to extension:" + e);
}
@@ -91,7 +99,7 @@ protected void postMessage(Session session, String level, String message, boolea
}
protected Optional getURL(Session session) {
- Optional ip = getIP(session);
+ Optional ip = SessionUtil.getClusterIP(resourceClient, session);
Optional port = getPort(session);
if (ip.isPresent() && port.isPresent()) {
return Optional.of("http://" + ip.get() + ":" + port.get() + MONITOR_BASE_PATH + POST_MESSAGE);
@@ -99,18 +107,12 @@ protected Optional getURL(Session session) {
return Optional.empty();
}
- protected Optional getIP(Session session) {
- Optional sessionIP = resourceClient.getClusterIPFromSessionName(session.getSpec().getName());
- return sessionIP;
- }
-
protected Optional getPort(Session session) {
String appDefinitionId = session.getSpec().getAppDefinition();
- Optional optionalAppDefinition = resourceClient.appDefinitions().get(appDefinitionId);
- if (optionalAppDefinition.isPresent()) {
- return Optional.of(optionalAppDefinition.get().getSpec().getMonitor().getPort());
- }
- return Optional.empty();
+ return resourceClient.appDefinitions().get(appDefinitionId)//
+ .map(AppDefinition::getSpec)//
+ .map(AppDefinitionSpec::getMonitor)//
+ .map(AppDefinitionSpec.Monitor::getPort);
}
protected boolean isEnabled() {
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/plugins/MonitorActivityTracker.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/plugins/MonitorActivityTracker.java
index 9303b893e..35b41bfc6 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/plugins/MonitorActivityTracker.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/plugins/MonitorActivityTracker.java
@@ -33,6 +33,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
+import org.eclipse.theia.cloud.common.k8s.resource.OperatorStatus;
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
@@ -47,6 +48,14 @@
import okhttp3.Response;
import okhttp3.ResponseBody;
+import io.sentry.ISpan;
+import io.sentry.ITransaction;
+import io.sentry.Sentry;
+import io.sentry.SpanStatus;
+import io.sentry.okhttp.SentryOkHttpEventListener;
+import io.sentry.okhttp.SentryOkHttpInterceptor;
+import org.eclipse.theia.cloud.common.tracing.Tracing;
+
public class MonitorActivityTracker implements OperatorPlugin {
private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();
@@ -68,6 +77,9 @@ public class MonitorActivityTracker implements OperatorPlugin {
@Inject
private TheiaCloudOperatorArguments arguments;
+ private final OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(new SentryOkHttpInterceptor())
+ .eventListener(new SentryOkHttpEventListener()).build();
+
@Override
public void start() {
if (arguments.isEnableMonitor() && arguments.isEnableActivityTracker()) {
@@ -78,85 +90,138 @@ public void start() {
}
protected void pingAllSessions() {
- List sessions = resourceClient.sessions().list();
- String correlationId = generateCorrelationId();
-
- LOGGER.debug("Pinging sessions: " + sessions);
-
- for (Session session : sessions) {
- Optional sessionIP = resourceClient.getClusterIPFromSessionName(session.getSpec().getName());
- if (sessionIP.isPresent()) {
- String appDefinitionName = session.getSpec().getAppDefinition();
- Optional appDefinitionOptional = resourceClient.appDefinitions().get(appDefinitionName);
- if (appDefinitionOptional.isPresent()) {
- AppDefinition appDefinition = appDefinitionOptional.get();
- int timeoutAfter = appDefinition.getSpec().getMonitor().getActivityTracker().getTimeoutAfter();
- int notifyAfter = appDefinition.getSpec().getMonitor().getActivityTracker().getNotifyAfter();
- int port = appDefinition.getSpec().getMonitor().getPort();
-
- pingSession(correlationId, session, sessionIP.get(), port, timeoutAfter, notifyAfter);
+ ITransaction transaction = Tracing.startTransaction("monitor.activity-check", "monitor");
+ try {
+ // Only look at handled sessions (handled sessions have a lastActivity)
+ List sessions = resourceClient.sessions().list().stream()
+ .filter(session -> OperatorStatus.HANDLED.equals(session.getStatus().getOperatorStatus())).toList();
+ String correlationId = generateCorrelationId();
+
+ LOGGER.debug("Pinging sessions: " + sessions);
+
+ transaction.setData("session_count", sessions.size());
+
+ int successCount = 0;
+ int failureCount = 0;
+ int missingIPCount = 0;
+
+ for (Session session : sessions) {
+ Optional sessionIP = resourceClient.getClusterIPFromSessionName(session.getSpec().getName());
+ if (sessionIP.isPresent()) {
+ String appDefinitionName = session.getSpec().getAppDefinition();
+ Optional appDefinitionOptional = resourceClient.appDefinitions()
+ .get(appDefinitionName);
+ if (appDefinitionOptional.isPresent()) {
+ AppDefinition appDefinition = appDefinitionOptional.get();
+ int timeoutAfter = appDefinition.getSpec().getMonitor().getActivityTracker().getTimeoutAfter();
+ int notifyAfter = appDefinition.getSpec().getMonitor().getActivityTracker().getNotifyAfter();
+ int port = appDefinition.getSpec().getMonitor().getPort();
+
+ boolean success = pingSession(transaction, correlationId, session, sessionIP.get(), port,
+ timeoutAfter, notifyAfter);
+ if (success) {
+ successCount++;
+ } else {
+ failureCount++;
+ }
+ }
+ } else {
+ missingIPCount++;
+ LOGGER.error("No ClusterIP found for session " + session.getSpec().getName());
+ Sentry.addBreadcrumb("No ClusterIP found for session " + session.getSpec().getName());
}
- } else {
- LOGGER.error("No ClusterIP found for session " + session.getSpec().getName());
}
+
+ transaction.setData("success_count", successCount);
+ transaction.setData("failure_count", failureCount);
+ transaction.setData("missing_ip_count", missingIPCount);
+ transaction.setStatus(SpanStatus.OK);
+ } catch (Exception e) {
+ transaction.setStatus(SpanStatus.INTERNAL_ERROR);
+ transaction.setThrowable(e);
+ throw e;
+ } finally {
+ transaction.finish();
}
}
- protected void pingSession(String correlationId, Session session, String sessionURL, int port, int shutdownAfter,
- int notifyAfter) {
+ protected boolean pingSession(ITransaction transaction, String correlationId, Session session, String sessionURL,
+ int port, int shutdownAfter, int notifyAfter) {
String sessionName = session.getSpec().getName();
- logInfo(sessionName, "Pinging session at " + sessionURL);
- OkHttpClient client = new OkHttpClient().newBuilder().build();
-
- String getActivityURL = getURL(sessionURL, port, GET_ACTIVITY);
- logInfo(sessionName, "GET " + getActivityURL);
+ ISpan sessionSpan = transaction.startChild("monitor.ping-session", "monitor.session");
+ sessionSpan.setData("session_name", sessionName);
+ sessionSpan.setData("session_url", sessionURL);
- Request getActivityRequest = new Request.Builder().url(getActivityURL)
- .addHeader("Authorization", "Bearer " + session.getSpec().getSessionSecret()).get().build();
- Response getActivityResponse;
try {
- getActivityResponse = client.newCall(getActivityRequest).execute();
- ResponseBody body = getActivityResponse.body();
- if (getActivityResponse.code() == 200 && body != null) {
- long lastReportedMilliseconds = Long.valueOf(body.string());
+ logInfo(sessionName, "Pinging session at " + sessionURL);
- session = updateLastActivity(correlationId, session, lastReportedMilliseconds);
- } else {
- logInfo(sessionName,
- "REQUEST FAILED (Returned " + getActivityResponse.code() + ": " + "GET " + getActivityURL);
+ String getActivityURL = getURL(sessionURL, port, GET_ACTIVITY);
+ logInfo(sessionName, "GET " + getActivityURL);
+
+ boolean success = false;
+ try {
+ Request getActivityRequest = new Request.Builder().url(getActivityURL)
+ .addHeader("Authorization", "Bearer " + session.getSpec().getSessionSecret()).get().build();
+ Response getActivityResponse = httpClient.newCall(getActivityRequest).execute();
+ ResponseBody body = getActivityResponse.body();
+
+ if (getActivityResponse.code() == 200 && body != null) {
+ long lastReportedMilliseconds = Long.valueOf(body.string());
+ session = updateLastActivity(correlationId, session, lastReportedMilliseconds);
+ success = true;
+ } else {
+ logInfo(sessionName,
+ "REQUEST FAILED (Returned " + getActivityResponse.code() + ": " + "GET " + getActivityURL);
+ }
+ } catch (IOException e) {
+ logInfo(sessionName, "REQUEST FAILED: " + "GET " + getActivityURL + ". Error: " + e);
}
- } catch (IOException e) {
- logInfo(sessionName, "REQUEST FAILED: " + "GET " + getActivityURL + ". Error: " + e);
- }
- Date lastActivityDate = new Date(session.getNonNullStatus().getLastActivity());
- Date currentDate = new Date(OffsetDateTime.now(ZoneOffset.UTC).toInstant().toEpochMilli());
-
- long minutesPassed = getMinutesPassed(lastActivityDate, currentDate);
-
- String minutes = minutesPassed == 1 ? "minute" : "minutes";
- logInfo(sessionName, "Last reported activity was: " + formatDate(lastActivityDate) + " (" + minutesPassed + " "
- + minutes + " ago)");
-
- if (minutesPassed < shutdownAfter) {
- if (minutesPassed >= notifyAfter) {
- logInfo(sessionName, "Notifying session as timeout of " + notifyAfter + " minutes was reached!");
- String postPopupURL = getURL(sessionURL, port, POST_POPUP);
- logInfo(sessionName, "POST " + postPopupURL);
- MediaType mediaType = MediaType.parse("text/plain");
- RequestBody body = RequestBody.create(mediaType, "");
- Request postRequest = new Request.Builder().url(postPopupURL)
- .addHeader("Authorization", "Bearer " + session.getSpec().getSessionSecret()).post(body)
- .build();
- try {
- client.newCall(postRequest).execute();
- } catch (IOException e) {
- logInfo(sessionName, "REQUEST FAILED: " + "POST " + postPopupURL);
+ Date lastActivityDate = new Date(session.getNonNullStatus().getLastActivity());
+ Date currentDate = new Date(OffsetDateTime.now(ZoneOffset.UTC).toInstant().toEpochMilli());
+ long minutesPassed = getMinutesPassed(lastActivityDate, currentDate);
+
+ sessionSpan.setData("minutes_since_activity", minutesPassed);
+ String minutes = minutesPassed == 1 ? "minute" : "minutes";
+ logInfo(sessionName, "Last reported activity was: " + formatDate(lastActivityDate) + " (" + minutesPassed
+ + " " + minutes + " ago)");
+
+ if (minutesPassed < shutdownAfter) {
+ if (minutesPassed >= notifyAfter) {
+ logInfo(sessionName, "Notifying session as timeout of " + notifyAfter + " minutes was reached!");
+
+ String postPopupURL = getURL(sessionURL, port, POST_POPUP);
+ logInfo(sessionName, "POST " + postPopupURL);
+ try {
+ MediaType mediaType = MediaType.parse("text/plain");
+ RequestBody body = RequestBody.create("", mediaType);
+ Request postRequest = new Request.Builder().url(postPopupURL)
+ .addHeader("Authorization", "Bearer " + session.getSpec().getSessionSecret()).post(body)
+ .build();
+ httpClient.newCall(postRequest).execute();
+ } catch (IOException e) {
+ logInfo(sessionName, "REQUEST FAILED: " + "POST " + postPopupURL);
+ }
}
+ sessionSpan.setStatus(SpanStatus.OK);
+ } else {
+ // Timeout reached
+ sessionSpan.setData("action", "timeout_shutdown");
+ stopNonActiveSession(sessionSpan, correlationId, session, shutdownAfter);
+ sessionSpan.setStatus(SpanStatus.OK);
}
- } else {
- // Timeout reached
- stopNonActiveSession(correlationId, session, shutdownAfter);
+
+ return success;
+ } catch (Exception e) {
+ LOGGER.error(
+ formatLogMessage(correlationId, correlationId, "Exception while pinging session " + sessionName),
+ e);
+ sessionSpan.setStatus(SpanStatus.INTERNAL_ERROR);
+ sessionSpan.setThrowable(e);
+ Sentry.captureException(e);
+ return false;
+ } finally {
+ sessionSpan.finish();
}
}
@@ -174,16 +239,28 @@ protected long getMinutesPassed(Date lastActivity, Date currentTime) {
return TimeUnit.MILLISECONDS.toMinutes(timePassed);
}
- protected void stopNonActiveSession(String correlationId, Session session, int shutdownAfter) {
+ protected void stopNonActiveSession(ISpan parentSpan, String correlationId, Session session, int shutdownAfter) {
String sessionName = session.getSpec().getName();
+ ISpan deleteSpan = parentSpan.startChild("monitor.delete-session", "monitor.cleanup");
+ deleteSpan.setData("session_name", sessionName);
+ deleteSpan.setData("timeout_minutes", shutdownAfter);
+
try {
this.messagingService.sendTimeoutMessage(session,
"Timeout of " + shutdownAfter + " minutes of inactivity was reached!");
logInfo(sessionName, "Deleting session as timeout of " + shutdownAfter + " minutes was reached!");
resourceClient.sessions().delete(COR_ID_NOACTIVITYPREFIX + correlationId, sessionName);
+ deleteSpan.setStatus(SpanStatus.OK);
+ Sentry.captureMessage(
+ "Session stopped due to inactivity: " + sessionName + " (timeout: " + shutdownAfter + " minutes)");
} catch (Exception e) {
LOGGER.error(formatLogMessage(COR_ID_NOACTIVITYPREFIX, correlationId, "Exception trying to delete session"),
e);
+ deleteSpan.setStatus(SpanStatus.INTERNAL_ERROR);
+ deleteSpan.setThrowable(e);
+ Sentry.captureException(e);
+ } finally {
+ deleteSpan.finish();
}
}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/pool/PrewarmedResourcePool.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/pool/PrewarmedResourcePool.java
new file mode 100644
index 000000000..8300a20a0
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/pool/PrewarmedResourcePool.java
@@ -0,0 +1,1404 @@
+package org.eclipse.theia.cloud.operator.pool;
+
+import static org.eclipse.theia.cloud.common.util.LogMessageUtil.formatLogMessage;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+import org.eclipse.theia.cloud.common.util.LabelsUtil;
+import org.eclipse.theia.cloud.common.util.NamingUtil;
+import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
+import org.eclipse.theia.cloud.operator.handler.AddedHandlerUtil;
+import org.eclipse.theia.cloud.operator.sidecar.SidecarConfig;
+import org.eclipse.theia.cloud.operator.sidecar.SidecarManager;
+import org.eclipse.theia.cloud.operator.replacements.DefaultPersistentVolumeTemplateReplacements;
+import org.eclipse.theia.cloud.operator.util.JavaResourceUtil;
+import org.eclipse.theia.cloud.operator.util.K8sResourceFactory;
+import org.eclipse.theia.cloud.operator.util.K8sUtil;
+import org.eclipse.theia.cloud.operator.util.OwnershipManager;
+import org.eclipse.theia.cloud.operator.util.OwnershipManager.OwnerContext;
+import org.eclipse.theia.cloud.operator.util.ResourceLifecycleManager;
+import org.eclipse.theia.cloud.common.tracing.Tracing;
+import org.eclipse.theia.cloud.operator.util.TheiaCloudConfigMapUtil;
+import org.eclipse.theia.cloud.operator.util.TheiaCloudDeploymentUtil;
+import org.eclipse.theia.cloud.operator.util.TheiaCloudHandlerUtil;
+import org.eclipse.theia.cloud.operator.util.TheiaCloudK8sUtil;
+import org.eclipse.theia.cloud.operator.util.TheiaCloudServiceUtil;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import io.fabric8.kubernetes.api.model.ConfigMap;
+import io.fabric8.kubernetes.api.model.HasMetadata;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.client.KubernetesClientException;
+import io.fabric8.kubernetes.client.dsl.PodResource;
+import io.sentry.ISpan;
+import io.sentry.SpanStatus;
+
+/**
+ * Manages a pool of prewarmed (eager start) resources for an AppDefinition. Responsibilities: - Creating/scaling the
+ * pool of prewarmed instances - Reserving instances for sessions - Releasing instances back to the pool - Cleaning up
+ * pool resources
+ */
+@Singleton
+public class PrewarmedResourcePool {
+
+ private static final Logger LOGGER = LogManager.getLogger(PrewarmedResourcePool.class);
+
+ public static final String EAGER_START_REFRESH_ANNOTATION = "theia-cloud.io/eager-start-refresh";
+ public static final String APPDEFINITION_GENERATION_LABEL = "theia-cloud.io/appdefinition-generation";
+
+ @Inject
+ private TheiaCloudClient client;
+
+ @Inject
+ private K8sResourceFactory resourceFactory;
+
+ @Inject
+ private SidecarManager sidecarManager;
+
+ @Inject
+ private TheiaCloudOperatorArguments arguments;
+
+ /**
+ * Represents a reserved instance from the pool.
+ */
+ public static class PoolInstance {
+ private final int instanceId;
+ private final Service externalService;
+ private final Service internalService;
+ private final String deploymentName;
+
+ public PoolInstance(int instanceId, Service externalService, Service internalService, String deploymentName) {
+ this.instanceId = instanceId;
+ this.externalService = externalService;
+ this.internalService = internalService;
+ this.deploymentName = deploymentName;
+ }
+
+ public int getInstanceId() {
+ return instanceId;
+ }
+
+ public Service getExternalService() {
+ return externalService;
+ }
+
+ public Service getInternalService() {
+ return internalService;
+ }
+
+ public String getDeploymentName() {
+ return deploymentName;
+ }
+ }
+
+ /**
+ * Result of a reservation attempt.
+ */
+ public static class ReservationResult {
+ private final ReservationOutcome outcome;
+ private final PoolInstance instance;
+
+ private ReservationResult(ReservationOutcome outcome, PoolInstance instance) {
+ this.outcome = outcome;
+ this.instance = instance;
+ }
+
+ public ReservationOutcome getOutcome() {
+ return outcome;
+ }
+
+ public Optional getInstance() {
+ return Optional.ofNullable(instance);
+ }
+
+ public static ReservationResult success(PoolInstance instance) {
+ return new ReservationResult(ReservationOutcome.SUCCESS, instance);
+ }
+
+ public static ReservationResult noCapacity() {
+ return new ReservationResult(ReservationOutcome.NO_CAPACITY, null);
+ }
+
+ public static ReservationResult error() {
+ return new ReservationResult(ReservationOutcome.ERROR, null);
+ }
+ }
+
+ public enum ReservationOutcome {
+ SUCCESS, NO_CAPACITY, ERROR
+ }
+
+ // ========== Pool Management ==========
+
+ /**
+ * Ensures the pool has the specified minimum number of instances. Creates missing resources (services, configmaps,
+ * deployments).
+ */
+ public boolean ensureCapacity(AppDefinition appDef, int minInstances, String correlationId) {
+ String appDefName = appDef.getSpec().getName();
+ ISpan span = Tracing.childSpan("pool.ensure_capacity", "Ensure pool capacity");
+
+ span.setTag("app_definition", appDefName);
+ span.setData("min_instances", minInstances);
+
+ LOGGER.info(formatLogMessage(correlationId, "Ensuring pool capacity: " + minInstances + " for " + appDefName));
+
+ try {
+ String ownerName = appDef.getMetadata().getName();
+ String ownerUID = appDef.getMetadata().getUid();
+ Map labels = new HashMap<>();
+
+ // Get existing resources
+ ISpan fetchSpan = span.startChild("pool.fetch_existing", "Fetch existing resources");
+ List existingServices = K8sUtil.getExistingServices(client.kubernetes(), client.namespace(),
+ ownerName, ownerUID);
+ List existingDeployments = K8sUtil.getExistingDeployments(client.kubernetes(),
+ client.namespace(), ownerName, ownerUID);
+ List existingConfigMaps = K8sUtil.getExistingConfigMaps(client.kubernetes(), client.namespace(),
+ ownerName, ownerUID);
+
+ fetchSpan.setData("existing_services", existingServices.size());
+ fetchSpan.setData("existing_deployments", existingDeployments.size());
+ fetchSpan.setData("existing_configmaps", existingConfigMaps.size());
+ Tracing.finishSuccess(fetchSpan);
+
+ // Compute missing IDs
+ Set missingServiceIds = TheiaCloudServiceUtil.computeIdsOfMissingServices(appDef, correlationId,
+ minInstances, existingServices);
+ Set missingDeploymentIds = TheiaCloudDeploymentUtil.computeIdsOfMissingDeployments(appDef,
+ correlationId, minInstances, existingDeployments);
+
+ span.setData("missing_service_ids", missingServiceIds.size());
+ span.setData("missing_deployment_ids", missingDeploymentIds.size());
+
+ boolean success = true;
+
+ // Create missing services
+ if (!missingServiceIds.isEmpty()) {
+ ISpan serviceSpan = span.startChild("pool.create_services", "Create missing services");
+ serviceSpan.setData("count", missingServiceIds.size());
+ int created = 0;
+ int failed = 0;
+ for (int instance : missingServiceIds) {
+ ISpan svcSpan = serviceSpan.startChild("k8s.service.create", "Create service " + instance);
+ svcSpan.setData("instance_id", instance);
+ boolean extOk = resourceFactory
+ .createServiceForEagerInstance(appDef, instance, labels, correlationId).isPresent();
+ boolean intOk = resourceFactory
+ .createInternalServiceForEagerInstance(appDef, instance, labels, correlationId).isPresent();
+ if (extOk && intOk) {
+ created++;
+ Tracing.finishSuccess(svcSpan);
+ } else {
+ failed++;
+ success = false;
+ svcSpan.setTag("outcome", "failure"); Tracing.finish(svcSpan, SpanStatus.INTERNAL_ERROR);
+ }
+ }
+ serviceSpan.setData("created", created);
+ serviceSpan.setData("failed", failed);
+ serviceSpan.setTag("outcome", failed == 0 ? "success" : "failure");
+ Tracing.finish(serviceSpan, failed == 0 ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ }
+
+ // Create missing configmaps (if using Keycloak)
+ if (arguments.isUseKeycloak()) {
+ List proxyConfigMaps = existingConfigMaps.stream().filter(
+ cm -> "proxy".equals(cm.getMetadata().getLabels().get("theia-cloud.io/template-purpose")))
+ .collect(Collectors.toList());
+ List emailConfigMaps = existingConfigMaps.stream().filter(
+ cm -> "emails".equals(cm.getMetadata().getLabels().get("theia-cloud.io/template-purpose")))
+ .collect(Collectors.toList());
+
+ Set missingProxyIds = TheiaCloudConfigMapUtil.computeIdsOfMissingProxyConfigMaps(appDef,
+ correlationId, minInstances, proxyConfigMaps);
+ Set missingEmailIds = TheiaCloudConfigMapUtil.computeIdsOfMissingEmailConfigMaps(appDef,
+ correlationId, minInstances, emailConfigMaps);
+
+ if (!missingProxyIds.isEmpty() || !missingEmailIds.isEmpty()) {
+ ISpan cmSpan = span.startChild("pool.create_configmaps", "Create missing configmaps");
+ cmSpan.setData("missing_proxy", missingProxyIds.size());
+ cmSpan.setData("missing_email", missingEmailIds.size());
+
+ for (int instance : missingProxyIds) {
+ success &= resourceFactory
+ .createProxyConfigMapForEagerInstance(appDef, instance, labels, correlationId)
+ .isPresent();
+ }
+ for (int instance : missingEmailIds) {
+ success &= resourceFactory
+ .createEmailConfigMapForEagerInstance(appDef, instance, labels, correlationId)
+ .isPresent();
+ }
+ cmSpan.setTag("outcome", success ? "success" : "failure"); Tracing.finish(cmSpan, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ }
+ }
+
+ // Ensure PVCs exist first for sidecars with mountWorkspace=true.
+ for (int instance = 1; instance <= minInstances; instance++) {
+ createInstancePvc(appDef, instance, correlationId);
+ }
+
+ // Create sidecar resources BEFORE Theia deployments so DNS resolves at pod startup
+ success &= sidecarManager.ensurePrewarmedSidecarCapacity(appDef, minInstances, labels, correlationId);
+
+ // Create missing deployments (with shared PVC)
+ if (!missingDeploymentIds.isEmpty()) {
+ ISpan deploySpan = span.startChild("pool.create_deployments", "Create missing deployments");
+ deploySpan.setData("count", missingDeploymentIds.size());
+ int created = 0;
+ int failed = 0;
+ for (int instance : missingDeploymentIds) {
+ ISpan depSpan = deploySpan.startChild("k8s.deployment.create", "Create deployment " + instance);
+ depSpan.setData("instance_id", instance);
+ Optional pvcName = createInstancePvc(appDef, instance, correlationId);
+ if (resourceFactory.createDeploymentForEagerInstance(appDef, instance, pvcName, labels, correlationId)
+ .isPresent()) {
+ created++;
+ Tracing.finishSuccess(depSpan);
+ } else {
+ failed++;
+ success = false;
+ depSpan.setTag("outcome", "failure"); Tracing.finish(depSpan, SpanStatus.INTERNAL_ERROR);
+ }
+ }
+ deploySpan.setData("created", created);
+ deploySpan.setData("failed", failed);
+ deploySpan.setTag("outcome", failed == 0 ? "success" : "failure");
+ Tracing.finish(deploySpan, failed == 0 ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ }
+
+ span.setTag("outcome", success ? "success" : "failure"); Tracing.finish(span, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ return success;
+
+ } catch (Exception e) {
+ Tracing.finishError(span, e);
+ throw e;
+ }
+ }
+
+ /**
+ * Reconciles the pool to match the target instance count. Creates missing instances, removes excess instances
+ * (respecting ownership).
+ */
+ public boolean reconcile(AppDefinition appDef, int targetInstances, String correlationId) {
+ String appDefName = appDef.getSpec().getName();
+ ISpan span = Tracing.childSpan("pool.reconcile", "Reconcile pool");
+
+ span.setTag("app_definition", appDefName);
+ span.setData("target_instances", targetInstances);
+ span.setData("generation", appDef.getMetadata().getGeneration());
+
+ LOGGER.info(formatLogMessage(correlationId, "Reconciling pool to " + targetInstances + " instances"));
+
+ try {
+ String ownerName = appDef.getMetadata().getName();
+ String ownerUID = appDef.getMetadata().getUid();
+ OwnerContext owner = OwnerContext.of(ownerName, ownerUID, AppDefinition.API, AppDefinition.KIND);
+ long currentGeneration = appDef.getMetadata().getGeneration();
+ Map labels = new HashMap<>();
+
+ boolean success = true;
+
+ // Reconcile services
+ ISpan serviceSpan = span.startChild("pool.reconcile_services", "Reconcile services");
+ List existingServices = K8sUtil.getExistingServices(client.kubernetes(), client.namespace(),
+ ownerName, ownerUID);
+ List externalServices = existingServices.stream()
+ .filter(s -> !s.getMetadata().getName().endsWith("-int")).collect(Collectors.toList());
+ List internalServices = existingServices.stream()
+ .filter(s -> s.getMetadata().getName().endsWith("-int")).collect(Collectors.toList());
+ Set missingServiceIds = TheiaCloudServiceUtil.computeIdsOfMissingServices(appDef, correlationId,
+ targetInstances, existingServices);
+
+ serviceSpan.setData("existing_external", externalServices.size());
+ serviceSpan.setData("existing_internal", internalServices.size());
+ serviceSpan.setData("missing", missingServiceIds.size());
+
+ // Reconcile external services
+ ResourceLifecycleManager.ReconcileResult extResult = ResourceLifecycleManager
+ .reconcile(ResourceLifecycleManager.ReconcileContext. builder()
+ .correlationId(correlationId).existingResources(externalServices)
+ .missingIds(missingServiceIds).targetCount(targetInstances).owner(owner)
+ .resourceAccessor(
+ s -> client.kubernetes().services().inNamespace(client.namespace()).resource(s))
+ .idExtractor(s -> TheiaCloudServiceUtil.getId(correlationId, appDef, s))
+ .resourceTypeName("service").createResource(instance -> {
+ resourceFactory.createServiceForEagerInstance(appDef, instance, labels, correlationId);
+ }).shouldRecreate(
+ s -> OwnershipManager.isOwnedSolelyBy(s, owner)
+ && isOutdated(s, currentGeneration))
+ .recreateResource(s -> {
+ Integer id = TheiaCloudServiceUtil.getId(correlationId, appDef, s);
+ if (id != null) {
+ resourceFactory.createServiceForEagerInstance(appDef, id, labels, correlationId);
+ }
+ }).build());
+ success &= extResult.isSuccess();
+
+ // Reconcile internal services
+ ResourceLifecycleManager.ReconcileResult intResult = ResourceLifecycleManager
+ .reconcile(ResourceLifecycleManager.ReconcileContext. builder()
+ .correlationId(correlationId).existingResources(internalServices)
+ .missingIds(missingServiceIds).targetCount(targetInstances).owner(owner)
+ .resourceAccessor(
+ s -> client.kubernetes().services().inNamespace(client.namespace()).resource(s))
+ .idExtractor(s -> TheiaCloudServiceUtil.getId(correlationId, appDef, s))
+ .resourceTypeName("internal service").createResource(instance -> {
+ resourceFactory.createInternalServiceForEagerInstance(appDef, instance, labels,
+ correlationId);
+ }).shouldRecreate(
+ s -> OwnershipManager.isOwnedSolelyBy(s, owner)
+ && isOutdated(s, currentGeneration))
+ .recreateResource(s -> {
+ Integer id = TheiaCloudServiceUtil.getId(correlationId, appDef, s);
+ if (id != null) {
+ resourceFactory.createInternalServiceForEagerInstance(appDef, id, labels,
+ correlationId);
+ }
+ }).build());
+ success &= intResult.isSuccess();
+
+ int created = extResult.getCreated() + intResult.getCreated();
+ int deleted = extResult.getDeleted() + intResult.getDeleted();
+ int recreated = extResult.getRecreated() + intResult.getRecreated();
+ int skipped = extResult.getSkipped() + intResult.getSkipped();
+ success = extResult.isSuccess() && intResult.isSuccess();
+ serviceSpan.setData("created_count", created);
+ serviceSpan.setData("deleted_count", deleted);
+ serviceSpan.setData("recreated_count", recreated);
+ serviceSpan.setData("skipped_count", skipped);
+ serviceSpan.setTag("outcome", success ? "success" : "failure");
+ serviceSpan.setTag("had_changes", (created + deleted + recreated) > 0 ? "true" : "false");
+ serviceSpan.finish();
+
+ // Reconcile configmaps (if using Keycloak)
+ if (arguments.isUseKeycloak()) {
+ ISpan cmSpan = span.startChild("pool.reconcile_configmaps", "Reconcile configmaps");
+ List existingConfigMaps = K8sUtil.getExistingConfigMaps(client.kubernetes(),
+ client.namespace(), ownerName, ownerUID);
+ List proxyConfigMaps = existingConfigMaps.stream().filter(
+ cm -> "proxy".equals(cm.getMetadata().getLabels().get("theia-cloud.io/template-purpose")))
+ .collect(Collectors.toList());
+ List emailConfigMaps = existingConfigMaps.stream().filter(
+ cm -> "emails".equals(cm.getMetadata().getLabels().get("theia-cloud.io/template-purpose")))
+ .collect(Collectors.toList());
+
+ Set missingProxyIds = TheiaCloudConfigMapUtil.computeIdsOfMissingProxyConfigMaps(appDef,
+ correlationId, targetInstances, proxyConfigMaps);
+ Set missingEmailIds = TheiaCloudConfigMapUtil.computeIdsOfMissingEmailConfigMaps(appDef,
+ correlationId, targetInstances, emailConfigMaps);
+
+ ResourceLifecycleManager.ReconcileResult proxyResult = ResourceLifecycleManager
+ .reconcile(ResourceLifecycleManager.ReconcileContext. builder()
+ .correlationId(correlationId).existingResources(proxyConfigMaps)
+ .missingIds(missingProxyIds).targetCount(targetInstances).owner(owner)
+ .resourceAccessor(cm -> client.kubernetes().configMaps().inNamespace(client.namespace())
+ .resource(cm))
+ .idExtractor(cm -> TheiaCloudConfigMapUtil.getProxyId(correlationId, appDef, cm))
+ .resourceTypeName("proxy configmap")
+ .createResource(instance -> resourceFactory.createProxyConfigMapForEagerInstance(appDef,
+ instance, labels, correlationId))
+ .shouldRecreate(cm -> OwnershipManager.isOwnedSolelyBy(cm, owner)
+ && isOutdated(cm, currentGeneration))
+ .recreateResource(cm -> {
+ Integer id = TheiaCloudConfigMapUtil.getProxyId(correlationId, appDef, cm);
+ if (id != null) {
+ resourceFactory.createProxyConfigMapForEagerInstance(appDef, id, labels,
+ correlationId);
+ }
+ }).build());
+ success &= proxyResult.isSuccess();
+
+ ResourceLifecycleManager.ReconcileResult emailResult = ResourceLifecycleManager
+ .reconcile(ResourceLifecycleManager.ReconcileContext. builder()
+ .correlationId(correlationId).existingResources(emailConfigMaps)
+ .missingIds(missingEmailIds).targetCount(targetInstances).owner(owner)
+ .resourceAccessor(cm -> client.kubernetes().configMaps().inNamespace(client.namespace())
+ .resource(cm))
+ .idExtractor(cm -> TheiaCloudConfigMapUtil.getEmailId(correlationId, appDef, cm))
+ .resourceTypeName("email configmap")
+ .createResource(instance -> resourceFactory.createEmailConfigMapForEagerInstance(appDef,
+ instance, labels, correlationId))
+ .shouldRecreate(cm -> OwnershipManager.isOwnedSolelyBy(cm, owner)
+ && isOutdated(cm, currentGeneration))
+ .recreateResource(cm -> {
+ Integer id = TheiaCloudConfigMapUtil.getEmailId(correlationId, appDef, cm);
+ if (id != null) {
+ resourceFactory.createEmailConfigMapForEagerInstance(appDef, id, labels,
+ correlationId);
+ }
+ }).build());
+ success &= emailResult.isSuccess();
+
+ int cmCreated = proxyResult.getCreated() + emailResult.getCreated();
+ int cmDeleted = proxyResult.getDeleted() + emailResult.getDeleted();
+ int cmRecreated = proxyResult.getRecreated() + emailResult.getRecreated();
+ int cmSkipped = proxyResult.getSkipped() + emailResult.getSkipped();
+ boolean cmSuccess = proxyResult.isSuccess() && emailResult.isSuccess();
+ cmSpan.setData("created_count", cmCreated);
+ cmSpan.setData("deleted_count", cmDeleted);
+ cmSpan.setData("recreated_count", cmRecreated);
+ cmSpan.setData("skipped_count", cmSkipped);
+ cmSpan.setTag("outcome", cmSuccess ? "success" : "failure");
+ cmSpan.setTag("had_changes", (cmCreated + cmDeleted + cmRecreated) > 0 ? "true" : "false");
+ cmSpan.finish();
+ }
+
+ // Ensure PVCs exist first for sidecars with mountWorkspace=true.
+ for (int instance = 1; instance <= targetInstances; instance++) {
+ createInstancePvc(appDef, instance, correlationId);
+ }
+
+ // Reconcile sidecar resources BEFORE Theia deployments so DNS resolves at pod startup
+ success &= sidecarManager.reconcilePrewarmedSidecars(appDef, targetInstances, labels, correlationId);
+
+ // Reconcile deployments
+ ISpan deploySpan = span.startChild("pool.reconcile_deployments", "Reconcile deployments");
+ List existingDeployments = K8sUtil.getExistingDeployments(client.kubernetes(),
+ client.namespace(), ownerName, ownerUID);
+ Set missingDeploymentIds = TheiaCloudDeploymentUtil.computeIdsOfMissingDeployments(appDef,
+ correlationId, targetInstances, existingDeployments);
+
+ deploySpan.setData("existing", existingDeployments.size());
+ deploySpan.setData("missing", missingDeploymentIds.size());
+
+ ResourceLifecycleManager.ReconcileResult deployResult = ResourceLifecycleManager
+ .reconcile(ResourceLifecycleManager.ReconcileContext. builder()
+ .correlationId(correlationId).existingResources(existingDeployments)
+ .missingIds(missingDeploymentIds).targetCount(targetInstances).owner(owner)
+ .resourceAccessor(d -> client.kubernetes().apps().deployments()
+ .inNamespace(client.namespace()).resource(d))
+ .idExtractor(d -> TheiaCloudDeploymentUtil.getId(correlationId, appDef, d))
+ .resourceTypeName("deployment")
+ .createResource(instance -> {
+ Optional pvcName = createInstancePvc(appDef, instance, correlationId);
+ resourceFactory.createDeploymentForEagerInstance(appDef, instance, pvcName, labels, correlationId);
+ })
+ .shouldRecreate(
+ d -> OwnershipManager.isOwnedSolelyBy(d, owner)
+ && isOutdated(d, currentGeneration))
+ .recreateResource(d -> {
+ Integer id = TheiaCloudDeploymentUtil.getId(correlationId, appDef, d);
+ if (id != null) {
+ Optional pvcName = createInstancePvc(appDef, id, correlationId);
+ resourceFactory.createDeploymentForEagerInstance(appDef, id, pvcName, labels, correlationId);
+ }
+ }).build());
+ success &= deployResult.isSuccess();
+
+ deploySpan.setData("created_count", deployResult.getCreated());
+ deploySpan.setData("deleted_count", deployResult.getDeleted());
+ deploySpan.setData("recreated_count", deployResult.getRecreated());
+ deploySpan.setData("skipped_count", deployResult.getSkipped());
+ deploySpan.setTag("outcome", deployResult.isSuccess() ? "success" : "failure");
+ deploySpan.setTag("had_changes", (deployResult.getCreated() + deployResult.getDeleted() + deployResult.getRecreated()) > 0 ? "true" : "false");
+ deploySpan.finish();
+
+ span.setTag("outcome", success ? "success" : "failure"); Tracing.finish(span, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ return success;
+
+ } catch (Exception e) {
+ Tracing.finishError(span, e);
+ throw e;
+ }
+ }
+
+ /**
+ * Reconciles a single instance after session release. - If instanceId > minInstances → delete all resources for
+ * this instance - If resource generation != current AppDefinition generation → recreate - Otherwise → do nothing
+ */
+ public void reconcileInstance(AppDefinition appDef, int instanceId, String correlationId) {
+ String appDefName = appDef.getSpec().getName();
+ int minInstances = appDef.getSpec().getMinInstances();
+ long currentGeneration = appDef.getMetadata().getGeneration();
+
+ ISpan span = Tracing.childSpan("pool.reconcile_instance", "Reconcile instance " + instanceId);
+
+ span.setTag("app_definition", appDefName);
+ span.setData("instance_id", instanceId);
+ span.setData("min_instances", minInstances);
+ span.setData("generation", currentGeneration);
+
+ try {
+ String ownerName = appDef.getMetadata().getName();
+ String ownerUID = appDef.getMetadata().getUid();
+
+ LOGGER.info(formatLogMessage(correlationId, "Reconciling instance " + instanceId + " (minInstances="
+ + minInstances + ", generation=" + currentGeneration + ")"));
+
+ // Find resources for this instance
+ List allServices = K8sUtil.getExistingServices(client.kubernetes(), client.namespace(), ownerName,
+ ownerUID);
+ List allDeployments = K8sUtil.getExistingDeployments(client.kubernetes(), client.namespace(),
+ ownerName, ownerUID);
+ List allConfigMaps = K8sUtil.getExistingConfigMaps(client.kubernetes(), client.namespace(),
+ ownerName, ownerUID);
+
+ // Filter to just this instance
+ List instanceServices = allServices.stream()
+ .filter(s -> instanceId == parseInstanceIdOrDefault(s, -1)).collect(Collectors.toList());
+ List instanceDeployments = allDeployments.stream()
+ .filter(d -> instanceId == parseDeploymentInstanceIdOrDefault(appDef, d, -1))
+ .collect(Collectors.toList());
+ List instanceConfigMaps = allConfigMaps.stream()
+ .filter(cm -> instanceId == parseConfigMapInstanceIdOrDefault(appDef, cm, -1))
+ .collect(Collectors.toList());
+
+ span.setData("found_services", instanceServices.size());
+ span.setData("found_deployments", instanceDeployments.size());
+ span.setData("found_configmaps", instanceConfigMaps.size());
+
+ if (instanceId > minInstances) {
+ span.setTag("action", "delete_excess");
+ LOGGER.info(formatLogMessage(correlationId,
+ "Instance " + instanceId + " exceeds minInstances (" + minInstances + "), deleting"));
+
+ ISpan deleteSpan = span.startChild("pool.delete_excess_instance", "Delete excess instance");
+ deleteInstanceResources(instanceServices, instanceDeployments, instanceConfigMaps, correlationId);
+ sidecarManager.deletePrewarmedSidecars(appDef, instanceId, correlationId);
+ deleteInstancePvc(appDef, instanceId, correlationId);
+ Tracing.finishSuccess(deleteSpan);
+ Tracing.finishSuccess(span);
+ return;
+ }
+
+ boolean outdated = isOutdated(instanceServices, currentGeneration)
+ || isOutdated(instanceDeployments, currentGeneration)
+ || isOutdated(instanceConfigMaps, currentGeneration);
+
+ if (outdated) {
+ span.setTag("action", "recreate_outdated");
+ LOGGER.info(formatLogMessage(correlationId,
+ "Instance " + instanceId + " has outdated resources, recreating"));
+
+ ISpan recreateSpan = span.startChild("pool.recreate_outdated_instance", "Recreate outdated instance");
+ deleteInstanceResources(instanceServices, instanceDeployments, instanceConfigMaps, correlationId);
+ sidecarManager.deletePrewarmedSidecars(appDef, instanceId, correlationId);
+ deleteInstancePvc(appDef, instanceId, correlationId);
+ createInstanceResources(appDef, instanceId, correlationId);
+ Tracing.finishSuccess(recreateSpan);
+ Tracing.finishSuccess(span);
+ return;
+ }
+
+ span.setTag("action", "no_action");
+ LOGGER.info(formatLogMessage(correlationId, "Instance " + instanceId + " is up-to-date, no action needed"));
+ Tracing.finishSuccess(span);
+
+ } catch (Exception e) {
+ Tracing.finishError(span, e);
+ throw e;
+ }
+ }
+
+ private boolean isOutdated(List extends HasMetadata> resources, long currentGeneration) {
+ for (var resource : resources) {
+ if (isOutdated(resource, currentGeneration)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isOutdated(HasMetadata resource, long currentGeneration) {
+ Map labels = resource.getMetadata().getLabels();
+ if (labels == null) {
+ return true;
+ }
+ String genLabel = labels.get(APPDEFINITION_GENERATION_LABEL);
+ if (genLabel == null) {
+ return true;
+ }
+ try {
+ long resourceGen = Long.parseLong(genLabel);
+ return resourceGen != currentGeneration;
+ } catch (NumberFormatException e) {
+ return true;
+ }
+ }
+
+ private void deleteInstanceResources(List services, List deployments,
+ List configMaps, String correlationId) {
+ for (Service s : services) {
+ try {
+ client.kubernetes().services().inNamespace(client.namespace()).resource(s).delete();
+ LOGGER.trace(formatLogMessage(correlationId, "Deleted service " + s.getMetadata().getName()));
+ } catch (KubernetesClientException e) {
+ LOGGER.warn(formatLogMessage(correlationId, "Failed to delete service " + s.getMetadata().getName()),
+ e);
+ }
+ }
+ for (Deployment d : deployments) {
+ try {
+ client.kubernetes().apps().deployments().inNamespace(client.namespace()).resource(d).delete();
+ LOGGER.trace(formatLogMessage(correlationId, "Deleted deployment " + d.getMetadata().getName()));
+ } catch (KubernetesClientException e) {
+ LOGGER.warn(formatLogMessage(correlationId, "Failed to delete deployment " + d.getMetadata().getName()),
+ e);
+ }
+ }
+ for (ConfigMap cm : configMaps) {
+ try {
+ client.kubernetes().configMaps().inNamespace(client.namespace()).resource(cm).delete();
+ LOGGER.trace(formatLogMessage(correlationId, "Deleted configmap " + cm.getMetadata().getName()));
+ } catch (KubernetesClientException e) {
+ LOGGER.warn(formatLogMessage(correlationId, "Failed to delete configmap " + cm.getMetadata().getName()),
+ e);
+ }
+ }
+ }
+
+ private void createInstanceResources(AppDefinition appDef, int instanceId, String correlationId) {
+ Map labels = new HashMap<>();
+
+ resourceFactory.createServiceForEagerInstance(appDef, instanceId, labels, correlationId);
+ resourceFactory.createInternalServiceForEagerInstance(appDef, instanceId, labels, correlationId);
+
+ if (arguments.isUseKeycloak()) {
+ resourceFactory.createProxyConfigMapForEagerInstance(appDef, instanceId, labels, correlationId);
+ resourceFactory.createEmailConfigMapForEagerInstance(appDef, instanceId, labels, correlationId);
+ }
+
+ Optional pvcName = createInstancePvc(appDef, instanceId, correlationId);
+
+ // Create sidecar Deployment+Service BEFORE Theia Deployment so DNS resolves at pod startup
+ sidecarManager.createPrewarmedSidecars(appDef, instanceId, pvcName, labels, correlationId);
+
+ resourceFactory.createDeploymentForEagerInstance(appDef, instanceId, pvcName, labels, correlationId);
+ }
+
+ private int parseInstanceIdOrDefault(Service service, int defaultValue) {
+ Integer id = parseInstanceId(service);
+ return id != null ? id : defaultValue;
+ }
+
+ private int parseDeploymentInstanceIdOrDefault(AppDefinition appDef, Deployment deployment, int defaultValue) {
+ Integer id = TheiaCloudDeploymentUtil.getId(null, appDef, deployment);
+ return id != null ? id : defaultValue;
+ }
+
+ private int parseConfigMapInstanceIdOrDefault(AppDefinition appDef, ConfigMap configMap, int defaultValue) {
+ Integer id = TheiaCloudConfigMapUtil.getProxyId(null, appDef, configMap);
+ if (id != null) {
+ return id;
+ }
+ id = TheiaCloudConfigMapUtil.getEmailId(null, appDef, configMap);
+ return id != null ? id : defaultValue;
+ }
+
+ /**
+ * Releases all pool resources for an app definition (used during deletion).
+ */
+ public boolean releaseAll(AppDefinition appDef, String correlationId) {
+ String appDefName = appDef.getSpec().getName();
+ ISpan span = Tracing.childSpan("pool.release_all", "Release all pool resources");
+
+ span.setTag("app_definition", appDefName);
+
+ LOGGER.info(formatLogMessage(correlationId, "Releasing all pool resources for " + appDefName));
+
+ try {
+ String ownerName = appDef.getMetadata().getName();
+ String ownerUID = appDef.getMetadata().getUid();
+ OwnerContext owner = OwnerContext.of(ownerName, ownerUID);
+
+ boolean success = true;
+
+ ISpan svcSpan = span.startChild("pool.release_services", "Release services");
+ List services = K8sUtil.getExistingServices(client.kubernetes(), client.namespace(), ownerName,
+ ownerUID);
+ svcSpan.setData("count", services.size());
+ success &= ResourceLifecycleManager.releaseOwnership(services, owner,
+ s -> client.kubernetes().services().inNamespace(client.namespace()).resource(s), "service",
+ correlationId);
+ svcSpan.setTag("outcome", success ? "success" : "failure"); Tracing.finish(svcSpan, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+
+ ISpan cmSpan = span.startChild("pool.release_configmaps", "Release configmaps");
+ List configMaps = K8sUtil.getExistingConfigMaps(client.kubernetes(), client.namespace(),
+ ownerName, ownerUID);
+ cmSpan.setData("count", configMaps.size());
+ boolean cmSuccess = ResourceLifecycleManager.releaseOwnership(configMaps, owner,
+ cm -> client.kubernetes().configMaps().inNamespace(client.namespace()).resource(cm), "configmap",
+ correlationId);
+ success &= cmSuccess;
+ cmSpan.setTag("outcome", cmSuccess ? "success" : "failure"); Tracing.finish(cmSpan, cmSuccess ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+
+ ISpan deploySpan = span.startChild("pool.release_deployments", "Release deployments");
+ List deployments = K8sUtil.getExistingDeployments(client.kubernetes(), client.namespace(),
+ ownerName, ownerUID);
+ deploySpan.setData("count", deployments.size());
+ boolean deploySuccess = ResourceLifecycleManager.releaseOwnership(deployments, owner,
+ d -> client.kubernetes().apps().deployments().inNamespace(client.namespace()).resource(d),
+ "deployment", correlationId);
+ success &= deploySuccess;
+ deploySpan.setTag("outcome", deploySuccess ? "success" : "failure"); Tracing.finish(deploySpan, deploySuccess ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+
+ span.setTag("outcome", success ? "success" : "failure"); Tracing.finish(span, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ return success;
+
+ } catch (Exception e) {
+ Tracing.finishError(span, e);
+ throw e;
+ }
+ }
+
+ // ========== Instance Reservation ==========
+
+ /**
+ * Reserves a prewarmed instance for a session. Adds the session as an owner of the instance's resources.
+ */
+ public synchronized ReservationResult reserveInstance(Session session, AppDefinition appDef, String correlationId) {
+ String sessionName = session.getSpec().getName();
+ String appDefName = appDef.getSpec().getName();
+
+ ISpan span = Tracing.childSpan("pool.reserve", "Reserve pool instance");
+
+ span.setTag("app_definition", appDefName);
+ span.setData("session_name", sessionName);
+
+ LOGGER.info(formatLogMessage(correlationId, "Attempting to reserve instance for session " + sessionName));
+
+ try {
+ String appDefOwnerName = appDef.getMetadata().getName();
+ String appDefOwnerUID = appDef.getMetadata().getUid();
+ String sessionUID = session.getMetadata().getUid();
+
+ // Get all services for this app definition
+ List existingServices = K8sUtil.getExistingServices(client.kubernetes(), client.namespace(),
+ appDefOwnerName, appDefOwnerUID);
+
+ // Separate external and internal services
+ List externalServices = existingServices.stream()
+ .filter(s -> !s.getMetadata().getName().endsWith("-int")).collect(Collectors.toList());
+ List internalServices = existingServices.stream()
+ .filter(s -> s.getMetadata().getName().endsWith("-int")).collect(Collectors.toList());
+
+ // Track pool capacity
+ int totalCapacity = externalServices.size();
+ int availableCount = (int) externalServices.stream().filter(s -> TheiaCloudServiceUtil.isUnusedService(s))
+ .count();
+ span.setData("pool.total_capacity", totalCapacity);
+ span.setData("pool.available", availableCount);
+
+ // Check if session already has a reservation
+ Optional alreadyReservedExternal = TheiaCloudServiceUtil.getServiceOwnedBySession(sessionName,
+ sessionUID, externalServices);
+ Optional alreadyReservedInternal = TheiaCloudServiceUtil.getServiceOwnedBySession(sessionName,
+ sessionUID, internalServices);
+
+ if (alreadyReservedExternal.isPresent() && alreadyReservedInternal.isPresent()) {
+ Integer extId = parseInstanceId(alreadyReservedExternal.get());
+ Integer intId = parseInstanceId(alreadyReservedInternal.get());
+ if (extId == null || intId == null || !extId.equals(intId)) {
+ LOGGER.error(formatLogMessage(correlationId, "Reservation mismatch for session " + sessionName));
+ span.setTag("pool.outcome", "error");;
+ span.setTag("outcome", "error"); Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return ReservationResult.error();
+ }
+ String deploymentName = TheiaCloudDeploymentUtil.getDeploymentName(appDef, extId);
+ span.setTag("reservation.type", "already_reserved");
+ span.setTag("pool.outcome", "success");
+ span.setData("instance_id", extId);;
+ Tracing.finishSuccess(span);
+ return ReservationResult.success(new PoolInstance(extId, alreadyReservedExternal.get(),
+ alreadyReservedInternal.get(), deploymentName));
+ }
+
+ // Build instance maps
+ Map externalByInstance = buildInstanceMap(externalServices);
+ Map internalByInstance = buildInstanceMap(internalServices);
+
+ // Handle partial reservation
+ if (alreadyReservedExternal.isPresent() ^ alreadyReservedInternal.isPresent()) {
+ span.setTag("reservation.type", "partial_recovery");
+ ReservationResult result = handlePartialReservation(session, appDef, alreadyReservedExternal,
+ alreadyReservedInternal, externalByInstance, internalByInstance, correlationId);
+ span.setTag("pool.outcome", result.getOutcome().name().toLowerCase());
+ Integer instanceId = result.getInstance().map(PoolInstance::getInstanceId).orElse(null);
+ if (instanceId != null) {
+ span.setData("instance_id", instanceId);
+ }
+ String outcome = result.getOutcome() == ReservationOutcome.SUCCESS ? "success"
+ : result.getOutcome().name().toLowerCase();
+ SpanStatus status = result.getOutcome() == ReservationOutcome.SUCCESS ? SpanStatus.OK
+ : result.getOutcome() == ReservationOutcome.NO_CAPACITY ? SpanStatus.RESOURCE_EXHAUSTED
+ : SpanStatus.INTERNAL_ERROR;
+ span.setTag("outcome", outcome);
+ Tracing.finish(span, status);
+ return result;
+ }
+
+ // Find available instance
+ List availableIds = externalByInstance.entrySet().stream()
+ .filter(e -> TheiaCloudServiceUtil.isUnusedService(e.getValue())).filter(e -> {
+ Service internal = internalByInstance.get(e.getKey());
+ return internal != null && TheiaCloudServiceUtil.isUnusedService(internal);
+ }).map(Map.Entry::getKey).sorted(Comparator.naturalOrder()).collect(Collectors.toList());
+
+ if (availableIds.isEmpty()) {
+ LOGGER.info(formatLogMessage(correlationId, "No prewarmed instances available"));
+ span.setTag("reservation.type", "no_capacity");
+ span.setTag("pool.outcome", "no_capacity");;
+ span.setTag("outcome", "no_capacity"); Tracing.finish(span, SpanStatus.RESOURCE_EXHAUSTED);
+ return ReservationResult.noCapacity();
+ }
+
+ int chosenInstance = availableIds.get(0);
+ Service chosenExternal = externalByInstance.get(chosenInstance);
+ Service chosenInternal = internalByInstance.get(chosenInstance);
+
+ span.setTag("reservation.type", "new");
+ span.setData("chosen_instance", chosenInstance);
+
+ // Reserve both services
+ ISpan reserveExtSpan = span.startChild("pool.reserve_external_service", "Reserve external service");
+ try {
+ reserveService(chosenExternal, sessionName, sessionUID, correlationId);
+ Tracing.finishSuccess(reserveExtSpan);
+ } catch (KubernetesClientException e) {
+ LOGGER.error(formatLogMessage(correlationId, "Failed to reserve external service"), e);
+ Tracing.finishError(reserveExtSpan, e);
+ span.setTag("pool.outcome", "error");;
+ span.setTag("outcome", "error"); Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return ReservationResult.error();
+ }
+
+ ISpan reserveIntSpan = span.startChild("pool.reserve_internal_service", "Reserve internal service");
+ try {
+ reserveService(chosenInternal, sessionName, sessionUID, correlationId);
+ Tracing.finishSuccess(reserveIntSpan);
+ } catch (KubernetesClientException e) {
+ LOGGER.error(formatLogMessage(correlationId, "Failed to reserve internal service"), e);
+ Tracing.finishError(reserveIntSpan, e);
+ rollbackReservation(chosenExternal, sessionName, sessionUID, correlationId);
+ span.setTag("pool.outcome", "error");;
+ span.setTag("outcome", "error"); Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return ReservationResult.error();
+ }
+
+ String deploymentName = TheiaCloudDeploymentUtil.getDeploymentName(appDef, chosenInstance);
+ span.setTag("pool.outcome", "success");
+ span.setData("instance_id", chosenInstance);;
+ Tracing.finishSuccess(span);
+ return ReservationResult
+ .success(new PoolInstance(chosenInstance, chosenExternal, chosenInternal, deploymentName));
+
+ } catch (Exception e) {
+ Tracing.finishError(span, e);
+ throw e;
+ }
+ }
+
+ /**
+ * Completes the session setup after reservation. Adds session labels, reserves deployment, configures email config.
+ */
+ public boolean completeSessionSetup(Session session, AppDefinition appDef, PoolInstance instance,
+ String correlationId) {
+
+ ISpan span = Tracing.childSpan("pool.complete_setup", "Complete session setup");
+
+ span.setData("session_name", session.getSpec().getName());
+ span.setData("instance_id", instance.getInstanceId());
+
+ try {
+ String sessionName = session.getMetadata().getName();
+ String sessionUID = session.getMetadata().getUid();
+
+ Map sessionLabels = LabelsUtil.createSessionLabels(session, appDef);
+
+ // Add labels to services
+ ISpan labelSpan = span.startChild("pool.add_session_labels", "Add session labels to services");
+ try {
+ addSessionLabelsToService(instance.getExternalService(), sessionLabels, correlationId);
+ addSessionLabelsToService(instance.getInternalService(), sessionLabels, correlationId);
+ Tracing.finishSuccess(labelSpan);
+ } catch (KubernetesClientException e) {
+ LOGGER.error(formatLogMessage(correlationId, "Failed to add session labels to services"), e);
+ Tracing.finishError(labelSpan, e);
+ span.setTag("outcome", "failure"); Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return false;
+ }
+
+ // Reserve deployment
+ ISpan deploySpan = span.startChild("pool.reserve_deployment", "Reserve deployment");
+ try {
+ client.kubernetes().inNamespace(session.getMetadata().getNamespace()).apps().deployments().withName(instance.getDeploymentName()).edit(
+ d -> TheiaCloudHandlerUtil.addOwnerReferenceToItem(correlationId, sessionName, sessionUID, d));
+ Tracing.finishSuccess(deploySpan);
+ } catch (KubernetesClientException e) {
+ LOGGER.error(formatLogMessage(correlationId, "Failed to reserve deployment"), e);
+ Tracing.finishError(deploySpan, e);
+ span.setTag("outcome", "failure"); Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return false;
+ }
+
+ // Configure email config (if using Keycloak)
+ if (arguments.isUseKeycloak()) {
+ ISpan emailSpan = span.startChild("pool.configure_email", "Configure email config");
+ String emailConfigName = TheiaCloudConfigMapUtil.getEmailConfigName(appDef, instance.getInstanceId());
+ try {
+ client.kubernetes().inNamespace(session.getMetadata().getNamespace()).configMaps().withName(emailConfigName).edit(cm -> {
+ cm.setData(Collections.singletonMap(AddedHandlerUtil.FILENAME_AUTHENTICATED_EMAILS_LIST,
+ session.getSpec().getUser()));
+ return cm;
+ });
+ Tracing.finishSuccess(emailSpan);
+ } catch (KubernetesClientException e) {
+ LOGGER.error(formatLogMessage(correlationId, "Failed to configure email config"), e);
+ Tracing.finishError(emailSpan, e);
+ span.setTag("outcome", "failure"); Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return false;
+ }
+
+ // Trigger pod refresh
+ ISpan refreshSpan = span.startChild("pool.refresh_pods", "Trigger pod refresh");
+ refreshPods(instance.getDeploymentName(), correlationId);
+ Tracing.finishSuccess(refreshSpan);
+ }
+
+ Tracing.finishSuccess(span);
+ return true;
+
+ } catch (Exception e) {
+ Tracing.finishError(span, e);
+ throw e;
+ }
+ }
+
+ /**
+ * Releases an instance back to the pool (used when session ends). Removes session ownership and clears
+ * session-specific data.
+ */
+ public boolean releaseInstance(Session session, AppDefinition appDef, String correlationId) {
+ String sessionName = session.getSpec().getName();
+ String appDefName = appDef.getSpec().getName();
+
+ ISpan span = Tracing.childSpan("pool.release_instance", "Release pool instance");
+
+ span.setTag("app_definition", appDefName);
+ span.setData("session_name", sessionName);
+
+ LOGGER.info(formatLogMessage(correlationId, "Releasing instance for session " + sessionName));
+
+ try {
+ String sessionUID = session.getMetadata().getUid();
+
+ // Find services owned by this session
+ Map sessionLabels = LabelsUtil.createSessionLabels(session, appDef);
+ List services = findServicesByLabels(sessionLabels, correlationId);
+
+ if (services.isEmpty()) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "No services found for session " + session.getSpec().getName()));
+ span.setTag("error.reason", "no_services_found");
+ span.setTag("outcome", "failure"); Tracing.finish(span, SpanStatus.NOT_FOUND);
+ return false;
+ }
+
+ List externalServices = services.stream().filter(s -> !s.getMetadata().getName().endsWith("-int"))
+ .collect(Collectors.toList());
+ List internalServices = services.stream().filter(s -> s.getMetadata().getName().endsWith("-int"))
+ .collect(Collectors.toList());
+
+ if (externalServices.size() != 1 || internalServices.size() != 1) {
+ LOGGER.error(formatLogMessage(correlationId, "Expected 1 external and 1 internal service, found "
+ + externalServices.size() + " and " + internalServices.size()));
+ span.setTag("error.reason", "service_count_mismatch");
+ span.setData("external_count", externalServices.size());
+ span.setData("internal_count", internalServices.size());
+ span.setTag("outcome", "failure"); Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return false;
+ }
+
+ Service externalService = externalServices.get(0);
+ Service internalService = internalServices.get(0);
+
+ Integer instanceId = parseInstanceId(externalService);
+ if (instanceId == null) {
+ LOGGER.error(formatLogMessage(correlationId, "Cannot determine instance ID from service"));
+ span.setTag("error.reason", "cannot_parse_instance_id");
+ span.setTag("outcome", "failure"); Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ return false;
+ }
+ span.setData("instance_id", instanceId);
+
+ boolean success = true;
+
+ // Clean up services
+ ISpan svcSpan = span.startChild("pool.cleanup_services", "Clean up services");
+ success &= cleanupService(externalService, sessionName, sessionUID, correlationId);
+ success &= cleanupService(internalService, sessionName, sessionUID, correlationId);
+ svcSpan.setTag("outcome", success ? "success" : "failure"); Tracing.finish(svcSpan, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+
+ // Clean up deployment
+ ISpan deploySpan = span.startChild("pool.cleanup_deployment", "Clean up deployment");
+ String deploymentName = TheiaCloudDeploymentUtil.getDeploymentName(appDef, instanceId);
+ try {
+ client.kubernetes().apps().deployments().withName(deploymentName).edit(d -> TheiaCloudHandlerUtil
+ .removeOwnerReferenceFromItem(correlationId, sessionName, sessionUID, d));
+ Tracing.finishSuccess(deploySpan);
+ } catch (KubernetesClientException e) {
+ LOGGER.error(formatLogMessage(correlationId, "Failed to clean up deployment"), e);
+ Tracing.finishError(deploySpan, e);
+ success = false;
+ }
+
+ // Clear email config
+ if (arguments.isUseKeycloak()) {
+ ISpan emailSpan = span.startChild("pool.clear_email_config", "Clear email config");
+ String emailConfigName = TheiaCloudConfigMapUtil.getEmailConfigName(appDef, instanceId);
+ try {
+ client.kubernetes().configMaps().withName(emailConfigName).edit(cm -> {
+ cm.setData(Collections.singletonMap(AddedHandlerUtil.FILENAME_AUTHENTICATED_EMAILS_LIST, null));
+ return cm;
+ });
+ Tracing.finishSuccess(emailSpan);
+ } catch (KubernetesClientException e) {
+ LOGGER.error(formatLogMessage(correlationId, "Failed to clear email config"), e);
+ Tracing.finishError(emailSpan, e);
+ success = false;
+ }
+ }
+
+ // Delete pod to reset state
+ ISpan podSpan = span.startChild("pool.delete_pod", "Delete pod to reset state");
+ deletePod(deploymentName, correlationId);
+ Tracing.finishSuccess(podSpan);
+
+ span.setTag("outcome", success ? "success" : "failure"); Tracing.finish(span, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ return success;
+
+ } catch (Exception e) {
+ Tracing.finishError(span, e);
+ throw e;
+ }
+ }
+
+ // ========== Helper Methods ==========
+
+ private Map buildInstanceMap(List services) {
+ Map map = new HashMap<>();
+ for (Service s : services) {
+ Integer id = parseInstanceId(s);
+ if (id != null) {
+ map.putIfAbsent(id, s);
+ }
+ }
+ return map;
+ }
+
+ private Integer parseInstanceId(Service service) {
+ String id = TheiaCloudK8sUtil.extractIdFromName(service.getMetadata());
+ if (id == null || id.isBlank()) {
+ return null;
+ }
+ try {
+ return Integer.valueOf(id);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+
+ private ReservationResult handlePartialReservation(Session session, AppDefinition appDef,
+ Optional reservedExternal, Optional reservedInternal, Map externalMap,
+ Map internalMap, String correlationId) {
+
+ String sessionName = session.getMetadata().getName();
+ String sessionUID = session.getMetadata().getUid();
+
+ Service reserved = reservedExternal.orElseGet(reservedInternal::get);
+ Integer instance = parseInstanceId(reserved);
+ if (instance == null) {
+ LOGGER.error(formatLogMessage(correlationId, "Cannot parse instance from partially reserved service"));
+ rollbackReservation(reserved, sessionName, sessionUID, correlationId);
+ return ReservationResult.error();
+ }
+
+ Service counterpart = reservedExternal.isPresent() ? internalMap.get(instance) : externalMap.get(instance);
+ if (counterpart == null || !TheiaCloudServiceUtil.isUnusedService(counterpart)) {
+ LOGGER.warn(formatLogMessage(correlationId, "Partial reservation cannot be completed, rolling back"));
+ rollbackReservation(reserved, sessionName, sessionUID, correlationId);
+ return ReservationResult.noCapacity();
+ }
+
+ try {
+ reserveService(counterpart, sessionName, sessionUID, correlationId);
+ } catch (KubernetesClientException e) {
+ LOGGER.error(formatLogMessage(correlationId, "Failed to complete partial reservation"), e);
+ rollbackReservation(reserved, sessionName, sessionUID, correlationId);
+ return ReservationResult.error();
+ }
+
+ Service ext = reservedExternal.orElse(counterpart);
+ Service in = reservedInternal.orElse(counterpart);
+ String deploymentName = TheiaCloudDeploymentUtil.getDeploymentName(appDef, instance);
+ return ReservationResult.success(new PoolInstance(instance, ext, in, deploymentName));
+ }
+
+ private void reserveService(Service service, String sessionName, String sessionUID, String correlationId) {
+ client.services().inNamespace(client.namespace()).withName(service.getMetadata().getName())
+ .edit(s -> TheiaCloudHandlerUtil.addOwnerReferenceToItem(correlationId, sessionName, sessionUID, s));
+ }
+
+ private void rollbackReservation(Service service, String sessionName, String sessionUID, String correlationId) {
+ try {
+ client.services().inNamespace(client.namespace()).withName(service.getMetadata().getName()).edit(s -> {
+ TheiaCloudHandlerUtil.removeOwnerReferenceFromItem(correlationId, sessionName, sessionUID, s);
+ return s;
+ });
+ } catch (KubernetesClientException e) {
+ LOGGER.warn(formatLogMessage(correlationId, "Failed to rollback reservation"), e);
+ }
+ }
+
+ private void addSessionLabelsToService(Service service, Map labels, String correlationId) {
+ client.services().inNamespace(client.namespace()).withName(service.getMetadata().getName()).edit(s -> {
+ Map existing = s.getMetadata().getLabels();
+ if (existing == null) {
+ existing = new HashMap<>();
+ s.getMetadata().setLabels(existing);
+ }
+ existing.putAll(labels);
+ return s;
+ });
+ }
+
+ private boolean cleanupService(Service service, String sessionName, String sessionUID, String correlationId) {
+ int attempts = 0;
+ while (attempts < 3) {
+ try {
+ client.services().inNamespace(service.getMetadata().getNamespace()).withName(service.getMetadata().getName()).edit(s -> {
+ TheiaCloudHandlerUtil.removeOwnerReferenceFromItem(correlationId, sessionName, sessionUID, s);
+ if (s.getMetadata().getLabels() != null) {
+ s.getMetadata().getLabels().keySet().removeAll(LabelsUtil.getSessionSpecificLabelKeys());
+ }
+ return s;
+ });
+ return true;
+ } catch (KubernetesClientException e) {
+ attempts++;
+ if (attempts >= 3) {
+ LOGGER.error(formatLogMessage(correlationId, "Failed to cleanup service after 3 attempts"), e);
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+
+ private List findServicesByLabels(Map labels, String correlationId) {
+ String labelSelector = labels.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue())
+ .collect(Collectors.joining(","));
+ return client.kubernetes().services().inNamespace(client.namespace()).withLabelSelector(labelSelector).list()
+ .getItems();
+ }
+
+ private void refreshPods(String deploymentName, String correlationId) {
+ try {
+ // Get deployment to extract selector labels
+ Deployment deployment = client.kubernetes().apps().deployments().inNamespace(client.namespace())
+ .withName(deploymentName).get();
+ if (deployment == null || deployment.getSpec() == null || deployment.getSpec().getSelector() == null
+ || deployment.getSpec().getSelector().getMatchLabels() == null) {
+ return;
+ }
+
+ // Build label selector from deployment's matchLabels
+ Map selectorLabels = deployment.getSpec().getSelector().getMatchLabels();
+ String labelSelector = selectorLabels.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue())
+ .collect(Collectors.joining(","));
+
+ // Query pods using label selector
+ client.kubernetes().pods().inNamespace(client.namespace()).withLabelSelector(labelSelector).list()
+ .getItems().forEach(pod -> {
+ // Null-check metadata and ownerReferences before streaming
+ if (pod.getMetadata() == null || pod.getMetadata().getOwnerReferences() == null) {
+ return;
+ }
+ // Check if pod belongs to this deployment via owner references
+ if (pod.getMetadata().getOwnerReferences().stream()
+ .anyMatch(or -> or.getName().startsWith(deploymentName))) {
+ // Null-check and initialize annotations before mutating
+ if (pod.getMetadata().getAnnotations() == null) {
+ pod.getMetadata().setAnnotations(new HashMap<>());
+ }
+ pod.getMetadata().getAnnotations().put(EAGER_START_REFRESH_ANNOTATION,
+ Instant.now().toString());
+ PodResource podResource = client.pods().inNamespace(client.namespace())
+ .withName(pod.getMetadata().getName());
+ podResource.edit(p -> pod);
+ }
+ });
+ } catch (KubernetesClientException e) {
+ LOGGER.warn(formatLogMessage(correlationId, "Failed to refresh pods"), e);
+ }
+ }
+
+ private void deletePod(String deploymentName, String correlationId) {
+ try {
+ Optional pod = client.kubernetes().pods().list().getItems().stream()
+ .filter(p -> p.getMetadata().getName().startsWith(deploymentName)).findAny();
+ if (pod.isPresent()) {
+ LOGGER.info(formatLogMessage(correlationId, "Deleting pod " + pod.get().getMetadata().getName()));
+ client.pods().withName(pod.get().getMetadata().getName()).delete();
+ }
+ } catch (KubernetesClientException e) {
+ LOGGER.error(formatLogMessage(correlationId, "Failed to delete pod"), e);
+ }
+ }
+
+ // ========== PVC Management ==========
+
+ private static final String TEMPLATE_PERSISTENTVOLUMECLAIM_YAML = "/templatePersistentVolumeClaim.yaml";
+ private static final int PVC_DELETION_WAIT_ATTEMPTS = 30;
+ private static final long PVC_DELETION_WAIT_MILLIS = 1000;
+
+ static String getInstancePvcName(AppDefinition appDef, int instanceId) {
+ return NamingUtil.createNameWithSuffix(appDef, instanceId, "pvc");
+ }
+
+ /**
+ * Creates a PersistentVolumeClaim for an eager-start pool instance. The PVC is owned by the AppDefinition so it
+ * survives session lifecycle and is cleaned up when the AppDefinition is deleted or the instance is scaled down.
+ *
+ * @return the PVC name if created successfully, or empty if creation fails
+ */
+ private Optional createInstancePvc(AppDefinition appDef, int instanceId, String correlationId) {
+ if (!SidecarConfig.requiresSharedWorkspace(appDef)) {
+ return Optional.empty();
+ }
+
+ String pvcName = getInstancePvcName(appDef, instanceId);
+
+ Optional existingPvc = client
+ .persistentVolumeClaimsClient().get(pvcName);
+ if (existingPvc.isPresent()
+ && existingPvc.get().getMetadata() != null
+ && existingPvc.get().getMetadata().getDeletionTimestamp() == null) {
+ LOGGER.info(formatLogMessage(correlationId,
+ "PVC " + pvcName + " already exists for instance " + instanceId));
+ return Optional.of(pvcName);
+ }
+
+ if (existingPvc.isPresent()) {
+ LOGGER.warn(formatLogMessage(correlationId,
+ "PVC " + pvcName + " is terminating for instance " + instanceId
+ + ". Waiting for deletion before recreation."));
+ if (!waitUntilPvcDeleted(pvcName, correlationId)) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "PVC " + pvcName + " is still present/terminating after waiting. Skipping recreation."));
+ return Optional.empty();
+ }
+ }
+
+ ISpan span = Tracing.childSpan("pool.create_pvc", "Create instance PVC");
+ span.setData("pvc_name", pvcName);
+ span.setData("instance_id", instanceId);
+
+ LOGGER.info(formatLogMessage(correlationId,
+ "Creating PVC " + pvcName + " for pool instance " + instanceId));
+
+ try {
+ Map replacements = new LinkedHashMap<>();
+ replacements.put(DefaultPersistentVolumeTemplateReplacements.PLACEHOLDER_PERSISTENTVOLUMENAME, pvcName);
+ replacements.put(TheiaCloudHandlerUtil.PLACEHOLDER_NAMESPACE, client.namespace());
+ replacements.put(DefaultPersistentVolumeTemplateReplacements.PLACEHOLDER_LABEL_WORKSPACE_NAME,
+ appDef.getSpec().getName() + "-instance-" + instanceId);
+ replacements.put(DefaultPersistentVolumeTemplateReplacements.PLACEHOLDER_STORAGE_CLASS_NAME,
+ arguments.getStorageClassName() != null ? arguments.getStorageClassName() : "");
+ replacements.put(DefaultPersistentVolumeTemplateReplacements.PLACEHOLDER_REQUESTED_STORAGE,
+ arguments.getRequestedStorage() != null ? arguments.getRequestedStorage()
+ : DefaultPersistentVolumeTemplateReplacements.DEFAULT_REQUESTED_STORAGE);
+ replacements.put(DefaultPersistentVolumeTemplateReplacements.PLACEHOLDER_ACCESS_MODE,
+ DefaultPersistentVolumeTemplateReplacements.READ_WRITE_MANY);
+
+ String pvcYaml = JavaResourceUtil.readResourceAndReplacePlaceholders(
+ TEMPLATE_PERSISTENTVOLUMECLAIM_YAML, replacements, correlationId);
+
+ Optional result =
+ client.persistentVolumeClaimsClient().loadAndCreate(correlationId, pvcYaml,
+ claim -> claim.addOwnerReference(appDef));
+
+ if (result.isPresent()) {
+ Tracing.finishSuccess(span);
+ LOGGER.info(formatLogMessage(correlationId,
+ "Successfully created PVC " + pvcName));
+ return Optional.of(pvcName);
+ } else {
+ span.setTag("outcome", "failure");
+ Tracing.finish(span, SpanStatus.INTERNAL_ERROR);
+ LOGGER.error(formatLogMessage(correlationId,
+ "Failed to create PVC " + pvcName));
+ return Optional.empty();
+ }
+
+ } catch (IOException | URISyntaxException e) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "Error loading PVC template for instance " + instanceId), e);
+ Tracing.finishError(span, e);
+ return Optional.empty();
+ } catch (RuntimeException e) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "Unexpected error creating PVC for instance " + instanceId), e);
+ Tracing.finishError(span, e);
+ return Optional.empty();
+ }
+ }
+
+ private void deleteInstancePvc(AppDefinition appDef, int instanceId, String correlationId) {
+ String pvcName = getInstancePvcName(appDef, instanceId);
+
+ LOGGER.info(formatLogMessage(correlationId,
+ "Deleting PVC " + pvcName + " for pool instance " + instanceId));
+
+ try {
+ client.kubernetes().persistentVolumeClaims()
+ .inNamespace(client.namespace())
+ .withName(pvcName)
+ .delete();
+ LOGGER.info(formatLogMessage(correlationId, "Deleted PVC " + pvcName));
+ } catch (KubernetesClientException e) {
+ LOGGER.warn(formatLogMessage(correlationId, "Failed to delete PVC " + pvcName), e);
+ }
+ }
+
+ private boolean waitUntilPvcDeleted(String pvcName, String correlationId) {
+ for (int attempt = 1; attempt <= PVC_DELETION_WAIT_ATTEMPTS; attempt++) {
+ if (client.persistentVolumeClaimsClient().get(pvcName).isEmpty()) {
+ return true;
+ }
+
+ try {
+ Thread.sleep(PVC_DELETION_WAIT_MILLIS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ LOGGER.warn(formatLogMessage(correlationId,
+ "Interrupted while waiting for PVC deletion: " + pvcName), e);
+ return false;
+ }
+ }
+
+ return client.persistentVolumeClaimsClient().get(pvcName).isEmpty();
+ }
+}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/pv/MinikubePersistentVolumeCreator.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/pv/MinikubePersistentVolumeCreator.java
index b8bc52711..2fac66b33 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/pv/MinikubePersistentVolumeCreator.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/pv/MinikubePersistentVolumeCreator.java
@@ -64,8 +64,7 @@ public Optional createAndApplyPersistentVolume(String correlat
e);
return Optional.empty();
}
- return client.persistentVolumesClient().loadAndCreate(correlationId, persistentVolumeYaml,
- volume -> volume.addOwnerReference(workspace));
+ return client.persistentVolumesClient().loadAndCreate(correlationId, persistentVolumeYaml);
}
@Override
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/replacements/DefaultDeploymentTemplateReplacements.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/replacements/DefaultDeploymentTemplateReplacements.java
index 78a3508b3..aba4a6b98 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/replacements/DefaultDeploymentTemplateReplacements.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/replacements/DefaultDeploymentTemplateReplacements.java
@@ -27,6 +27,7 @@
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+import org.eclipse.theia.cloud.common.util.DataBridgeUtil;
import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
import org.eclipse.theia.cloud.operator.ingress.IngressPathProvider;
import org.eclipse.theia.cloud.operator.util.TheiaCloudConfigMapUtil;
@@ -49,6 +50,8 @@ public class DefaultDeploymentTemplateReplacements implements DeploymentTemplate
public static final String PLACEHOLDER_MEMORY_REQUESTS = "placeholder-memory-requests";
public static final String PLACEHOLDER_UID = "placeholder-uid";
+ public static final String PLACEHOLDER_ENV_SERVICE_AUTH_TOKEN = "placeholder-env-service-auth-token";
+ /** @deprecated Use PLACEHOLDER_ENV_SERVICE_AUTH_TOKEN instead */
public static final String PLACEHOLDER_ENV_APP_ID = "placeholder-env-app-id";
public static final String PLACEHOLDER_ENV_SERVICE_URL = "placeholder-env-service-url";
public static final String PLACEHOLDER_ENV_SESSION_UID = "placeholder-env-session-uid";
@@ -65,6 +68,14 @@ public class DefaultDeploymentTemplateReplacements implements DeploymentTemplate
public static final String PLACEHOLDER_ENABLE_ACTIVITY_TRACKER = "placeholder-enable-activity-tracker";
public static final String PLACEHOLDER_OAUTH2_PROXY_VERSION = "placeholder-oauth2-proxy-version";
+ public static final String PLACEHOLDER_ENV_BUILD_CACHE_ENABLED = "placeholder-build-cache-enabled";
+ public static final String PLACEHOLDER_ENV_BUILD_CACHE_URL = "placeholder-build-cache-url";
+ public static final String PLACEHOLDER_ENV_BAZEL_BUILD_CACHE_URL = "placeholder-bazel-build-cache-url";
+ public static final String PLACEHOLDER_ENV_BUILD_CACHE_PUSH = "placeholder-build-cache-push";
+
+ public static final String PLACEHOLDER_ENV_DEPENDENCY_CACHE_ENABLED = "placeholder-dependency-cache-enabled";
+ public static final String PLACEHOLDER_ENV_DEPENDENCY_CACHE_URL = "placeholder-dependency-cache-url";
+
protected static final String DEFAULT_UID = "1000";
@Inject
@@ -126,7 +137,9 @@ protected Map getEnvironmentVariables(AppDefinition appDefinitio
protected Map getEnvironmentVariables(AppDefinition appDefinition, Optional session) {
Map environmentVariables = new LinkedHashMap();
- environmentVariables.put(PLACEHOLDER_ENV_APP_ID, arguments.getAppId());
+ // Set both new and old placeholders for backwards compatibility
+ environmentVariables.put(PLACEHOLDER_ENV_SERVICE_AUTH_TOKEN, arguments.getServiceAuthToken());
+ environmentVariables.put(PLACEHOLDER_ENV_APP_ID, arguments.getServiceAuthToken());
environmentVariables.put(PLACEHOLDER_ENV_SERVICE_URL, arguments.getServiceUrl());
environmentVariables.put(PLACEHOLDER_ENV_SESSION_UID, session.map(s -> s.getMetadata().getUid()).orElse(""));
environmentVariables.put(PLACEHOLDER_ENV_SESSION_NAME, session.map(s -> s.getSpec().getName()).orElse(""));
@@ -145,6 +158,36 @@ protected Map getEnvironmentVariables(AppDefinition appDefinitio
environmentVariables.put(PLACEHOLDER_ENV_SESSION_KEYCLOAK_CLIENT_ID, "");
}
+ boolean gradleCacheActive = arguments.isEnableBuildCaching() && arguments.getBuildCacheUrl() != null
+ && !arguments.getBuildCacheUrl().trim().isEmpty();
+ boolean bazelCacheActive = arguments.isEnableBuildCaching() && arguments.getBazelBuildCacheUrl() != null
+ && !arguments.getBazelBuildCacheUrl().trim().isEmpty();
+
+ if (gradleCacheActive || bazelCacheActive) {
+ environmentVariables.put(PLACEHOLDER_ENV_BUILD_CACHE_ENABLED, "true");
+ environmentVariables.put(PLACEHOLDER_ENV_BUILD_CACHE_URL,
+ gradleCacheActive ? arguments.getBuildCacheUrl().trim() : "");
+ environmentVariables.put(PLACEHOLDER_ENV_BAZEL_BUILD_CACHE_URL,
+ bazelCacheActive ? arguments.getBazelBuildCacheUrl().trim() : "");
+ environmentVariables.put(PLACEHOLDER_ENV_BUILD_CACHE_PUSH,
+ arguments.isEnableBuildCachePush() ? "true" : "false");
+ } else {
+ environmentVariables.put(PLACEHOLDER_ENV_BUILD_CACHE_ENABLED, "false");
+ environmentVariables.put(PLACEHOLDER_ENV_BUILD_CACHE_URL, "");
+ environmentVariables.put(PLACEHOLDER_ENV_BAZEL_BUILD_CACHE_URL, "");
+ environmentVariables.put(PLACEHOLDER_ENV_BUILD_CACHE_PUSH, "false");
+ }
+
+ if (arguments.isEnableDependencyCaching() && arguments.getDependencyCacheUrl() != null
+ && !arguments.getDependencyCacheUrl().trim().isEmpty()) {
+ environmentVariables.put(PLACEHOLDER_ENV_DEPENDENCY_CACHE_ENABLED, "true");
+ environmentVariables.put(PLACEHOLDER_ENV_DEPENDENCY_CACHE_URL,
+ arguments.getDependencyCacheUrl().trim());
+ } else {
+ environmentVariables.put(PLACEHOLDER_ENV_DEPENDENCY_CACHE_ENABLED, "false");
+ environmentVariables.put(PLACEHOLDER_ENV_DEPENDENCY_CACHE_URL, "");
+ }
+
if (arguments.isEnableMonitor()) {
if (appDefinition.getSpec().getMonitor() != null && appDefinition.getSpec().getMonitor().getPort() > 0) {
String port = String.valueOf(appDefinition.getSpec().getMonitor().getPort());
@@ -165,6 +208,35 @@ protected Map getEnvironmentVariables(AppDefinition appDefinitio
} else {
environmentVariables.put(TheiaCloudHandlerUtil.PLACEHOLDER_MONITOR_PORT, "");
}
+
+ // Handle data bridge port and strategy
+ // Data bridge is only enabled for eager sessions (session.isEmpty()).
+ // For lazy sessions, environment variables are set directly on the session resource.
+ boolean dataBridgeEnabled = DataBridgeUtil.isDataBridgeEnabled(appDefinition.getSpec()) && session.isEmpty();
+ if (!dataBridgeEnabled) {
+ environmentVariables.put(TheiaCloudHandlerUtil.PLACEHOLDER_DATA_BRIDGE_CONTAINER_PORT, "");
+ environmentVariables.put(TheiaCloudHandlerUtil.PLACEHOLDER_DATA_BRIDGE_ENV_PORT, "");
+ environmentVariables.put(TheiaCloudHandlerUtil.PLACEHOLDER_DATA_BRIDGE_ENABLED, "0");
+ environmentVariables.put(TheiaCloudHandlerUtil.PLACEHOLDER_DATA_BRIDGE_STRATEGY, "");
+ } else {
+ int portNumber = DataBridgeUtil.getDataBridgePort(appDefinition.getSpec());
+ if (portNumber == appDefinition.getSpec().getPort() || (appDefinition.getSpec().getMonitor() != null
+ && portNumber == appDefinition.getSpec().getMonitor().getPort())) {
+ // Just remove the placeholder, otherwise the port would be duplicate
+ environmentVariables.put(TheiaCloudHandlerUtil.PLACEHOLDER_DATA_BRIDGE_CONTAINER_PORT, "");
+ environmentVariables.put(TheiaCloudHandlerUtil.PLACEHOLDER_DATA_BRIDGE_ENV_PORT, "");
+ } else {
+ // Replace the placeholder with the port information
+ environmentVariables.put(TheiaCloudHandlerUtil.PLACEHOLDER_DATA_BRIDGE_CONTAINER_PORT,
+ "- containerPort: " + portNumber + "\n" + " name: data-bridge");
+ // Set the environment variable value
+ environmentVariables.put(TheiaCloudHandlerUtil.PLACEHOLDER_DATA_BRIDGE_ENV_PORT,
+ String.valueOf(portNumber));
+ }
+ environmentVariables.put(TheiaCloudHandlerUtil.PLACEHOLDER_DATA_BRIDGE_ENABLED, "1");
+ environmentVariables.put(TheiaCloudHandlerUtil.PLACEHOLDER_DATA_BRIDGE_STRATEGY, "data-bridge");
+ }
+
return environmentVariables;
}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/replacements/DefaultPersistentVolumeTemplateReplacements.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/replacements/DefaultPersistentVolumeTemplateReplacements.java
index 8bc019a78..74022f53f 100644
--- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/replacements/DefaultPersistentVolumeTemplateReplacements.java
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/replacements/DefaultPersistentVolumeTemplateReplacements.java
@@ -18,6 +18,7 @@
import java.util.LinkedHashMap;
import java.util.Map;
+import org.eclipse.theia.cloud.common.k8s.client.TheiaCloudClient;
import org.eclipse.theia.cloud.common.k8s.resource.workspace.Workspace;
import org.eclipse.theia.cloud.common.util.WorkspaceUtil;
import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
@@ -31,12 +32,18 @@ public class DefaultPersistentVolumeTemplateReplacements implements PersistentVo
public static final String PLACEHOLDER_LABEL_WORKSPACE_NAME = "placeholder-label-workspace-name";
public static final String PLACEHOLDER_STORAGE_CLASS_NAME = "placeholder-storage-class-name";
public static final String PLACEHOLDER_REQUESTED_STORAGE = "placeholder-requested-storage";
+ public static final String PLACEHOLDER_ACCESS_MODE = "placeholder-access-mode";
public static final String DEFAULT_REQUESTED_STORAGE = "250Mi";
+ public static final String READ_WRITE_ONCE = "ReadWriteOnce";
+ public static final String READ_WRITE_MANY = "ReadWriteMany";
@Inject
TheiaCloudOperatorArguments arguments;
+ @Inject
+ TheiaCloudClient client;
+
@Override
public Map getPersistentVolumeReplacements(String namespace, Workspace workspace) {
Map replacements = new LinkedHashMap();
@@ -57,7 +64,20 @@ public Map getPersistentVolumeClaimReplacements(String namespace
replacements.put(PLACEHOLDER_STORAGE_CLASS_NAME, orEmpty(arguments.getStorageClassName()));
replacements.put(PLACEHOLDER_REQUESTED_STORAGE,
orDefault(arguments.getRequestedStorage(), DEFAULT_REQUESTED_STORAGE));
+ replacements.put(PLACEHOLDER_ACCESS_MODE, getAccessMode(workspace));
return replacements;
}
+ protected String getAccessMode(Workspace workspace) {
+ String appDefinitionName = workspace.getSpec().getAppDefinition();
+ if (appDefinitionName == null || appDefinitionName.isBlank()) {
+ return READ_WRITE_ONCE;
+ }
+
+ return client.appDefinitions().get(appDefinitionName)
+ .filter(appDef -> appDef.getSpec() != null && appDef.getSpec().requiresSharedWorkspace())
+ .map(appDef -> READ_WRITE_MANY)
+ .orElse(READ_WRITE_ONCE);
+ }
+
}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/sidecar/SidecarConfig.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/sidecar/SidecarConfig.java
new file mode 100644
index 000000000..ae0e474ca
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/sidecar/SidecarConfig.java
@@ -0,0 +1,108 @@
+/********************************************************************************
+ * Copyright (C) 2025 EclipseSource and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+package org.eclipse.theia.cloud.operator.sidecar;
+
+import java.util.List;
+
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.SidecarSpec;
+
+/**
+ * Immutable configuration for a single sidecar container.
+ * Derived from {@link SidecarSpec} (v1beta11 CRD).
+ */
+public record SidecarConfig(
+ String name,
+ String image,
+ int containerPort,
+ List languages,
+ String cpuLimit,
+ String memoryLimit,
+ String cpuRequest,
+ String memoryRequest,
+ boolean mountWorkspace
+) {
+
+ /** Default port all sidecar containers should use internally. */
+ public static final int DEFAULT_PORT = 5000;
+
+ /**
+ * Creates a SidecarConfig from a v1beta11 {@link SidecarSpec}, applying defaults for missing fields.
+ */
+ public static SidecarConfig fromSpec(SidecarSpec spec) {
+ return new SidecarConfig(
+ spec.getName(),
+ spec.getImage(),
+ spec.getPort() > 0 ? spec.getPort() : DEFAULT_PORT,
+ spec.getLanguages() != null ? spec.getLanguages() : List.of(),
+ spec.getCpuLimit() != null && !spec.getCpuLimit().isBlank() ? spec.getCpuLimit() : "500m",
+ spec.getMemoryLimit() != null && !spec.getMemoryLimit().isBlank() ? spec.getMemoryLimit() : "1Gi",
+ spec.getCpuRequest() != null && !spec.getCpuRequest().isBlank() ? spec.getCpuRequest() : "100m",
+ spec.getMemoryRequest() != null && !spec.getMemoryRequest().isBlank() ? spec.getMemoryRequest() : "256Mi",
+ spec.isMountWorkspace()
+ );
+ }
+
+ /**
+ * Returns the environment variable name for this sidecar's host.
+ * Example: sidecar name "lang-java" → "SIDECAR_LANG_JAVA_HOST"
+ */
+ public String hostEnvVar() {
+ return "SIDECAR_" + name.toUpperCase().replace("-", "_") + "_HOST";
+ }
+
+ /**
+ * Returns the environment variable name for this sidecar's port.
+ * Example: sidecar name "lang-java" → "SIDECAR_LANG_JAVA_PORT"
+ */
+ public String portEnvVar() {
+ return "SIDECAR_" + name.toUpperCase().replace("-", "_") + "_PORT";
+ }
+
+ /**
+ * Checks if the given AppDefinition has any sidecar configuration
+ * (v1beta11 sidecars list).
+ */
+ public static boolean hasSidecars(AppDefinition appDef) {
+ if (appDef == null || appDef.getSpec() == null) {
+ return false;
+ }
+
+ return appDef.getSpec().hasSidecars();
+ }
+
+ /**
+ * Checks if the given AppDefinition requires a shared workspace PVC.
+ */
+ public static boolean requiresSharedWorkspace(AppDefinition appDef) {
+ if (appDef == null || appDef.getSpec() == null) {
+ return false;
+ }
+
+ return appDef.getSpec().requiresSharedWorkspace();
+ }
+
+ /**
+ * Extracts the list of SidecarConfigs from an AppDefinition.
+ * Uses spec.sidecars only.
+ */
+ public static List getSidecarConfigs(AppDefinition appDef) {
+ if (appDef == null || appDef.getSpec() == null) {
+ return List.of();
+ }
+
+ List sidecars = appDef.getSpec().getSidecars();
+ if (sidecars != null && !sidecars.isEmpty()) {
+ return sidecars.stream().map(SidecarConfig::fromSpec).toList();
+ }
+
+ return List.of();
+ }
+}
diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/sidecar/SidecarManager.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/sidecar/SidecarManager.java
new file mode 100644
index 000000000..553a89297
--- /dev/null
+++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/sidecar/SidecarManager.java
@@ -0,0 +1,472 @@
+/********************************************************************************
+ * Copyright (C) 2025 EclipseSource and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+package org.eclipse.theia.cloud.operator.sidecar;
+
+import static org.eclipse.theia.cloud.common.util.LogMessageUtil.formatLogMessage;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinition;
+import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinitionSpec;
+import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
+import org.eclipse.theia.cloud.common.tracing.Tracing;
+import org.eclipse.theia.cloud.operator.util.TheiaCloudPersistentVolumeUtil;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import io.fabric8.kubernetes.api.model.Container;
+import io.fabric8.kubernetes.api.model.EnvVar;
+import io.fabric8.kubernetes.api.model.EnvVarBuilder;
+import io.fabric8.kubernetes.api.model.PodSpec;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.sentry.ISpan;
+import io.sentry.SpanStatus;
+
+@Singleton
+public class SidecarManager {
+
+ private static final Logger LOGGER = LogManager.getLogger(SidecarManager.class);
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ @Inject
+ private SidecarResourceFactory factory;
+
+ /**
+ * Returns true if the given AppDefinition has sidecar configuration.
+ */
+ public boolean hasSidecars(AppDefinition appDef) {
+ return SidecarConfig.hasSidecars(appDef);
+ }
+
+ /**
+ * Returns the list of sidecar configurations for the given AppDefinition.
+ */
+ public List getSidecarConfigs(AppDefinition appDef) {
+ return SidecarConfig.getSidecarConfigs(appDef);
+ }
+
+ /**
+ * Validates that all sidecar names are unique within the AppDefinition.
+ *
+ * @return true if valid or no sidecars configured, false when duplicates are detected
+ */
+ public boolean validateUniqueSidecarNames(AppDefinition appDef, String correlationId) {
+ List configs = getSidecarConfigs(appDef);
+ if (configs.isEmpty()) {
+ return true;
+ }
+
+ Set seen = new HashSet<>();
+ Set duplicates = new LinkedHashSet<>();
+ for (SidecarConfig config : configs) {
+ if (!seen.add(config.name())) {
+ duplicates.add(config.name());
+ }
+ }
+
+ if (!duplicates.isEmpty()) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "[Sidecar] Duplicate sidecar names in AppDefinition '" + appDef.getSpec().getName()
+ + "': " + duplicates + ". Sidecar names must be unique."));
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Creates all sidecar resources (Service + Deployment per sidecar) for a lazy session.
+ * On failure of any sidecar, rolls back all previously created sidecars for this session.
+ *
+ * @return true if all sidecars were created (or none were needed), false on any failure
+ */
+ public boolean createSidecars(
+ Session session,
+ AppDefinition appDef,
+ Optional pvcName,
+ String correlationId) {
+
+ Optional> configsOpt = getValidatedSidecarConfigs(appDef, correlationId);
+ if (configsOpt.isEmpty()) {
+ return false;
+ }
+ List configs = configsOpt.get();
+ if (configs.isEmpty()) {
+ LOGGER.debug(formatLogMessage(correlationId, "[Sidecar] No sidecars configured for " + appDef.getSpec().getName()));
+ return true;
+ }
+
+ ISpan span = Tracing.childSpan("sidecar.create_all", "Create all sidecars");
+ span.setData("session", session.getMetadata().getName());
+ span.setData("sidecar_count", configs.size());
+
+ LOGGER.info(formatLogMessage(correlationId,
+ "[Sidecar] Creating " + configs.size() + " sidecar(s) for session " + session.getMetadata().getName()));
+
+ List created = new ArrayList<>();
+ try {
+ for (SidecarConfig config : configs) {
+ ISpan sidecarSpan = Tracing.childSpan(span, "sidecar.create", "Create sidecar " + config.name());
+ sidecarSpan.setData("sidecar_name", config.name());
+ sidecarSpan.setData("image", config.image());
+
+ boolean serviceOk = factory.createService(session, config, correlationId).isPresent();
+ if (!serviceOk) {
+ sidecarSpan.setTag("outcome", "service_failed");
+ Tracing.finish(sidecarSpan, SpanStatus.INTERNAL_ERROR);
+ throw new RuntimeException("Failed to create sidecar service for " + config.name());
+ }
+
+ boolean deploymentOk = factory.createDeployment(session, config, pvcName, appDef.getSpec(), correlationId).isPresent();
+ if (!deploymentOk) {
+ sidecarSpan.setTag("outcome", "deployment_failed");
+ Tracing.finish(sidecarSpan, SpanStatus.INTERNAL_ERROR);
+ throw new RuntimeException("Failed to create sidecar deployment for " + config.name());
+ }
+
+ Tracing.finishSuccess(sidecarSpan);
+ created.add(config);
+ }
+
+ Tracing.finishSuccess(span);
+ LOGGER.info(formatLogMessage(correlationId, "[Sidecar] Successfully created all sidecars"));
+ return true;
+
+ } catch (Exception e) {
+ LOGGER.error(formatLogMessage(correlationId,
+ "[Sidecar] Error creating sidecars, rolling back " + created.size() + " created sidecar(s)"), e);
+
+ // Rollback: delete all resources created so far
+ try {
+ factory.deleteResources(session, correlationId);
+ } catch (Exception rollbackEx) {
+ LOGGER.error(formatLogMessage(correlationId, "[Sidecar] Rollback failed"), rollbackEx);
+ }
+
+ Tracing.finishError(span, e);
+ return false;
+ }
+ }
+
+ /**
+ * Injects sidecar environment variables into a Theia deployment that is being created.
+ * Adds per-sidecar HOST/PORT env vars plus a SIDECAR_CONFIG JSON array.
+ *
+ * Called at Theia deployment creation time (before the deployment is submitted to K8s),
+ * so the Theia pod starts with env vars already set — no restart needed.
+ */
+ public void injectSidecarEnvVars(
+ Session session,
+ Deployment theiaDeployment,
+ AppDefinition appDef,
+ String correlationId) {
+
+ Optional> configsOpt = getValidatedSidecarConfigs(appDef, correlationId);
+ if (configsOpt.isEmpty()) {
+ return;
+ }
+ List configs = configsOpt.get();
+ if (configs.isEmpty()) {
+ LOGGER.debug(formatLogMessage(correlationId, "[Sidecar] No sidecars configured, skipping env var injection"));
+ return;
+ }
+
+ ISpan span = Tracing.childSpan("sidecar.inject_env", "Inject sidecar env vars");
+ span.setData("sidecar_count", configs.size());
+
+ try {
+ AppDefinitionSpec appDefSpec = appDef.getSpec();
+ PodSpec podSpec = theiaDeployment.getSpec().getTemplate().getSpec();
+ Container container = TheiaCloudPersistentVolumeUtil.getTheiaContainer(podSpec, appDefSpec);
+
+ if (container == null) {
+ LOGGER.error(formatLogMessage(correlationId, "[Sidecar] Could not find Theia container in deployment"));
+ span.setTag("outcome", "container_not_found");
+ Tracing.finish(span, SpanStatus.NOT_FOUND);
+ return;
+ }
+
+ List envVars = container.getEnv();
+ if (envVars == null) {
+ envVars = new ArrayList<>();
+ container.setEnv(envVars);
+ }
+
+ for (SidecarConfig config : configs) {
+ String serviceName = SidecarResourceFactory.getLazyResourceName(session, config);
+ envVars.add(new EnvVarBuilder()
+ .withName(config.hostEnvVar())
+ .withValue(serviceName)
+ .build());
+ envVars.add(new EnvVarBuilder()
+ .withName(config.portEnvVar())
+ .withValue(String.valueOf(config.containerPort()))
+ .build());
+ }
+
+ String configJson = serializeSidecarConfigJson(configs,
+ config -> SidecarResourceFactory.getLazyResourceName(session, config));
+
+ envVars.add(new EnvVarBuilder()
+ .withName("SIDECAR_CONFIG")
+ .withValue(configJson)
+ .build());
+
+ Tracing.finishSuccess(span);
+ LOGGER.info(formatLogMessage(correlationId,
+ "[Sidecar] Injected env vars for " + configs.size() + " sidecar(s)"));
+
+ } catch (Exception e) {
+ LOGGER.error(formatLogMessage(correlationId, "[Sidecar] Error injecting env vars"), e);
+ Tracing.finishError(span, e);
+ }
+ }
+
+ /**
+ * Injects sidecar env vars for a prewarmed (eager) deployment.
+ * Service names are deterministic from appDef + instanceId + sidecar name.
+ */
+ public void injectPrewarmedSidecarEnvVars(
+ Deployment theiaDeployment,
+ AppDefinition appDef,
+ int instanceId,
+ String correlationId) {
+
+ Optional> configsOpt = getValidatedSidecarConfigs(appDef, correlationId);
+ if (configsOpt.isEmpty()) {
+ return;
+ }
+ List configs = configsOpt.get();
+ if (configs.isEmpty()) {
+ return;
+ }
+
+ ISpan span = Tracing.childSpan("sidecar.inject_prewarmed_env", "Inject prewarmed sidecar env vars");
+
+ try {
+ AppDefinitionSpec appDefSpec = appDef.getSpec();
+ PodSpec podSpec = theiaDeployment.getSpec().getTemplate().getSpec();
+ Container container = TheiaCloudPersistentVolumeUtil.getTheiaContainer(podSpec, appDefSpec);
+
+ if (container == null) {
+ LOGGER.error(formatLogMessage(correlationId, "[Sidecar] Could not find Theia container in prewarmed deployment"));
+ Tracing.finish(span, SpanStatus.NOT_FOUND);
+ return;
+ }
+
+ List envVars = container.getEnv();
+ if (envVars == null) {
+ envVars = new ArrayList<>();
+ container.setEnv(envVars);
+ }
+
+ for (SidecarConfig config : configs) {
+ String serviceName = SidecarResourceFactory.getPrewarmedResourceName(appDef, instanceId, config);
+
+ envVars.add(new EnvVarBuilder()
+ .withName(config.hostEnvVar())
+ .withValue(serviceName)
+ .build());
+ envVars.add(new EnvVarBuilder()
+ .withName(config.portEnvVar())
+ .withValue(String.valueOf(config.containerPort()))
+ .build());
+ }
+
+ String configJson = serializeSidecarConfigJson(configs,
+ config -> SidecarResourceFactory.getPrewarmedResourceName(appDef, instanceId, config));
+
+ envVars.add(new EnvVarBuilder()
+ .withName("SIDECAR_CONFIG")
+ .withValue(configJson)
+ .build());
+
+ Tracing.finishSuccess(span);
+
+ } catch (Exception e) {
+ LOGGER.error(formatLogMessage(correlationId, "[Sidecar] Error injecting prewarmed env vars"), e);
+ Tracing.finishError(span, e);
+ }
+ }
+
+ /**
+ * Deletes all sidecar resources for a lazy session.
+ */
+ public void deleteSidecars(Session session, String correlationId) {
+ factory.deleteResources(session, correlationId);
+ }
+
+ /**
+ * Deletes all prewarmed sidecar resources for a specific instance of an AppDefinition.
+ */
+ public void deletePrewarmedSidecars(AppDefinition appDef, int instanceId, String correlationId) {
+ Optional> configsOpt = getValidatedSidecarConfigs(appDef, correlationId);
+ if (configsOpt.isEmpty()) {
+ return;
+ }
+ List configs = configsOpt.get();
+ for (SidecarConfig config : configs) {
+ factory.deletePrewarmedResources(appDef, instanceId, config, correlationId);
+ }
+ }
+
+ /**
+ * Restarts sidecar pods for a prewarmed instance WITHOUT deleting the Deployment or Service.
+ * Deletes the Pod(s) managed by each sidecar Deployment, allowing the Deployment controller
+ * to recreate them with a fresh state. Used during session release so the sidecar
+ * infrastructure stays in place for pool reuse.
+ */
+ public void restartPrewarmedSidecarPods(AppDefinition appDef, int instanceId, String correlationId) {
+ List configs = getSidecarConfigs(appDef);
+ if (configs.isEmpty()) {
+ return;
+ }
+
+ ISpan span = Tracing.childSpan("sidecar.restart_prewarmed_pods", "Restart prewarmed sidecar pods");
+ span.setData("instance_id", instanceId);
+ span.setData("sidecar_count", configs.size());
+
+ LOGGER.info(formatLogMessage(correlationId,
+ "[Sidecar] Restarting " + configs.size() + " sidecar pod(s) for instance " + instanceId));
+
+ for (SidecarConfig config : configs) {
+ String resourceName = SidecarResourceFactory.getPrewarmedResourceName(appDef, instanceId, config);
+ factory.deletePodsForDeployment(resourceName, correlationId);
+ }
+
+ Tracing.finishSuccess(span);
+ }
+
+ /**
+ * Creates prewarmed sidecar resources (Service + Deployment) for a single pool instance.
+ * Called during pool creation / reconciliation.
+ */
+ public boolean createPrewarmedSidecars(
+ AppDefinition appDef,
+ int instanceId,
+ Optional pvcName,
+ Map additionalLabels,
+ String correlationId) {
+
+ Optional> configsOpt = getValidatedSidecarConfigs(appDef, correlationId);
+ if (configsOpt.isEmpty()) {
+ return false;
+ }
+ List configs = configsOpt.get();
+ if (configs.isEmpty()) {
+ return true;
+ }
+
+ ISpan span = Tracing.childSpan("sidecar.create_prewarmed", "Create prewarmed sidecars");
+ span.setData("instance_id", instanceId);
+ span.setData("sidecar_count", configs.size());
+
+ boolean success = true;
+ for (SidecarConfig config : configs) {
+ success &= factory.createPrewarmedService(appDef, instanceId, config, additionalLabels, correlationId).isPresent();
+ success &= factory.createPrewarmedDeployment(appDef, instanceId, config, pvcName, additionalLabels, correlationId).isPresent();
+ }
+
+ span.setTag("outcome", success ? "success" : "failure");
+ Tracing.finish(span, success ? SpanStatus.OK : SpanStatus.INTERNAL_ERROR);
+ return success;
+ }
+
+ /**
+ * Ensures prewarmed sidecar capacity exists for the given number of instances.
+ */
+ public boolean ensurePrewarmedSidecarCapacity(
+ AppDefinition appDef,
+ int minInstances,
+ Map labels,
+ String correlationId) {
+
+ Optional> configsOpt = getValidatedSidecarConfigs(appDef, correlationId);
+ if (configsOpt.isEmpty()) {
+ return false;
+ }
+ List configs = configsOpt.get();
+ if (configs.isEmpty()) {
+ return true;
+ }
+
+ boolean success = true;
+ for (int instance = 1; instance <= minInstances; instance++) {
+ for (SidecarConfig config : configs) {
+ String resourceName = SidecarResourceFactory.getPrewarmedResourceName(appDef, instance, config);
+
+ var kube = factory.getClient().kubernetes();
+ String namespace = factory.getClient().namespace();
+
+ boolean serviceExists = kube.services().inNamespace(namespace).withName(resourceName).get() != null;
+ boolean deploymentExists = kube.apps().deployments().inNamespace(namespace).withName(resourceName)
+ .get() != null;
+
+ Optional pvcName = factory.lookupExistingPvc(appDef, instance);
+
+ if (!serviceExists) {
+ success &= factory.createPrewarmedService(appDef, instance, config, labels, correlationId)
+ .isPresent();
+ }
+ if (!deploymentExists) {
+ success &= factory.createPrewarmedDeployment(appDef, instance, config, pvcName, labels,
+ correlationId).isPresent();
+ }
+ }
+ }
+ return success;
+ }
+
+ /**
+ * Reconciles prewarmed sidecar resources for the given target count.
+ */
+ public boolean reconcilePrewarmedSidecars(
+ AppDefinition appDef,
+ int targetInstances,
+ Map labels,
+ String correlationId) {
+ // Same logic as ensureCapacity — create any missing
+ return ensurePrewarmedSidecarCapacity(appDef, targetInstances, labels, correlationId);
+ }
+
+ private Optional> getValidatedSidecarConfigs(AppDefinition appDef, String correlationId) {
+ if (!validateUniqueSidecarNames(appDef, correlationId)) {
+ return Optional.empty();
+ }
+ return Optional.of(getSidecarConfigs(appDef));
+ }
+
+ private String serializeSidecarConfigJson(List configs,
+ Function serviceNameResolver) throws JsonProcessingException {
+ List