diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index b4eb094389..ff9bd2a6c4 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -2,110 +2,57 @@ env: RETENTION_DAYS: "10" steps: - - label: ":white_check_mark: Check Shell" - key: "check-shell" - command: ./ops/check.sh shell - - - label: ":lock: Check Security" - key: "check-security" - command: ./ops/check.sh security - - - label: ":lock: Check CSS px" - key: "check-px" - command: ./ops/check.sh px - - - label: ":lock: Deny CSS hex" - key: "deny-css-hex-check" - command: ./ops/check.sh hex - - - label: ":lock: Deny CSS rgba" - key: "deny-css-rgba-check" - command: ./ops/check.sh rgba - - - label: ":lock: Check .* in backend" - key: "check-dot-star" - command: ./ops/check.sh dot-star - - - label: ":white_check_mark: Check Backend" - if: build.branch == "main" && build.message =~ /(?i)\[backend\]/ - key: "check-backend" - command: ./ops/check.sh backend - - - label: ":white_check_mark: Check Frontend" - if: build.branch == "main" && build.message =~ /(?i)\[frontend\]/ - key: "check-frontend" - command: ./ops/check.sh frontend - - - label: ":mag: Check Frontend License" - key: "check-frontend-license" - commands: ./ops/check.sh frontend-license - - - label: ":mag: Check Backend License" - key: "check-backend-license" - commands: ./ops/check.sh backend-license - plugins: - - artifacts#v1.9.0: - upload: - - "backend/build/reports/dependency-license/**/*" - name: "backend-license-report" - expire_in: "${RETENTION_DAYS} days" - - label: ":cloudformation: Deploy infra" if: build.branch == "main" && build.message =~ /(?i)\[infra\]/ key: "deploy-infra" - depends_on: - - "check-shell" - - "check-security" - - "check-frontend" - - "check-px" - - deny-css-rgba-check - - deny-css-hex-check - - "check-backend" - - "check-frontend-license" - - "check-backend-license" env: AWSHost: "$AWS_HOST" AWSAccountId: "$AWS_ACCOUNT_ID" AWSRegion: "$AWS_REGION" command: ./ops/deploy.sh infra + - label: ":white_check_mark: GitHub Basic Check" + if: build.branch == "main" + key: "check-github-basic" + command: ./ops/check.sh github-basic-passed + env: + COMMIT_SHA: "$BUILDKITE_COMMIT" + GITHUB_TOKEN: "$E2E_TOKEN_GITHUB" + BRANCH: "$BUILDKITE_BRANCH" + depends_on: + - "deploy-infra" + - label: ":react: Build Frontend" if: build.branch == "main" && build.message =~ /(?i)\[frontend\]/ key: "build-frontend" - depends_on: "deploy-infra" + depends_on: + - "check-github-basic" command: ./ops/build.sh frontend - label: ":java: Build Backend" if: build.branch == "main" && build.message =~ /(?i)\[backend\]/ key: "build-backend" - depends_on: "deploy-infra" + depends_on: + - "check-github-basic" command: ./ops/build.sh backend - label: ":rocket: Deploy e2e" - if: build.branch == "main" && (build.message =~ /(?i)\[frontend\]/ || build.message =~ /(?i)\[backend\]/) + if: build.branch == "main" key: "deploy-e2e" depends_on: - "build-frontend" - "build-backend" + - "check-github-basic" command: ./ops/deploy.sh e2e - label: ":rocket: Run e2e" - branches: main + if: build.branch == "main" key: "check-e2e" depends_on: - "deploy-e2e" - - "check-shell" - - "check-security" - - "check-frontend" - - "check-px" - - deny-css-rgba-check - - deny-css-hex-check - - "check-backend" - - "check-frontend-license" - - "check-backend-license" command: ./ops/check.sh e2e-container plugins: - - artifacts#v1.9.0: + - artifacts#v1.9.3: upload: "./e2e-reports.tar.gz" expire_in: "${RETENTION_DAYS} days" diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 46b1c6c1a6..969401ce55 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -6,6 +6,8 @@ body: - type: markdown attributes: value: | + ## Request Detail + The issue list is reserved exclusively for bug reports and feature requests. For usage questions, please use the following resources: @@ -54,8 +56,51 @@ body: description: What tools will support your request feature? multiple: true options: - - Board - - Pipeline Tool - - Source Control + - Board (like Jira) + - Pipeline Tool (like buildkite) + - Source Control (like github) + validations: + required: true + + - type: markdown + attributes: + value: | + ## Account Detail + + Let's know more about you and your account. We will horizontally evaluate all received requests to adjust the priority. + + **Below information are important in terms of prioritization.** + + - type: input + id: account_info + attributes: + label: Account name + description: What's your account name? + placeholder: Make sure it could be found in jigsaw + validations: + required: true + + - type: input + id: account_location + attributes: + label: Account location + description: Which country you account locate at? + validations: + required: true + + - type: input + id: account_size + attributes: + label: Teams in Account + description: How many teams will adopt heartbeat after feature release? + validations: + required: true + + - type: input + id: expected_date + attributes: + label: Expected launch date + description: What is the latest possible launch date you can accept? + placeholder: 2024-12 validations: - required: true \ No newline at end of file + required: false diff --git a/.github/workflows/Docs.yaml b/.github/workflows/Docs.yaml index 9507aebc86..f691316b16 100644 --- a/.github/workflows/Docs.yaml +++ b/.github/workflows/Docs.yaml @@ -28,7 +28,7 @@ jobs: - name: Build docs run: pnpm run build - name: Deploy to github pages - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./docs/dist diff --git a/.github/workflows/Release.yaml b/.github/workflows/Release.yaml index 7314fcb29d..4319387ab5 100644 --- a/.github/workflows/Release.yaml +++ b/.github/workflows/Release.yaml @@ -24,7 +24,7 @@ jobs: - name: Validate Gradle wrapper uses: gradle/wrapper-validation-action@v2 - name: Set up Gradle - uses: gradle/gradle-build-action@v3.1.0 + uses: gradle/gradle-build-action@v3.2.1 - name: Build run: ./gradlew clean build - uses: actions/upload-artifact@v4 @@ -91,10 +91,23 @@ jobs: tags: | ghcr.io/${{ env.LOWCASE_REPO_NAME }}_backend:${{ env.TAG_NAME }} ghcr.io/${{ env.LOWCASE_REPO_NAME }}_backend:latest - release: + + build-sbom: runs-on: ubuntu-latest needs: - build_and_push_image + steps: + - uses: actions/checkout@v4 + - uses: anchore/sbom-action@v0 + with: + path: ./ + artifact-name: ${{ env.REPO_NAME }}.${{ env.TAG_NAME }}.sbom.spdx.json + - uses: anchore/sbom-action/publish-sbom@v0 + + release: + runs-on: ubuntu-latest + needs: + - build-sbom steps: - uses: actions/checkout@v4 - name: Download frontend artifact @@ -119,7 +132,7 @@ jobs: ls echo "TAG_NAME=$(git tag --sort version:refname | tail -n 1)" >> "$GITHUB_ENV" - name: Upload zip file - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: files: ${{ env.REPO_NAME }}-${{ env.TAG_NAME }}.zip diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 8b30ba63a4..9fd7814e0d 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -91,7 +91,7 @@ jobs: - name: Validate Gradle wrapper uses: gradle/wrapper-validation-action@v2 - name: Set up Gradle - uses: gradle/gradle-build-action@v3.1.0 + uses: gradle/gradle-build-action@v3.2.1 - name: Test and check run: ./gradlew clean check - name: Build @@ -120,7 +120,7 @@ jobs: - name: Validate Gradle wrapper uses: gradle/wrapper-validation-action@v2 - name: Set up Gradle - uses: gradle/gradle-build-action@v3.1.0 + uses: gradle/gradle-build-action@v3.2.1 - name: License check run: ./gradlew clean checkLicense - uses: actions/upload-artifact@v4 @@ -228,21 +228,23 @@ jobs: run: | ./ops/check.sh frontend-license - # check-buildkite-status: - # if: ${{ github.event_name == 'pull_request' }} - # runs-on: ubuntu-latest - # steps: - # - name: Checkout code - # uses: actions/checkout@v4 - # - # - name: Check BuildKite status - # run: | - # buildkite_status=$(curl -H "Authorization: Bearer ${{ secrets.BUILDKITE_TOKEN }}" "https://api.buildkite.com/v2/organizations/thoughtworks-Heartbeat/pipelines/heartbeat/builds?branch=main"| jq -r '.[0].state') - # - # if [ "$buildkite_status" != "passed" ]; then - # echo "BuildKite build failed. Cannot merge the PR." - # exit 1 - # fi + check-buildkite-status: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Check BuildKite status + env: + BUILDKITE_TOKEN: ${{ secrets.BUILDKITE_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + CURRENT_ACTOR: ${{ github.actor }} + EVENT_NAME: ${{ github.event_name }} + CURRENT_BRANCH_NAME: ${{ github.ref }} + PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }} + run: | + ./ops/check.sh buildkite-status + images-check: runs-on: ubuntu-latest steps: @@ -289,6 +291,7 @@ jobs: - credential-check - frontend-license-check - backend-license-check + - check-buildkite-status runs-on: ubuntu-latest permissions: id-token: write @@ -459,13 +462,22 @@ jobs: npm install -g pnpm - name: Set env run: echo "HOME=/root" >> $GITHUB_ENV + - name: Install shell deps + run: | + apt-get update && apt-get install -y jq + jq --version + - name: Check e2e deployment + env: + BUILDKITE_TOKEN: ${{ secrets.BUILDKITE_TOKEN }} + COMMIT_SHA: ${{ github.sha }} + run: ./ops/check.sh buildkite-e2e-deployed - name: Run E2E env: APP_ORIGIN: ${{ vars.APP_HTTP_SCHEDULE }}://${{ secrets.AWS_EC2_IP_E2E }}:${{ secrets.AWS_EC2_IP_E2E_FRONTEND_PORT }} E2E_TOKEN_JIRA: ${{ secrets.E2E_TOKEN_JIRA }} E2E_TOKEN_BUILD_KITE: ${{ secrets.E2E_TOKEN_BUILD_KITE }} E2E_TOKEN_GITHUB: ${{ secrets.E2E_TOKEN_GITHUB }} - E2E_TOKEN_FLAG_AS_BLOCK_JIRA: ${{ secrets.E2E_TOKEN_FLAG_AS_BLOCK_JIRA }} + E2E_TOKEN_PIPELINE_NO_ORG_CONFIG_BUILDKITE: ${{ secrets.E2E_TOKEN_PIPELINE_NO_ORG_CONFIG_BUILDKITE }} shell: bash {0} run: ./ops/check.sh e2e - uses: actions/upload-artifact@v4 @@ -474,6 +486,14 @@ jobs: name: playwright-report path: frontend/e2e/reports/ retention-days: 30 + - name: Slack Notification + uses: rtCamp/action-slack-notify@v2 + if: always() + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + SLACK_ICON_EMOJI: ":heart-beat:" + SLACK_COLOR: ${{ job.status }} + SLACK_USERNAME: "Heartbeat E2E Status" deploy: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 09186b0ace..d0e1e07fa9 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ /out-tsc /logs /app +/stubs/logs/* frontend/cypress/ # Only exists if Bazel was run /bazel-out @@ -51,3 +52,4 @@ volume csv gitleaks-report.json +*.sbom.spdx.json diff --git a/.gitleaksignore b/.gitleaksignore index 27eafd5816..7307666c36 100644 --- a/.gitleaksignore +++ b/.gitleaksignore @@ -19,3 +19,4 @@ e001f3e4dc70deb4638d106d2ebfab520b9a2745:docs/src/components/Header/DocSearch.ts 6cff3275f5fcff29462e33b0508359b5d619ffec:docs/src/components/Header/DocSearch.tsx:generic-api-key:54 9102192bbe6790a348e5558cefbb051caa092411:_astro/DocSearch.d9740404.js:generic-api-key:13 a3fe6c206ca324e9e5e9a0e1422fd8c72845d855:_astro/DocSearch.d5fd0ff0.js:generic-api-key:13 +cb693e0c6117cb8f383b72e4bb1c8f2635b7b041:_astro/DocSearch.E1RdsI6d.js:generic-api-key:13 diff --git a/.trivyignore b/.trivyignore index 048fbfd363..b693001686 100644 --- a/.trivyignore +++ b/.trivyignore @@ -12,3 +12,5 @@ CVE-2023-49468 CVE-2024-0553 CVE-2024-0567 CVE-2024-22201 +CVE-2024-22259 +CVE-2024-28085 diff --git a/README.md b/README.md index 2106ab99b9..4dc2aa139a 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,57 @@ -# Heartbeat Project(2023/07) +# Heartbeat Project [![Build status](https://badge.buildkite.com/62f2d9def796f9bf8d79dc67e548341b6e3e3ad07631164b07.svg)](https://buildkite.com/heartbeat-backup/heartbeat)[![Codacy Badge](https://app.codacy.com/project/badge/Grade/2e19839055d3429598b2141884496c49)](https://www.codacy.com/gh/au-heartbeat/HeartBeat/dashboard?utm_source=github.com&utm_medium=referral&utm_content=au-heartbeat/HeartBeat&utm_campaign=Badge_Grade)[![Codacy Badge](https://app.codacy.com/project/badge/Coverage/2e19839055d3429598b2141884496c49)](https://www.codacy.com/gh/au-heartbeat/HeartBeat/dashboard?utm_source=github.com&utm_medium=referral&utm_content=au-heartbeat/HeartBeat&utm_campaign=Badge_Coverage) -[![Docs](https://github.com/au-heartbeat/HeartBeat/actions/workflows/Docs.yaml/badge.svg)](https://github.com/au-heartbeat/HeartBeat/actions/workflows/Docs.yaml) [![Frontend](https://github.com/au-heartbeat/HeartBeat/actions/workflows/frontend.yml/badge.svg)](https://github.com/au-heartbeat/HeartBeat/actions/workflows/frontend.yml) [![Backend](https://github.com/au-heartbeat/HeartBeat/actions/workflows/backend.yml/badge.svg)](https://github.com/au-heartbeat/HeartBeat/actions/workflows/backend.yml) [![Security](https://github.com/au-heartbeat/HeartBeat/actions/workflows/Security.yml/badge.svg)](https://github.com/au-heartbeat/HeartBeat/actions/workflows/Security.yml) [![Build and Deploy](https://github.com/au-heartbeat/Heartbeat/actions/workflows/build-and-deploy.yml/badge.svg)](https://github.com/au-heartbeat/Heartbeat/actions/workflows/build-and-deploy.yml) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=au-heartbeat-heartbeat-frontend&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=au-heartbeat-heartbeat-frontend) +[![Frontend Coverage](https://sonarcloud.io/api/project_badges/measure?project=au-heartbeat-heartbeat-frontend&metric=coverage)](https://sonarcloud.io/summary/new_code?id=au-heartbeat-heartbeat-frontend) +[![Backend Coverage](https://sonarcloud.io/api/project_badges/measure?project=au-heartbeat-heartbeat-backend&metric=coverage)](https://sonarcloud.io/summary/new_code?id=au-heartbeat-heartbeat-backend) + +[![Docs](https://github.com/au-heartbeat/HeartBeat/actions/workflows/Docs.yaml/badge.svg)](https://github.com/au-heartbeat/HeartBeat/actions/workflows/Docs.yaml) [![Build and Deploy](https://github.com/au-heartbeat/Heartbeat/actions/workflows/build-and-deploy.yml/badge.svg)](https://github.com/au-heartbeat/Heartbeat/actions/workflows/build-and-deploy.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![FOSSA Status](https://app.fossa.com/api/projects/custom%2B23211%2Fgithub.com%2Fau-heartbeat%2FHeartbeat.svg?type=large)](https://app.fossa.com/projects/custom%2B23211%2Fgithub.com%2Fau-heartbeat%2FHeartbeat?ref=badge_large) -- [Heartbeat Project(2023/07)](#heartbeat-project202307) +- [Heartbeat Project](#heartbeat-project) - [News](#news) - [1 About Heartbeat](#1-about-heartbeat) - [2 Support tools](#2-support-tools) - [3 Product Features](#3-product-features) - - [3.1 Config project info](#31-config-project-info) - [3.1.1 Config Board/Pipeline/Source data](#311-config-boardpipelinesource-data) - [3.1.2 Config search data](#312-config-search-data) + - [3.1.2.1 Date picker validation rules](#3121-date-picker-validation-rules) - [3.1.3 Config project account](#313-config-project-account) + - [3.1.3.1 Guideline for generating Jira token](#3131-guideline-for-generating-jira-token) + - [3.1.3.2 Guideline for generating Buildkite token](#3132-guideline-for-generating-buildkite-token) + - [3.1.3.3 Guideline for generating GitHub token](#3133-guideline-for-generating-github-token) + - [3.1.3.4 Authorize GitHub token with correct organization](#3134-authorize-github-token-with-correct-organization) - [3.2 Config Metrics data](#32-config-metrics-data) - - [3.2.1 Config Crews/Cycle Time](#321-config-crewscycle-time) + - [3.2.1 Config Crews/Board Mappings](#321-config-crewsboard-mappings) - [3.2.2 Setting Classification](#322-setting-classification) - - [3.2.3 Setting advanced settings](#323-setting-advanced-setting) - - [3.2.4 Pipeline configuration](#324-pipeline-configuration) + - [3.2.3 Rework times Setting](#323-rework-times-setting) + - [3.2.4 Setting advanced Setting](#324-setting-advanced-setting) + - [3.2.5 Pipeline configuration](#325-pipeline-configuration) - [3.3 Export and import config info](#33-export-and-import-config-info) - [3.3.1 Export Config Json File](#331-export-config-json-file) - [3.3.2 Import Config Json File](#332-import-config-json-file) - - [3.4 Generate Metrics Data](#34-generate-metrics-data) + - [3.4 Generate Metrics report](#34-generate-metrics-report) - [3.4.1 Velocity](#341-velocity) - [3.4.2 Cycle Time](#342-cycle-time) - [3.4.3 Classification](#343-classification) - - [3.4.4 Deployment Frequency](#344-deployment-frequency) - - [3.4.5 Lead time for changes Data](#345-lead-time-for-changes-data) - - [3.4.6 Change Failure Rate](#346-change-failure-rate) - - [3.4.7 Mean time to recovery](#347-mean-time-to-recovery) + - [3.4.4 Rework](#344-rework) + - [3.4.5 Deployment Frequency](#345-deployment-frequency) + - [3.4.6 Lead time for changes Data](#346-lead-time-for-changes-data) + - [3.4.7 Dev Change Failure Rate](#347-dev-change-failure-rate) + - [3.4.8 Dev Mean time to recovery](#348-dev-mean-time-to-recovery) - [3.5 Export original data](#35-export-original-data) - [3.5.1 Export board data](#351-export-board-data) + - [3.5.1.1 Done card exporting](#3511-done-card-exporting) + - [3.5.1.1 Undone card exporting](#3511-undone-card-exporting) - [3.5.2 Export pipeline data](#352-export-pipeline-data) - [3.6 Caching data](#36-caching-data) - [4 Known issues](#4-known-issues) - - [4.1 Change status name in Jira board](#41-change-status-name-in-jira-board-setting-when-there-are-cards-in-this-status) + - [4.1 Change status name in Jira board setting when there are cards in this status](#41--change-status-name-in-jira-board-setting-when-there-are-cards-in-this-status) - [5 Instructions](#5-instructions) - [5.1 Prepare for Jira Project](#51-prepare-for-jira-project) - [5.2 Prepare env to use Heartbeat tool](#52-prepare-env-to-use-heartbeat-tool) @@ -48,16 +60,19 @@ - [6.1.1 How to build and local preview](#611-how-to-build-and-local-preview) - [6.1.2 How to run unit tests](#612-how-to-run-unit-tests) - [6.1.3 How to generate a test report](#613-how-to-generate-a-test-report) - - [6.1.4 How to run e2e tests locally](#614-how-to-run-e2e-tests-locally) + - [6.1.4 How to run E2E tests locally](#614-how-to-run-e2e-tests-locally) + - [6.2 How to run backend](#62-how-to-run-backend) - [7 How to trigger BuildKite Pipeline](#7-how-to-trigger-buildkite-pipeline) - [Release](#release) - [Release command in main branch](#release-command-in-main-branch) -- [7 How to use](#7-how-to-use) - - [7.1 Docker-compose](#71-docker-compose) - - [7.1.1 Customize story point field in Jira](#711-customize-story-point-field-in-jira) - - [7.1.2 Multiple instance deployment](#712-multiple-instance-deployment) - - [7.2 K8S](#72-k8s) - - [7.2.1 Multiple instance deployment](#721-multiple-instance-deployment) +- [8 How to use](#8-how-to-use) + - [8.1 Docker-compose](#81-docker-compose) + - [8.1.1 Customize story point field in Jira](#811-customize-story-point-field-in-jira) + - [8.1.2 Multiple instance deployment](#812-multiple-instance-deployment) + - [8.2 K8S](#82-k8s) + - [8.2.1 Multiple instance deployment](#821-multiple-instance-deployment) +- [9 Contribution](#9-contribution) +- [10 Pipeline Strategy](#10-pipeline-strategy) # News @@ -67,24 +82,27 @@ - [Nov 6 2023 - Release Heartbeat - 1.1.2](release-notes/20231106.md) - [Nov 21 2023 - Release Heartbeat - 1.1.3](release-notes/20231121.md) - [Dev 4 2023 - Release Heartbeat - 1.1.4](release-notes/20231204.md) - - [Feb 29 2024 - Release Heartbeat - 1.1.5](release-notes/20240229.md) +- [Feb 29 2024 - Release Heartbeat - 1.1.5](release-notes/20240229.md) +- [Apr 2 2024 - Release heartbeat - 1.1.6](release-notes/20240402.md) # 1 About Heartbeat Heartbeat is a tool for tracking project delivery metrics that can help you get a better understanding of delivery performance. This product allows you easily get all aspects of source data faster and more accurate to analyze team delivery performance which enables delivery teams and team leaders focusing on driving continuous improvement and enhancing team productivity and efficiency. -State of DevOps Report is launching in 2019. In this webinar, The 4 key metrics research team and Google Cloud share key metrics to measure DevOps performance, measure the effectiveness of development and delivery practices. They searching about six years, developed four metrics that provide a high-level systems view of software delivery and performance. +State of DevOps Report is launching in 2019. In this webinar, The 4 key metrics research team and Google Cloud share key metrics to measure DevOps performance, measure the effectiveness of development and delivery practices. They searching about six years, developed four metrics that provide a high-level systems view of software delivery and performance. Based on that, Heartbeat introduce below metrics as below. -**Here are the four Key meterics:** +**8 metrics supported by heartbeat:** -1. Deployment Frequency (DF) -2. Lead Time for changes (LTC) -3. Mean Time To Recover (MTTR) -4. Change Failure Rate (CFR) -In Heartbeat tool, we also have some other metrics, like: Velocity, Cycle Time and Classification. So we can collect DF, LTC, CFR, Velocity, Cycle Time and Classification. +1. [Velocity](#341-velocity) +2. [Cycle time](#341-velocity) +3. [Classification](#343-classification) +4. [Rework](#344-rework) +5. [Deployment Frequency](#345-deployment-frequency) +6. [Lead Time for changes](#346-lead-time-for-changes-data) +7. [Dev Change Failure Rate](#347-dev-change-failure-rate) +8. [Dev Mean Time To Recovery](#348-dev-mean-time-to-recovery) -For MTTR meter, specifically, if the pipeline stay in failed status during the selected period, the unfixed part will not be included for MTTR calculation. # 2 Support tools @@ -129,21 +147,30 @@ All need to select which data you want to get, for now, we support seven metrics ![Image 3-3](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/3.png)\ _Image 3-3,Metrics Data_ +##### 3.1.2.1 Date picker validation rules + +User can not select future time in calendar (both start time & end time). The max date interval between start time and end time is 31 days (e.g. 01/01/2024 - 01/31/2024). + +Invalid dates may be, e.g. future dates, interval between start time and end time is more than 31 days, end time is before start time, etc. + +If user selects and invalid date, a warning may be shown. + #### 3.1.3 Config project account Because all metrics data from different tools that your projects use. Need to have the access to these tools then you can get the data. So after select time period and metrics data, then you need to input the config for different tools(Image 3-4). According to your selected required data, you need to input account settings for the respective data source. Below is the mapping between your selected data to data source. -| Required Data | Datasource | -| --------------------- | -------------- | -| Velocity | Board | -| Cycle time | Board | -| Classification | Board | -| Lead time for changes | Repo,Pipeline | -| Deployment frequency | Pipeline | -| Change failure rate | Pipeline | -| Mean time to recovery | Pipeline | +| Required Data | Datasource | +|---------------------------| -------------- | +| Velocity | Board | +| Cycle time | Board | +| Classification | Board | +| Rework times | Board | +| Lead time for changes | Repo,Pipeline | +| Deployment frequency | Pipeline | +| Dev change failure rate | Pipeline | +| Dev mean time to recovery | Pipeline | ![Image 3-4](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/4.png)\ Image 3-4,Project config @@ -157,27 +184,44 @@ Image 3-4,Project config |Site|Site is the domain for your jira board, like below URL, `dorametrics` is the site
https://dorametrics.atlassian.net/jira/software/projects/ADM/boards/2 | |Email|The email can access to the Jira board | |Token|Generate a new token with below link, https://id.atlassian.com/manage-profile/security/api-tokens | +##### 3.1.3.1 Guideline for generating Jira token +![Image 3-5](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/guideline-for-generating-token/generate-jira-token.png) +_Image 3-5, create Jira token_ **The details for Pipeline:** |Items|Description| |---|---| |PipelineTool| The pipeline tool you team use, currently heartbeat only support buildkite| |Token|Generate buildkite token with below link, https://buildkite.com/user/api-access-tokens| +##### 3.1.3.2 Guideline for generating Buildkite token +Select organization for you pipeline +![Image 3-6](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/guideline-for-generating-token/generate-buildkite-token-org.png) +Choose "Read Builds","Read Organizations" and "Read Pipelines". +![Image 3-6](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/guideline-for-generating-token/generate-buildkite-token.png) +_Image 3-6, generate Buildkite token_ **The details for SourceControl:** |Items|Description| |---|---| |SourceControl|The source control tool you team use, currently heartbeat only support Github| |Token|Generate Github token with below link(classic one), https://github.com/settings/tokens| - +##### 3.1.3.3 Guideline for generating GitHub token +Generate new token (classic) +![Image 3-7](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/guideline-for-generating-token/generate-github-token-entry.png) +Select repo from scopes +![Image 3-7](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/guideline-for-generating-token/generate-github-token.png) +_Image 3-7, generate classic GitHub token_ +##### 3.1.3.4 Authorize GitHub token with correct organization +![Image 3-8](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/guideline-for-generating-token/unauthorized.png) +_Image 3-8, authorize GitHub token with correct organization_ ### 3.2 Config Metrics data After inputting the details info, users need to click the `Verify` button to verify if can access to these tool. Once verified, they could click the `Next` button go to next page -- Config Metrics page(Image 3-5,Image 3-6,Image 3-7) -#### 3.2.1 Config Crews/Cycle Time +#### 3.2.1 Config Crews/Board Mappings -![Image 3-5](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/5.png)\ -_Image 3-5, Crews/Cycle Time config_ +![Image 3-9](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/5.png)\ +_Image 3-9, Crews/Board Mappings config_ **Crew Settings:** You could select your team members from a list get from board source. The list will include the assignees for those tickets that finished in the time period selected in the last step. @@ -195,46 +239,57 @@ _Image 3-5, Crews/Cycle Time config_ | Done | It means the tickets are already done. Cycle time doesn't include this time. | | -- | If you don't need to map, you can select -- | +**By Status**: user can click the toggle selected button to choose the mapping relationship by column or by status. It support multiple status map in to one column, just as the picture shows the TODO and INPROGRESS board status can be mapped to different heartbeat states. + +![Image 3-10](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/16.png)\ +_Image 3-10,By Status_ + #### 3.2.2 Setting Classification -![Image 3-6](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/6.png)\ -_Image 3-6,Classification Settings_ +![Image 3-11](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/6.png)\ +_Image 3-11,Classification Settings_ In classification settings, it will list all Context fields for your jira board. Users can select anyone to get the data for them. And according to your selection, in the export page, you will see the classification report to provide more insight with your board data. -#### 3.2.3 Setting advanced Setting +#### 3.2.3 Rework times Setting +![Image 3-12](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/rework-setting-image/rework-times-settings.png)\ +_Image 3-12,Rework times Settings_ + +In Rework times settings, it contains Rework to which state Input and Exclude which states(optional) Input. The options in the Rework to which state Input are all from Board mappings, the options are ordered, and when an option is selected, the rework information of the option and all subsequent options will be counted in the report page and export file. The Exclude which states(optional) Input can help you exclude certain subsequent options (image 3-7). -![Image 3-7](https://jsd.cdn.zzko.cn/gh/au-heartbeat/data-hosting@main/advanced-setting-image/advance-settings.png)\ -_Image 3-7,advanced Settings_ +#### 3.2.4 Setting advanced Setting + +![Image 3-13](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/advanced-setting-image/advance-settings.png)\ +_Image 3-13,advanced Settings_ In advanced settings, it contains story points Input and Flagged Input. Users can input story points and Flagged custom-field on their own when the jira board has permission restriction . And according to these input, in the export page, user can get correct story points and block days how to find the story points and Flagged custom-field? -![Image 3-8](https://jsd.cdn.zzko.cn/gh/au-heartbeat/data-hosting@main/advanced-setting-image/devtool-network.png)\ -_Image 3-8,devTool-network-part_ +![Image 3-14](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/advanced-setting-image/devtool-network.png)\ +_Image 3-14,devTool-network-part_ -![Image 3-9](https://jsd.cdn.zzko.cn/gh/au-heartbeat/data-hosting@main/advanced-setting-image/card-history.png)\ -_Image 3-9,card-history_ +![Image 3-15](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/advanced-setting-image/card-history.png)\ +_Image 3-15,card-history_ -![Image 3-10](https://jsd.cdn.zzko.cn/gh/au-heartbeat/data-hosting@main/advanced-setting-image/find-custom-field-api.png)\ -_Image 3-10,find-custom-field-api_ +![Image 3-16](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/advanced-setting-image/find-custom-field-api.png)\ +_Image 3-16,find-custom-field-api_ -![Image 3-11](https://jsd.cdn.zzko.cn/gh/au-heartbeat/data-hosting@main/advanced-setting-image/story-point-custom-field.png)\ -_Image 3-11,story-point-custom-field_ +![Image 3-17](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/advanced-setting-image/story-point-custom-field.png)\ +_Image 3-17,story-point-custom-field_ -![Image 3-12](https://jsd.cdn.zzko.cn/gh/au-heartbeat/data-hosting@main/advanced-setting-image/flagged-custom-field.png)\ -_Image 3-12,flagged-custom-field_ +![Image 3-18](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/advanced-setting-image/flagged-custom-field.png)\ +_Image 3-18,flagged-custom-field_ 1. user need to go to the jira board and click one card , then open dev tool switch to network part. 2. then click card's history part. 3. at that time, user can see one api call which headers request URL is https://xxx.atlassian.net/rest/gira/1/ . -4. then go to review part, find fieldDisplayName which show Flagged and story point estimate and get the fieldId as the custom-field that user need to input in advanced settings. from image 3-11 and 3-12 we can find that flagged custom field is customfield_10021, story points custom field is customfield_10016. +4. then go to review part, find fieldDisplayName which show Flagged and story point estimate and get the fieldId as the custom-field that user need to input in advanced settings. from image 3-13 and 3-14 we can find that flagged custom field is customfield_10021, story points custom field is customfield_10016. -#### 3.2.4 Pipeline configuration +#### 3.2.5 Pipeline configuration -![Image 3-13](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/7.png)\ -_Image 3-13,Settings for Pipeline_ +![Image 3-19](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/7.png)\ +_Image 3-19,Settings for Pipeline_ They are sharing the similar settings which you need to specify the pipeline step so that Heartbeat will know in which pipeline and step, team consider it as deploy to PROD. So that we could use it to calculate metrics. @@ -248,69 +303,116 @@ They are sharing the similar settings which you need to specify the pipeline ste ### 3.3.1 Export Config Json File -When user first use this tool, need to create a project, and do some config. To avoid the user entering configuration information repeatedly every time, we provide a “Save” button in the config and metrics pages. In config page, click the save button, it will save all items in config page in a Json file. If you click the save button in the metrics page, it will save all items in config and metrics settings in a Json file. Here is the json file (Image 3-8)。Note: Below screenshot just contains a part of data. +When user first use this tool, need to create a project, and do some config. To avoid the user entering configuration information repeatedly every time, we provide a “Save” button in the config and metrics pages. In config page, click the save button, it will save all items in config page in a Json file. If you click the save button in the metrics page, it will save all items in config and metrics settings in a Json file. Here is the json file (Image 3-16)。Note: Below screenshot just contains a part of data. -![Image 3-14](https://user-images.githubusercontent.com/995849/89784710-b4c41180-db4b-11ea-9bc4-db14ce98ef69.png)\ -_Image 3-14, Config Json file_ +![Image 3-20](https://user-images.githubusercontent.com/995849/89784710-b4c41180-db4b-11ea-9bc4-db14ce98ef69.png)\ +_Image 3-20, Config Json file_ ### 3.3.2 Import Config Json File -When user already saved config file before, then you don’t need to create a new project. In the home page, can click Import Project from File button(Image 3-1) to select the config file. If your config file is too old, and the tool already have some new feature change, then if you import the config file, it will get some warning info(Image 3-9). You need to re-select some info, then go to the next page. +When user already saved config file before, then you don’t need to create a new project. In the home page, can click Import Project from File button(Image 3-1) to select the config file. If your config file is too old, and the tool already have some new feature change, then if you import the config file, it will get some warning info(Image 3-17). You need to re-select some info, then go to the next page. -![Image 3-15](https://user-images.githubusercontent.com/995849/89784267-f902e200-db4a-11ea-9d0b-a8ab29a8819e.png)\ -_Image 3-15, Warning message_ +![Image 3-21](https://user-images.githubusercontent.com/995849/89784267-f902e200-db4a-11ea-9d0b-a8ab29a8819e.png)\ +_Image 3-21, Warning message_ ## 3.4 Generate Metrics report After setup and configuration, then it will generate the heartbeat dashboard. -![Image 3-16](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/8.png) +![Image 3-22](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/8.png) +_Image 3-22, Report page_ You could find the drill down from `show more >` link from dashboard. ### 3.4.1 Velocity -In Velocity Report, it will list the corresponding data by Story Point and the number of story tickets. (image 3-10) -![Image 3-16](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/9.png)\ -_Image 3-16,Velocity Report_ +In Velocity Report, it will list the corresponding data by Story Point and the number of story tickets. (image 3-19) +- `Velocity` : includes how many story points and cards we have completed within selected time period. +- Definition for 'Velocity(Story Point)‘: how many story point we have completed within selected time period. +- Formula for 'Velocity(Story Point): sum of story points for done cards in selected time period +- Definition for 'Throughput(Cards Count): how many story cards we have completed within selected time period. +- Formula for 'Throughput(Cards Count): sum of cards count for done cards in selected time period + + +![Image 3-23](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/9.png)\ +_Image 3-23,Velocity Report_ ### 3.4.2 Cycle Time The calculation process data and final result of Cycle Time are calculated by rounding method, and two digits are kept after the decimal point. Such as: 3.567... Is 3.56; 3.564... Is 3.56. +- `Cycle time`: the time it take for each card start ‘to do’ until move to ‘done’. +- Definition for ‘Average Cycle Time(Days/SP)’: how many days does it take on average to complete a point? +- Formula for ‘Average Cycle Time(Days/SP)’: sum of cycle time for done cards/done cards story points +- Definition for ‘Average Cycle Time(Days/Card)’: how many days does it take on average to complete a card? +- Formula for ‘Average Cycle Time(Days/Card)’: sum of cycle time for done cards/done cards count -![Image 3-17](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/10.png)\ -_Image 3-17,Cycle Time Report_ +![Image 3-24](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/10.png)\ +_Image 3-24,Cycle Time Report_ ### 3.4.3 Classification It will show the classification data of Board based on your selection on `Classification Settings` in metrics page. The percentage value represent the count of that type tickets vs total count of tickets. +- `Classification`: provide different dimensions to view how much efforts team spent within selected time period. +- for example: spike cards account for 17.65% of the total completed cards + +![Image 3-25](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/11.png)\ +_Image 3-25,Classification Report_ + +### 3.4.4 Rework + +- Definition for ‘Rework': cards roll back from a later state to a previous state, for example, one card move from 'testing' state to 'in dev' state, which means this card is reworked. +- Formula for 'Total rework times': the total number of rework times in all done cards +- Formula for 'Total rework cards': the total number of rework cards in all done cards +- Formula for 'Rework cards ratio': total rework cards/throughput + +It will show the rework data of board on your selection on `Rework times settins` in metrics page (image 3-21). + +If "to do" is selected in the "Rework to which column", we will count the number of times the subsequent options in the options are reworked back to the "to do" state. + -![Image 3-18](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/11.png)\ -_Image 3-18,Classification Report_ + +![Image 3-26](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/rework-setting-image/rework-detail.png)\ +_Image 3-26,Rework Report_ -### 3.4.4 Deployment Frequency +### 3.4.5 Deployment Frequency +- Definition for ‘Deployment Frequency': this metrics records how often you deploy code to production on a daily basis. +- Formula for ‘Deployment Frequency': the umber of build for(Status = passed & Valid = true)/working days +![Image 3-27](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/export/export-pipline-data.png)\ +_Image 3-27,export pipline data_ +![Image 3-28](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/12.png)\ +_Image 3-28,Deployment Frequency Report_ -![Image 3-19](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/12.png)\ -_Image 3-19,Deployment Frequency Report_ +### 3.4.6 Lead time for changes Data +- Formula for ‘PR lead time': + - if PR exist : PR lead time = PR merged time - first code committed time + - if no PR or revert PR: PR lead time = 0 -### 3.4.5 Lead time for changes Data +- Formula for ‘Pipeline lead time': + - if PR exist: Pipeline lead time = Job Complete Time - PR merged time + - if no PR: Pipeline lead time = Job Complete Time - Job Start Time -![Image 3-20](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/13.png)\ -_Image 3-20,Lead time for changes Report_ -### 3.4.6 Change Failure Rate -![Image 3-21](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/14.png)\ -_Image 3-21,Change Failure Rate Report_ +![Image 3-29](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/13.png)\ +_Image 3-29,Lead time for changes Report_ -### 3.4.7 Mean time to recovery +### 3.4.7 Dev Change Failure Rate +- Definition for ‘Dev Change Failure Rate': this metrics is different from the official definition of change failure rate, in heartbeat, we definite this metrics based on development,which is the percentage of failed pipelines in the total pipelines, and you chan select different pipeline as your final step,and this value is lower means failed pipeline is fewer. +- Formula for ‘Dev Change Failure Rate': the number of build for (Status = failed)/the number of build for [(Status = passed & Valid = true)+ the number of build for (status=failed)] -![Image 3-22](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/15.png)\ -_Image 3-22,mean time to recovery +![Image 3-30](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/14.png)\ +_Image 3-30,Dev Change Failure Rate Report_ + +### 3.4.8 Dev Mean time to recovery +- Definition for ‘Dev Mean time to recovery': this metrics is also different from the official definition of Mean time to recovery. This metrics comes from pipeline, and it records how long it generally takes to restore when pipeline failed, and If this value is less than 8 hours, it means ‘red does not last overnight’, which means our repair speed is relatively good. +- Formula for ‘Dev Mean time to recovery': sum[he time difference from the first fail to the first pass for deployment completed time]/ the number of repairs + +![Image 3-31](https://cdn.jsdelivr.net/gh/au-heartbeat/data-hosting@main/readme/15.png)\ +_Image 3-31,mean time to recovery ## 3.5 Export original data -After generating the report, you can export the original data for your board and pipeline (Image 3-15). Users can click the “Export board data” or “Export pipeline data” button to export the original data. +After generating the report, you can export the original data for your board and pipeline (Image 3-18). Users can click the “Export board data” or “Export pipeline data” button to export the original data. ### 3.5.1 Export board data @@ -318,14 +420,14 @@ It will export a csv file for board data #### 3.5.1.1 Done card exporting -Export the all done tickets during the time period(Image 1) +Export the all done tickets during the time period(Image 3-18) #### 3.5.1.1 Undone card exporting -Export the latest updated 50 non-done tickets in your current active board. And it will order by heartbeat state and then last status change date(Image 3-16) +Export the latest updated 50 non-done tickets in your current active board. And it will order by heartbeat state and then last status change date(Image 3-28) -![Image 3-22](https://user-images.githubusercontent.com/995849/89784291-01f3b380-db4b-11ea-8f5a-d475e80014fb.png)\ -_Image 3-22,Exported Board Data_ +![Image 3-32](https://user-images.githubusercontent.com/995849/89784291-01f3b380-db4b-11ea-8f5a-d475e80014fb.png)\ +_Image 3-32,Exported Board Data_ **All columns for Jira board:** |Column name |Description| @@ -352,13 +454,14 @@ _Image 3-22,Exported Board Data_ |Block Days|Blocked days for each ticket| |Review Days|--| |Original Cycle Time: {Column Name}|The data for Jira board original data | - +|Rework: total - {rework state} | The total number of rework times | +|Rework: from {subsequent status} | The number of rework times | ### 3.5.2 Export pipeline data -It will export a csv file for pipeline data (image 3-17). +It will export a csv file for pipeline data (image 3-29). -![Image 3-23](https://user-images.githubusercontent.com/995849/89784293-0324e080-db4b-11ea-975d-6609024aac49.png)\ -_Image 3-23,Exported Pipeline Data_ +![Image 3-33](https://user-images.githubusercontent.com/995849/89784293-0324e080-db4b-11ea-975d-6609024aac49.png)\ +_Image 3-33,Exported Pipeline Data_ **All columns for pipeline data:** |Column name |Description| @@ -443,7 +546,7 @@ pnpm test pnpm coverage ``` -## 6.1.4 How to run e2e tests locally +## 6.1.4 How to run E2E tests locally 2. Start the backend service @@ -459,12 +562,14 @@ cd HearBeat/frontend pnpm start ``` -4. Run the e2e tests +4. Run the E2E tests ``` cd HearBeat/frontend -pnpm e2e +pnpm run e2e:headed ``` +## 6.2 How to run backend +Refer to [run backend](backend/README.md#1-how-to-start-backend-application) # 7 How to trigger BuildKite Pipeline @@ -491,9 +596,9 @@ git tag -d {tag name} git push origin :refs/tags/{tag name} ``` -# 7 How to use +# 8 How to use -## 7.1 Docker-compose +## 8.1 Docker-compose First, create a `docker-compose.yml` file, and copy below code into the file. @@ -523,7 +628,7 @@ Then, execute this command docker-compose up -d frontend ``` -### 7.1.1 Customize story point field in Jira +### 8.1.1 Customize story point field in Jira Specifically, story point field can be indicated in `docker-compose.yml`. You can do it as below. @@ -548,7 +653,7 @@ services: restart: always ``` -### 7.1.2 Multiple instance deployment +### 8.1.2 Multiple instance deployment Specifically, if you want to run with multiple instances. You can do it with below docker compose file. @@ -579,7 +684,7 @@ volumes: file_volume: ``` -## 7.2 K8S +## 8.2 K8S First, create a `k8s-heartbeat.yml` file, and copy below code into the file. @@ -634,7 +739,7 @@ spec: apiVersion: v1 kind: Service metadata: - name: frontend + name: **frontend** spec: selector: app: frontend @@ -651,6 +756,50 @@ Then, execute this command kubectl apply -f k8s-heartbeat.yml ``` -### 7.2.1 Multiple instance deployment +### 8.2.1 Multiple instance deployment You also can deploy Heartbeats in multiple instances using K8S through the following [documentation](https://au-heartbeat.github.io/Heartbeat/en/devops/how-to-deploy-heartbeat-in-multiple-instances-by-k8s/). + +# 9 Contribution + +We love your input! Please see our [contributing guide](contribution.md) to get started. Thank you 🙏 to all our contributors! + +# 10 Pipeline Strategy + +Now, Heartbeat uses `GitHub Actions` and `BuildKite` to build and deploy Heartbeat application. + +But there is some constrains, like some pipeline dependency. + +So, committer should pay attention to this flow when there is some pipeline issues. + +```mermaid + sequenceDiagram + actor Committer + participant GitHub_Actions as GitHub Actions + participant BuildKite + + Committer ->> GitHub_Actions : Push code + Committer ->> BuildKite : Push code + loop 30s/40 times + BuildKite->> GitHub_Actions: Check the basic check(all check before 'deploy-infra' job) has been passed + GitHub_Actions -->> BuildKite: Basic check has passed? + alt Yes + BuildKite ->> BuildKite: Build and deploy e2e env + Note over BuildKite, GitHub_Actions: Some times passed + loop 30s/60 times + GitHub_Actions ->> BuildKite: Request to check if the e2e has been deployed + BuildKite -->> GitHub_Actions: e2e deployment status, if the e2e has been deployed? + alt Yes + GitHub_Actions ->> GitHub_Actions: Run e2e check on GitHub actions + Note over BuildKite, GitHub_Actions: Some times passed + GitHub_Actions -->> Committer: Response the pipeline result to committer + BuildKite -->> Committer: Response the pipeline result to committer + else No + GitHub_Actions -->> Committer: Break the pipeline + end + end + else No + BuildKite -->> Committer: Break the pipeline + end + end +``` diff --git a/backend/build.gradle b/backend/build.gradle index 46692478e5..25fe3a9b70 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -3,9 +3,9 @@ plugins { id 'jacoco' id 'pmd' id 'org.springframework.boot' version '3.1.9' - id 'io.spring.dependency-management' version '1.1.0' - id "io.spring.javaformat" version "0.0.38" - id 'com.github.jk1.dependency-license-report' version '2.1' + id 'io.spring.dependency-management' version '1.1.4' + id "io.spring.javaformat" version "0.0.41" + id 'com.github.jk1.dependency-license-report' version '2.6' id "org.sonarqube" version "4.4.1.3373" } @@ -29,7 +29,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-log4j2' implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework:spring-core:6.1.3' + implementation 'org.springframework:spring-core:6.1.5' implementation("org.springframework.cloud:spring-cloud-starter-openfeign:4.0.2") { exclude group: 'commons-fileupload', module: 'commons-fileupload' } @@ -41,22 +41,23 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-cache' implementation 'org.ehcache:ehcache:3.10.8' implementation 'javax.annotation:javax.annotation-api:1.3.2' - implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.google.code.gson:gson:2.10.1' testImplementation 'junit:junit:4.13.2' - compileOnly 'org.projectlombok:lombok:1.18.26' - annotationProcessor 'org.projectlombok:lombok:1.18.26' + compileOnly 'org.projectlombok:lombok:1.18.32' + annotationProcessor 'org.projectlombok:lombok:1.18.32' testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' - testCompileOnly 'org.projectlombok:lombok:1.18.26' - testAnnotationProcessor 'org.projectlombok:lombok:1.18.26' - implementation 'com.opencsv:opencsv:5.5.2' - implementation 'org.apache.commons:commons-text:1.10.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' + testCompileOnly 'org.projectlombok:lombok:1.18.32' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.32' + implementation 'com.opencsv:opencsv:5.9' + implementation 'org.apache.commons:commons-text:1.11.0' + implementation 'org.awaitility:awaitility:3.1.6' } tasks.named('test') { useJUnitPlatform() testLogging { - events "passed", "skipped", "failed" + events "skipped", "failed" } finalizedBy jacocoTestReport } @@ -72,6 +73,7 @@ sonar { property "sonar.projectKey", "au-heartbeat-heartbeat-backend" property "sonar.organization", "au-heartbeat" property "sonar.host.url", "https://sonarcloud.io" + property "sonar.exclusions", "src/main/java/heartbeat/HeartbeatApplication.java,src/main/java/heartbeat/config/**,src/main/java/heartbeat/util/SystemUtil.java" } } @@ -100,6 +102,36 @@ jacocoTestCoverageVerification { violationRules { rule { limit { + counter = 'INSTRUCTION' + value = 'COVEREDRATIO' + minimum = 1.0 + } + } + rule { + limit { + counter = 'LINE' + value = 'COVEREDRATIO' + minimum = 1.0 + } + } + rule { + limit { + counter = 'METHOD' + value = 'COVEREDRATIO' + minimum = 1.0 + } + } + rule { + limit { + counter = 'BRANCH' + value = 'COVEREDRATIO' + minimum = 0.90 + } + } + rule { + limit { + counter = 'CLASS' + value = 'COVEREDRATIO' minimum = 1.0 } } diff --git a/backend/gradle/wrapper/gradle-wrapper.jar b/backend/gradle/wrapper/gradle-wrapper.jar index c1962a79e2..e6441136f3 100644 Binary files a/backend/gradle/wrapper/gradle-wrapper.jar and b/backend/gradle/wrapper/gradle-wrapper.jar differ diff --git a/backend/gradle/wrapper/gradle-wrapper.properties b/backend/gradle/wrapper/gradle-wrapper.properties index 37aef8d3f0..b82aa23a4f 100644 --- a/backend/gradle/wrapper/gradle-wrapper.properties +++ b/backend/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/backend/gradlew b/backend/gradlew index aeb74cbb43..1aa94a4269 100755 --- a/backend/gradlew +++ b/backend/gradlew @@ -83,7 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -130,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -141,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -149,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -198,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/backend/gradlew.bat b/backend/gradlew.bat index 6689b85bee..7101f8e467 100644 --- a/backend/gradlew.bat +++ b/backend/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/backend/src/main/java/heartbeat/client/BuildKiteFeignClient.java b/backend/src/main/java/heartbeat/client/BuildKiteFeignClient.java index c129c531be..1d1a8fdd2f 100644 --- a/backend/src/main/java/heartbeat/client/BuildKiteFeignClient.java +++ b/backend/src/main/java/heartbeat/client/BuildKiteFeignClient.java @@ -3,11 +3,8 @@ import heartbeat.client.decoder.BuildKiteFeignClientDecoder; import heartbeat.client.dto.pipeline.buildkite.BuildKiteBuildInfo; import heartbeat.client.dto.pipeline.buildkite.BuildKiteOrganizationsInfo; -import heartbeat.client.dto.pipeline.buildkite.BuildKiteTokenInfo; import heartbeat.client.dto.pipeline.buildkite.BuildKitePipelineDTO; - -import java.util.List; - +import heartbeat.client.dto.pipeline.buildkite.BuildKiteTokenInfo; import org.springframework.cache.annotation.Cacheable; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.HttpStatus; @@ -19,6 +16,8 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; +import java.util.List; + @FeignClient(name = "buildKiteFeignClient", url = "${buildKite.url}", configuration = BuildKiteFeignClientDecoder.class) public interface BuildKiteFeignClient { @@ -32,10 +31,9 @@ public interface BuildKiteFeignClient { @ResponseStatus(HttpStatus.OK) List getBuildKiteOrganizationsInfo(@RequestHeader("Authorization") String token); - @Cacheable(cacheNames = "pipelineInfo", key = "#token+'-'+#organizationId+'-'+#page+'-'+#perPage") @GetMapping(path = "v2/organizations/{organizationId}/pipelines?page={page}&per_page={perPage}") @ResponseStatus(HttpStatus.OK) - List getPipelineInfo(@RequestHeader("Authorization") String token, + ResponseEntity> getPipelineInfo(@RequestHeader("Authorization") String token, @PathVariable String organizationId, @PathVariable String page, @PathVariable String perPage); @GetMapping(path = "v2/organizations/{organizationId}/pipelines/{pipelineId}/builds", diff --git a/backend/src/main/java/heartbeat/client/JiraFeignClient.java b/backend/src/main/java/heartbeat/client/JiraFeignClient.java index 402f47008b..c9c8c24e64 100644 --- a/backend/src/main/java/heartbeat/client/JiraFeignClient.java +++ b/backend/src/main/java/heartbeat/client/JiraFeignClient.java @@ -51,4 +51,8 @@ CardHistoryResponseDTO getJiraCardHistoryByCount(URI baseUrl, @PathVariable Stri @GetMapping(path = "rest/api/2/project/{projectIdOrKey}") JiraBoardProject getProject(URI baseUrl, @PathVariable String projectIdOrKey, @RequestHeader String authorization); + // This api is solely used for site url checking + @GetMapping(path = "/rest/api/3/dashboard") + String getDashboard(URI baseUrl, @RequestHeader String authorization); + } diff --git a/backend/src/main/java/heartbeat/client/decoder/BuildKiteFeignClientDecoder.java b/backend/src/main/java/heartbeat/client/decoder/BuildKiteFeignClientDecoder.java index 8ec1c38811..c7706925ae 100644 --- a/backend/src/main/java/heartbeat/client/decoder/BuildKiteFeignClientDecoder.java +++ b/backend/src/main/java/heartbeat/client/decoder/BuildKiteFeignClientDecoder.java @@ -1,6 +1,5 @@ package heartbeat.client.decoder; -import feign.FeignException; import feign.Response; import feign.codec.ErrorDecoder; import heartbeat.util.ExceptionUtil; @@ -12,12 +11,17 @@ public class BuildKiteFeignClientDecoder implements ErrorDecoder { @Override public Exception decode(String methodKey, Response response) { + String errorMessage = switch (methodKey) { + case "getTokenInfo" -> "Failed to get token info"; + case "getBuildKiteOrganizationsInfo" -> "Failed to get BuildKite OrganizationsInfo info"; + case "getPipelineInfo" -> "Failed to get pipeline info"; + case "getPipelineSteps" -> "Failed to get pipeline steps"; + case "getPipelineStepsInfo" -> "Failed to get pipeline steps info"; + default -> "Failed to get buildkite info"; + }; + log.error("Failed to get BuildKite info_response status: {}, method key: {}", response.status(), methodKey); HttpStatus statusCode = HttpStatus.valueOf(response.status()); - FeignException exception = FeignException.errorStatus(methodKey, response); - String errorMessage = String.format("Failed to get BuildKite info_status: %s, reason: %s", statusCode, - exception.getMessage()); - return ExceptionUtil.handleCommonFeignClientException(statusCode, errorMessage); } diff --git a/backend/src/main/java/heartbeat/client/decoder/GitHubFeignClientDecoder.java b/backend/src/main/java/heartbeat/client/decoder/GitHubFeignClientDecoder.java index cd0dbb235d..a71b1c8b57 100644 --- a/backend/src/main/java/heartbeat/client/decoder/GitHubFeignClientDecoder.java +++ b/backend/src/main/java/heartbeat/client/decoder/GitHubFeignClientDecoder.java @@ -1,6 +1,5 @@ package heartbeat.client.decoder; -import feign.FeignException; import feign.Response; import feign.codec.ErrorDecoder; import heartbeat.util.ExceptionUtil; @@ -12,13 +11,18 @@ public class GitHubFeignClientDecoder implements ErrorDecoder { @Override public Exception decode(String methodKey, Response response) { + String errorMessage = switch (methodKey) { + case "verifyToken" -> "Failed to verify token"; + case "verifyCanReadTargetBranch" -> "Failed to verify canRead target branch"; + case "getCommitInfo" -> "Failed to get commit info"; + case "getPullRequestCommitInfo" -> "Failed to get pull request commit info"; + case "getPullRequestListInfo" -> "Failed to get pull request list info"; + default -> "Failed to get github info"; + }; + log.error("Failed to get GitHub info_response status: {}, method key: {}", response.status(), methodKey); HttpStatus statusCode = HttpStatus.valueOf(response.status()); - FeignException exception = FeignException.errorStatus(methodKey, response); - String errorMessage = String.format("Failed to get GitHub info_status: %s, reason: %s", statusCode, - exception.getMessage()); return ExceptionUtil.handleCommonFeignClientException(statusCode, errorMessage); - } } diff --git a/backend/src/main/java/heartbeat/client/decoder/JiraFeignClientDecoder.java b/backend/src/main/java/heartbeat/client/decoder/JiraFeignClientDecoder.java index a858ad4b97..8af55eed41 100644 --- a/backend/src/main/java/heartbeat/client/decoder/JiraFeignClientDecoder.java +++ b/backend/src/main/java/heartbeat/client/decoder/JiraFeignClientDecoder.java @@ -1,6 +1,5 @@ package heartbeat.client.decoder; -import feign.FeignException; import feign.Response; import feign.codec.ErrorDecoder; import heartbeat.util.ExceptionUtil; @@ -12,11 +11,19 @@ public class JiraFeignClientDecoder implements ErrorDecoder { @Override public Exception decode(String methodKey, Response response) { + String errorMessage = switch (methodKey) { + case "getJiraBoardConfiguration" -> "Failed to get jira board configuration"; + case "getColumnStatusCategory" -> "Failed to get column status category"; + case "getJiraCards" -> "Failed to get jira cards"; + case "getJiraCardHistoryByCount" -> "Failed to get jira card history by count"; + case "getTargetField" -> "Failed to get target field"; + case "getBoard" -> "Failed to get board"; + case "getProject" -> "Failed to get project"; + default -> "Failed to get jira info"; + }; + log.error("Failed to get Jira info_response status: {}, method key: {}", response.status(), methodKey); HttpStatus statusCode = HttpStatus.valueOf(response.status()); - FeignException exception = FeignException.errorStatus(methodKey, response); - String errorMessage = String.format("Failed to get Jira info_status: %s, reason: %s", statusCode, - exception.getMessage()); return ExceptionUtil.handleCommonFeignClientException(statusCode, errorMessage); } diff --git a/backend/src/main/java/heartbeat/client/dto/board/jira/HistoryDetail.java b/backend/src/main/java/heartbeat/client/dto/board/jira/HistoryDetail.java index 2ce8c78b28..bed9f960b1 100644 --- a/backend/src/main/java/heartbeat/client/dto/board/jira/HistoryDetail.java +++ b/backend/src/main/java/heartbeat/client/dto/board/jira/HistoryDetail.java @@ -22,6 +22,8 @@ public class HistoryDetail implements Serializable { private Actor actor; + private String fieldDisplayName; + @Getter @Setter @Builder diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/AuthorOuter.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/AuthorOuter.java deleted file mode 100644 index 566a81519c..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/AuthorOuter.java +++ /dev/null @@ -1,66 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class AuthorOuter implements Serializable { - - private String login; - - private String id; - - @JsonProperty("node_id") - private String nodeId; - - @JsonProperty("avatar_url") - private String avatarUrl; - - @JsonProperty("gravatar_id") - private String gravatarId; - - private String url; - - @JsonProperty("html_url") - private String htmlUrl; - - @JsonProperty("followers_url") - private String followersUrl; - - @JsonProperty("following_url") - private String followingUrl; - - @JsonProperty("gists_url") - private String gistsUrl; - - @JsonProperty("starred_url") - private String starredUrl; - - @JsonProperty("subscriptions_url") - private String subscriptionsUrl; - - @JsonProperty("organizations_url") - private String organizationsUrl; - - @JsonProperty("repos_url") - private String reposUrl; - - @JsonProperty("events_url") - private String eventsUrl; - - @JsonProperty("received_events_url") - private String receivedEventsUrl; - - private String type; - - private Boolean siteAdmin; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Base.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Base.java deleted file mode 100644 index e2bc62608b..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Base.java +++ /dev/null @@ -1,26 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Base implements Serializable { - - private String label; - - private String ref; - - private String sha; - - private User user; - - private Repo repo; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Comment.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Comment.java deleted file mode 100644 index 84d4dfbfcb..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Comment.java +++ /dev/null @@ -1,18 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Comment implements Serializable { - - private String href; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Commits.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Commits.java deleted file mode 100644 index 6a6087d8fc..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Commits.java +++ /dev/null @@ -1,18 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Commits implements Serializable { - - private String href; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitterOuter.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitterOuter.java deleted file mode 100644 index 23eb7d5cef..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitterOuter.java +++ /dev/null @@ -1,67 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class CommitterOuter implements Serializable { - - private String login; - - private String id; - - @JsonProperty("node_id") - private String nodeId; - - @JsonProperty("avatar_url") - private String avatarUrl; - - @JsonProperty("gravatar_id") - private String gravatarId; - - private String url; - - @JsonProperty("html_url") - private String htmlUrl; - - @JsonProperty("followers_url") - private String followersUrl; - - @JsonProperty("following_url") - private String followingUrl; - - @JsonProperty("gists_url") - private String gistsUrl; - - @JsonProperty("starred_url") - private String starredUrl; - - @JsonProperty("subscriptions_url") - private String subscriptionsUrl; - - @JsonProperty("organizations_url") - private String organizationsUrl; - - @JsonProperty("repos_url") - private String reposUrl; - - @JsonProperty("events_url") - private String eventsUrl; - - @JsonProperty("received_events_url") - private String receivedEventsUrl; - - private String type; - - @JsonProperty("site_admin") - private Boolean siteAdmin; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/File.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/File.java deleted file mode 100644 index 5279069781..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/File.java +++ /dev/null @@ -1,40 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class File implements Serializable { - - private String sha; - - private String filename; - - private String status; - - private Integer additions; - - private Integer deletions; - - private Integer changes; - - @JsonProperty("blob_url") - private String blobUrl; - - @JsonProperty("raw_url") - private String rawUrl; - - @JsonProperty("contents_url") - private String contentsUrl; - - private String patch; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubPull.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubPull.java deleted file mode 100644 index 5ff0372e2b..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubPull.java +++ /dev/null @@ -1,20 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class GitHubPull { - - private String createdAt; - - private String mergedAt; - - private Integer number; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Head.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Head.java deleted file mode 100644 index dab44a8da7..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Head.java +++ /dev/null @@ -1,26 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Head implements Serializable { - - private String label; - - private String ref; - - private String sha; - - private User user; - - private Repo repo; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Html.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Html.java deleted file mode 100644 index c9f7b0d42c..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Html.java +++ /dev/null @@ -1,18 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Html implements Serializable { - - private String href; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Issue.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Issue.java deleted file mode 100644 index b935061bb3..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Issue.java +++ /dev/null @@ -1,18 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Issue implements Serializable { - - private String href; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/License.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/License.java deleted file mode 100644 index 46ce69b932..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/License.java +++ /dev/null @@ -1,29 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class License implements Serializable { - - private String key; - - private String name; - - @JsonProperty("spdx_id") - private String spdxId; - - private String url; - - @JsonProperty("node_id") - private String nodeId; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/LinkCollection.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/LinkCollection.java deleted file mode 100644 index 5caf031822..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/LinkCollection.java +++ /dev/null @@ -1,35 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class LinkCollection implements Serializable { - - private Self self; - - private Html html; - - private Issue issue; - - private Comment comment; - - @JsonProperty("review_comments") - private ReviewComments reviewComments; - - @JsonProperty("review_comment") - private ReviewComment reviewComment; - - private Commits commits; - - private Status status; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Owner.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Owner.java deleted file mode 100644 index 24fd8a1a92..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Owner.java +++ /dev/null @@ -1,67 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Owner implements Serializable { - - private String login; - - private Integer id; - - @JsonProperty("node_id") - private String nodeId; - - @JsonProperty("avatar_url") - private String avatarUrl; - - @JsonProperty("gravatar_id") - private String gravatarId; - - private String url; - - @JsonProperty("html_url") - private String htmlUrl; - - @JsonProperty("followers_url") - private String followersUrl; - - @JsonProperty("following_url") - private String followingUrl; - - @JsonProperty("gists_url") - private String gistsUrl; - - @JsonProperty("starred_url") - private String starredUrl; - - @JsonProperty("subscriptions_url") - private String subscriptionsUrl; - - @JsonProperty("organizations_url") - private String organizationsUrl; - - @JsonProperty("repos_url") - private String reposUrl; - - @JsonProperty("events_url") - private String eventsUrl; - - @JsonProperty("received_events_url") - private String receivedEventsUrl; - - private String type; - - @JsonProperty("site_admin") - private Boolean siteAdmin; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Parent.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Parent.java deleted file mode 100644 index 291a39b40f..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Parent.java +++ /dev/null @@ -1,24 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Parent implements Serializable { - - private String sha; - - private String url; - - @JsonProperty("html_url") - private String htmlUrl; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/PullRequestInfo.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/PullRequestInfo.java index 2b2f06ee99..5572cf6cd9 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/PullRequestInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/PullRequestInfo.java @@ -3,13 +3,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; import java.io.Serializable; -@Data +@Setter @Builder @NoArgsConstructor @AllArgsConstructor @@ -18,6 +18,8 @@ public class PullRequestInfo implements Serializable { private Integer number; + private String url; + @JsonProperty("created_at") private String createdAt; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Repo.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Repo.java deleted file mode 100644 index ee55d42092..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Repo.java +++ /dev/null @@ -1,240 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; -import java.util.List; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Repo implements Serializable { - - private Integer id; - - @JsonProperty("node_id") - private String nodeId; - - private String name; - - @JsonProperty("full_name") - private String fullName; - - @JsonProperty("private") - private Boolean _private; - - private Owner owner; - - @JsonProperty("html_url") - private String htmlUrl; - - private String description; - - private Boolean fork; - - private String url; - - @JsonProperty("forks_url") - private String forksUrl; - - @JsonProperty("keys_url") - private String keysUrl; - - @JsonProperty("collaborators_url") - private String collaboratorsUrl; - - @JsonProperty("teams_url") - private String teamsUrl; - - @JsonProperty("hooks_url") - private String hooksUrl; - - @JsonProperty("issue_events_url") - private String issueEventsUrl; - - @JsonProperty("events_url") - private String eventsUrl; - - @JsonProperty("assignees_url") - private String assigneesUrl; - - @JsonProperty("branches_url") - private String branchesUrl; - - @JsonProperty("tags_url") - private String tagsUrl; - - @JsonProperty("blobs_url") - private String blobsUrl; - - @JsonProperty("git_tags_url") - private String gitTagsUrl; - - @JsonProperty("git_refs_url") - private String gitRefsUrl; - - @JsonProperty("trees_url") - private String treesUrl; - - @JsonProperty("statuses_url") - private String statusesUrl; - - @JsonProperty("languages_url") - private String languagesUrl; - - @JsonProperty("stargazers_url") - private String stargazersUrl; - - @JsonProperty("contributors_url") - private String contributorsUrl; - - @JsonProperty("subscribers_url") - private String subscribersUrl; - - @JsonProperty("subscription_url") - private String subscriptionUrl; - - @JsonProperty("commits_url") - private String commitsUrl; - - @JsonProperty("git_commits_url") - private String gitCommitsUrl; - - @JsonProperty("comments_url") - private String commentsUrl; - - @JsonProperty("issue_comment_url") - private String issueCommentUrl; - - @JsonProperty("contents_url") - private String contentsUrl; - - @JsonProperty("compare_url") - private String compareUrl; - - @JsonProperty("merges_url") - private String mergesUrl; - - @JsonProperty("archive_url") - private String archiveUrl; - - @JsonProperty("downloads_url") - private String downloadsUrl; - - @JsonProperty("issues_url") - private String issuesUrl; - - @JsonProperty("pulls_url") - private String pullsUrl; - - @JsonProperty("milestones_url") - private String milestonesUrl; - - @JsonProperty("notifications_url") - private String notificationsUrl; - - @JsonProperty("labels_url") - private String labelsUrl; - - @JsonProperty("releases_url") - private String releasesUrl; - - @JsonProperty("deployments_url") - private String deploymentsUrl; - - @JsonProperty("created_at") - private String createdAt; - - @JsonProperty("updated_at") - private String updatedAt; - - @JsonProperty("pushed_at") - private String pushedAt; - - @JsonProperty("git_url") - private String gitUrl; - - @JsonProperty("ssh_url") - private String sshUrl; - - @JsonProperty("clone_url") - private String cloneUrl; - - @JsonProperty("svn_url") - private String svnUrl; - - private String homepage; - - private Integer size; - - @JsonProperty("stargazers_count") - private Integer stargazersCount; - - @JsonProperty("watchers_count") - private Integer watchersCount; - - private String language; - - @JsonProperty("has_issues") - private Boolean hasIssues; - - @JsonProperty("has_projects") - private Boolean hasProjects; - - @JsonProperty("has_downloads") - private Boolean hasDownloads; - - @JsonProperty("has_wiki") - private Boolean hasWiki; - - @JsonProperty("has_pages") - private Boolean hasPages; - - @JsonProperty("has_discussions") - private Boolean hasDiscussions; - - @JsonProperty("forks_count") - private Integer forksCount; - - @JsonProperty("mirror_url") - private Object mirrorUrl; - - @JsonProperty("archived") - private Boolean archived; - - private Boolean disabled; - - @JsonProperty("open_issues_count") - private Integer openIssuesCount; - - private License license; - - @JsonProperty("allow_forking") - private Boolean allowForking; - - @JsonProperty("is_template") - private Boolean isTemplate; - - @JsonProperty("web_commit_signoff_required") - private Boolean webCommitSignoffRequired; - - private List topics; - - private String visibility; - - private Integer forks; - - @JsonProperty("open_issues") - private Integer openIssues; - - private Integer watchers; - - @JsonProperty("default_branch") - private String defaultBranch; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComment.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComment.java deleted file mode 100644 index 52d50eff3e..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComment.java +++ /dev/null @@ -1,18 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ReviewComment implements Serializable { - - private String href; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComments.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComments.java deleted file mode 100644 index b040cf3f29..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComments.java +++ /dev/null @@ -1,18 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ReviewComments implements Serializable { - - private String href; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Self.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Self.java deleted file mode 100644 index 20aa3b6afb..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Self.java +++ /dev/null @@ -1,18 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Self implements Serializable { - - private String href; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Stats.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Stats.java deleted file mode 100644 index ce1dd29918..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Stats.java +++ /dev/null @@ -1,22 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Stats implements Serializable { - - private Integer total; - - private Integer additions; - - private Integer deletions; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Status.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Status.java deleted file mode 100644 index 59d115d7f9..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Status.java +++ /dev/null @@ -1,18 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Status implements Serializable { - - private String href; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Tree.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Tree.java deleted file mode 100644 index 2bb563eed6..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Tree.java +++ /dev/null @@ -1,20 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Tree implements Serializable { - - private String sha; - - private String url; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/User.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/User.java deleted file mode 100644 index 4d82094d20..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/User.java +++ /dev/null @@ -1,67 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class User implements Serializable { - - private String login; - - private Integer id; - - @JsonProperty("node_id") - private String nodeId; - - @JsonProperty("avatar_url") - private String avatarUrl; - - @JsonProperty("gravatar_id") - private String gravatarId; - - private String url; - - @JsonProperty("html_url") - private String htmlUrl; - - @JsonProperty("followers_url") - private String followersUrl; - - @JsonProperty("following_url") - private String followingUrl; - - @JsonProperty("gists_url") - private String gistsUrl; - - @JsonProperty("starred_url") - private String starredUrl; - - @JsonProperty("subscriptions_url") - private String subscriptionsUrl; - - @JsonProperty("organizations_url") - private String organizationsUrl; - - @JsonProperty("repos_url") - private String reposUrl; - - @JsonProperty("events_url") - private String eventsUrl; - - @JsonProperty("received_events_url") - private String receivedEventsUrl; - - private String type; - - @JsonProperty("site_admin") - private Boolean siteAdmin; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Verification.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Verification.java deleted file mode 100644 index 5991419b87..0000000000 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Verification.java +++ /dev/null @@ -1,24 +0,0 @@ -package heartbeat.client.dto.codebase.github; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Verification implements Serializable { - - private Boolean verified; - - private String reason; - - private String signature; - - private String payload; - -} diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKitePipelineDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKitePipelineDTO.java index 4c6e0ac6bf..0d9e765205 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKitePipelineDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKitePipelineDTO.java @@ -1,14 +1,12 @@ package heartbeat.client.dto.pipeline.buildkite; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; -import java.util.Date; import java.util.List; @Data @@ -18,85 +16,12 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class BuildKitePipelineDTO implements Serializable { - private String id; - - @JsonProperty("graphql_id") - private String graphqlId; - - private String url; - - @JsonProperty("web_url") - private String webUrl; - private String name; - private String description; - private String slug; private String repository; - @JsonProperty("cluster_id") - private String clusterId; - - @JsonProperty("branch_configuration") - private String branchConfiguration; - - @JsonProperty("default_branch") - private String defaultBranch; - - @JsonProperty("skip_queued_branch_builds") - private String skipQueuedBranchBuilds; - - @JsonProperty("skip_queued_branch_builds_filter") - private String skipQueuedBranchBuildsFilter; - - @JsonProperty("cancel_running_branch_builds") - private String cancelRunningBranchBuilds; - - @JsonProperty("cancel_running_branch_builds_filter") - private String cancelRunningBranchBuildsFilter; - - @JsonProperty("allow_rebuilds") - private String allowRebuilds; - - private ProviderDTO provider; - - @JsonProperty("builds_url") - private String buildsUrl; - - @JsonProperty("badge_url") - private String badgeUrl; - - private CreatedByDTO createdBy; - - @JsonProperty("created_at") - private Date createdAt; - - @JsonProperty("archived_at") - private Date archivedAt; - - private EnvDTO env; - - @JsonProperty("scheduled_builds_count") - private int scheduledBuildsCount; - - @JsonProperty("running_builds_count") - private int runningBuildsCount; - - @JsonProperty("scheduled_jobs_count") - private int scheduledJobsCount; - - @JsonProperty("running_jobs_count") - private int runningJobsCount; - - @JsonProperty("waiting_jobs_count") - private int waitingJobsCount; - - private String visibility; - - private List tags; - private List steps; } diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/PageBuildKitePipelineInfoDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/PageBuildKitePipelineInfoDTO.java new file mode 100644 index 0000000000..1f0071b361 --- /dev/null +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/PageBuildKitePipelineInfoDTO.java @@ -0,0 +1,23 @@ +package heartbeat.client.dto.pipeline.buildkite; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PageBuildKitePipelineInfoDTO implements Serializable { + + private int totalPage; + + private List firstPageInfo; + +} diff --git a/backend/src/main/java/heartbeat/config/CacheConfig.java b/backend/src/main/java/heartbeat/config/CacheConfig.java index 5f0f03c7c4..3ef122d294 100644 --- a/backend/src/main/java/heartbeat/config/CacheConfig.java +++ b/backend/src/main/java/heartbeat/config/CacheConfig.java @@ -2,19 +2,14 @@ import heartbeat.client.dto.board.jira.CardHistoryResponseDTO; import heartbeat.client.dto.board.jira.FieldResponseDTO; +import heartbeat.client.dto.board.jira.HolidaysResponseDTO; import heartbeat.client.dto.board.jira.JiraBoardConfigDTO; import heartbeat.client.dto.board.jira.JiraBoardProject; import heartbeat.client.dto.board.jira.JiraBoardVerifyDTO; import heartbeat.client.dto.board.jira.StatusSelfDTO; -import java.time.Duration; -import java.util.List; -import javax.cache.CacheManager; -import javax.cache.Caching; -import javax.cache.spi.CachingProvider; - -import heartbeat.client.dto.board.jira.HolidaysResponseDTO; import heartbeat.client.dto.codebase.github.CommitInfo; import heartbeat.client.dto.pipeline.buildkite.BuildKiteTokenInfo; +import heartbeat.client.dto.pipeline.buildkite.PageBuildKitePipelineInfoDTO; import heartbeat.client.dto.pipeline.buildkite.PageStepsInfoDto; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.ExpiryPolicyBuilder; @@ -25,6 +20,12 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import javax.cache.CacheManager; +import javax.cache.Caching; +import javax.cache.spi.CachingProvider; +import java.time.Duration; +import java.util.List; + @Configuration @EnableCaching public class CacheConfig { @@ -45,7 +46,7 @@ public CacheManager ehCacheManager() { cacheManager.createCache("holidayResult", getCacheConfiguration(HolidaysResponseDTO.class)); cacheManager.createCache("tokenInfo", getCacheConfiguration(BuildKiteTokenInfo.class)); cacheManager.createCache("buildKiteOrganizationInfo", getCacheConfiguration(List.class)); - cacheManager.createCache("pipelineInfo", getCacheConfiguration(List.class)); + cacheManager.createCache("pagePipelineInfo", getCacheConfiguration(PageBuildKitePipelineInfoDTO.class)); cacheManager.createCache("pageStepsInfo", getCacheConfiguration(PageStepsInfoDto.class)); cacheManager.createCache("pipelineStepsInfo", getCacheConfiguration(List.class)); cacheManager.createCache("githubOrganizationInfo", getCacheConfiguration(List.class)); diff --git a/backend/src/main/java/heartbeat/config/SwaggerConfig.java b/backend/src/main/java/heartbeat/config/SwaggerConfig.java index 2c6fe9c26e..b3a7e323b6 100644 --- a/backend/src/main/java/heartbeat/config/SwaggerConfig.java +++ b/backend/src/main/java/heartbeat/config/SwaggerConfig.java @@ -4,6 +4,8 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.servers.Server; +import org.springframework.beans.factory.annotation.Value; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -12,11 +14,14 @@ @Configuration public class SwaggerConfig { + @Value("${heartbeat.swagger.host}") + private String swaggerHost; + @Bean public OpenAPI customOpenAPI() { return new OpenAPI().components(new Components()) .info(new Info().title("Backend API").version("1.0")) - .servers(List.of(new Server().url("http://13.214.14.43:4321/api/v1"))); + .servers(List.of(new Server().url(String.format("%s/api/v1", this.swaggerHost)))); } } diff --git a/backend/src/main/java/heartbeat/controller/board/dto/request/CardStepsEnum.java b/backend/src/main/java/heartbeat/controller/board/dto/request/CardStepsEnum.java index e41b4d5383..7ee6bc3394 100644 --- a/backend/src/main/java/heartbeat/controller/board/dto/request/CardStepsEnum.java +++ b/backend/src/main/java/heartbeat/controller/board/dto/request/CardStepsEnum.java @@ -1,21 +1,32 @@ package heartbeat.controller.board.dto.request; +import java.util.Map; +import java.util.Set; + public enum CardStepsEnum { - TODO("To do"), ANALYSE("Analysis"), DEVELOPMENT("In Dev"), BLOCK("Block"), TESTING("Testing"), REVIEW("Review"), - DONE("Done"), CLOSED("Closed"), WAITING("Waiting for testing"), FLAG("FLAG"), REMOVEFLAG("removeFlag"), - UNKNOWN("UNKNOWN"); + TODO("To do", "To do"), ANALYSE("Analysis", "Analysis"), DEVELOPMENT("In Dev", "In dev"), BLOCK("Block", "Block"), + FLAG("FLAG", "Flag"), REMOVEFLAG("removeFlag", "Remove flag"), REVIEW("Review", "Review"), + WAITING("Waiting for testing", "Waiting for testing"), TESTING("Testing", "Testing"), DONE("Done", "Done"), + CLOSED("Closed", "Closed"), UNKNOWN("UNKNOWN", "Unknown"); private final String value; - CardStepsEnum(String value) { + private final String alias; + + CardStepsEnum(String value, String alias) { this.value = value; + this.alias = alias; } public String getValue() { return value; } + public String getAlias() { + return alias; + } + public static CardStepsEnum fromValue(String type) { for (CardStepsEnum cardStepsEnum : values()) { if (cardStepsEnum.value.equals(type)) { @@ -25,4 +36,10 @@ public static CardStepsEnum fromValue(String type) { throw new IllegalArgumentException("Type does not find!"); } + public static final Map> reworkJudgmentMap = Map.of(TODO, + Set.of(ANALYSE, DEVELOPMENT, BLOCK, FLAG, REVIEW, WAITING, TESTING, DONE), ANALYSE, + Set.of(DEVELOPMENT, BLOCK, FLAG, REVIEW, WAITING, TESTING, DONE), DEVELOPMENT, + Set.of(BLOCK, FLAG, REVIEW, WAITING, TESTING, DONE), BLOCK, Set.of(REVIEW, WAITING, TESTING, DONE), REVIEW, + Set.of(WAITING, TESTING, DONE), WAITING, Set.of(TESTING, DONE), TESTING, Set.of(DONE)); + } diff --git a/backend/src/main/java/heartbeat/controller/board/dto/request/ReworkTimesSetting.java b/backend/src/main/java/heartbeat/controller/board/dto/request/ReworkTimesSetting.java new file mode 100644 index 0000000000..599fa54907 --- /dev/null +++ b/backend/src/main/java/heartbeat/controller/board/dto/request/ReworkTimesSetting.java @@ -0,0 +1,26 @@ +package heartbeat.controller.board.dto.request; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@Builder +public class ReworkTimesSetting { + + private String reworkState; + + private List excludedStates; + + public CardStepsEnum getEnumReworkState() { + return CardStepsEnum.fromValue(reworkState); + } + + public List getEnumExcludeStates() { + return excludedStates.stream().map(CardStepsEnum::fromValue).toList(); + } + +} diff --git a/backend/src/main/java/heartbeat/controller/board/dto/request/StoryPointsAndCycleTimeRequest.java b/backend/src/main/java/heartbeat/controller/board/dto/request/StoryPointsAndCycleTimeRequest.java index 62d208f250..51226a9d14 100644 --- a/backend/src/main/java/heartbeat/controller/board/dto/request/StoryPointsAndCycleTimeRequest.java +++ b/backend/src/main/java/heartbeat/controller/board/dto/request/StoryPointsAndCycleTimeRequest.java @@ -36,4 +36,6 @@ public class StoryPointsAndCycleTimeRequest { private boolean treatFlagCardAsBlock; + private ReworkTimesSetting reworkTimesSetting; + } diff --git a/backend/src/main/java/heartbeat/controller/board/dto/response/CardCollection.java b/backend/src/main/java/heartbeat/controller/board/dto/response/CardCollection.java index 29902daaf4..95d0d467b5 100644 --- a/backend/src/main/java/heartbeat/controller/board/dto/response/CardCollection.java +++ b/backend/src/main/java/heartbeat/controller/board/dto/response/CardCollection.java @@ -19,4 +19,8 @@ public class CardCollection { private List jiraCardDTOList; + private int reworkCardNumber; + + private double reworkRatio; + } diff --git a/backend/src/main/java/heartbeat/controller/board/dto/response/JiraCardDTO.java b/backend/src/main/java/heartbeat/controller/board/dto/response/JiraCardDTO.java index 4c39dc2798..0e07792d13 100644 --- a/backend/src/main/java/heartbeat/controller/board/dto/response/JiraCardDTO.java +++ b/backend/src/main/java/heartbeat/controller/board/dto/response/JiraCardDTO.java @@ -8,9 +8,12 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; @Data @Builder @@ -26,8 +29,14 @@ public class JiraCardDTO { private CardCycleTime cardCycleTime; + private List reworkTimesInfos; + + private Integer totalReworkTimes; + private Object cycleTimeFlat; + private Object reworkTimesFlat; + @Nullable private String totalCycleTimeDivideStoryPoints; @@ -56,4 +65,22 @@ public Object buildCycleTimeFlatObject() { return cycleTimeFlat; } + @JsonIgnore + public Object buildReworkTimesFlatObject() { + if (CollectionUtils.isEmpty(this.getReworkTimesInfos())) { + return null; + } + Map reworkTimesMap = this.getReworkTimesInfos() + .stream() + .collect(Collectors.toMap(reworkTimesInfo -> reworkTimesInfo.getState().getAlias(), + ReworkTimesInfo::getTimes)); + reworkTimesMap.put("totalReworkTimes", totalReworkTimes); + return reworkTimesMap; + } + + @JsonIgnore + public void calculateTotalReworkTimes() { + this.totalReworkTimes = reworkTimesInfos.stream().mapToInt(ReworkTimesInfo::getTimes).sum(); + } + } diff --git a/backend/src/main/java/heartbeat/controller/board/dto/response/ReworkTimesInfo.java b/backend/src/main/java/heartbeat/controller/board/dto/response/ReworkTimesInfo.java new file mode 100644 index 0000000000..3318620be4 --- /dev/null +++ b/backend/src/main/java/heartbeat/controller/board/dto/response/ReworkTimesInfo.java @@ -0,0 +1,19 @@ +package heartbeat.controller.board.dto.response; + +import heartbeat.controller.board.dto.request.CardStepsEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@Builder +public class ReworkTimesInfo { + + private CardStepsEnum state; + + private Integer times; + +} diff --git a/backend/src/main/java/heartbeat/controller/report/ReportController.java b/backend/src/main/java/heartbeat/controller/report/ReportController.java index 7f28eaa25b..cfb7f506cc 100644 --- a/backend/src/main/java/heartbeat/controller/report/ReportController.java +++ b/backend/src/main/java/heartbeat/controller/report/ReportController.java @@ -2,7 +2,6 @@ import heartbeat.controller.report.dto.request.GenerateReportRequest; import heartbeat.controller.report.dto.request.ReportType; -import heartbeat.controller.report.dto.request.MetricType; import heartbeat.controller.report.dto.response.CallbackResponse; import heartbeat.controller.report.dto.response.ReportResponse; import heartbeat.service.report.GenerateReporterService; @@ -54,23 +53,15 @@ public InputStreamResource exportCSV( public ResponseEntity generateReport(@PathVariable String reportId) { log.info("Start to generate report_reportId: {}", reportId); ReportResponse reportResponse = generateReporterService.getComposedReportResponse(reportId); - if (reportResponse.isAllMetricsCompleted()) { - log.info("Successfully generate Report_reportId: {}, reports: {}", reportId, reportResponse); - generateReporterService.generateCSVForMetric(reportResponse, reportId); - return ResponseEntity.status(HttpStatus.CREATED).body(reportResponse); - } return ResponseEntity.status(HttpStatus.OK).body(reportResponse); } - @PostMapping("{metricType}") - public ResponseEntity generateReport( - @Schema(type = "string", allowableValues = { "board", "dora" }, - accessMode = Schema.AccessMode.READ_ONLY) @PathVariable MetricType metricType, - @RequestBody GenerateReportRequest request) { - log.info("Start to generate report_metricType: {}", metricType); - reportService.generateReportByType(request, metricType); + @PostMapping + public ResponseEntity generateReport(@RequestBody GenerateReportRequest request) { + log.info("Start to generate report"); + reportService.generateReport(request); String callbackUrl = "/reports/" + request.getCsvTimeStamp(); - log.info("Successfully generate report_metricsType: {}", metricType); + log.info("Successfully generate report"); return ResponseEntity.status(HttpStatus.ACCEPTED) .body(CallbackResponse.builder().callbackUrl(callbackUrl).interval(interval).build()); } diff --git a/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateReportRequest.java b/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateReportRequest.java index 96c273cd5e..5444a78e7e 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateReportRequest.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateReportRequest.java @@ -29,6 +29,8 @@ public class GenerateReportRequest { private List metrics; + private List metricTypes; + private JiraBoardSetting jiraBoardSetting; private BuildKiteSetting buildKiteSetting; @@ -40,7 +42,10 @@ public class GenerateReportRequest { @JsonIgnore public List getPipelineMetrics() { - return this.metrics.stream().map(String::toLowerCase).filter(MetricsUtil.buildKiteMetrics::contains).toList(); + return this.metrics.stream() + .map(String::toLowerCase) + .filter(MetricsUtil.BUILDKITE_METRICS.getValue()::contains) + .toList(); } public List getMetrics() { @@ -49,12 +54,18 @@ public List getMetrics() { @JsonIgnore public List getSourceControlMetrics() { - return this.metrics.stream().map(String::toLowerCase).filter(MetricsUtil.codebaseMetrics::contains).toList(); + return this.metrics.stream() + .map(String::toLowerCase) + .filter(MetricsUtil.CODEBASE_METRICS.getValue()::contains) + .toList(); } @JsonIgnore public List getBoardMetrics() { - return this.metrics.stream().map(String::toLowerCase).filter(MetricsUtil.kanbanMetrics::contains).toList(); + return this.metrics.stream() + .map(String::toLowerCase) + .filter(MetricsUtil.KANBAN_METRICS.getValue()::contains) + .toList(); } @JsonIgnore @@ -72,11 +83,6 @@ public String getBoardReportId() { return IdUtil.getBoardReportId(this.csvTimeStamp); } - @JsonIgnore - public String getDoraReportId() { - return IdUtil.getDoraReportId(this.csvTimeStamp); - } - @JsonIgnore public GenerateReportRequest toPipelineRequest() { return GenerateReportRequest.builder() diff --git a/backend/src/main/java/heartbeat/controller/report/dto/request/JiraBoardSetting.java b/backend/src/main/java/heartbeat/controller/report/dto/request/JiraBoardSetting.java index 6b3a1ae0d7..7a05c9061c 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/request/JiraBoardSetting.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/request/JiraBoardSetting.java @@ -1,6 +1,7 @@ package heartbeat.controller.report.dto.request; import heartbeat.controller.board.dto.request.RequestJiraBoardColumnSetting; +import heartbeat.controller.board.dto.request.ReworkTimesSetting; import heartbeat.controller.board.dto.response.TargetField; import lombok.AllArgsConstructor; import lombok.Builder; @@ -39,4 +40,6 @@ public class JiraBoardSetting { private List overrideFields; + private ReworkTimesSetting reworkTimesSetting; + } diff --git a/backend/src/main/java/heartbeat/controller/report/dto/request/MetricEnum.java b/backend/src/main/java/heartbeat/controller/report/dto/request/MetricEnum.java index 82139aae22..caf477f831 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/request/MetricEnum.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/request/MetricEnum.java @@ -3,8 +3,9 @@ public enum MetricEnum { VELOCITY("velocity"), CYCLE_TIME("cycle time"), CLASSIFICATION("classification"), - DEPLOYMENT_FREQUENCY("deployment frequency"), CHANGE_FAILURE_RATE("change failure rate"), - MEAN_TIME_TO_RECOVERY("mean time to recovery"), LEAD_TIME_FOR_CHANGES("lead time for changes"); + DEPLOYMENT_FREQUENCY("deployment frequency"), DEV_CHANGE_FAILURE_RATE("dev change failure rate"), + DEV_MEAN_TIME_TO_RECOVERY("dev mean time to recovery"), LEAD_TIME_FOR_CHANGES("lead time for changes"), + REWORK_TIMES("rework times"); private final String value; diff --git a/backend/src/main/java/heartbeat/controller/report/dto/response/AvgChangeFailureRate.java b/backend/src/main/java/heartbeat/controller/report/dto/response/AvgDevChangeFailureRate.java similarity index 90% rename from backend/src/main/java/heartbeat/controller/report/dto/response/AvgChangeFailureRate.java rename to backend/src/main/java/heartbeat/controller/report/dto/response/AvgDevChangeFailureRate.java index ecfc2826c4..fa3c4c6b10 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/response/AvgChangeFailureRate.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/response/AvgDevChangeFailureRate.java @@ -9,7 +9,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class AvgChangeFailureRate { +public class AvgDevChangeFailureRate { @Builder.Default private String name = "Average"; diff --git a/backend/src/main/java/heartbeat/controller/report/dto/response/AvgMeanTimeToRecovery.java b/backend/src/main/java/heartbeat/controller/report/dto/response/AvgDevMeanTimeToRecovery.java similarity index 75% rename from backend/src/main/java/heartbeat/controller/report/dto/response/AvgMeanTimeToRecovery.java rename to backend/src/main/java/heartbeat/controller/report/dto/response/AvgDevMeanTimeToRecovery.java index e828c92c10..093955b721 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/response/AvgMeanTimeToRecovery.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/response/AvgDevMeanTimeToRecovery.java @@ -10,9 +10,10 @@ @NoArgsConstructor @AllArgsConstructor @Builder -public class AvgMeanTimeToRecovery { +public class AvgDevMeanTimeToRecovery { - private final String name = "Average"; + @Builder.Default + private String name = "Average"; private BigDecimal timeToRecovery; diff --git a/backend/src/main/java/heartbeat/controller/report/dto/response/ChangeFailureRate.java b/backend/src/main/java/heartbeat/controller/report/dto/response/DevChangeFailureRate.java similarity index 58% rename from backend/src/main/java/heartbeat/controller/report/dto/response/ChangeFailureRate.java rename to backend/src/main/java/heartbeat/controller/report/dto/response/DevChangeFailureRate.java index 0faf01c901..8b9ee253b6 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/response/ChangeFailureRate.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/response/DevChangeFailureRate.java @@ -11,10 +11,10 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class ChangeFailureRate { +public class DevChangeFailureRate { - private AvgChangeFailureRate avgChangeFailureRate; + private AvgDevChangeFailureRate avgDevChangeFailureRate; - private List changeFailureRateOfPipelines; + private List devChangeFailureRateOfPipelines; } diff --git a/backend/src/main/java/heartbeat/controller/report/dto/response/ChangeFailureRateOfPipeline.java b/backend/src/main/java/heartbeat/controller/report/dto/response/DevChangeFailureRateOfPipeline.java similarity index 88% rename from backend/src/main/java/heartbeat/controller/report/dto/response/ChangeFailureRateOfPipeline.java rename to backend/src/main/java/heartbeat/controller/report/dto/response/DevChangeFailureRateOfPipeline.java index 04cb80a73e..bd74a96871 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/response/ChangeFailureRateOfPipeline.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/response/DevChangeFailureRateOfPipeline.java @@ -9,7 +9,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class ChangeFailureRateOfPipeline { +public class DevChangeFailureRateOfPipeline { private String name; diff --git a/backend/src/main/java/heartbeat/controller/report/dto/response/MeanTimeToRecovery.java b/backend/src/main/java/heartbeat/controller/report/dto/response/DevMeanTimeToRecovery.java similarity index 57% rename from backend/src/main/java/heartbeat/controller/report/dto/response/MeanTimeToRecovery.java rename to backend/src/main/java/heartbeat/controller/report/dto/response/DevMeanTimeToRecovery.java index 812236594d..8c0a30270c 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/response/MeanTimeToRecovery.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/response/DevMeanTimeToRecovery.java @@ -10,10 +10,10 @@ @NoArgsConstructor @AllArgsConstructor @Builder -public class MeanTimeToRecovery { +public class DevMeanTimeToRecovery { - private AvgMeanTimeToRecovery avgMeanTimeToRecovery; + private AvgDevMeanTimeToRecovery avgDevMeanTimeToRecovery; - private List meanTimeRecoveryPipelines; + private List devMeanTimeToRecoveryOfPipelines; } diff --git a/backend/src/main/java/heartbeat/controller/report/dto/response/MeanTimeToRecoveryOfPipeline.java b/backend/src/main/java/heartbeat/controller/report/dto/response/DevMeanTimeToRecoveryOfPipeline.java similarity index 58% rename from backend/src/main/java/heartbeat/controller/report/dto/response/MeanTimeToRecoveryOfPipeline.java rename to backend/src/main/java/heartbeat/controller/report/dto/response/DevMeanTimeToRecoveryOfPipeline.java index b5a1df9d00..2462864884 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/response/MeanTimeToRecoveryOfPipeline.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/response/DevMeanTimeToRecoveryOfPipeline.java @@ -1,23 +1,21 @@ package heartbeat.controller.report.dto.response; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.math.BigDecimal; + @Data @NoArgsConstructor @AllArgsConstructor @Builder -public class MeanTimeToRecoveryOfPipeline { +public class DevMeanTimeToRecoveryOfPipeline { - @JsonProperty("name") - private String pipelineName; + private String name; - @JsonProperty("step") - private String pipelineStep; + private String step; private BigDecimal timeToRecovery; diff --git a/backend/src/main/java/heartbeat/controller/report/dto/response/MetricsDataCompleted.java b/backend/src/main/java/heartbeat/controller/report/dto/response/MetricsDataCompleted.java index bea9b9ef2c..c3fc9fe488 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/response/MetricsDataCompleted.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/response/MetricsDataCompleted.java @@ -6,6 +6,8 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.Optional; + @Builder @NoArgsConstructor @AllArgsConstructor @@ -17,6 +19,10 @@ public class MetricsDataCompleted { private Boolean doraMetricsCompleted; + private Boolean overallMetricCompleted; + + private Boolean isSuccessfulCreateCsvFile; + public Boolean boardMetricsCompleted() { return boardMetricsCompleted; } @@ -25,4 +31,17 @@ public Boolean doraMetricsCompleted() { return doraMetricsCompleted; } + public Boolean overallMetricCompleted() { + return overallMetricCompleted; + } + + public Boolean isSuccessfulCreateCsvFile() { + return isSuccessfulCreateCsvFile; + } + + public Boolean allMetricsCompleted() { + return Optional.ofNullable(boardMetricsCompleted).orElse(true) + && Optional.ofNullable(doraMetricsCompleted).orElse(true) && overallMetricCompleted; + } + } diff --git a/backend/src/main/java/heartbeat/controller/report/dto/response/ReportResponse.java b/backend/src/main/java/heartbeat/controller/report/dto/response/ReportResponse.java index 9088295488..2ea152555c 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/response/ReportResponse.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/response/ReportResponse.java @@ -21,21 +21,27 @@ public class ReportResponse { private DeploymentFrequency deploymentFrequency; - private ChangeFailureRate changeFailureRate; + private DevChangeFailureRate devChangeFailureRate; - private MeanTimeToRecovery meanTimeToRecovery; + private DevMeanTimeToRecovery devMeanTimeToRecovery; private LeadTimeForChanges leadTimeForChanges; private ReportMetricsError reportMetricsError; + private Rework rework; + private Long exportValidityTime; - private boolean boardMetricsCompleted; + private Boolean boardMetricsCompleted; + + private Boolean doraMetricsCompleted; + + private Boolean overallMetricsCompleted; - private boolean doraMetricsCompleted; + private Boolean allMetricsCompleted; - private boolean allMetricsCompleted; + private Boolean isSuccessfulCreateCsvFile; public ReportResponse(Long exportValidityTime) { this.exportValidityTime = exportValidityTime; diff --git a/backend/src/main/java/heartbeat/controller/report/dto/response/Rework.java b/backend/src/main/java/heartbeat/controller/report/dto/response/Rework.java new file mode 100644 index 0000000000..c3f462d278 --- /dev/null +++ b/backend/src/main/java/heartbeat/controller/report/dto/response/Rework.java @@ -0,0 +1,36 @@ +package heartbeat.controller.report.dto.response; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +public class Rework { + + private Integer totalReworkTimes; + + private String reworkState; + + private Integer fromAnalysis; + + private Integer fromInDev; + + private Integer fromBlock; + + private Integer fromWaitingForTesting; + + private Integer fromTesting; + + private Integer fromReview; + + private Integer fromDone; + + private Integer totalReworkCards; + + private Integer throughput; + + private Double reworkCardsRatio; + +} diff --git a/backend/src/main/java/heartbeat/handler/AsyncExceptionHandler.java b/backend/src/main/java/heartbeat/handler/AsyncExceptionHandler.java index fd51251e4a..3cb5c4e8c8 100644 --- a/backend/src/main/java/heartbeat/handler/AsyncExceptionHandler.java +++ b/backend/src/main/java/heartbeat/handler/AsyncExceptionHandler.java @@ -16,14 +16,14 @@ public class AsyncExceptionHandler extends AsyncDataBaseHandler { public void put(String reportId, BaseException e) { - createFileByType(ERROR, reportId, new Gson().toJson(e)); + createFileByType(ERROR, reportId, new Gson().toJson(new AsyncExceptionDTO(e))); } - public BaseException get(String reportId) { + public AsyncExceptionDTO get(String reportId) { return readFileByType(ERROR, reportId, AsyncExceptionDTO.class); } - public BaseException remove(String reportId) { + public AsyncExceptionDTO remove(String reportId) { return readAndRemoveFileByType(ERROR, reportId, AsyncExceptionDTO.class); } diff --git a/backend/src/main/java/heartbeat/handler/AsyncMetricsDataHandler.java b/backend/src/main/java/heartbeat/handler/AsyncMetricsDataHandler.java index 8d8b0ac166..0cfc91a082 100644 --- a/backend/src/main/java/heartbeat/handler/AsyncMetricsDataHandler.java +++ b/backend/src/main/java/heartbeat/handler/AsyncMetricsDataHandler.java @@ -5,18 +5,12 @@ import heartbeat.controller.report.dto.response.MetricsDataCompleted; import heartbeat.exception.GenerateReportException; import heartbeat.handler.base.AsyncDataBaseHandler; -import heartbeat.service.report.MetricsDataDTO; -import heartbeat.util.IdUtil; -import heartbeat.util.ValueUtil; -import jakarta.annotation.Nullable; import lombok.RequiredArgsConstructor; import lombok.Synchronized; import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Component; import java.io.File; -import java.util.Objects; -import java.util.stream.Stream; import static heartbeat.handler.base.FIleType.METRICS_DATA_COMPLETED; @@ -25,6 +19,8 @@ @RequiredArgsConstructor public class AsyncMetricsDataHandler extends AsyncDataBaseHandler { + private static final String GENERATE_REPORT_ERROR = "Failed to update metrics data completed through this timestamp."; + public void putMetricsDataCompleted(String timeStamp, MetricsDataCompleted metricsDataCompleted) { try { acquireLock(METRICS_DATA_COMPLETED, timeStamp); @@ -44,11 +40,15 @@ public void deleteExpireMetricsDataCompletedFile(long currentTimeStamp, File dir } @Synchronized - public void updateMetricsDataCompletedInHandler(String metricDataFileId, MetricType metricType) { + public void updateMetricsDataCompletedInHandler(String metricDataFileId, MetricType metricType, + boolean isCreateCsvSuccess) { MetricsDataCompleted previousMetricsCompleted = getMetricsDataCompleted(metricDataFileId); if (previousMetricsCompleted == null) { - log.error("Failed to update metrics data completed through this timestamp."); - throw new GenerateReportException("Failed to update metrics data completed through this timestamp."); + log.error(GENERATE_REPORT_ERROR); + throw new GenerateReportException(GENERATE_REPORT_ERROR); + } + if (isCreateCsvSuccess) { + previousMetricsCompleted.setIsSuccessfulCreateCsvFile(true); } switch (metricType) { case BOARD -> previousMetricsCompleted.setBoardMetricsCompleted(true); @@ -59,31 +59,14 @@ public void updateMetricsDataCompletedInHandler(String metricDataFileId, MetricT putMetricsDataCompleted(metricDataFileId, previousMetricsCompleted); } - public MetricsDataDTO getReportReadyStatusByTimeStamp(String timeStamp) { - Boolean boardReadyStatus = getReadyStatus(IdUtil.getBoardReportId(timeStamp), MetricType.BOARD); - boolean isBoardReady = ValueUtil.valueOrDefault(false, boardReadyStatus); - - Boolean doraReadyStatus = getReadyStatus(IdUtil.getDoraReportId(timeStamp), MetricType.DORA); - boolean isDoraReady = ValueUtil.valueOrDefault(false, doraReadyStatus); - - boolean isReportReady = Stream.of(boardReadyStatus, doraReadyStatus) - .filter(Objects::nonNull) - .allMatch(Boolean::booleanValue); - return new MetricsDataDTO(isBoardReady, isDoraReady, isReportReady); - } - - @Nullable - private Boolean getReadyStatus(String fileId, MetricType metricType) { - MetricsDataCompleted metricsDataCompleted = getMetricsDataCompleted(fileId); - if (metricsDataCompleted == null) { - return null; - } - else if (metricType == MetricType.BOARD) { - return metricsDataCompleted.boardMetricsCompleted(); - } - else { - return metricsDataCompleted.doraMetricsCompleted(); + public void updateOverallMetricsCompletedInHandler(String metricDataFileId) { + MetricsDataCompleted previousMetricsCompleted = getMetricsDataCompleted(metricDataFileId); + if (previousMetricsCompleted == null) { + log.error(GENERATE_REPORT_ERROR); + throw new GenerateReportException(GENERATE_REPORT_ERROR); } + previousMetricsCompleted.setOverallMetricCompleted(true); + putMetricsDataCompleted(metricDataFileId, previousMetricsCompleted); } } diff --git a/backend/src/main/java/heartbeat/handler/base/AsyncDataBaseHandler.java b/backend/src/main/java/heartbeat/handler/base/AsyncDataBaseHandler.java index aff961671c..9d382729af 100644 --- a/backend/src/main/java/heartbeat/handler/base/AsyncDataBaseHandler.java +++ b/backend/src/main/java/heartbeat/handler/base/AsyncDataBaseHandler.java @@ -16,7 +16,6 @@ import java.util.Objects; import java.util.Optional; -import static heartbeat.handler.base.FIleType.METRICS_DATA_COMPLETED; import static heartbeat.service.report.scheduler.DeleteExpireCSVScheduler.EXPORT_CSV_VALIDITY_TIME; @Log4j2 @@ -28,7 +27,7 @@ public class AsyncDataBaseHandler { public static final String SUFFIX_LOCK = ".lock"; - public static final String FILENAME_SPLIT_PATTERN = "\\s*\\-|\\.\\s*"; + public static final String FILENAME_SPLIT_PATTERN = "[-.]"; protected synchronized void createFileByType(FIleType fIleType, String fileId, String json) { createDirToConvertData(fIleType); @@ -95,12 +94,12 @@ protected T readAndRemoveFileByType(FIleType fIleType, String fileId, Class< } catch (IOException | RuntimeException e) { log.info("Failed remove file type: {}, file name: {}", fIleType.getType(), fileId); - throw new GenerateReportException("Failed remove " + fIleType.getType() + " file " + fileId); + throw new GenerateReportException("Failed remove " + fIleType.getType() + " file with file:" + fileId); } } else { throw new GenerateReportException( - "Failed remove " + fIleType.getType() + " file " + fileId + "invalid file name"); + "Failed read and remove " + fIleType.getType() + " file with file name :" + fileId); } } @@ -126,8 +125,7 @@ public void acquireLock(FIleType fIleType, String fileId) { } } else { - throw new GenerateReportException( - "Failed locked " + fIleType.getType() + " file " + fileId + "invalid file name"); + throw new GenerateReportException("Failed locked " + fIleType.getType() + " lock :" + fileId); } } @@ -149,8 +147,7 @@ protected void unLock(FIleType fIleType, String fileId) { } } else { - throw new GenerateReportException( - "Failed unlocked " + fIleType.getType() + " file " + fileId + "invalid file name"); + throw new GenerateReportException("Failed unlocked " + fIleType.getType() + " lock :" + fileId); } } diff --git a/backend/src/main/java/heartbeat/handler/base/AsyncExceptionDTO.java b/backend/src/main/java/heartbeat/handler/base/AsyncExceptionDTO.java index 8e39fb6e25..53a8095d84 100644 --- a/backend/src/main/java/heartbeat/handler/base/AsyncExceptionDTO.java +++ b/backend/src/main/java/heartbeat/handler/base/AsyncExceptionDTO.java @@ -1,11 +1,20 @@ package heartbeat.handler.base; import heartbeat.exception.BaseException; +import lombok.AllArgsConstructor; +import lombok.Data; -public class AsyncExceptionDTO extends BaseException { +@Data +@AllArgsConstructor +public class AsyncExceptionDTO { - public AsyncExceptionDTO(String message, int status) { - super(message, status); + private String message; + + private int status; + + public AsyncExceptionDTO(BaseException e) { + this.message = e.getMessage(); + this.status = e.getStatus(); } } diff --git a/backend/src/main/java/heartbeat/service/board/jira/JiraService.java b/backend/src/main/java/heartbeat/service/board/jira/JiraService.java index c9fc4b5830..b22b29a3dd 100644 --- a/backend/src/main/java/heartbeat/service/board/jira/JiraService.java +++ b/backend/src/main/java/heartbeat/service/board/jira/JiraService.java @@ -28,6 +28,7 @@ import heartbeat.controller.board.dto.request.BoardVerifyRequestParam; import heartbeat.controller.board.dto.request.CardStepsEnum; import heartbeat.controller.board.dto.request.RequestJiraBoardColumnSetting; +import heartbeat.controller.board.dto.request.ReworkTimesSetting; import heartbeat.controller.board.dto.request.StoryPointsAndCycleTimeRequest; import heartbeat.controller.board.dto.response.BoardConfigDTO; import heartbeat.controller.board.dto.response.CardCollection; @@ -38,6 +39,7 @@ import heartbeat.controller.board.dto.response.CycleTimeInfoDTO; import heartbeat.controller.board.dto.response.JiraCardDTO; import heartbeat.controller.board.dto.response.JiraColumnDTO; +import heartbeat.controller.board.dto.response.ReworkTimesInfo; import heartbeat.controller.board.dto.response.StatusChangedItem; import heartbeat.controller.board.dto.response.StepsDay; import heartbeat.controller.board.dto.response.TargetField; @@ -58,24 +60,35 @@ import org.springframework.util.CollectionUtils; import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.net.URI; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.EnumMap; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; +import static heartbeat.controller.board.dto.request.CardStepsEnum.BLOCK; +import static heartbeat.controller.board.dto.request.CardStepsEnum.FLAG; +import static heartbeat.controller.board.dto.request.CardStepsEnum.fromValue; +import static heartbeat.controller.board.dto.request.CardStepsEnum.reworkJudgmentMap; import static java.lang.Long.parseLong; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; @@ -98,6 +111,10 @@ public class JiraService { private static final String NONE_DONE_CARD_TAG = "nonDone"; + public static final String FLAGGED = "flagged"; + + public static final String IMPEDIMENT = "impediment"; + private final ThreadPoolTaskExecutor customTaskExecutor; private final JiraFeignClient jiraFeignClient; @@ -117,13 +134,18 @@ public void shutdownExecutor() { public String verify(BoardType boardType, BoardVerifyRequestParam boardVerifyRequestParam) { URI baseUrl = urlGenerator.getUri(boardVerifyRequestParam.getSite()); - if (!BoardType.JIRA.equals(boardType)) { - throw new BadRequestException("boardType param is not correct"); - } + verifyBoardTypeIsJira(boardType); + try { + jiraFeignClient.getDashboard(baseUrl, boardVerifyRequestParam.getToken()); + } + catch (NotFoundException e) { + throw new NotFoundException("site is incorrect"); + } try { JiraBoardVerifyDTO jiraBoardVerifyDTO = jiraFeignClient.getBoard(baseUrl, boardVerifyRequestParam.getBoardId(), boardVerifyRequestParam.getToken()); + return jiraBoardVerifyDTO.getLocation().getProjectKey(); } catch (NotFoundException e) { @@ -145,9 +167,7 @@ public String verify(BoardType boardType, BoardVerifyRequestParam boardVerifyReq public BoardConfigDTO getInfo(BoardType boardType, BoardRequestParam boardRequestParam) { URI baseUrl = urlGenerator.getUri(boardRequestParam.getSite()); try { - if (!BoardType.JIRA.equals(boardType)) { - throw new BadRequestException("boardType param is not correct"); - } + verifyBoardTypeIsJira(boardType); String jiraBoardStyle = jiraFeignClient .getProject(baseUrl, boardRequestParam.getProjectKey(), boardRequestParam.getToken()) .getStyle(); @@ -216,11 +236,17 @@ public BoardConfigDTO getJiraConfiguration(BoardType boardType, BoardRequestPara } } + private static void verifyBoardTypeIsJira(BoardType boardType) { + if (!BoardType.JIRA.equals(boardType)) { + throw new BadRequestException("boardType param is not correct"); + } + } + private boolean isIgnoredTargetField(TargetField targetField) { return (FIELDS_IGNORE.contains(targetField.getKey())) || FIELDS_IGNORE.contains(targetField.getName()); } - public CardCollection getStoryPointsAndCycleTimeForDoneCards(StoryPointsAndCycleTimeRequest request, + public CardCollection getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(StoryPointsAndCycleTimeRequest request, List boardColumns, List users, String assigneeFilter) { BoardType boardType = BoardType.fromValue(request.getType()); URI baseUrl = urlGenerator.getUri(request.getSite()); @@ -242,13 +268,27 @@ public CardCollection getStoryPointsAndCycleTimeForDoneCards(StoryPointsAndCycle } List realDoneCards = getRealDoneCards(request, boardColumns, users, baseUrl, allDoneCards, jiraCardWithFields.getTargetFields(), assigneeFilter); + double storyPointSum = realDoneCards.stream() .mapToDouble(card -> card.getBaseInfo().getFields().getStoryPoints()) .sum(); + int reworkCardNumber = realDoneCards.stream() + .filter(realDoneCard -> realDoneCard.getReworkTimesInfos() + .stream() + .anyMatch(reworkTimesInfo -> reworkTimesInfo.getTimes() != 0)) + .toList() + .size(); + double reworkRatio = realDoneCards.isEmpty() ? 0 + : BigDecimal.valueOf(reworkCardNumber) + .divide(BigDecimal.valueOf(realDoneCards.size()), 4, RoundingMode.HALF_UP) + .doubleValue(); + return CardCollection.builder() .storyPointSum(storyPointSum) .cardsNumber(realDoneCards.size()) + .reworkCardNumber(reworkCardNumber) + .reworkRatio(reworkRatio) .jiraCardDTOList(realDoneCards) .build(); } @@ -380,8 +420,7 @@ private JiraCardWithFields getAllCards(BoardType boardType, URI baseUrl, BoardRe private AllCardsResponseDTO formatAllCards(String allCardResponse, List targetFields, List overrideFields) { - Gson gson = new Gson(); - AllCardsResponseDTO allCardsResponseDTO = gson.fromJson(allCardResponse, AllCardsResponseDTO.class); + AllCardsResponseDTO allCardsResponseDTO = new Gson().fromJson(allCardResponse, AllCardsResponseDTO.class); List jiraCards = allCardsResponseDTO.getIssues(); JsonArray elements = JsonParser.parseString(allCardResponse).getAsJsonObject().get("issues").getAsJsonArray(); @@ -408,41 +447,40 @@ private AllCardsResponseDTO formatAllCards(String allCardResponse, List { + Sprint sprint = sprintMap.get(jiraCard.getKey()); + jiraCard.getFields().setSprint(sprint); + }); return allCardsResponseDTO; } - private static Map getCustomfieldMap(Gson gson, Map sprintMap, - Map resultMap, JsonElement element, JsonObject jsonElement) { + private Map getCustomFieldMap(JsonElement element, Map resultMap, + JsonObject jsonElement, Map sprintMap) { Map customFieldMap = new HashMap<>(); - for (Map.Entry entry : resultMap.entrySet()) { - String customFieldKey = entry.getKey(); - String customFieldValue = entry.getValue(); + resultMap.forEach((customFieldKey, customFieldValue) -> { if (jsonElement.has(customFieldKey)) { JsonElement fieldValue = jsonElement.get(customFieldKey); - if (customFieldValue.equals("Sprint") && !fieldValue.isJsonNull() && fieldValue.isJsonArray()) { - JsonArray jsonArray = fieldValue.getAsJsonArray(); - if (!jsonArray.isJsonNull() && !jsonArray.isEmpty()) { - Type listType = new TypeToken>() { - }.getType(); - List sprints = gson.fromJson(jsonArray, listType); - sprints.sort(Comparator.comparing(Sprint::getCompleteDate, - Comparator.nullsLast(Comparator.comparing(ZonedDateTime::parse)))); - sprintMap.put(element.getAsJsonObject().get("key").getAsString(), - sprints.get(sprints.size() - 1)); - } - } - else if (customFieldValue.equals("Story point estimate") && !fieldValue.isJsonNull() - && fieldValue.isJsonPrimitive()) { + fieldValue = mapFieldValue(element, sprintMap, customFieldValue, fieldValue); + customFieldMap.put(customFieldKey, fieldValue); + } + }); + return customFieldMap; + } + + private JsonElement mapFieldValue(JsonElement element, Map sprintMap, String customFieldValue, + JsonElement fieldValue) { + switch (customFieldValue) { + case "Sprint" -> Optional.ofNullable(getSprint(fieldValue)) + .ifPresentOrElse(it -> sprintMap.put(element.getAsJsonObject().get("key").getAsString(), it), () -> { + }); + case "Story point estimate" -> { + if (!fieldValue.isJsonNull() && fieldValue.isJsonPrimitive()) { JsonPrimitive jsonPrimitive = fieldValue.getAsJsonPrimitive(); if (jsonPrimitive.isNumber()) { Number numberValue = jsonPrimitive.getAsNumber(); @@ -450,17 +488,35 @@ else if (customFieldValue.equals("Story point estimate") && !fieldValue.isJsonNu fieldValue = new JsonPrimitive(doubleValue); } } - else if (customFieldValue.equals("Flagged") && !fieldValue.isJsonNull() && fieldValue.isJsonArray()) { + } + case "Flagged" -> { + if (!fieldValue.isJsonNull() && fieldValue.isJsonArray()) { JsonArray jsonArray = fieldValue.getAsJsonArray(); if (!jsonArray.isJsonNull() && !jsonArray.isEmpty()) { JsonElement targetField = jsonArray.get(jsonArray.size() - 1); fieldValue = targetField.getAsJsonObject().get("value"); } } - customFieldMap.put(customFieldKey, fieldValue); + } + default -> { } } - return customFieldMap; + return fieldValue; + } + + private Sprint getSprint(JsonElement fieldValue) { + if (!fieldValue.isJsonNull() && fieldValue.isJsonArray()) { + JsonArray jsonArray = fieldValue.getAsJsonArray(); + if (!jsonArray.isJsonNull() && !jsonArray.isEmpty()) { + Type listType = new TypeToken>() { + }.getType(); + List sprints = new Gson().fromJson(jsonArray, listType); + sprints.sort(Comparator.comparing(Sprint::getCompleteDate, + Comparator.nullsLast(Comparator.comparing(ZonedDateTime::parse)))); + return sprints.get(sprints.size() - 1); + } + } + return null; } private String parseJiraJql(BoardType boardType, List doneColumns, BoardRequestParam boardRequestParam) { @@ -554,9 +610,9 @@ private List getRealDoneCards(StoryPointsAndCycleTimeRequest reques jiraCards.forEach(doneCard -> { CardHistoryResponseDTO cardHistoryResponseDTO = getJiraCardHistory(baseUrl, doneCard.getKey(), 0, request.getToken()); + List assigneeSet = getAssigneeSet(cardHistoryResponseDTO, filterMethod, doneCard); CycleTimeInfoDTO cycleTimeInfoDTO = getCycleTime(cardHistoryResponseDTO, request.isTreatFlagCardAsBlock(), keyFlagged, request.getStatus()); - List assigneeSet = getAssigneeSet(cardHistoryResponseDTO, filterMethod, doneCard); if (users.stream().anyMatch(assigneeSet::contains)) { JiraCardDTO jiraCardDTO = JiraCardDTO.builder() .baseInfo(doneCard) @@ -564,13 +620,135 @@ private List getRealDoneCards(StoryPointsAndCycleTimeRequest reques .originCycleTime(cycleTimeInfoDTO.getOriginCycleTimeInfos()) .cardCycleTime(calculateCardCycleTime(doneCard.getKey(), cycleTimeInfoDTO.getCycleTimeInfos(), boardColumns)) + .reworkTimesInfos(getReworkTimesInfo(cardHistoryResponseDTO, request.getReworkTimesSetting(), + request.isTreatFlagCardAsBlock(), boardColumns)) .build(); + jiraCardDTO.calculateTotalReworkTimes(); realDoneCards.add(jiraCardDTO); } }); return realDoneCards; } + private List getReworkTimesInfo(CardHistoryResponseDTO jiraCardHistory, + ReworkTimesSetting reworkTimesSetting, boolean considerFlagAsBlock, + List boardColumns) { + if (Objects.isNull(reworkTimesSetting)) { + return List.of(); + } + Map stateMap = buildBoardStateMap(boardColumns); + if (considerFlagAsBlock) { + return getReworkTimesInfoWhenConsiderFlagAsBlock(jiraCardHistory, reworkTimesSetting.getEnumReworkState(), + new HashSet<>(reworkTimesSetting.getEnumExcludeStates()), stateMap); + } + else { + return getReworkTimesInfoWhenNotConsiderFlagAsBlock(jiraCardHistory, + reworkTimesSetting.getEnumReworkState(), new HashSet<>(reworkTimesSetting.getEnumExcludeStates()), + stateMap); + } + } + + private List getReworkTimesInfoWhenConsiderFlagAsBlock(CardHistoryResponseDTO jiraCardHistory, + CardStepsEnum reworkState, Set excludedStates, Map stateMap) { + Map reworkTimesMap = initializeReworkTimesMap(reworkState, excludedStates, stateMap); + reworkTimesMap.put(FLAG, 0); + AtomicReference currentState = new AtomicReference<>(); + AtomicBoolean hasFlag = new AtomicBoolean(false); + jiraCardHistory.getItems() + .stream() + .filter(jiraCardHistoryItem -> STATUS_FIELD_ID.equalsIgnoreCase(jiraCardHistoryItem.getFieldId()) + || FLAGGED.equalsIgnoreCase(jiraCardHistoryItem.getFieldDisplayName())) + .forEach(jiraCardHistoryItem -> { + if (STATUS_FIELD_ID.equalsIgnoreCase(jiraCardHistoryItem.getFieldId())) { + currentState + .set(convertBoardStateToEnumState(jiraCardHistoryItem.getTo().getDisplayName(), stateMap)); + if (!hasFlag.get()) { + calculateReworkTimesMap(reworkState, excludedStates, reworkTimesMap, jiraCardHistoryItem, + stateMap); + } + } + else { + if (IMPEDIMENT.equalsIgnoreCase(jiraCardHistoryItem.getTo().getDisplayName())) { + hasFlag.set(true); + CardStepsEnum from = Objects.requireNonNull(currentState).get(); + calculateTimes(reworkState, excludedStates, reworkTimesMap, from, FLAG); + } + else { + hasFlag.set(false); + CardStepsEnum to = Objects.requireNonNull(currentState).get(); + calculateTimes(reworkState, excludedStates, reworkTimesMap, FLAG, to); + } + } + }); + if (reworkJudgmentMap.get(fromValue(reworkState.getValue())).contains(BLOCK)) { + reworkTimesMap.put(BLOCK, reworkTimesMap.getOrDefault(BLOCK, 0) + reworkTimesMap.get(FLAG)); + } + reworkTimesMap.remove(FLAG); + return reworkTimesMap.entrySet() + .stream() + .map(entry -> new ReworkTimesInfo(entry.getKey(), entry.getValue())) + .toList(); + } + + private static Map initializeReworkTimesMap(CardStepsEnum reworkState, + Set excludedStates, Map stateMap) { + Map reworkTimesMap = new EnumMap<>(CardStepsEnum.class); + Set stateReworkEnums = new HashSet<>(reworkJudgmentMap.get(reworkState)); + stateReworkEnums.removeAll(excludedStates); + stateReworkEnums.stream().filter(stateMap.values()::contains).forEach(state -> reworkTimesMap.put(state, 0)); + return reworkTimesMap; + } + + private Map buildBoardStateMap(List boardColumns) { + return boardColumns.stream() + .collect(Collectors.toMap(boardColumn -> boardColumn.getName().toUpperCase(), + boardColumn -> CardStepsEnum.fromValue(boardColumn.getValue()))); + } + + private boolean isRework(CardStepsEnum from, CardStepsEnum to, Set excludedStates) { + return !excludedStates.contains(from) && reworkJudgmentMap.get(to).contains(from); + } + + private CardStepsEnum convertBoardStateToEnumState(String value, Map stateMap) { + if (stateMap.containsKey(value.toUpperCase())) { + return stateMap.get(value.toUpperCase()); + } + return CardStepsEnum.UNKNOWN; + } + + private List getReworkTimesInfoWhenNotConsiderFlagAsBlock(CardHistoryResponseDTO jiraCardHistory, + CardStepsEnum reworkState, Set excludedStates, Map stateMap) { + Map reworkTimesMap = initializeReworkTimesMap(reworkState, excludedStates, stateMap); + reworkTimesMap.remove(FLAG); + jiraCardHistory.getItems() + .stream() + .filter(jiraCardHistoryItem -> STATUS_FIELD_ID.equalsIgnoreCase(jiraCardHistoryItem.getFieldId())) + .forEach(jiraCardHistoryItem -> calculateReworkTimesMap(reworkState, excludedStates, reworkTimesMap, + jiraCardHistoryItem, stateMap)); + return reworkTimesMap.entrySet() + .stream() + .map(entry -> new ReworkTimesInfo(entry.getKey(), entry.getValue())) + .toList(); + } + + private void calculateReworkTimesMap(CardStepsEnum reworkState, Set excludedStates, + Map reworkTimesMap, HistoryDetail jiraCardHistoryItem, + Map stateMap) { + CardStepsEnum from = convertBoardStateToEnumState(jiraCardHistoryItem.getFrom().getDisplayName(), stateMap); + CardStepsEnum to = convertBoardStateToEnumState(jiraCardHistoryItem.getTo().getDisplayName(), stateMap); + calculateTimes(reworkState, excludedStates, reworkTimesMap, from, to); + } + + private void calculateTimes(CardStepsEnum reworkState, Set excludedStates, + Map reworkTimesMap, CardStepsEnum from, CardStepsEnum to) { + if (!to.equals(reworkState)) { + return; + } + if (isRework(from, to, excludedStates)) { + reworkTimesMap.computeIfPresent(from, (key, value) -> value + 1); + } + } + private List getAssigneeSet(CardHistoryResponseDTO jiraCardHistory, String assigneeFilter, JiraCard doneCard) { List assigneeSet = new ArrayList<>(); @@ -771,7 +949,7 @@ private CardCustomFieldKey covertCustomFieldKey(List model, List cardCustomFieldKey.setStoryPoints(value.getKey()); case "sprint" -> cardCustomFieldKey.setSprint(value.getKey()); - case "flagged" -> cardCustomFieldKey.setFlagged(value.getKey()); + case FLAGGED -> cardCustomFieldKey.setFlagged(value.getKey()); default -> { } } @@ -786,7 +964,7 @@ private CardCustomFieldKey covertCustomFieldKey(List model, List ("flagged").equalsIgnoreCase(targetField.getName())) + .filter(targetField -> FLAGGED.equalsIgnoreCase(targetField.getName())) .map(TargetField::getKey) .filter(key -> !key.isEmpty()) .findFirst() diff --git a/backend/src/main/java/heartbeat/service/pipeline/buildkite/BuildKiteService.java b/backend/src/main/java/heartbeat/service/pipeline/buildkite/BuildKiteService.java index 6c4a733db7..7d4ba73e58 100644 --- a/backend/src/main/java/heartbeat/service/pipeline/buildkite/BuildKiteService.java +++ b/backend/src/main/java/heartbeat/service/pipeline/buildkite/BuildKiteService.java @@ -4,6 +4,7 @@ import heartbeat.client.dto.pipeline.buildkite.BuildKiteBuildInfo; import heartbeat.client.dto.pipeline.buildkite.BuildKiteJob; import heartbeat.client.dto.pipeline.buildkite.BuildKiteOrganizationsInfo; +import heartbeat.client.dto.pipeline.buildkite.BuildKitePipelineDTO; import heartbeat.client.dto.pipeline.buildkite.BuildKiteTokenInfo; import heartbeat.client.dto.pipeline.buildkite.DeployInfo; import heartbeat.client.dto.pipeline.buildkite.DeployTimes; @@ -29,10 +30,10 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Comparator; import java.util.concurrent.CompletableFuture; import java.util.stream.IntStream; @@ -45,6 +46,8 @@ public class BuildKiteService { private static final String CANCELED_STATE = "canceled"; + public static final String BEARER_TITLE = "Bearer "; + private final CachePageService cachePageService; private final ThreadPoolTaskExecutor customTaskExecutor; @@ -148,7 +151,7 @@ private List fetchPipelineStepsByPage(String token, Deployme log.info( "Start to paginated pipeline steps pagination info, orgId: {}, pipelineId: {}, stepsParam: {}, page:{}", orgId, pipelineId, stepsParam, page); - String realToken = "Bearer " + token; + String realToken = BEARER_TITLE + token; stepsParam.setStartTime(TimeUtil.convertToISOFormat(stepsParam.getStartTime())); stepsParam.setEndTime(TimeUtil.convertToISOFormat(stepsParam.getEndTime())); @@ -175,6 +178,18 @@ private List fetchPipelineStepsByPage(String token, Deployme return pageStepsInfo; } + private CompletableFuture> getBuildKitePipelineInfoAsync(String orgSlug, + String buildKiteToken, int page, String perPage) { + return CompletableFuture.supplyAsync(() -> { + log.info("Start to paginated pipeline info, orgId: {}, page:{}, perPage:{}", orgSlug, page, perPage); + var pipelineInfo = buildKiteFeignClient.getPipelineInfo(buildKiteToken, orgSlug, String.valueOf(page), + perPage); + log.info("Successfully get paginated pipeline info, orgSlug: {}, page:{}, perPage:{}", orgSlug, page, + perPage); + return pipelineInfo.getBody(); + }, customTaskExecutor); + } + private CompletableFuture> getBuildKiteStepsAsync(String token, String organizationId, String pipelineId, PipelineStepsParam stepsParam, String perPage, int page, List branch) { return CompletableFuture.supplyAsync(() -> { @@ -244,7 +259,7 @@ private List getBuildsByState(List buildInfos, public void verifyToken(String token) { try { - String buildKiteToken = "Bearer " + token; + String buildKiteToken = BEARER_TITLE + token; log.info("Start to query token permissions by token"); BuildKiteTokenInfo buildKiteTokenInfo = buildKiteFeignClient.getTokenInfo(buildKiteToken); log.info("Successfully query token permissions by token, token info scopes: {}", @@ -264,7 +279,7 @@ public void verifyToken(String token) { public BuildKiteResponseDTO getBuildKiteInfo(TokenParam tokenParam) { try { - String buildKiteToken = "Bearer " + tokenParam.getToken(); + String buildKiteToken = BEARER_TITLE + tokenParam.getToken(); log.info("Start to query BuildKite organizations by token"); List buildKiteOrganizationsInfo = buildKiteFeignClient .getBuildKiteOrganizationsInfo(buildKiteToken); @@ -272,8 +287,7 @@ public BuildKiteResponseDTO getBuildKiteInfo(TokenParam tokenParam) { log.info("Start to query BuildKite pipelineInfo by organizations slug: {}", buildKiteOrganizationsInfo); List buildKiteInfoList = buildKiteOrganizationsInfo.stream() - .flatMap(org -> buildKiteFeignClient.getPipelineInfo(buildKiteToken, org.getSlug(), "1", "100") - .stream() + .flatMap(org -> getPipelineInfoList(org, buildKiteToken).stream() .map(pipeline -> PipelineTransformer.fromBuildKitePipelineDto(pipeline, org.getSlug(), org.getName()))) .toList(); @@ -294,6 +308,32 @@ public BuildKiteResponseDTO getBuildKiteInfo(TokenParam tokenParam) { } } + private List getPipelineInfoList(BuildKiteOrganizationsInfo org, String buildKiteToken) { + String firstPage = "1"; + String perPage = "100"; + var pipelineInfoResponse = cachePageService.getPipelineInfoList(org.getSlug(), buildKiteToken, firstPage, + perPage); + var firstPageStepsInfo = pipelineInfoResponse.getFirstPageInfo(); + int totalPage = pipelineInfoResponse.getTotalPage(); + List pagePipelineInfo = new ArrayList<>(); + if (Objects.nonNull(firstPageStepsInfo)) { + pagePipelineInfo.addAll(firstPageStepsInfo); + } + if (totalPage > 1) { + List>> futures = IntStream + .range(Integer.parseInt(firstPage) + 1, totalPage + 1) + .mapToObj(page -> getBuildKitePipelineInfoAsync(org.getSlug(), buildKiteToken, page, perPage)) + .toList(); + + var buildKiteBuildInfos = futures.stream() + .map(CompletableFuture::join) + .flatMap(Collection::stream) + .toList(); + pagePipelineInfo.addAll(buildKiteBuildInfos); + } + return pagePipelineInfo; + } + public BuildKiteJob getBuildKiteJob(List jobs, List steps, List states, String startTime, String endTime) { Instant startDate = Instant.ofEpochMilli(Long.parseLong(startTime)); diff --git a/backend/src/main/java/heartbeat/service/pipeline/buildkite/CachePageService.java b/backend/src/main/java/heartbeat/service/pipeline/buildkite/CachePageService.java index 90a8625aa1..59bac169bd 100644 --- a/backend/src/main/java/heartbeat/service/pipeline/buildkite/CachePageService.java +++ b/backend/src/main/java/heartbeat/service/pipeline/buildkite/CachePageService.java @@ -2,6 +2,7 @@ import heartbeat.client.BuildKiteFeignClient; import heartbeat.client.dto.pipeline.buildkite.BuildKiteBuildInfo; +import heartbeat.client.dto.pipeline.buildkite.PageBuildKitePipelineInfoDTO; import heartbeat.client.dto.pipeline.buildkite.PageStepsInfoDto; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -41,16 +42,43 @@ public PageStepsInfoDto fetchPageStepsInfo(String realToken, String orgId, Strin return PageStepsInfoDto.builder().firstPageStepsInfo(firstPageStepsInfo).totalPage(totalPage).build(); } + @Cacheable(cacheNames = "pagePipelineInfo", key = "#buildKiteToken+'-'+#orgSlug+'-'+#page+'-'+#perPage") + public PageBuildKitePipelineInfoDTO getPipelineInfoList(String orgSlug, String buildKiteToken, String page, + String perPage) { + var pipelineInfoResponse = buildKiteFeignClient.getPipelineInfo(buildKiteToken, orgSlug, page, perPage); + log.info("Successfully get paginated pipeline info pagination info, orgSlug: {}, page:{}", orgSlug, 1); + + int totalPage = parseTotalPage(pipelineInfoResponse.getHeaders().get(BUILD_KITE_LINK_HEADER)); + log.info("Successfully parse the total page_total page: {}", totalPage); + + return PageBuildKitePipelineInfoDTO.builder() + .firstPageInfo(pipelineInfoResponse.getBody()) + .totalPage(totalPage) + .build(); + } + private int parseTotalPage(@Nullable List linkHeader) { if (linkHeader == null) { return 1; } String lastLink = linkHeader.stream().map(link -> link.replaceAll("per_page=\\d+", "")).findFirst().orElse(""); - Matcher matcher = Pattern.compile("page=(\\d+)[^>]*>;\\s*rel=\"last\"").matcher(lastLink); - if (matcher.find()) { - return Integer.parseInt(matcher.group(1)); + int lastIndex = lastLink.indexOf("rel=\"last\""); + if (lastIndex == -1) { + return 1; + } + String beforeLastRel = lastLink.substring(0, lastIndex); + Matcher matcher = Pattern.compile("page=(\\d+)").matcher(beforeLastRel); + + String lastNumber = null; + while (matcher.find()) { + lastNumber = matcher.group(1); + } + if (lastNumber != null) { + return Integer.parseInt(lastNumber); + } + else { + return 1; } - return 1; } } diff --git a/backend/src/main/java/heartbeat/service/report/BoardSheetGenerator.java b/backend/src/main/java/heartbeat/service/report/BoardSheetGenerator.java new file mode 100644 index 0000000000..453713b72f --- /dev/null +++ b/backend/src/main/java/heartbeat/service/report/BoardSheetGenerator.java @@ -0,0 +1,69 @@ +package heartbeat.service.report; + +import heartbeat.controller.board.dto.response.JiraCardDTO; +import heartbeat.controller.report.dto.response.BoardCSVConfig; +import lombok.Builder; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ArrayUtils; + +import java.util.List; + +@Builder +public class BoardSheetGenerator { + + private List jiraCardDTOList; + + private List fields; + + private List extraFields; + + private List reworkFields; + + private CSVFileGenerator csvFileGenerator; + + private String[][] sheet; + + String[][] generate() { + return sheet; + } + + BoardSheetGenerator mergeBaseInfoAndCycleTimeSheet() { + String[][] baseInfoAndCycleTimeSheet = csvFileGenerator.assembleBoardData(jiraCardDTOList, fields, extraFields); + sheet = mergeSheetHorizontally(sheet, baseInfoAndCycleTimeSheet); + return this; + } + + BoardSheetGenerator mergeReworkTimesSheet() { + if (CollectionUtils.isEmpty(reworkFields)) { + return this; + } + int columnCount = reworkFields.size(); + String[][] reworkTimesSheet = new String[jiraCardDTOList.size() + 1][columnCount]; + + for (int column = 0; column < columnCount; column++) { + reworkTimesSheet[0][column] = reworkFields.get(column).getLabel(); + } + for (int row = 0; row < jiraCardDTOList.size(); row++) { + JiraCardDTO cardDTO = jiraCardDTOList.get(row); + for (int column = 0; column < columnCount; column++) { + reworkTimesSheet[row + 1][column] = csvFileGenerator.getExtraDataPerRow(cardDTO.getReworkTimesFlat(), + reworkFields.get(column)); + } + } + sheet = mergeSheetHorizontally(sheet, reworkTimesSheet); + return this; + } + + private String[][] mergeSheetHorizontally(String[][] sheet, String[][] sheetToMerge) { + int rows = jiraCardDTOList.size() + 1; + String[][] combinedArray = new String[rows][]; + if (ArrayUtils.isEmpty(sheet)) { + return sheetToMerge; + } + for (int i = 0; i < rows; i++) { + combinedArray[i] = ArrayUtils.addAll(sheet[i], sheetToMerge[i]); + } + return combinedArray; + } + +} diff --git a/backend/src/main/java/heartbeat/service/report/CSVFileGenerator.java b/backend/src/main/java/heartbeat/service/report/CSVFileGenerator.java index b5abcd2097..cf7788abec 100644 --- a/backend/src/main/java/heartbeat/service/report/CSVFileGenerator.java +++ b/backend/src/main/java/heartbeat/service/report/CSVFileGenerator.java @@ -5,27 +5,28 @@ import com.opencsv.CSVWriter; import heartbeat.controller.board.dto.response.JiraCardDTO; import heartbeat.controller.report.dto.request.ReportType; -import heartbeat.controller.report.dto.response.AvgChangeFailureRate; import heartbeat.controller.report.dto.response.AvgDeploymentFrequency; +import heartbeat.controller.report.dto.response.AvgDevChangeFailureRate; +import heartbeat.controller.report.dto.response.AvgDevMeanTimeToRecovery; import heartbeat.controller.report.dto.response.AvgLeadTimeForChanges; -import heartbeat.controller.report.dto.response.AvgMeanTimeToRecovery; import heartbeat.controller.report.dto.response.BoardCSVConfig; import heartbeat.controller.report.dto.response.BoardCSVConfigEnum; -import heartbeat.controller.report.dto.response.ChangeFailureRate; -import heartbeat.controller.report.dto.response.ChangeFailureRateOfPipeline; import heartbeat.controller.report.dto.response.Classification; import heartbeat.controller.report.dto.response.ClassificationNameValuePair; import heartbeat.controller.report.dto.response.CycleTime; import heartbeat.controller.report.dto.response.CycleTimeForSelectedStepItem; import heartbeat.controller.report.dto.response.DeploymentFrequency; import heartbeat.controller.report.dto.response.DeploymentFrequencyOfPipeline; +import heartbeat.controller.report.dto.response.DevChangeFailureRate; +import heartbeat.controller.report.dto.response.DevChangeFailureRateOfPipeline; +import heartbeat.controller.report.dto.response.DevMeanTimeToRecovery; +import heartbeat.controller.report.dto.response.DevMeanTimeToRecoveryOfPipeline; import heartbeat.controller.report.dto.response.LeadTimeForChanges; import heartbeat.controller.report.dto.response.LeadTimeForChangesOfPipelines; import heartbeat.controller.report.dto.response.LeadTimeInfo; -import heartbeat.controller.report.dto.response.MeanTimeToRecovery; -import heartbeat.controller.report.dto.response.MeanTimeToRecoveryOfPipeline; import heartbeat.controller.report.dto.response.PipelineCSVInfo; import heartbeat.controller.report.dto.response.ReportResponse; +import heartbeat.controller.report.dto.response.Rework; import heartbeat.controller.report.dto.response.Velocity; import heartbeat.exception.FileIOException; import heartbeat.exception.GenerateReportException; @@ -42,6 +43,8 @@ import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -49,6 +52,7 @@ import java.util.stream.Stream; import static heartbeat.service.report.calculator.ClassificationCalculator.pickDisplayNameFromObj; +import static heartbeat.util.DecimalUtil.formatDecimalFour; import static heartbeat.util.TimeUtil.convertToSimpleISOFormat; import static java.util.concurrent.TimeUnit.HOURS; @@ -65,6 +69,8 @@ public class CSVFileGenerator { private static final String CANCELED_STATUS = "canceled"; + private static final String REWORK_FIELD = "Rework"; + private static InputStreamResource readStringFromCsvFile(String fileName) { try { InputStream inputStream = new FileInputStream(fileName); @@ -101,54 +107,54 @@ public void convertPipelineDataToCSV(List leadTimeData, String csvWriter.writeNext(headers); - for (PipelineCSVInfo csvInfo : leadTimeData) { - String committerName = null; - String commitDate = null; - String creatorName = null; - String organization = csvInfo.getOrganizationName(); - String pipelineName = csvInfo.getPipeLineName(); - String stepName = csvInfo.getStepName(); - String valid = String.valueOf(csvInfo.getValid()).toLowerCase(); - String buildNumber = String.valueOf(csvInfo.getBuildInfo().getNumber()); - String state = csvInfo.getPiplineStatus().equals(CANCELED_STATUS) ? CANCELED_STATUS - : csvInfo.getDeployInfo().getState(); - String branch = csvInfo.getBuildInfo().getBranch(); - if (csvInfo.getCommitInfo() != null) { - committerName = csvInfo.getCommitInfo().getCommit().getAuthor().getName(); - commitDate = csvInfo.getCommitInfo().getCommit().getAuthor().getDate(); - } - - if (csvInfo.getBuildInfo().getCreator() != null - && csvInfo.getBuildInfo().getCreator().getName() != null) { - creatorName = csvInfo.getBuildInfo().getCreator().getName(); - } - - LeadTimeInfo leadTimeInfo = csvInfo.getLeadTimeInfo(); - String firstCommitTimeInPr = leadTimeInfo.getFirstCommitTimeInPr(); - String prCreatedTime = leadTimeInfo.getPrCreatedTime(); - String prMergedTime = leadTimeInfo.getPrMergedTime(); - String jobFinishTime = csvInfo.getDeployInfo().getJobFinishTime(); - String totalTime = leadTimeInfo.getTotalTime(); - String prLeadTime = leadTimeInfo.getPrLeadTime(); - String pipelineLeadTime = leadTimeInfo.getPipelineLeadTime(); - - String[] rowData = { organization, pipelineName, stepName, valid, buildNumber, committerName, - creatorName, firstCommitTimeInPr, commitDate, prCreatedTime, prMergedTime, jobFinishTime, - totalTime, prLeadTime, pipelineLeadTime, state, branch }; - - csvWriter.writeNext(rowData); - } + leadTimeData.stream().map(this::getRowData).forEach(csvWriter::writeNext); } catch (IOException e) { - log.error("Failed to write file", e); + log.error("Failed to write pipeline file", e); throw new FileIOException(e); } } else { - throw new GenerateReportException("Failed to generate csv file,invalid csvTimestamp"); + throw new GenerateReportException("Failed to generate pipeline csv file, invalid csvTimestamp"); } } + private String[] getRowData(PipelineCSVInfo csvInfo) { + String committerName = null; + String commitDate = null; + if (csvInfo.getCommitInfo() != null) { + committerName = csvInfo.getCommitInfo().getCommit().getAuthor().getName(); + commitDate = csvInfo.getCommitInfo().getCommit().getAuthor().getDate(); + } + + String creatorName = null; + if (csvInfo.getBuildInfo().getCreator() != null && csvInfo.getBuildInfo().getCreator().getName() != null) { + creatorName = csvInfo.getBuildInfo().getCreator().getName(); + } + + String organization = csvInfo.getOrganizationName(); + String pipelineName = csvInfo.getPipeLineName(); + String stepName = csvInfo.getStepName(); + String valid = String.valueOf(csvInfo.getValid()).toLowerCase(); + String buildNumber = String.valueOf(csvInfo.getBuildInfo().getNumber()); + String state = csvInfo.getPiplineStatus().equals(CANCELED_STATUS) ? CANCELED_STATUS + : csvInfo.getDeployInfo().getState(); + String branch = csvInfo.getBuildInfo().getBranch(); + + LeadTimeInfo leadTimeInfo = csvInfo.getLeadTimeInfo(); + String firstCommitTimeInPr = leadTimeInfo.getFirstCommitTimeInPr(); + String prCreatedTime = leadTimeInfo.getPrCreatedTime(); + String prMergedTime = leadTimeInfo.getPrMergedTime(); + String jobFinishTime = csvInfo.getDeployInfo().getJobFinishTime(); + String totalTime = leadTimeInfo.getTotalTime(); + String prLeadTime = leadTimeInfo.getPrLeadTime(); + String pipelineLeadTime = leadTimeInfo.getPipelineLeadTime(); + + return new String[] { organization, pipelineName, stepName, valid, buildNumber, committerName, creatorName, + firstCommitTimeInPr, commitDate, prCreatedTime, prMergedTime, jobFinishTime, totalTime, prLeadTime, + pipelineLeadTime, state, branch }; + } + public InputStreamResource getDataFromCSV(ReportType reportDataType, long csvTimeStamp) { return switch (reportDataType) { case METRIC -> readStringFromCsvFile( @@ -170,37 +176,44 @@ private void createCsvDirToConvertData() { public void convertBoardDataToCSV(List cardDTOList, List fields, List extraFields, String csvTimeStamp) { log.info("Start to create board csv directory"); + String[][] mergedArrays = assembleBoardData(cardDTOList, fields, extraFields); + writeDataToCSV(csvTimeStamp, mergedArrays); + } + + public void writeDataToCSV(String csvTimeStamp, String[][] mergedArrays) { createCsvDirToConvertData(); String fileName = CSVFileNameEnum.BOARD.getValue() + FILENAME_SEPARATOR + csvTimeStamp + CSV_EXTENSION; if (!fileName.contains("..") && fileName.startsWith(FILE_LOCAL_PATH)) { try (CSVWriter writer = new CSVWriter(new FileWriter(fileName))) { - List fixedFields = new ArrayList<>(fields); - fixedFields.removeAll(extraFields); - - String[][] fixedFieldsData = getFixedFieldsData(cardDTOList, fixedFields); - String[][] extraFieldsData = getExtraFieldsData(cardDTOList, extraFields); - - String[] fixedFieldsRow = fixedFieldsData[0]; - String targetElement = "Cycle Time"; - List fixedFieldsRowList = Arrays.asList(fixedFieldsRow); - int targetIndex = fixedFieldsRowList.indexOf(targetElement) + 1; - - String[][] mergedArrays = mergeArrays(fixedFieldsData, extraFieldsData, targetIndex); - writer.writeAll(Arrays.asList(mergedArrays)); - } catch (IOException e) { - log.error("Failed to write file", e); + log.error("Failed to write board file", e); throw new FileIOException(e); } } else { - throw new GenerateReportException("Failed to generate csv file,invalid csvTimestamp"); + throw new GenerateReportException("Failed to generate board csv file, invalid csvTimestamp"); } } + public String[][] assembleBoardData(List cardDTOList, List fields, + List extraFields) { + List fixedFields = new ArrayList<>(fields); + fixedFields.removeAll(extraFields); + + String[][] fixedFieldsData = getFixedFieldsData(cardDTOList, fixedFields); + String[][] extraFieldsData = getExtraFieldsData(cardDTOList, extraFields); + + String[] fixedFieldsRow = fixedFieldsData[0]; + String targetElement = "Cycle Time"; + List fixedFieldsRowList = Arrays.asList(fixedFieldsRow); + int targetIndex = fixedFieldsRowList.indexOf(targetElement) + 1; + + return mergeArrays(fixedFieldsData, extraFieldsData, targetIndex); + } + public String[][] mergeArrays(String[][] fixedFieldsData, String[][] extraFieldsData, int fixedColumnCount) { int mergedColumnLength = fixedFieldsData[0].length + extraFieldsData[0].length; String[][] mergedArray = new String[fixedFieldsData.length][mergedColumnLength]; @@ -289,33 +302,7 @@ private String[] getFixedDataPerRow(JiraCardDTO cardDTO, int columnCount) { rowData[0] = cardDTO.getBaseInfo().getKey(); if (cardDTO.getBaseInfo().getFields() != null) { - rowData[1] = cardDTO.getBaseInfo().getFields().getSummary(); - rowData[2] = cardDTO.getBaseInfo().getFields().getIssuetype().getName(); - rowData[3] = cardDTO.getBaseInfo().getFields().getStatus().getName(); - if (cardDTO.getBaseInfo().getFields().getLastStatusChangeDate() != null) { - rowData[4] = convertToSimpleISOFormat(cardDTO.getBaseInfo().getFields().getLastStatusChangeDate()); - } - rowData[5] = String.valueOf(cardDTO.getBaseInfo().getFields().getStoryPoints()); - if (cardDTO.getBaseInfo().getFields().getAssignee() != null) { - rowData[6] = cardDTO.getBaseInfo().getFields().getAssignee().getDisplayName(); - } - if (cardDTO.getBaseInfo().getFields().getReporter() != null) { - rowData[7] = cardDTO.getBaseInfo().getFields().getReporter().getDisplayName(); - } - - rowData[8] = cardDTO.getBaseInfo().getFields().getProject().getKey(); - rowData[9] = cardDTO.getBaseInfo().getFields().getProject().getName(); - rowData[10] = cardDTO.getBaseInfo().getFields().getPriority().getName(); - - if (cardDTO.getBaseInfo().getFields().getParent() != null) { - rowData[11] = cardDTO.getBaseInfo().getFields().getParent().getFields().getSummary(); - } - - if (cardDTO.getBaseInfo().getFields().getSprint() != null) { - rowData[12] = cardDTO.getBaseInfo().getFields().getSprint().getName(); - } - - rowData[13] = String.join(",", cardDTO.getBaseInfo().getFields().getLabels()); + fixDataWithFields(cardDTO, rowData); } } @@ -332,7 +319,37 @@ private String[] getFixedDataPerRow(JiraCardDTO cardDTO, int columnCount) { return rowData; } - private String getExtraDataPerRow(Object object, BoardCSVConfig extraField) { + private void fixDataWithFields(JiraCardDTO cardDTO, String[] rowData) { + rowData[1] = cardDTO.getBaseInfo().getFields().getSummary(); + rowData[2] = cardDTO.getBaseInfo().getFields().getIssuetype().getName(); + rowData[3] = cardDTO.getBaseInfo().getFields().getStatus().getName(); + if (cardDTO.getBaseInfo().getFields().getLastStatusChangeDate() != null) { + rowData[4] = convertToSimpleISOFormat(cardDTO.getBaseInfo().getFields().getLastStatusChangeDate()); + } + rowData[5] = String.valueOf(cardDTO.getBaseInfo().getFields().getStoryPoints()); + if (cardDTO.getBaseInfo().getFields().getAssignee() != null) { + rowData[6] = cardDTO.getBaseInfo().getFields().getAssignee().getDisplayName(); + } + if (cardDTO.getBaseInfo().getFields().getReporter() != null) { + rowData[7] = cardDTO.getBaseInfo().getFields().getReporter().getDisplayName(); + } + + rowData[8] = cardDTO.getBaseInfo().getFields().getProject().getKey(); + rowData[9] = cardDTO.getBaseInfo().getFields().getProject().getName(); + rowData[10] = cardDTO.getBaseInfo().getFields().getPriority().getName(); + + if (cardDTO.getBaseInfo().getFields().getParent() != null) { + rowData[11] = cardDTO.getBaseInfo().getFields().getParent().getFields().getSummary(); + } + + if (cardDTO.getBaseInfo().getFields().getSprint() != null) { + rowData[12] = cardDTO.getBaseInfo().getFields().getSprint().getName(); + } + + rowData[13] = String.join(",", cardDTO.getBaseInfo().getFields().getLabels()); + } + + public String getExtraDataPerRow(Object object, BoardCSVConfig extraField) { Map elementMap = (Map) object; if (elementMap == null) { return null; @@ -381,12 +398,12 @@ public void convertMetricDataToCSV(ReportResponse reportResponse, String csvTime csvWriter.writeAll(convertReportResponseToCSVRows(reportResponse)); } catch (IOException e) { - log.error("Failed to write file", e); + log.error("Failed to write metric file", e); throw new FileIOException(e); } } else { - throw new GenerateReportException("Failed to generate csv file,invalid csvTimestamp"); + throw new GenerateReportException("Failed to generate metric csv file, invalid csvTimestamp"); } } @@ -405,6 +422,11 @@ private List convertReportResponseToCSVRows(ReportResponse reportRespo if (classificationList != null) classificationList.forEach(classification -> rows.addAll(getRowsFormClassification(classification))); + Rework rework = reportResponse.getRework(); + if (rework != null) { + rows.addAll(getRowFromRework(rework)); + } + DeploymentFrequency deploymentFrequency = reportResponse.getDeploymentFrequency(); if (deploymentFrequency != null) rows.addAll(getRowsFromDeploymentFrequency(deploymentFrequency)); @@ -413,13 +435,13 @@ private List convertReportResponseToCSVRows(ReportResponse reportRespo if (leadTimeForChanges != null) rows.addAll(getRowsFromLeadTimeForChanges(leadTimeForChanges)); - ChangeFailureRate changeFailureRate = reportResponse.getChangeFailureRate(); - if (changeFailureRate != null) - rows.addAll(getRowsFromChangeFailureRate(changeFailureRate)); + DevChangeFailureRate devChangeFailureRate = reportResponse.getDevChangeFailureRate(); + if (devChangeFailureRate != null) + rows.addAll(getRowsFromDevChangeFailureRate(devChangeFailureRate)); - MeanTimeToRecovery meanTimeToRecovery = reportResponse.getMeanTimeToRecovery(); - if (meanTimeToRecovery != null) - rows.addAll(getRowsFromMeanTimeToRecovery(meanTimeToRecovery)); + DevMeanTimeToRecovery devMeanTimeToRecovery = reportResponse.getDevMeanTimeToRecovery(); + if (devMeanTimeToRecovery != null) + rows.addAll(getRowsFromDevMeanTimeToRecovery(devMeanTimeToRecovery)); return rows; } @@ -433,24 +455,25 @@ private List getRowsFormVelocity(Velocity velocity) { } private List getRowsFromCycleTime(CycleTime cycleTime) { + String cycleTimeTitle = "Cycle time"; List rows = new ArrayList<>(); List rowsForSelectedStepItemAverageTime = new ArrayList<>(); - rows.add(new String[] { "Cycle time", "Average cycle time(days/storyPoint)", + rows.add(new String[] { cycleTimeTitle, "Average cycle time(days/storyPoint)", String.valueOf(cycleTime.getAverageCycleTimePerSP()) }); - rows.add(new String[] { "Cycle time", "Average cycle time(days/card)", + rows.add(new String[] { cycleTimeTitle, "Average cycle time(days/card)", String.valueOf(cycleTime.getAverageCycleTimePerCard()) }); List swimlaneList = cycleTime.getSwimlaneList(); swimlaneList.forEach(cycleTimeForSelectedStepItem -> { - String StepName = formatStepName(cycleTimeForSelectedStepItem); + String stepName = formatStepName(cycleTimeForSelectedStepItem); double proportion = cycleTimeForSelectedStepItem.getTotalTime() / cycleTime.getTotalTimeForCards(); - rows.add(new String[] { "Cycle time", "Total " + StepName + " time / Total cycle time", + rows.add(new String[] { cycleTimeTitle, "Total " + stepName + " time / Total cycle time", DecimalUtil.formatDecimalTwo(proportion * 100) }); rowsForSelectedStepItemAverageTime - .add(new String[] { "Cycle time", "Average " + StepName + " time(days/storyPoint)", + .add(new String[] { cycleTimeTitle, "Average " + stepName + " time(days/storyPoint)", DecimalUtil.formatDecimalTwo(cycleTimeForSelectedStepItem.getAverageTimeForSP()) }); rowsForSelectedStepItemAverageTime - .add(new String[] { "Cycle time", "Average " + StepName + " time(days/card)", + .add(new String[] { cycleTimeTitle, "Average " + stepName + " time(days/card)", DecimalUtil.formatDecimalTwo(cycleTimeForSelectedStepItem.getAverageTimeForCards()) }); }); rows.addAll(rowsForSelectedStepItemAverageTime); @@ -458,6 +481,15 @@ private List getRowsFromCycleTime(CycleTime cycleTime) { return rows; } + private List getRowFromRework(Rework rework) { + List rows = new ArrayList<>(); + rows.add(new String[] { REWORK_FIELD, "Total rework times", String.valueOf(rework.getTotalReworkTimes()) }); + rows.add(new String[] { REWORK_FIELD, "Total rework cards", String.valueOf(rework.getTotalReworkCards()) }); + rows.add(new String[] { REWORK_FIELD, "Rework cards ratio(Total rework cards/Throughput)", + formatDecimalFour(rework.getReworkCardsRatio()) }); + return rows; + } + private String formatStepName(CycleTimeForSelectedStepItem cycleTimeForSelectedStepItem) { return switch (cycleTimeForSelectedStepItem.getOptionalItemName()) { case "In Dev" -> "development"; @@ -483,7 +515,7 @@ private List getRowsFromDeploymentFrequency(DeploymentFrequency deploy List deploymentFrequencyOfPipelines = deploymentFrequency .getDeploymentFrequencyOfPipelines(); deploymentFrequencyOfPipelines.forEach(pipeline -> rows.add(new String[] { "Deployment frequency", - pipeline.getName() + " / " + pipeline.getStep().replaceAll(":\\w+: ", "") + pipeline.getName() + " / " + extractPipelineStep(pipeline.getStep()) + " / Deployment frequency(Deployments/Day)", DecimalUtil.formatDecimalTwo(pipeline.getDeploymentFrequency()) })); @@ -496,32 +528,38 @@ private List getRowsFromDeploymentFrequency(DeploymentFrequency deploy return rows; } + private String extractPipelineStep(String step) { + return step.replaceAll(":\\w+: ", ""); + } + private List getRowsFromLeadTimeForChanges(LeadTimeForChanges leadTimeForChanges) { List rows = new ArrayList<>(); + List leadTimeForChangesOfPipelines = leadTimeForChanges .getLeadTimeForChangesOfPipelines(); + String leadTimeForChangesTitle = "Lead time for changes"; leadTimeForChangesOfPipelines.forEach(pipeline -> { - String pipelineStep = pipeline.getStep().replaceAll(":\\w+: ", ""); - rows.add(new String[] { "Lead time for changes", + String pipelineStep = extractPipelineStep(pipeline.getStep()); + rows.add(new String[] { leadTimeForChangesTitle, pipeline.getName() + " / " + pipelineStep + " / PR Lead Time", DecimalUtil.formatDecimalTwo(TimeUtils.minutesToUnit(pipeline.getPrLeadTime(), HOURS)) }); - rows.add(new String[] { "Lead time for changes", + rows.add(new String[] { leadTimeForChangesTitle, pipeline.getName() + " / " + pipelineStep + " / Pipeline Lead Time", DecimalUtil.formatDecimalTwo(TimeUtils.minutesToUnit(pipeline.getPipelineLeadTime(), HOURS)) }); - rows.add(new String[] { "Lead time for changes", + rows.add(new String[] { leadTimeForChangesTitle, pipeline.getName() + " / " + pipelineStep + " / Total Lead Time", DecimalUtil.formatDecimalTwo(TimeUtils.minutesToUnit(pipeline.getTotalDelayTime(), HOURS)) }); }); AvgLeadTimeForChanges avgLeadTimeForChanges = leadTimeForChanges.getAvgLeadTimeForChanges(); if (leadTimeForChangesOfPipelines.size() > 1) { - rows.add(new String[] { "Lead time for changes", avgLeadTimeForChanges.getName() + " / PR Lead Time", + rows.add(new String[] { leadTimeForChangesTitle, avgLeadTimeForChanges.getName() + " / PR Lead Time", DecimalUtil .formatDecimalTwo(TimeUtils.minutesToUnit(avgLeadTimeForChanges.getPrLeadTime(), HOURS)) }); - rows.add(new String[] { "Lead time for changes", avgLeadTimeForChanges.getName() + " / Pipeline Lead Time", + rows.add(new String[] { leadTimeForChangesTitle, avgLeadTimeForChanges.getName() + " / Pipeline Lead Time", DecimalUtil.formatDecimalTwo( TimeUtils.minutesToUnit(avgLeadTimeForChanges.getPipelineLeadTime(), HOURS)) }); - rows.add(new String[] { "Lead time for changes", avgLeadTimeForChanges.getName() + " / Total Lead Time", + rows.add(new String[] { leadTimeForChangesTitle, avgLeadTimeForChanges.getName() + " / Total Lead Time", DecimalUtil .formatDecimalTwo(TimeUtils.minutesToUnit(avgLeadTimeForChanges.getTotalDelayTime(), HOURS)) }); } @@ -529,37 +567,38 @@ private List getRowsFromLeadTimeForChanges(LeadTimeForChanges leadTime return rows; } - private List getRowsFromChangeFailureRate(ChangeFailureRate changeFailureRate) { + private List getRowsFromDevChangeFailureRate(DevChangeFailureRate devChangeFailureRate) { List rows = new ArrayList<>(); - List changeFailureRateOfPipelines = changeFailureRate - .getChangeFailureRateOfPipelines(); - changeFailureRateOfPipelines.forEach(pipeline -> rows.add(new String[] { "Change failure rate", - pipeline.getName() + " / " + pipeline.getStep().replaceAll(":\\w+: ", "") + " / Failure rate", - DecimalUtil.formatDecimalTwo(pipeline.getFailureRate() * 100) })); - - AvgChangeFailureRate avgChangeFailureRate = changeFailureRate.getAvgChangeFailureRate(); - if (changeFailureRateOfPipelines.size() > 1) - rows.add(new String[] { "Change failure rate", avgChangeFailureRate.getName() + " / Failure rate", - DecimalUtil.formatDecimalTwo(avgChangeFailureRate.getFailureRate() * 100) }); + List devChangeFailureRateOfPipelines = devChangeFailureRate + .getDevChangeFailureRateOfPipelines(); + devChangeFailureRateOfPipelines.forEach(pipeline -> rows.add(new String[] { "Dev change failure rate", + pipeline.getName() + " / " + extractPipelineStep(pipeline.getStep()) + " / Dev change failure rate", + DecimalUtil.formatDecimalFour(pipeline.getFailureRate()) })); + + AvgDevChangeFailureRate avgDevChangeFailureRate = devChangeFailureRate.getAvgDevChangeFailureRate(); + if (devChangeFailureRateOfPipelines.size() > 1) + rows.add(new String[] { "Dev change failure rate", + avgDevChangeFailureRate.getName() + " / Dev change failure rate", + DecimalUtil.formatDecimalTwo(avgDevChangeFailureRate.getFailureRate() * 100) }); return rows; } - private List getRowsFromMeanTimeToRecovery(MeanTimeToRecovery meanTimeToRecovery) { + private List getRowsFromDevMeanTimeToRecovery(DevMeanTimeToRecovery devMeanTimeToRecovery) { List rows = new ArrayList<>(); - List meanTimeRecoveryPipelines = meanTimeToRecovery - .getMeanTimeRecoveryPipelines(); - meanTimeRecoveryPipelines.forEach(pipeline -> rows.add(new String[] { "Mean Time To Recovery", - pipeline.getPipelineName() + " / " + pipeline.getPipelineStep().replaceAll(":\\w+: ", "") - + " / Mean Time To Recovery", + List devMeanTimeToRecoveryOfPipelines = devMeanTimeToRecovery + .getDevMeanTimeToRecoveryOfPipelines(); + devMeanTimeToRecoveryOfPipelines.forEach(pipeline -> rows.add(new String[] { "Dev mean time to recovery", + pipeline.getName() + " / " + extractPipelineStep(pipeline.getStep()) + " / Dev mean time to recovery", DecimalUtil .formatDecimalTwo(TimeUtils.millisToUnit(pipeline.getTimeToRecovery().doubleValue(), HOURS)) })); - AvgMeanTimeToRecovery avgMeanTimeToRecovery = meanTimeToRecovery.getAvgMeanTimeToRecovery(); - if (meanTimeRecoveryPipelines.size() > 1) - rows.add(new String[] { "Mean Time To Recovery", - avgMeanTimeToRecovery.getName() + " / Mean Time To Recovery", DecimalUtil.formatDecimalTwo( - TimeUtils.millisToUnit(avgMeanTimeToRecovery.getTimeToRecovery().doubleValue(), HOURS)) }); + AvgDevMeanTimeToRecovery avgDevMeanTimeToRecovery = devMeanTimeToRecovery.getAvgDevMeanTimeToRecovery(); + if (devMeanTimeToRecoveryOfPipelines.size() > 1) + rows.add(new String[] { "Dev mean time to recovery", + avgDevMeanTimeToRecovery.getName() + " / Dev mean time to recovery", + DecimalUtil.formatDecimalTwo(TimeUtils + .millisToUnit(avgDevMeanTimeToRecovery.getTimeToRecovery().doubleValue(), HOURS)) }); return rows; } diff --git a/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java b/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java index 8791757500..ca2a75f973 100644 --- a/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java +++ b/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java @@ -1,9 +1,11 @@ package heartbeat.service.report; -import heartbeat.client.dto.codebase.github.PipelineLeadTime; +import heartbeat.controller.board.dto.request.CardStepsEnum; +import heartbeat.controller.board.dto.response.CardCollection; import heartbeat.controller.report.dto.request.GenerateReportRequest; import heartbeat.controller.report.dto.request.JiraBoardSetting; import heartbeat.controller.report.dto.response.ErrorInfo; +import heartbeat.controller.report.dto.response.MetricsDataCompleted; import heartbeat.controller.report.dto.response.PipelineCSVInfo; import heartbeat.controller.report.dto.response.ReportMetricsError; import heartbeat.controller.report.dto.response.ReportResponse; @@ -15,12 +17,14 @@ import heartbeat.handler.AsyncExceptionHandler; import heartbeat.handler.AsyncMetricsDataHandler; import heartbeat.handler.AsyncReportRequestHandler; -import heartbeat.service.report.calculator.ChangeFailureRateCalculator; +import heartbeat.handler.base.AsyncExceptionDTO; +import heartbeat.service.report.calculator.DevChangeFailureRateCalculator; import heartbeat.service.report.calculator.ClassificationCalculator; import heartbeat.service.report.calculator.CycleTimeCalculator; import heartbeat.service.report.calculator.DeploymentFrequencyCalculator; import heartbeat.service.report.calculator.LeadTimeForChangesCalculator; import heartbeat.service.report.calculator.MeanToRecoveryCalculator; +import heartbeat.service.report.calculator.ReworkCalculator; import heartbeat.service.report.calculator.VelocityCalculator; import heartbeat.service.report.calculator.model.FetchedData; import heartbeat.service.report.calculator.model.FetchedData.BuildKiteData; @@ -35,11 +39,13 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import static heartbeat.controller.report.dto.request.MetricType.BOARD; import static heartbeat.controller.report.dto.request.MetricType.DORA; import static heartbeat.service.report.scheduler.DeleteExpireCSVScheduler.EXPORT_CSV_VALIDITY_TIME; import static heartbeat.util.ValueUtil.getValueOrNull; +import static java.util.Objects.isNull; @Service @RequiredArgsConstructor @@ -48,6 +54,8 @@ public class GenerateReporterService { private final KanbanService kanbanService; + private final KanbanCsvService kanbanCsvService; + private final PipelineService pipelineService; private final WorkDay workDay; @@ -56,7 +64,7 @@ public class GenerateReporterService { private final DeploymentFrequencyCalculator deploymentFrequency; - private final ChangeFailureRateCalculator changeFailureRate; + private final DevChangeFailureRateCalculator devChangeFailureRate; private final MeanToRecoveryCalculator meanToRecoveryCalculator; @@ -68,6 +76,8 @@ public class GenerateReporterService { private final LeadTimeForChangesCalculator leadTimeForChangesCalculator; + private final ReworkCalculator reworkCalculator; + private final AsyncReportRequestHandler asyncReportRequestHandler; private final AsyncMetricsDataHandler asyncMetricsDataHandler; @@ -83,7 +93,6 @@ public void generateBoardReport(GenerateReportRequest request) { boardReportId); try { saveReporterInHandler(generateBoardReporter(request), boardReportId); - asyncMetricsDataHandler.updateMetricsDataCompletedInHandler(boardReportId, BOARD); log.info( "Successfully generate board report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _boardReportId: {}", request.getMetrics(), request.getConsiderHoliday(), request.getStartTime(), request.getEndTime(), @@ -92,7 +101,9 @@ public void generateBoardReport(GenerateReportRequest request) { catch (BaseException e) { asyncExceptionHandler.put(boardReportId, e); if (List.of(401, 403, 404).contains(e.getStatus())) - asyncMetricsDataHandler.updateMetricsDataCompletedInHandler(boardReportId, BOARD); + asyncMetricsDataHandler.updateMetricsDataCompletedInHandler( + IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp()), BOARD, false); + } } @@ -102,16 +113,18 @@ public void generateDoraReport(GenerateReportRequest request) { FetchedData fetchedData = new FetchedData(); if (CollectionUtils.isNotEmpty(request.getPipelineMetrics())) { GenerateReportRequest pipelineRequest = request.toPipelineRequest(); - fetchOriginalData(pipelineRequest, fetchedData); generatePipelineReport(pipelineRequest, fetchedData); } if (CollectionUtils.isNotEmpty(request.getSourceControlMetrics())) { GenerateReportRequest sourceControlRequest = request.toSourceControlRequest(); - fetchOriginalData(sourceControlRequest, fetchedData); generateSourceControlReport(sourceControlRequest, fetchedData); } - generateCSVForPipeline(request, fetchedData.getBuildKiteData()); - asyncMetricsDataHandler.updateMetricsDataCompletedInHandler(request.getDoraReportId(), DORA); + + MetricsDataCompleted previousMetricsCompleted = asyncMetricsDataHandler + .getMetricsDataCompleted(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp())); + if (Boolean.FALSE.equals(previousMetricsCompleted.doraMetricsCompleted())) { + CompletableFuture.runAsync(() -> generateCSVForPipeline(request, fetchedData.getBuildKiteData())); + } } private void generatePipelineReport(GenerateReportRequest request, FetchedData fetchedData) { @@ -121,6 +134,7 @@ private void generatePipelineReport(GenerateReportRequest request, FetchedData f request.getPipelineMetrics(), request.getConsiderHoliday(), request.getStartTime(), request.getEndTime(), pipelineReportId); try { + fetchBuildKiteData(request, fetchedData); saveReporterInHandler(generatePipelineReporter(request, fetchedData), pipelineReportId); log.info( "Successfully generate pipeline report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _pipelineReportId: {}", @@ -130,7 +144,8 @@ private void generatePipelineReport(GenerateReportRequest request, FetchedData f catch (BaseException e) { asyncExceptionHandler.put(pipelineReportId, e); if (List.of(401, 403, 404).contains(e.getStatus())) - asyncMetricsDataHandler.updateMetricsDataCompletedInHandler(request.getDoraReportId(), DORA); + asyncMetricsDataHandler.updateMetricsDataCompletedInHandler( + IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp()), DORA, false); } } @@ -141,6 +156,7 @@ private void generateSourceControlReport(GenerateReportRequest request, FetchedD request.getSourceControlMetrics(), request.getConsiderHoliday(), request.getStartTime(), request.getEndTime(), sourceControlReportId); try { + fetchGitHubData(request, fetchedData); saveReporterInHandler(generateSourceControlReporter(request, fetchedData), sourceControlReportId); log.info( "Successfully generate source control report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _sourceControlReportId: {}", @@ -150,7 +166,8 @@ private void generateSourceControlReport(GenerateReportRequest request, FetchedD catch (BaseException e) { asyncExceptionHandler.put(sourceControlReportId, e); if (List.of(401, 403, 404).contains(e.getStatus())) - asyncMetricsDataHandler.updateMetricsDataCompletedInHandler(request.getDoraReportId(), DORA); + asyncMetricsDataHandler.updateMetricsDataCompletedInHandler( + IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp()), DORA, false); } } @@ -169,9 +186,9 @@ private synchronized ReportResponse generatePipelineReporter(GenerateReportReque case "deployment frequency" -> reportResponse.setDeploymentFrequency( deploymentFrequency.calculate(fetchedData.getBuildKiteData().getDeployTimesList(), Long.parseLong(request.getStartTime()), Long.parseLong(request.getEndTime()))); - case "change failure rate" -> reportResponse.setChangeFailureRate( - changeFailureRate.calculate(fetchedData.getBuildKiteData().getDeployTimesList())); - case "mean time to recovery" -> reportResponse.setMeanTimeToRecovery( + case "dev change failure rate" -> reportResponse.setDevChangeFailureRate( + devChangeFailureRate.calculate(fetchedData.getBuildKiteData().getDeployTimesList())); + case "dev mean time to recovery" -> reportResponse.setDevMeanTimeToRecovery( meanToRecoveryCalculator.calculate(fetchedData.getBuildKiteData().getDeployTimesList())); default -> { // TODO @@ -184,30 +201,61 @@ private synchronized ReportResponse generatePipelineReporter(GenerateReportReque private synchronized ReportResponse generateBoardReporter(GenerateReportRequest request) { workDay.changeConsiderHolidayMode(request.getConsiderHoliday()); - FetchedData fetchedData = fetchOriginalData(request, new FetchedData()); + FetchedData fetchedData = fetchJiraBoardData(request, new FetchedData()); ReportResponse reportResponse = new ReportResponse(EXPORT_CSV_VALIDITY_TIME); JiraBoardSetting jiraBoardSetting = request.getJiraBoardSetting(); request.getBoardMetrics().forEach(metric -> { switch (metric) { - case "velocity" -> reportResponse.setVelocity(velocityCalculator - .calculateVelocity(fetchedData.getCardCollectionInfo().getRealDoneCardCollection())); - case "cycle time" -> reportResponse.setCycleTime(cycleTimeCalculator.calculateCycleTime( - fetchedData.getCardCollectionInfo().getRealDoneCardCollection(), - jiraBoardSetting.getBoardColumns())); - case "classification" -> reportResponse - .setClassificationList(classificationCalculator.calculate(jiraBoardSetting.getTargetFields(), - fetchedData.getCardCollectionInfo().getRealDoneCardCollection())); + case "velocity" -> assembleVelocity(fetchedData, reportResponse); + case "cycle time" -> assembleCycleTime(fetchedData, reportResponse, jiraBoardSetting); + case "classification" -> assembleClassification(fetchedData, reportResponse, jiraBoardSetting); + case "rework times" -> assembleReworkInfo(request, fetchedData, reportResponse); default -> { // TODO } } }); + CompletableFuture.runAsync(() -> generateCsvForBoard(request, fetchedData)); return reportResponse; } + private void generateCsvForBoard(GenerateReportRequest request, FetchedData fetchedData) { + kanbanCsvService.generateCsvInfo(request, fetchedData.getCardCollectionInfo().getRealDoneCardCollection(), + fetchedData.getCardCollectionInfo().getNonDoneCardCollection()); + asyncMetricsDataHandler + .updateMetricsDataCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp()), BOARD, true); + } + + private void assembleVelocity(FetchedData fetchedData, ReportResponse reportResponse) { + CardCollection cardCollection = fetchedData.getCardCollectionInfo().getRealDoneCardCollection(); + reportResponse.setVelocity(velocityCalculator.calculateVelocity(cardCollection)); + } + + private void assembleCycleTime(FetchedData fetchedData, ReportResponse reportResponse, + JiraBoardSetting jiraBoardSetting) { + reportResponse.setCycleTime(cycleTimeCalculator.calculateCycleTime( + fetchedData.getCardCollectionInfo().getRealDoneCardCollection(), jiraBoardSetting.getBoardColumns())); + } + + private void assembleClassification(FetchedData fetchedData, ReportResponse reportResponse, + JiraBoardSetting jiraBoardSetting) { + reportResponse.setClassificationList(classificationCalculator.calculate(jiraBoardSetting.getTargetFields(), + fetchedData.getCardCollectionInfo().getRealDoneCardCollection())); + } + + private void assembleReworkInfo(GenerateReportRequest request, FetchedData fetchedData, + ReportResponse reportResponse) { + if (isNull(request.getJiraBoardSetting().getReworkTimesSetting())) { + return; + } + CardCollection realDoneCardCollection = fetchedData.getCardCollectionInfo().getRealDoneCardCollection(); + CardStepsEnum enumReworkState = request.getJiraBoardSetting().getReworkTimesSetting().getEnumReworkState(); + reportResponse.setRework(reworkCalculator.calculateRework(realDoneCardCollection, enumReworkState)); + } + private synchronized ReportResponse generateSourceControlReporter(GenerateReportRequest request, FetchedData fetchedData) { workDay.changeConsiderHolidayMode(request.getConsiderHoliday()); @@ -227,25 +275,24 @@ private synchronized ReportResponse generateSourceControlReporter(GenerateReport return reportResponse; } - private FetchedData fetchOriginalData(GenerateReportRequest request, FetchedData fetchedData) { + private void fetchBuildKiteData(GenerateReportRequest request, FetchedData fetchedData) { + if (request.getBuildKiteSetting() == null) + throw new BadRequestException("Failed to fetch BuildKite info due to BuildKite setting is null."); + fetchedData.setBuildKiteData(pipelineService.fetchBuildKiteInfo(request)); + } + + private void fetchGitHubData(GenerateReportRequest request, FetchedData fetchedData) { + if (request.getCodebaseSetting() == null) + throw new BadRequestException("Failed to fetch Github info due to code base setting is null."); + fetchedData.setBuildKiteData(pipelineService.fetchGitHubData(request)); + } + + private FetchedData fetchJiraBoardData(GenerateReportRequest request, FetchedData fetchedData) { if (CollectionUtils.isNotEmpty(request.getBoardMetrics())) { if (request.getJiraBoardSetting() == null) throw new BadRequestException("Failed to fetch Jira info due to Jira board setting is null."); fetchedData.setCardCollectionInfo(kanbanService.fetchDataFromKanban(request)); } - - if (CollectionUtils.isNotEmpty(request.getSourceControlMetrics())) { - if (request.getCodebaseSetting() == null) - throw new BadRequestException("Failed to fetch Github info due to code base setting is null."); - fetchedData.setBuildKiteData(pipelineService.fetchGithubData(request)); - } - - if (CollectionUtils.isNotEmpty(request.getPipelineMetrics())) { - if (request.getBuildKiteSetting() == null) - throw new BadRequestException("Failed to fetch BuildKite info due to BuildKite setting is null."); - fetchedData.setBuildKiteData(pipelineService.fetchBuildKiteInfo(request)); - } - return fetchedData; } @@ -255,6 +302,8 @@ private void generateCSVForPipeline(GenerateReportRequest request, BuildKiteData request.getBuildKiteSetting().getDeploymentEnvList()); csvFileGenerator.convertPipelineDataToCSV(pipelineData, request.getCsvTimeStamp()); + asyncMetricsDataHandler + .updateMetricsDataCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp()), DORA, true); } public void generateCSVForMetric(ReportResponse reportContent, String csvTimeStamp) { @@ -265,7 +314,7 @@ private void saveReporterInHandler(ReportResponse reportContent, String reportId asyncReportRequestHandler.putReport(reportId, reportContent); } - private ErrorInfo handleAsyncExceptionAndGetErrorInfo(BaseException exception) { + private ErrorInfo handleAsyncExceptionAndGetErrorInfo(AsyncExceptionDTO exception) { if (Objects.nonNull(exception)) { int status = exception.getStatus(); final String errorMessage = exception.getMessage(); @@ -286,7 +335,7 @@ private void deleteOldCSV(long currentTimeStamp, File directory) { if (!ObjectUtils.isEmpty(files)) { for (File file : files) { String fileName = file.getName(); - String[] splitResult = fileName.split("\\s*\\-|\\.\\s*"); + String[] splitResult = fileName.split("[-.]"); String timeStamp = splitResult[1]; if (validateExpire(currentTimeStamp, Long.parseLong(timeStamp)) && !file.delete() && file.exists()) { log.error("Failed to deleted expired CSV file, file name: {}", fileName); @@ -317,15 +366,15 @@ private ReportResponse getReportFromHandler(String reportId) { return asyncReportRequestHandler.getReport(reportId); } - public MetricsDataDTO checkReportReadyStatus(String reportTimeStamp) { + public MetricsDataCompleted checkReportReadyStatus(String reportTimeStamp) { if (validateExpire(System.currentTimeMillis(), Long.parseLong(reportTimeStamp))) { throw new GenerateReportException("Failed to get report due to report time expires"); } - return asyncMetricsDataHandler.getReportReadyStatusByTimeStamp(reportTimeStamp); + return asyncMetricsDataHandler.getMetricsDataCompleted(IdUtil.getDataCompletedPrefix(reportTimeStamp)); } public ReportResponse getComposedReportResponse(String reportId) { - MetricsDataDTO reportReadyStatus = checkReportReadyStatus(reportId); + MetricsDataCompleted reportReadyStatus = checkReportReadyStatus(reportId); ReportResponse boardReportResponse = getReportFromHandler(IdUtil.getBoardReportId(reportId)); ReportResponse pipleineReportResponse = getReportFromHandler(IdUtil.getPipelineReportId(reportId)); @@ -336,22 +385,25 @@ public ReportResponse getComposedReportResponse(String reportId) { .velocity(getValueOrNull(boardReportResponse, ReportResponse::getVelocity)) .classificationList(getValueOrNull(boardReportResponse, ReportResponse::getClassificationList)) .cycleTime(getValueOrNull(boardReportResponse, ReportResponse::getCycleTime)) + .rework(getValueOrNull(boardReportResponse, ReportResponse::getRework)) .exportValidityTime(EXPORT_CSV_VALIDITY_TIME) .deploymentFrequency(getValueOrNull(pipleineReportResponse, ReportResponse::getDeploymentFrequency)) - .changeFailureRate(getValueOrNull(pipleineReportResponse, ReportResponse::getChangeFailureRate)) - .meanTimeToRecovery(getValueOrNull(pipleineReportResponse, ReportResponse::getMeanTimeToRecovery)) + .devChangeFailureRate(getValueOrNull(pipleineReportResponse, ReportResponse::getDevChangeFailureRate)) + .devMeanTimeToRecovery(getValueOrNull(pipleineReportResponse, ReportResponse::getDevMeanTimeToRecovery)) .leadTimeForChanges(getValueOrNull(sourceControlReportResponse, ReportResponse::getLeadTimeForChanges)) - .boardMetricsCompleted(reportReadyStatus.isBoardReady) - .doraMetricsCompleted(reportReadyStatus.isDoraReady) - .allMetricsCompleted(reportReadyStatus.isAllMetricsReady) + .boardMetricsCompleted(reportReadyStatus.boardMetricsCompleted()) + .doraMetricsCompleted(reportReadyStatus.doraMetricsCompleted()) + .overallMetricsCompleted(reportReadyStatus.overallMetricCompleted()) + .allMetricsCompleted(reportReadyStatus.allMetricsCompleted()) + .isSuccessfulCreateCsvFile(reportReadyStatus.isSuccessfulCreateCsvFile()) .reportMetricsError(reportMetricsError) .build(); } private ReportMetricsError getReportErrorAndHandleAsyncException(String reportId) { - BaseException boardException = asyncExceptionHandler.get(IdUtil.getBoardReportId(reportId)); - BaseException pipelineException = asyncExceptionHandler.get(IdUtil.getPipelineReportId(reportId)); - BaseException sourceControlException = asyncExceptionHandler.get(IdUtil.getSourceControlReportId(reportId)); + AsyncExceptionDTO boardException = asyncExceptionHandler.get(IdUtil.getBoardReportId(reportId)); + AsyncExceptionDTO pipelineException = asyncExceptionHandler.get(IdUtil.getPipelineReportId(reportId)); + AsyncExceptionDTO sourceControlException = asyncExceptionHandler.get(IdUtil.getSourceControlReportId(reportId)); return ReportMetricsError.builder() .boardMetricsError(handleAsyncExceptionAndGetErrorInfo(boardException)) .pipelineMetricsError(handleAsyncExceptionAndGetErrorInfo(pipelineException)) diff --git a/backend/src/main/java/heartbeat/service/report/KanbanCsvService.java b/backend/src/main/java/heartbeat/service/report/KanbanCsvService.java index 359f7d12f5..c9a0d3e6b2 100644 --- a/backend/src/main/java/heartbeat/service/report/KanbanCsvService.java +++ b/backend/src/main/java/heartbeat/service/report/KanbanCsvService.java @@ -11,11 +11,12 @@ import heartbeat.client.dto.board.jira.Status; import heartbeat.controller.board.dto.request.BoardRequestParam; import heartbeat.controller.board.dto.request.CardStepsEnum; +import heartbeat.controller.board.dto.request.RequestJiraBoardColumnSetting; import heartbeat.controller.board.dto.response.CardCollection; +import heartbeat.controller.board.dto.response.CycleTimeInfo; import heartbeat.controller.board.dto.response.JiraCardDTO; import heartbeat.controller.board.dto.response.JiraColumnDTO; import heartbeat.controller.board.dto.response.TargetField; -import heartbeat.controller.board.dto.response.CycleTimeInfo; import heartbeat.controller.report.dto.request.GenerateReportRequest; import heartbeat.controller.report.dto.request.JiraBoardSetting; import heartbeat.controller.report.dto.response.BoardCSVConfig; @@ -24,18 +25,25 @@ import heartbeat.service.board.jira.JiraService; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import static heartbeat.controller.board.dto.request.CardStepsEnum.BLOCK; +import static heartbeat.controller.board.dto.request.CardStepsEnum.FLAG; +import static heartbeat.controller.board.dto.request.CardStepsEnum.reworkJudgmentMap; + @Service @Log4j2 @RequiredArgsConstructor @@ -69,62 +77,51 @@ public void generateCsvInfo(GenerateReportRequest request, CardCollection realDo boardRequestParam.getToken()); JiraColumnResult jiraColumns = jiraService.getJiraColumns(boardRequestParam, baseUrl, jiraBoardConfigDTO); + List reworkFromStates = null; + CardStepsEnum reworkState = null; + if (request.getJiraBoardSetting().getReworkTimesSetting() != null) { + reworkState = request.getJiraBoardSetting().getReworkTimesSetting().getEnumReworkState(); + List reworkExcludeStates = request.getJiraBoardSetting() + .getReworkTimesSetting() + .getEnumExcludeStates(); + Set mappedColumns = request.getJiraBoardSetting() + .getBoardColumns() + .stream() + .map(RequestJiraBoardColumnSetting::getValue) + .map(CardStepsEnum::fromValue) + .collect(Collectors.toSet()); + if (Boolean.TRUE.equals(request.getJiraBoardSetting().getTreatFlagCardAsBlock())) { + mappedColumns.add(BLOCK); + } + reworkFromStates = reworkJudgmentMap.get(reworkState) + .stream() + .sorted() + .filter(state -> !reworkExcludeStates.contains(state)) + .filter(mappedColumns::contains) + .map(CardStepsEnum::getAlias) + .toList(); + + } this.generateCSVForBoard(realDoneCardCollection.getJiraCardDTOList(), nonDoneCardCollection.getJiraCardDTOList(), jiraColumns.getJiraColumnResponse(), - jiraBoardSetting.getTargetFields(), request.getCsvTimeStamp()); + jiraBoardSetting.getTargetFields(), request.getCsvTimeStamp(), reworkState, reworkFromStates); } private void generateCSVForBoard(List allDoneCards, List nonDoneCards, - List jiraColumns, List targetFields, String csvTimeStamp) { + List jiraColumns, List targetFields, String csvTimeStamp, + CardStepsEnum reworkState, List reworkFromStates) { List cardDTOList = new ArrayList<>(); List emptyJiraCard = List.of(JiraCardDTO.builder().build()); if (allDoneCards != null) { - if (allDoneCards.size() > 1) { - allDoneCards.sort((preCard, nextCard) -> { - Status preStatus = preCard.getBaseInfo().getFields().getStatus(); - Status nextStatus = nextCard.getBaseInfo().getFields().getStatus(); - Long preDateTimeStamp = preCard.getBaseInfo().getFields().getLastStatusChangeDate(); - Long nextDateTimeStamp = nextCard.getBaseInfo().getFields().getLastStatusChangeDate(); - if (Objects.isNull(preStatus) || Objects.isNull(nextStatus) || Objects.isNull(preDateTimeStamp) - || Objects.isNull(nextDateTimeStamp)) { - return jiraColumns.size() + 1; - } - else { - return nextDateTimeStamp.compareTo(preDateTimeStamp); - } - }); - } + sortAllDoneCardsByTime(allDoneCards, jiraColumns); cardDTOList.addAll(allDoneCards); } cardDTOList.addAll(emptyJiraCard); if (nonDoneCards != null) { - if (nonDoneCards.size() > 1) { - nonDoneCards.sort((preCard, nextCard) -> { - Status preStatus = preCard.getBaseInfo().getFields().getStatus(); - Status nextStatus = nextCard.getBaseInfo().getFields().getStatus(); - Long preDateTimeStamp = preCard.getBaseInfo().getFields().getLastStatusChangeDate(); - Long nextDateTimeStamp = nextCard.getBaseInfo().getFields().getLastStatusChangeDate(); - if (Objects.isNull(preStatus) || Objects.isNull(nextStatus)) { - return jiraColumns.size() + 1; - } - else { - String preCardStatusName = preStatus.getName(); - String nextCardStatusName = nextStatus.getName(); - int statusIndexComparison = getIndexForStatus(jiraColumns, nextCardStatusName) - - getIndexForStatus(jiraColumns, preCardStatusName); - - if (statusIndexComparison == 0 && Objects.nonNull(preDateTimeStamp) - && Objects.nonNull(nextDateTimeStamp)) { - return nextDateTimeStamp.compareTo(preDateTimeStamp); - } - - return statusIndexComparison; - } - }); - } + sortNonDoneCardsByStatusAndTime(nonDoneCards, jiraColumns); cardDTOList.addAll(nonDoneCards); } @@ -154,12 +151,82 @@ private void generateCSVForBoard(List allDoneCards, List reworkFields = new ArrayList<>(); + if (reworkState != null) { + reworkFields.add(BoardCSVConfig.builder() + .label("Rework: total - " + reworkState.getAlias()) + .value("totalReworkTimes") + .build()); + reworkFields.addAll(reworkFromStates.stream() + .map(state -> BoardCSVConfig.builder() + .label("Rework: from " + state) + .value("reworkTimesFlat." + state) + .build()) + .toList()); + } cardDTOList.forEach(card -> { card.setCycleTimeFlat(card.buildCycleTimeFlatObject()); card.setTotalCycleTimeDivideStoryPoints(card.getTotalCycleTimeDivideStoryPoints()); + card.setReworkTimesFlat(card.buildReworkTimesFlatObject()); }); - csvFileGenerator.convertBoardDataToCSV(cardDTOList, allBoardFields, newExtraFields, csvTimeStamp); + String[][] sheet = BoardSheetGenerator.builder() + .csvFileGenerator(csvFileGenerator) + .jiraCardDTOList(cardDTOList) + .fields(allBoardFields) + .extraFields(newExtraFields) + .reworkFields(reworkFields) + .build() + .mergeBaseInfoAndCycleTimeSheet() + .mergeReworkTimesSheet() + .generate(); + csvFileGenerator.writeDataToCSV(csvTimeStamp, sheet); + } + + private void sortNonDoneCardsByStatusAndTime(List nonDoneCards, List jiraColumns) { + if (nonDoneCards.size() > 1) { + nonDoneCards.sort((preCard, nextCard) -> { + Status preStatus = preCard.getBaseInfo().getFields().getStatus(); + Status nextStatus = nextCard.getBaseInfo().getFields().getStatus(); + Long preDateTimeStamp = preCard.getBaseInfo().getFields().getLastStatusChangeDate(); + Long nextDateTimeStamp = nextCard.getBaseInfo().getFields().getLastStatusChangeDate(); + if (Objects.isNull(preStatus) || Objects.isNull(nextStatus)) { + return jiraColumns.size() + 1; + } + else { + String preCardStatusName = preStatus.getName(); + String nextCardStatusName = nextStatus.getName(); + int statusIndexComparison = getIndexForStatus(jiraColumns, nextCardStatusName) + - getIndexForStatus(jiraColumns, preCardStatusName); + + if (statusIndexComparison == 0 && Objects.nonNull(preDateTimeStamp) + && Objects.nonNull(nextDateTimeStamp)) { + return nextDateTimeStamp.compareTo(preDateTimeStamp); + } + + return statusIndexComparison; + } + }); + } + } + + private void sortAllDoneCardsByTime(List allDoneCards, List jiraColumns) { + if (allDoneCards.size() > 1) { + allDoneCards.sort((preCard, nextCard) -> { + Status preStatus = preCard.getBaseInfo().getFields().getStatus(); + Status nextStatus = nextCard.getBaseInfo().getFields().getStatus(); + Long preDateTimeStamp = preCard.getBaseInfo().getFields().getLastStatusChangeDate(); + Long nextDateTimeStamp = nextCard.getBaseInfo().getFields().getLastStatusChangeDate(); + if (Objects.isNull(preStatus) || Objects.isNull(nextStatus) || Objects.isNull(preDateTimeStamp) + || Objects.isNull(nextDateTimeStamp)) { + return jiraColumns.size() + 1; + } + else { + return nextDateTimeStamp.compareTo(preDateTimeStamp); + } + }); + } } private List insertExtraFieldsAfterCycleTime(final List extraFields, @@ -255,7 +322,7 @@ private List getExtraFields(List targetFields, List private List getFixedBoardFields() { return Arrays.stream(BoardCSVConfigEnum.values()) .map(field -> BoardCSVConfig.builder().label(field.getLabel()).value(field.getValue()).build()) - .collect(Collectors.toList()); + .toList(); } private String getFieldDisplayValue(Object object) { diff --git a/backend/src/main/java/heartbeat/service/report/KanbanService.java b/backend/src/main/java/heartbeat/service/report/KanbanService.java index 9e44d35652..bdea34cc43 100644 --- a/backend/src/main/java/heartbeat/service/report/KanbanService.java +++ b/backend/src/main/java/heartbeat/service/report/KanbanService.java @@ -17,12 +17,9 @@ public class KanbanService { private final JiraService jiraService; - private final KanbanCsvService kanbanCsvService; - public FetchedData.CardCollectionInfo fetchDataFromKanban(GenerateReportRequest request) { CardCollection nonDoneCardCollection = fetchNonDoneCardCollection(request); CardCollection realDoneCardCollection = fetchRealDoneCardCollection(request); - kanbanCsvService.generateCsvInfo(request, realDoneCardCollection, nonDoneCardCollection); return FetchedData.CardCollectionInfo.builder() .realDoneCardCollection(realDoneCardCollection) @@ -34,7 +31,7 @@ private CardCollection fetchRealDoneCardCollection(GenerateReportRequest request JiraBoardSetting jiraBoardSetting = request.getJiraBoardSetting(); StoryPointsAndCycleTimeRequest storyPointsAndCycleTimeRequest = buildStoryPointsAndCycleTimeRequest( jiraBoardSetting, request.getStartTime(), request.getEndTime()); - return jiraService.getStoryPointsAndCycleTimeForDoneCards(storyPointsAndCycleTimeRequest, + return jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), jiraBoardSetting.getUsers(), jiraBoardSetting.getAssigneeFilter()); } @@ -60,6 +57,7 @@ private static StoryPointsAndCycleTimeRequest buildStoryPointsAndCycleTimeReques .targetFields(jiraBoardSetting.getTargetFields()) .overrideFields(jiraBoardSetting.getOverrideFields()) .treatFlagCardAsBlock(jiraBoardSetting.getTreatFlagCardAsBlock()) + .reworkTimesSetting(jiraBoardSetting.getReworkTimesSetting()) .build(); } diff --git a/backend/src/main/java/heartbeat/service/report/PipelineService.java b/backend/src/main/java/heartbeat/service/report/PipelineService.java index 66b4e1d3a3..7ba6350cbe 100644 --- a/backend/src/main/java/heartbeat/service/report/PipelineService.java +++ b/backend/src/main/java/heartbeat/service/report/PipelineService.java @@ -38,7 +38,7 @@ public class PipelineService { private final GitHubService gitHubService; - public FetchedData.BuildKiteData fetchGithubData(GenerateReportRequest request) { + public FetchedData.BuildKiteData fetchGitHubData(GenerateReportRequest request) { FetchedData.BuildKiteData buildKiteData = fetchBuildKiteInfo(request); Map repoMap = getRepoMap(request.getBuildKiteSetting().getDeploymentEnvList()); List pipelineLeadTimes = Collections.emptyList(); diff --git a/backend/src/main/java/heartbeat/service/report/ReportService.java b/backend/src/main/java/heartbeat/service/report/ReportService.java index e85b5d73ba..3f355ede28 100644 --- a/backend/src/main/java/heartbeat/service/report/ReportService.java +++ b/backend/src/main/java/heartbeat/service/report/ReportService.java @@ -1,18 +1,28 @@ package heartbeat.service.report; import heartbeat.controller.report.dto.request.GenerateReportRequest; -import heartbeat.controller.report.dto.request.ReportType; import heartbeat.controller.report.dto.request.MetricType; +import heartbeat.controller.report.dto.request.ReportType; import heartbeat.controller.report.dto.response.MetricsDataCompleted; +import heartbeat.controller.report.dto.response.ReportMetricsError; +import heartbeat.controller.report.dto.response.ReportResponse; import heartbeat.exception.NotFoundException; import heartbeat.handler.AsyncMetricsDataHandler; +import heartbeat.service.report.calculator.ReportGenerator; import heartbeat.util.IdUtil; import lombok.RequiredArgsConstructor; import org.springframework.core.io.InputStreamResource; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import static heartbeat.controller.report.dto.request.MetricType.BOARD; +import static heartbeat.controller.report.dto.request.MetricType.DORA; import static heartbeat.service.report.scheduler.DeleteExpireCSVScheduler.EXPORT_CSV_VALIDITY_TIME; @Service @@ -25,6 +35,8 @@ public class ReportService { private final GenerateReporterService generateReporterService; + private final ReportGenerator reportGenerator; + public InputStreamResource exportCsv(ReportType reportDataType, long csvTimestamp) { if (isExpiredTimeStamp(csvTimestamp)) { throw new NotFoundException("Failed to fetch CSV data due to CSV not found"); @@ -36,28 +48,54 @@ private boolean isExpiredTimeStamp(long timeStamp) { return timeStamp < System.currentTimeMillis() - EXPORT_CSV_VALIDITY_TIME; } - public void generateReportByType(GenerateReportRequest request, MetricType metricType) { - initializeMetricsDataCompletedInHandler(request.getCsvTimeStamp(), metricType); + public void generateReport(GenerateReportRequest request) { + List metricTypes = request.getMetricTypes(); + String timeStamp = request.getCsvTimeStamp(); + initializeMetricsDataCompletedInHandler(metricTypes, timeStamp); + Map> reportGeneratorMap = reportGenerator + .getReportGenerator(generateReporterService); + List> threadList = new ArrayList<>(); + for (MetricType metricType : metricTypes) { + CompletableFuture metricTypeThread = CompletableFuture + .runAsync(() -> reportGeneratorMap.get(metricType).accept(request)); + threadList.add(metricTypeThread); + } + CompletableFuture.runAsync(() -> { - switch (metricType) { - case BOARD -> generateReporterService.generateBoardReport(request); - case DORA -> generateReporterService.generateDoraReport(request); - default -> { - // TODO - } + for (CompletableFuture thread : threadList) { + thread.join(); } + + ReportResponse reportResponse = generateReporterService.getComposedReportResponse(timeStamp); + if (isNotGenerateMetricError(reportResponse.getReportMetricsError())) { + generateReporterService.generateCSVForMetric(reportResponse, timeStamp); + } + asyncMetricsDataHandler.updateOverallMetricsCompletedInHandler(IdUtil.getDataCompletedPrefix(timeStamp)); }); } - public void initializeMetricsDataCompletedInHandler(String timeStamp, MetricType metricType) { - if (metricType == MetricType.BOARD) { - asyncMetricsDataHandler.putMetricsDataCompleted(IdUtil.getBoardReportId(timeStamp), - MetricsDataCompleted.builder().boardMetricsCompleted(false).build()); - } - else { - asyncMetricsDataHandler.putMetricsDataCompleted(IdUtil.getDoraReportId(timeStamp), - MetricsDataCompleted.builder().doraMetricsCompleted(false).build()); + private boolean isNotGenerateMetricError(ReportMetricsError reportMetricsError) { + return Objects.isNull(reportMetricsError.getBoardMetricsError()) + && Objects.isNull(reportMetricsError.getSourceControlMetricsError()) + && Objects.isNull(reportMetricsError.getPipelineMetricsError()); + } + + private void initializeMetricsDataCompletedInHandler(List metricTypes, String timeStamp) { + MetricsDataCompleted previousMetricsDataCompleted = asyncMetricsDataHandler + .getMetricsDataCompleted(IdUtil.getDataCompletedPrefix(timeStamp)); + Boolean initializeBoardMetricsCompleted = null; + Boolean initializeDoraMetricsCompleted = null; + if (!Objects.isNull(previousMetricsDataCompleted)) { + initializeBoardMetricsCompleted = previousMetricsDataCompleted.boardMetricsCompleted(); + initializeDoraMetricsCompleted = previousMetricsDataCompleted.doraMetricsCompleted(); } + asyncMetricsDataHandler + .putMetricsDataCompleted(IdUtil.getDataCompletedPrefix(timeStamp), MetricsDataCompleted.builder() + .boardMetricsCompleted(metricTypes.contains(BOARD) ? Boolean.FALSE : initializeBoardMetricsCompleted) + .doraMetricsCompleted(metricTypes.contains(DORA) ? Boolean.FALSE : initializeDoraMetricsCompleted) + .overallMetricCompleted(Boolean.FALSE) + .isSuccessfulCreateCsvFile(Boolean.FALSE) + .build()); } } diff --git a/backend/src/main/java/heartbeat/service/report/WorkDay.java b/backend/src/main/java/heartbeat/service/report/WorkDay.java index 162b4871ab..94eee3c74f 100644 --- a/backend/src/main/java/heartbeat/service/report/WorkDay.java +++ b/backend/src/main/java/heartbeat/service/report/WorkDay.java @@ -21,7 +21,7 @@ @RequiredArgsConstructor public class WorkDay { - private static final long ONE_DAY = 1000 * 60 * 60 * 24; + private static final long ONE_DAY = 1000L * 60 * 60 * 24; private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); diff --git a/backend/src/main/java/heartbeat/service/report/calculator/ClassificationCalculator.java b/backend/src/main/java/heartbeat/service/report/calculator/ClassificationCalculator.java index d190b81d2a..c9f4255dc5 100644 --- a/backend/src/main/java/heartbeat/service/report/calculator/ClassificationCalculator.java +++ b/backend/src/main/java/heartbeat/service/report/calculator/ClassificationCalculator.java @@ -46,54 +46,52 @@ public List calculate(List targetFields, CardCollec JiraCardField jiraCardFields = jiraCardResponse.getBaseInfo().getFields(); Map tempFields = extractFields(jiraCardFields); - for (String tempFieldsKey : tempFields.keySet()) { - Object object = tempFields.get(tempFieldsKey); - if (object instanceof JsonArray objectArray) { - List objectList = new ArrayList<>(); - for (JsonElement element : objectArray) { - if (element.isJsonObject()) { - JsonObject jsonObject = element.getAsJsonObject(); - objectList.add(jsonObject); - } - } - mapArrayField(resultMap, tempFieldsKey, (List.of(objectList))); - } - else if (object instanceof List) { - mapArrayField(resultMap, tempFieldsKey, (List.of(object))); - } - else if (object != null) { - Map countMap = resultMap.get(tempFieldsKey); - if (countMap != null) { - String displayName = pickDisplayNameFromObj(object); - Integer count = countMap.getOrDefault(displayName, 0); - countMap.put(displayName, count > 0 ? count + 1 : 1); - countMap.put(NONE_KEY, countMap.get(NONE_KEY) - 1); - } - } - } + mapFields(tempFields, resultMap); } - for (Map.Entry> entry : resultMap.entrySet()) { - String fieldName = entry.getKey(); - Map valueMap = entry.getValue(); + resultMap.forEach((fieldName, valueMap) -> { List classificationNameValuePair = new ArrayList<>(); if (valueMap.get(NONE_KEY) == 0) { valueMap.remove(NONE_KEY); } - for (Map.Entry mapEntry : valueMap.entrySet()) { - String displayName = mapEntry.getKey(); - Integer count = mapEntry.getValue(); - classificationNameValuePair - .add(new ClassificationNameValuePair(displayName, (double) count / cards.getCardsNumber())); - } + valueMap.forEach((displayName, count) -> classificationNameValuePair + .add(new ClassificationNameValuePair(displayName, (double) count / cards.getCardsNumber()))); classificationFields.add(new Classification(nameMap.get(fieldName), classificationNameValuePair)); - } + }); + return classificationFields; } + private void mapFields(Map tempFields, Map> resultMap) { + tempFields.forEach((tempFieldsKey, object) -> { + if (object instanceof JsonArray objectArray) { + List objectList = new ArrayList<>(); + objectArray.forEach(element -> { + if (element.isJsonObject()) { + JsonObject jsonObject = element.getAsJsonObject(); + objectList.add(jsonObject); + } + }); + mapArrayField(resultMap, tempFieldsKey, (List.of(objectList))); + } + else if (object instanceof List) { + mapArrayField(resultMap, tempFieldsKey, (List.of(object))); + } + else if (object != null) { + Map countMap = resultMap.get(tempFieldsKey); + if (countMap != null) { + String displayName = pickDisplayNameFromObj(object); + Integer count = countMap.getOrDefault(displayName, 0); + countMap.put(displayName, count > 0 ? count + 1 : 1); + countMap.put(NONE_KEY, countMap.get(NONE_KEY) - 1); + } + } + }); + } + private void mapArrayField(Map> resultMap, String fieldsKey, List objects) { Map countMap = resultMap.get(fieldsKey); if (countMap != null) { @@ -144,7 +142,7 @@ public static String pickDisplayNameFromObj(Object object) { } private static String removeQuotes(String value) { - return value.replaceAll("\"", ""); + return value.replace("\"", ""); } public Map extractFields(JiraCardField jiraCardFields) { diff --git a/backend/src/main/java/heartbeat/service/report/calculator/ChangeFailureRateCalculator.java b/backend/src/main/java/heartbeat/service/report/calculator/DevChangeFailureRateCalculator.java similarity index 67% rename from backend/src/main/java/heartbeat/service/report/calculator/ChangeFailureRateCalculator.java rename to backend/src/main/java/heartbeat/service/report/calculator/DevChangeFailureRateCalculator.java index f254e0f88e..38c7247c07 100644 --- a/backend/src/main/java/heartbeat/service/report/calculator/ChangeFailureRateCalculator.java +++ b/backend/src/main/java/heartbeat/service/report/calculator/DevChangeFailureRateCalculator.java @@ -1,9 +1,9 @@ package heartbeat.service.report.calculator; import heartbeat.client.dto.pipeline.buildkite.DeployTimes; -import heartbeat.controller.report.dto.response.AvgChangeFailureRate; -import heartbeat.controller.report.dto.response.ChangeFailureRate; -import heartbeat.controller.report.dto.response.ChangeFailureRateOfPipeline; +import heartbeat.controller.report.dto.response.AvgDevChangeFailureRate; +import heartbeat.controller.report.dto.response.DevChangeFailureRate; +import heartbeat.controller.report.dto.response.DevChangeFailureRateOfPipeline; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -12,7 +12,7 @@ @RequiredArgsConstructor @Component -public class ChangeFailureRateCalculator { +public class DevChangeFailureRateCalculator { private static final String FORMAT_4_DECIMALS = "0.0000"; @@ -20,12 +20,12 @@ public class ChangeFailureRateCalculator { private int totalFailureCount = 0; - public ChangeFailureRate calculate(List deployTimesList) { + public DevChangeFailureRate calculate(List deployTimesList) { DecimalFormat decimalFormat = new DecimalFormat(FORMAT_4_DECIMALS); totalCount = 0; totalFailureCount = 0; - List changeFailureRateOfPipelines = deployTimesList.stream().map(item -> { + List devChangeFailureRateOfPipelines = deployTimesList.stream().map(item -> { int failedTimesOfPipeline = item.getFailed().size(); int validPassedTimesOfPipeline = (int) item.getPassed() .stream() @@ -39,7 +39,7 @@ public ChangeFailureRate calculate(List deployTimesList) { totalCount += totalTimesOfPipeline; totalFailureCount += failedTimesOfPipeline; - return ChangeFailureRateOfPipeline.builder() + return DevChangeFailureRateOfPipeline.builder() .name(item.getPipelineName()) .step(item.getPipelineStep()) .failedTimesOfPipeline(failedTimesOfPipeline) @@ -49,15 +49,15 @@ public ChangeFailureRate calculate(List deployTimesList) { }).toList(); float avgFailureRate = totalCount == 0 ? 0 : (float) totalFailureCount / totalCount; - AvgChangeFailureRate avgChangeFailureRate = AvgChangeFailureRate.builder() + AvgDevChangeFailureRate avgDevChangeFailureRate = AvgDevChangeFailureRate.builder() .totalTimes(totalCount) .totalFailedTimes(totalFailureCount) .failureRate(Float.parseFloat(decimalFormat.format(avgFailureRate))) .build(); - return ChangeFailureRate.builder() - .avgChangeFailureRate(avgChangeFailureRate) - .changeFailureRateOfPipelines(changeFailureRateOfPipelines) + return DevChangeFailureRate.builder() + .avgDevChangeFailureRate(avgDevChangeFailureRate) + .devChangeFailureRateOfPipelines(devChangeFailureRateOfPipelines) .build(); } diff --git a/backend/src/main/java/heartbeat/service/report/calculator/MeanToRecoveryCalculator.java b/backend/src/main/java/heartbeat/service/report/calculator/MeanToRecoveryCalculator.java index 14399274c8..827ebca49f 100644 --- a/backend/src/main/java/heartbeat/service/report/calculator/MeanToRecoveryCalculator.java +++ b/backend/src/main/java/heartbeat/service/report/calculator/MeanToRecoveryCalculator.java @@ -2,9 +2,9 @@ import heartbeat.client.dto.pipeline.buildkite.DeployInfo; import heartbeat.client.dto.pipeline.buildkite.DeployTimes; -import heartbeat.controller.report.dto.response.AvgMeanTimeToRecovery; -import heartbeat.controller.report.dto.response.MeanTimeToRecovery; -import heartbeat.controller.report.dto.response.MeanTimeToRecoveryOfPipeline; +import heartbeat.controller.report.dto.response.AvgDevMeanTimeToRecovery; +import heartbeat.controller.report.dto.response.DevMeanTimeToRecovery; +import heartbeat.controller.report.dto.response.DevMeanTimeToRecoveryOfPipeline; import heartbeat.controller.report.dto.response.TotalTimeAndRecoveryTimes; import java.math.BigDecimal; import java.math.RoundingMode; @@ -23,38 +23,41 @@ @Log4j2 public class MeanToRecoveryCalculator { - public MeanTimeToRecovery calculate(List deployTimes) { + public DevMeanTimeToRecovery calculate(List deployTimes) { if (deployTimes.isEmpty()) { - return new MeanTimeToRecovery(new AvgMeanTimeToRecovery(BigDecimal.ZERO), Collections.emptyList()); + return new DevMeanTimeToRecovery( + AvgDevMeanTimeToRecovery.builder().timeToRecovery(stripTrailingZeros(BigDecimal.ZERO)).build(), + Collections.emptyList()); } - List meanTimeRecoveryPipelines = deployTimes.stream() - .map(this::convertToMeanTimeToRecoveryOfPipeline) + List devMeanTimeToRecoveryOfPipelines = deployTimes.stream() + .map(this::convertToDevMeanTimeToRecoveryOfPipeline) .collect(Collectors.toList()); - BigDecimal avgMeanTimeToRecovery = meanTimeRecoveryPipelines.stream() - .map(MeanTimeToRecoveryOfPipeline::getTimeToRecovery) + BigDecimal avgDevMeanTimeToRecovery = devMeanTimeToRecoveryOfPipelines.stream() + .map(DevMeanTimeToRecoveryOfPipeline::getTimeToRecovery) .reduce(BigDecimal.ZERO, BigDecimal::add) - .divide(BigDecimal.valueOf(meanTimeRecoveryPipelines.size()), 8, RoundingMode.HALF_UP); - AvgMeanTimeToRecovery avgMeanTimeToRecoveryObj = new AvgMeanTimeToRecovery( - stripTrailingZeros(avgMeanTimeToRecovery)); + .divide(BigDecimal.valueOf(devMeanTimeToRecoveryOfPipelines.size()), 8, RoundingMode.HALF_UP); + AvgDevMeanTimeToRecovery avgDevMeanTimeToRecoveryObj = AvgDevMeanTimeToRecovery.builder() + .timeToRecovery(stripTrailingZeros(avgDevMeanTimeToRecovery)) + .build(); - return new MeanTimeToRecovery(avgMeanTimeToRecoveryObj, meanTimeRecoveryPipelines); + return new DevMeanTimeToRecovery(avgDevMeanTimeToRecoveryObj, devMeanTimeToRecoveryOfPipelines); } - private MeanTimeToRecoveryOfPipeline convertToMeanTimeToRecoveryOfPipeline(DeployTimes deploy) { + private DevMeanTimeToRecoveryOfPipeline convertToDevMeanTimeToRecoveryOfPipeline(DeployTimes deploy) { if (deploy.getFailed().isEmpty()) { - return new MeanTimeToRecoveryOfPipeline(deploy.getPipelineName(), deploy.getPipelineStep(), + return new DevMeanTimeToRecoveryOfPipeline(deploy.getPipelineName(), deploy.getPipelineStep(), BigDecimal.ZERO); } else { TotalTimeAndRecoveryTimes result = getTotalRecoveryTimeAndRecoveryTimes(deploy); - BigDecimal meanTimeToRecovery = BigDecimal.ZERO; + BigDecimal devMeanTimeToRecovery = BigDecimal.ZERO; if (result.getRecoveryTimes() != 0) { - meanTimeToRecovery = stripTrailingZeros(new BigDecimal(result.getTotalTimeToRecovery()) + devMeanTimeToRecovery = stripTrailingZeros(new BigDecimal(result.getTotalTimeToRecovery()) .divide(new BigDecimal(result.getRecoveryTimes()), 8, RoundingMode.HALF_UP)); } - return new MeanTimeToRecoveryOfPipeline(deploy.getPipelineName(), deploy.getPipelineStep(), - meanTimeToRecovery); + return new DevMeanTimeToRecoveryOfPipeline(deploy.getPipelineName(), deploy.getPipelineStep(), + devMeanTimeToRecovery); } } diff --git a/backend/src/main/java/heartbeat/service/report/calculator/ReportGenerator.java b/backend/src/main/java/heartbeat/service/report/calculator/ReportGenerator.java new file mode 100644 index 0000000000..67717f7945 --- /dev/null +++ b/backend/src/main/java/heartbeat/service/report/calculator/ReportGenerator.java @@ -0,0 +1,25 @@ +package heartbeat.service.report.calculator; + +import heartbeat.controller.report.dto.request.GenerateReportRequest; +import heartbeat.controller.report.dto.request.MetricType; +import heartbeat.service.report.GenerateReporterService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.function.Consumer; + +import static heartbeat.controller.report.dto.request.MetricType.BOARD; +import static heartbeat.controller.report.dto.request.MetricType.DORA; + +@RequiredArgsConstructor +@Component +public class ReportGenerator { + + public Map> getReportGenerator( + GenerateReporterService generateReporterService) { + return Map.of(BOARD, generateReporterService::generateBoardReport, DORA, + generateReporterService::generateDoraReport); + } + +} diff --git a/backend/src/main/java/heartbeat/service/report/calculator/ReworkCalculator.java b/backend/src/main/java/heartbeat/service/report/calculator/ReworkCalculator.java new file mode 100644 index 0000000000..b921b6e532 --- /dev/null +++ b/backend/src/main/java/heartbeat/service/report/calculator/ReworkCalculator.java @@ -0,0 +1,47 @@ +package heartbeat.service.report.calculator; + +import heartbeat.controller.board.dto.request.CardStepsEnum; +import heartbeat.controller.board.dto.response.CardCollection; +import heartbeat.controller.report.dto.response.Rework; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@RequiredArgsConstructor +@Component +public class ReworkCalculator { + + public Rework calculateRework(CardCollection realDoneCardCollection, CardStepsEnum reworkState) { + Rework rework = Rework.builder() + .reworkState(reworkState.getValue()) + .reworkCardsRatio(realDoneCardCollection.getReworkRatio()) + .totalReworkCards(realDoneCardCollection.getReworkCardNumber()) + .throughput(realDoneCardCollection.getCardsNumber()) + .totalReworkTimes(0) + .build(); + realDoneCardCollection.getJiraCardDTOList() + .stream() + .flatMap(jiraCardDTO -> jiraCardDTO.getReworkTimesInfos().stream()) + .forEach(reworkTimesInfo -> { + Integer times = reworkTimesInfo.getTimes(); + switch (reworkTimesInfo.getState()) { + case ANALYSE -> + rework.setFromAnalysis(Optional.ofNullable(rework.getFromAnalysis()).orElse(0) + times); + case DEVELOPMENT -> + rework.setFromInDev(Optional.ofNullable(rework.getFromInDev()).orElse(0) + times); + case BLOCK -> rework.setFromBlock(Optional.ofNullable(rework.getFromBlock()).orElse(0) + times); + case WAITING -> rework.setFromWaitingForTesting( + Optional.ofNullable(rework.getFromWaitingForTesting()).orElse(0) + times); + case TESTING -> + rework.setFromTesting(Optional.ofNullable(rework.getFromTesting()).orElse(0) + times); + case REVIEW -> rework.setFromReview(Optional.ofNullable(rework.getFromReview()).orElse(0) + times); + case DONE -> rework.setFromDone(Optional.ofNullable(rework.getFromDone()).orElse(0) + times); + default -> throw new IllegalStateException("Unexpected value: " + reworkTimesInfo.getState()); + } + rework.setTotalReworkTimes(rework.getTotalReworkTimes() + times); + }); + return rework; + } + +} diff --git a/backend/src/main/java/heartbeat/service/source/github/GitHubService.java b/backend/src/main/java/heartbeat/service/source/github/GitHubService.java index 445a87f3e1..526483e948 100644 --- a/backend/src/main/java/heartbeat/service/source/github/GitHubService.java +++ b/backend/src/main/java/heartbeat/service/source/github/GitHubService.java @@ -7,10 +7,12 @@ import heartbeat.client.dto.codebase.github.PullRequestInfo; import heartbeat.client.dto.pipeline.buildkite.DeployInfo; import heartbeat.client.dto.pipeline.buildkite.DeployTimes; +import heartbeat.exception.BadRequestException; import heartbeat.exception.BaseException; import heartbeat.exception.InternalServerErrorException; import heartbeat.exception.NotFoundException; import heartbeat.exception.PermissionDenyException; +import heartbeat.exception.UnauthorizedException; import heartbeat.service.source.github.model.PipelineInfoOfRepository; import heartbeat.util.GithubUtil; import lombok.RequiredArgsConstructor; @@ -18,12 +20,12 @@ import org.springframework.stereotype.Service; import java.time.Instant; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Objects; -import java.util.Collections; +import java.util.Optional; import java.util.concurrent.CompletableFuture; @Service @@ -64,7 +66,15 @@ public void verifyCanReadTargetBranch(String repository, String branch, String g } catch (NotFoundException e) { log.error("Failed to call GitHub with branch: {}, error: {} ", branch, e.getMessage()); - throw new PermissionDenyException(String.format("Unable to read target branch: %s", branch)); + throw new NotFoundException(String.format("Unable to read target branch: %s", branch)); + } + catch (PermissionDenyException e) { + log.error("Failed to call GitHub token access error, error: {} ", e.getMessage()); + throw new UnauthorizedException("Unable to read target organization"); + } + catch (UnauthorizedException e) { + log.error("Failed to call GitHub with token_error: {}, error: {} ", branch, e.getMessage()); + throw new BadRequestException(String.format("Unable to read target branch: %s, with token error", branch)); } catch (RuntimeException e) { Throwable cause = Optional.ofNullable(e.getCause()).orElse(e); @@ -163,7 +173,8 @@ private LeadTime getLeadTimeByPullRequest(String realToken, PipelineInfoOfReposi } Optional mergedPull = pullRequestInfos.stream() - .filter(gitHubPull -> gitHubPull.getMergedAt() != null) + .filter(gitHubPull -> gitHubPull.getMergedAt() != null + && gitHubPull.getUrl().contains(item.getRepository())) .min(Comparator.comparing(PullRequestInfo::getNumber)); if (mergedPull.isEmpty()) { diff --git a/backend/src/main/java/heartbeat/util/DecimalUtil.java b/backend/src/main/java/heartbeat/util/DecimalUtil.java index f42440a660..f60b328ef7 100644 --- a/backend/src/main/java/heartbeat/util/DecimalUtil.java +++ b/backend/src/main/java/heartbeat/util/DecimalUtil.java @@ -7,6 +7,8 @@ public interface DecimalUtil { String FORMAT_2_DECIMALS = "0.00"; + String FORMAT_4_DECIMALS = "0.0000"; + static String formatDecimalTwo(double value) { DecimalFormat decimalFormat = new DecimalFormat(FORMAT_2_DECIMALS); @@ -19,4 +21,8 @@ static String formatDecimalTwo(float value) { return Objects.equals(decimalFormat.format(value), "0.00") ? "0" : decimalFormat.format(value); } + static String formatDecimalFour(double value) { + return new DecimalFormat(FORMAT_4_DECIMALS).format(value); + } + } diff --git a/backend/src/main/java/heartbeat/util/IdUtil.java b/backend/src/main/java/heartbeat/util/IdUtil.java index 00e16679cf..d9181768af 100644 --- a/backend/src/main/java/heartbeat/util/IdUtil.java +++ b/backend/src/main/java/heartbeat/util/IdUtil.java @@ -4,20 +4,16 @@ public interface IdUtil { String BOARD_REPORT_PREFIX = "board-"; - String DORA_REPORT_PREFIX = "dora-"; - String PIPELINE_REPORT_PREFIX = "pipeline-"; String SOURCE_CONTROL_PREFIX = "sourceControl-"; + String DATA_COMPLETED_PREFIX = "dataCompleted-"; + static String getBoardReportId(String timeStamp) { return BOARD_REPORT_PREFIX + timeStamp; } - static String getDoraReportId(String timeStamp) { - return DORA_REPORT_PREFIX + timeStamp; - } - static String getPipelineReportId(String timeStamp) { return PIPELINE_REPORT_PREFIX + timeStamp; } @@ -26,4 +22,8 @@ static String getSourceControlReportId(String timeStamp) { return SOURCE_CONTROL_PREFIX + timeStamp; } + static String getDataCompletedPrefix(String timeStamp) { + return DATA_COMPLETED_PREFIX + timeStamp; + } + } diff --git a/backend/src/main/java/heartbeat/util/MetricsUtil.java b/backend/src/main/java/heartbeat/util/MetricsUtil.java index d75853b797..c81a6563fd 100644 --- a/backend/src/main/java/heartbeat/util/MetricsUtil.java +++ b/backend/src/main/java/heartbeat/util/MetricsUtil.java @@ -1,21 +1,28 @@ package heartbeat.util; import heartbeat.controller.report.dto.request.MetricEnum; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import java.util.List; import java.util.stream.Stream; -public interface MetricsUtil { +@Getter +@RequiredArgsConstructor +public enum MetricsUtil { - List kanbanMetrics = Stream.of(MetricEnum.VELOCITY, MetricEnum.CYCLE_TIME, MetricEnum.CLASSIFICATION) - .map(MetricEnum::getValue) - .toList(); + KANBAN_METRICS( + Stream.of(MetricEnum.VELOCITY, MetricEnum.CYCLE_TIME, MetricEnum.CLASSIFICATION, MetricEnum.REWORK_TIMES) + .map(MetricEnum::getValue) + .toList()), - List buildKiteMetrics = Stream - .of(MetricEnum.CHANGE_FAILURE_RATE, MetricEnum.DEPLOYMENT_FREQUENCY, MetricEnum.MEAN_TIME_TO_RECOVERY) + BUILDKITE_METRICS(Stream + .of(MetricEnum.DEV_CHANGE_FAILURE_RATE, MetricEnum.DEPLOYMENT_FREQUENCY, MetricEnum.DEV_MEAN_TIME_TO_RECOVERY) .map(MetricEnum::getValue) - .toList(); + .toList()), + + CODEBASE_METRICS(Stream.of(MetricEnum.LEAD_TIME_FOR_CHANGES).map(MetricEnum::getValue).toList()); - List codebaseMetrics = Stream.of(MetricEnum.LEAD_TIME_FOR_CHANGES).map(MetricEnum::getValue).toList(); + private final List value; } diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 213bd8e508..4d2a46062a 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -29,5 +29,7 @@ springdoc: path: /api-docs heartbeat: - version: 1.1.5 + swagger: + host: ${SWAGGER_HOST:http://localhost:4322} + version: 1.1.6 diff --git a/backend/src/test/java/heartbeat/TestFixtures.java b/backend/src/test/java/heartbeat/TestFixtures.java index 0b237754cd..422a00aac1 100644 --- a/backend/src/test/java/heartbeat/TestFixtures.java +++ b/backend/src/test/java/heartbeat/TestFixtures.java @@ -2,7 +2,7 @@ public class TestFixtures { - public static final String GITHUB_TOKEN = "ghp_12345jhgyui987654rdef43567yhu7654321"; // gitleaks:allow + public static final String GITHUB_TOKEN = "ghp_" + "12345j".repeat(6); public static final String BUILDKITE_TOKEN = "bkua_6xxxafcc3bxxxxxxb8xxx8d8dxxxf7897cc8b2f1"; diff --git a/backend/src/test/java/heartbeat/controller/report/ReporterControllerTest.java b/backend/src/test/java/heartbeat/controller/report/ReporterControllerTest.java index bd9d672073..9b29650093 100644 --- a/backend/src/test/java/heartbeat/controller/report/ReporterControllerTest.java +++ b/backend/src/test/java/heartbeat/controller/report/ReporterControllerTest.java @@ -1,13 +1,14 @@ package heartbeat.controller.report; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import heartbeat.controller.report.dto.request.GenerateReportRequest; import heartbeat.controller.report.dto.request.ReportType; -import heartbeat.controller.report.dto.request.MetricType; import heartbeat.controller.report.dto.response.ReportResponse; import heartbeat.exception.GenerateReportException; import heartbeat.service.report.GenerateReporterService; import heartbeat.service.report.ReportService; +import heartbeat.tools.TimeUtils; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -46,8 +47,6 @@ class ReporterControllerTest { private static final String REQUEST_FILE_PATH = "src/test/java/heartbeat/controller/report/request.json"; - private static final String RESPONSE_FILE_PATH = "src/test/java/heartbeat/controller/report/reportResponse.json"; - @MockBean private GenerateReporterService generateReporterService; @@ -60,38 +59,27 @@ class ReporterControllerTest { private final ObjectMapper mapper = new ObjectMapper(); @Test - void shouldReturnCreatedStatusWhenRequestToGenerateReportGivenAllMetricsCompletedIsTrue() throws Exception { - String reportId = Long.toString(System.currentTimeMillis()); - ReportResponse expectedReportResponse = mapper.readValue(new File(RESPONSE_FILE_PATH), ReportResponse.class); - when(generateReporterService.getComposedReportResponse(reportId)).thenReturn(expectedReportResponse); - - MockHttpServletResponse response = mockMvc - .perform(get("/reports/{reportId}", reportId).contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isCreated()) - .andReturn() - .getResponse(); - final var content = response.getContentAsString(); - ReportResponse actualReportResponse = mapper.readValue(content, ReportResponse.class); - - assertEquals(expectedReportResponse, actualReportResponse); - } - - @Test - void shouldReturnOkStatusWhenRequestToGenerateReportAllMetricsCompletedIsFalse() throws Exception { + void shouldGetSuccessDataGivenReportId() throws Exception { String reportId = Long.toString(System.currentTimeMillis()); - ReportResponse reportResponse = ReportResponse.builder() - .boardMetricsCompleted(false) - .allMetricsCompleted(false) + ReportResponse MockReportResponse = ReportResponse.builder() + .boardMetricsCompleted(true) + .allMetricsCompleted(true) .build(); - when(generateReporterService.getComposedReportResponse(reportId)).thenReturn(reportResponse); + when(generateReporterService.getComposedReportResponse(reportId)).thenReturn(MockReportResponse); - mockMvc.perform(get("/reports/{reportId}", reportId).contentType(MediaType.APPLICATION_JSON)) + String reportResponseString = mockMvc + .perform(get("/reports/{reportId}", reportId).contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) - .andExpect(jsonPath("$.allMetricsCompleted").value(false)) + .andExpect(jsonPath("$.allMetricsCompleted").value(true)) .andReturn() - .getResponse(); + .getResponse() + .getContentAsString(); + ReportResponse response = mapper.readValue(reportResponseString, new TypeReference<>() { + }); verify(generateReporterService).getComposedReportResponse(any()); + assertEquals(true, response.getBoardMetricsCompleted()); + assertEquals(true, response.getAllMetricsCompleted()); } @Test @@ -111,7 +99,7 @@ void shouldReturn500StatusWhenRequestGenerateReportGivenReportTimeIsExpired() th @Test void shouldReturnWhenExportCsv() throws Exception { - Long csvTimeStamp = 1685010080107L; + long csvTimeStamp = TimeUtils.mockTimeStamp(2023, 5, 25, 18, 21, 20); String expectedResponse = "csv data"; when(reporterService.exportCsv(ReportType.PIPELINE, csvTimeStamp)) @@ -134,10 +122,10 @@ void shouldReturnCallBackUrlWithAcceptedStatusAndInvokeWhenGenerateReportByType( String currentTimeStamp = "1685010080107"; request.setCsvTimeStamp(currentTimeStamp); - doAnswer(invocation -> null).when(reporterService).generateReportByType(request, MetricType.DORA); + doAnswer(invocation -> null).when(reporterService).generateReport(request); mockMvc - .perform(post("/reports/{metricType}", MetricType.DORA.metricType).contentType(MediaType.APPLICATION_JSON) + .perform(post("/reports").contentType(MediaType.APPLICATION_JSON) .content(mapper.writeValueAsString(request))) .andExpect(status().isAccepted()) .andExpect(jsonPath("$.callbackUrl").value("/reports/" + currentTimeStamp)) @@ -145,7 +133,7 @@ void shouldReturnCallBackUrlWithAcceptedStatusAndInvokeWhenGenerateReportByType( .andReturn() .getResponse(); - verify(reporterService, times(1)).generateReportByType(request, MetricType.DORA); + verify(reporterService, times(1)).generateReport(request); } } diff --git a/backend/src/test/java/heartbeat/controller/report/reportResponse.json b/backend/src/test/java/heartbeat/controller/report/reportResponse.json index 0c1eefadb6..863c783deb 100644 --- a/backend/src/test/java/heartbeat/controller/report/reportResponse.json +++ b/backend/src/test/java/heartbeat/controller/report/reportResponse.json @@ -96,14 +96,14 @@ } ] }, - "changeFailureRate": { - "avgChangeFailureRate": { + "devChangeFailureRate": { + "avgDevChangeFailureRate": { "name": "Average", "totalTimes": 56, "totalFailedTimes": 0, "failureRate": 0.0 }, - "changeFailureRateOfPipelines": [ + "devChangeFailureRateOfPipelines": [ { "name": "Heartbeat", "step": ":lock: Check Security", @@ -113,12 +113,12 @@ } ] }, - "meanTimeToRecovery": { - "avgMeanTimeToRecovery": { + "devMeanTimeToRecovery": { + "avgDevMeanTimeToRecovery": { "name": "Average", "timeToRecovery": 0 }, - "meanTimeRecoveryPipelines": [ + "devMeanTimeToRecoveryOfPipelines": [ { "timeToRecovery": 0, "name": "Heartbeat", diff --git a/backend/src/test/java/heartbeat/controller/report/request.json b/backend/src/test/java/heartbeat/controller/report/request.json index 4b30ad4d09..5cd7736aff 100644 --- a/backend/src/test/java/heartbeat/controller/report/request.json +++ b/backend/src/test/java/heartbeat/controller/report/request.json @@ -5,7 +5,7 @@ "Classification", "Lead time for changes", "Deployment frequency", - "Change failure rate" + "Dev change failure rate" ], "startTime": 1661702400000, "endTime": 1662739199000, diff --git a/backend/src/test/java/heartbeat/decoder/BuildKiteFeignClientDecoderTest.java b/backend/src/test/java/heartbeat/decoder/BuildKiteFeignClientDecoderTest.java index 74cbf71cc3..b915378da5 100644 --- a/backend/src/test/java/heartbeat/decoder/BuildKiteFeignClientDecoderTest.java +++ b/backend/src/test/java/heartbeat/decoder/BuildKiteFeignClientDecoderTest.java @@ -12,6 +12,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpStatus; @@ -28,7 +30,7 @@ void setup() { } @Test - void testDecode_UnauthorizedException() { + void testDecodeUnauthorizedException() { int statusCode = HttpStatus.UNAUTHORIZED.value(); Response response = responseMock.getMockResponse(statusCode); @@ -38,7 +40,7 @@ void testDecode_UnauthorizedException() { } @Test - void testDecode_NotFoundException() { + void testDecodeNotFoundException() { int statusCode = HttpStatus.NOT_FOUND.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -47,7 +49,7 @@ void testDecode_NotFoundException() { } @Test - void testDecode_HBTimeoutException() { + void testDecodeTimeoutException() { int statusCode = HttpStatus.SERVICE_UNAVAILABLE.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -56,7 +58,7 @@ void testDecode_HBTimeoutException() { } @Test - void testDecode_4xxRequestFailedException() { + void testDecode4xxRequestFailedException() { int statusCode = HttpStatus.METHOD_NOT_ALLOWED.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -66,7 +68,7 @@ void testDecode_4xxRequestFailedException() { } @Test - void testDecode_5xxRequestFailedException() { + void testDecode5xxRequestFailedException() { int statusCode = HttpStatus.BAD_GATEWAY.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -76,7 +78,7 @@ void testDecode_5xxRequestFailedException() { } @Test - void testDecode_UnKnownException() { + void testDecodeUnKnownException() { int statusCode = HttpStatus.SEE_OTHER.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -85,4 +87,19 @@ void testDecode_UnKnownException() { assertTrue(exception.getMessage().contains("UnKnown Error")); } + @ParameterizedTest + @CsvSource({ "getTokenInfo,Failed to get token info", + "getBuildKiteOrganizationsInfo,Failed to get BuildKite OrganizationsInfo info", + "getPipelineInfo,Failed to get pipeline info", "getPipelineSteps,Failed to get pipeline steps", + "getPipelineStepsInfo,Failed to get pipeline steps info" + + }) + void shouldDecodeExceptionErrorMessage(String methodKey, String expectedMsg) { + int statusCode = HttpStatus.NOT_FOUND.value(); + + Exception exception = decoder.decode(methodKey, responseMock.getMockResponse(statusCode)); + + assertEquals(expectedMsg, exception.getMessage()); + } + } diff --git a/backend/src/test/java/heartbeat/decoder/GitHubFeignClientDecoderTest.java b/backend/src/test/java/heartbeat/decoder/GitHubFeignClientDecoderTest.java index ee716bad50..bc93437928 100644 --- a/backend/src/test/java/heartbeat/decoder/GitHubFeignClientDecoderTest.java +++ b/backend/src/test/java/heartbeat/decoder/GitHubFeignClientDecoderTest.java @@ -13,6 +13,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpStatus; @@ -29,7 +31,7 @@ void setup() { } @Test - void testDecode_UnauthorizedException() { + void testDecodeUnauthorizedException() { int statusCode = HttpStatus.UNAUTHORIZED.value(); Response response = responseMock.getMockResponse(statusCode); @@ -39,7 +41,7 @@ void testDecode_UnauthorizedException() { } @Test - void testDecode_NotFoundException() { + void testDecodeNotFoundException() { int statusCode = HttpStatus.NOT_FOUND.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -48,7 +50,7 @@ void testDecode_NotFoundException() { } @Test - void testDecode_4xxRequestFailedException() { + void testDecode4xxRequestFailedException() { int statusCode = HttpStatus.METHOD_NOT_ALLOWED.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -58,7 +60,7 @@ void testDecode_4xxRequestFailedException() { } @Test - void testDecode_5xxRequestFailedException() { + void testDecode5xxRequestFailedException() { int statusCode = HttpStatus.BAD_GATEWAY.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -68,7 +70,7 @@ void testDecode_5xxRequestFailedException() { } @Test - void testDecode_UnKnownException() { + void testDecodeUnKnownException() { int statusCode = HttpStatus.SEE_OTHER.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -78,7 +80,7 @@ void testDecode_UnKnownException() { } @Test - void testDecode_HBTimeoutException() { + void testDecodeTimeoutException() { int statusCode = HttpStatus.SERVICE_UNAVAILABLE.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -87,7 +89,7 @@ void testDecode_HBTimeoutException() { } @Test - void testDecode_PermissionDenyException() { + void testDecodePermissionDenyException() { int statusCode = HttpStatus.FORBIDDEN.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -95,4 +97,20 @@ void testDecode_PermissionDenyException() { assertEquals(PermissionDenyException.class, exception.getClass()); } + @ParameterizedTest + @CsvSource({ "verifyToken,Failed to verify token", + "verifyCanReadTargetBranch,Failed to verify canRead target branch", + "getCommitInfo,Failed to get commit info", + "getPullRequestCommitInfo,Failed to get pull request commit info", + "getPullRequestListInfo,Failed to get pull request list info" + + }) + void shouldDecodeExceptionErrorMessage(String methodKey, String expectMsg) { + int statusCode = HttpStatus.NOT_FOUND.value(); + + Exception exception = decoder.decode(methodKey, responseMock.getMockResponse(statusCode)); + + assertEquals(expectMsg, exception.getMessage()); + } + } diff --git a/backend/src/test/java/heartbeat/decoder/JiraFeignClientDecoderTest.java b/backend/src/test/java/heartbeat/decoder/JiraFeignClientDecoderTest.java index 82be9ee1ac..d69e6843bd 100644 --- a/backend/src/test/java/heartbeat/decoder/JiraFeignClientDecoderTest.java +++ b/backend/src/test/java/heartbeat/decoder/JiraFeignClientDecoderTest.java @@ -12,6 +12,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpStatus; @@ -28,7 +30,7 @@ void setup() { } @Test - void testDecode_UnauthorizedException() { + void testDecodeUnauthorizedException() { int statusCode = HttpStatus.UNAUTHORIZED.value(); Response response = responseMock.getMockResponse(statusCode); @@ -38,7 +40,7 @@ void testDecode_UnauthorizedException() { } @Test - void testDecode_NotFoundException() { + void testDecodeNotFoundException() { int statusCode = HttpStatus.NOT_FOUND.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -47,7 +49,7 @@ void testDecode_NotFoundException() { } @Test - void testDecode_4xxRequestFailedException() { + void testDecode4xxRequestFailedException() { int statusCode = HttpStatus.METHOD_NOT_ALLOWED.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -57,7 +59,7 @@ void testDecode_4xxRequestFailedException() { } @Test - void testDecode_5xxRequestFailedException() { + void testDecode5xxRequestFailedException() { int statusCode = HttpStatus.BAD_GATEWAY.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -67,7 +69,7 @@ void testDecode_5xxRequestFailedException() { } @Test - void testDecode_UnKnownException() { + void testDecodeUnKnownException() { int statusCode = HttpStatus.SEE_OTHER.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -77,7 +79,7 @@ void testDecode_UnKnownException() { } @Test - void testDecode_HBTimeoutException() { + void testDecodeTimeoutException() { int statusCode = HttpStatus.SERVICE_UNAVAILABLE.value(); Exception exception = decoder.decode("methodKey", responseMock.getMockResponse(statusCode)); @@ -85,4 +87,18 @@ void testDecode_HBTimeoutException() { assertEquals(ServiceUnavailableException.class, exception.getClass()); } + @ParameterizedTest + @CsvSource({ "getJiraBoardConfiguration,Failed to get jira board configuration", + "getColumnStatusCategory,Failed to get column status category", "getJiraCards,Failed to get jira cards", + "getJiraCardHistoryByCount,Failed to get jira card history by count", + "getTargetField,Failed to get target field", "getBoard,Failed to get board", + "getProject,Failed to get project" }) + void shouldDecodeExceptionErrorMessage(String methodKey, String expectedMsg) { + int statusCode = HttpStatus.NOT_FOUND.value(); + + Exception exception = decoder.decode(methodKey, responseMock.getMockResponse(statusCode)); + + assertEquals(expectedMsg, exception.getMessage()); + } + } diff --git a/backend/src/test/java/heartbeat/handler/AsyncExceptionHandlerTest.java b/backend/src/test/java/heartbeat/handler/AsyncExceptionHandlerTest.java index 4a9780cf7e..0070b27e6f 100644 --- a/backend/src/test/java/heartbeat/handler/AsyncExceptionHandlerTest.java +++ b/backend/src/test/java/heartbeat/handler/AsyncExceptionHandlerTest.java @@ -1,8 +1,8 @@ package heartbeat.handler; -import heartbeat.exception.BaseException; import heartbeat.exception.GenerateReportException; import heartbeat.exception.UnauthorizedException; +import heartbeat.handler.base.AsyncExceptionDTO; import heartbeat.util.IdUtil; import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.AfterAll; @@ -69,6 +69,24 @@ void shouldDeleteAsyncException() { assertNull(asyncExceptionHandler.get(unExpireFile)); } + @Test + void shouldDeleteAsyncExceptionTmpFile() { + long fileId = System.currentTimeMillis(); + String currentTime = Long.toString(fileId); + String expireTime = Long.toString(fileId - 1900000L); + String unExpireFile = IdUtil.getBoardReportId(currentTime) + ".tmp"; + String expireFile = IdUtil.getBoardReportId(expireTime) + ".tmp"; + asyncExceptionHandler.put(unExpireFile, new UnauthorizedException("")); + asyncExceptionHandler.put(expireFile, new UnauthorizedException("")); + + asyncExceptionHandler.deleteExpireExceptionFile(fileId, new File(APP_OUTPUT_ERROR)); + + assertNull(asyncExceptionHandler.get(expireFile)); + assertNotNull(asyncExceptionHandler.get(unExpireFile)); + deleteTestFile(unExpireFile); + assertNull(asyncExceptionHandler.get(unExpireFile)); + } + @Test void shouldSafeDeleteAsyncExceptionWhenHaveManyThordToDeleteFile() throws InterruptedException { long fileId = System.currentTimeMillis(); @@ -162,7 +180,7 @@ void shouldPutAndRemoveAsyncException() { String boardReportId = IdUtil.getBoardReportId(currentTime); asyncExceptionHandler.put(boardReportId, new UnauthorizedException("test")); - BaseException baseException = asyncExceptionHandler.remove(boardReportId); + AsyncExceptionDTO baseException = asyncExceptionHandler.remove(boardReportId); assertEquals(HttpStatus.UNAUTHORIZED.value(), baseException.getStatus()); assertEquals("test", baseException.getMessage()); diff --git a/backend/src/test/java/heartbeat/handler/AsyncMetricsDataHandlerTest.java b/backend/src/test/java/heartbeat/handler/AsyncMetricsDataHandlerTest.java index e1c70cfa31..612465955d 100644 --- a/backend/src/test/java/heartbeat/handler/AsyncMetricsDataHandlerTest.java +++ b/backend/src/test/java/heartbeat/handler/AsyncMetricsDataHandlerTest.java @@ -3,8 +3,6 @@ import heartbeat.controller.report.dto.request.MetricType; import heartbeat.controller.report.dto.response.MetricsDataCompleted; import heartbeat.exception.GenerateReportException; -import heartbeat.service.report.MetricsDataDTO; -import heartbeat.util.IdUtil; import org.apache.commons.io.FileUtils; import org.junit.Assert; import org.junit.jupiter.api.AfterAll; @@ -95,16 +93,19 @@ void shouldDeleteMetricsDataReadyWhenMetricsFileIsExpire() throws IOException { String prefix = "prefix-"; String currentTimeFileId = prefix + currentTimeMillis; String expireTimeFileId = prefix + (currentTimeMillis - 1900000L); + String expireTimeLockFileId = prefix + (currentTimeMillis - 1900000L) + ".lock"; MetricsDataCompleted metricsDataCompleted = MetricsDataCompleted.builder() .boardMetricsCompleted(false) .build(); asyncMetricsDataHandler.putMetricsDataCompleted(currentTimeFileId, metricsDataCompleted); asyncMetricsDataHandler.putMetricsDataCompleted(expireTimeFileId, metricsDataCompleted); + asyncMetricsDataHandler.putMetricsDataCompleted(expireTimeLockFileId, metricsDataCompleted); asyncMetricsDataHandler.deleteExpireMetricsDataCompletedFile(currentTimeMillis, new File(APP_OUTPUT_METRICS)); assertNull(asyncMetricsDataHandler.getMetricsDataCompleted(expireTimeFileId)); + assertNull(asyncMetricsDataHandler.getMetricsDataCompleted(expireTimeLockFileId)); assertNotNull(asyncMetricsDataHandler.getMetricsDataCompleted(currentTimeFileId)); Files.deleteIfExists(Path.of(APP_OUTPUT_METRICS + "/" + currentTimeFileId)); assertNull(asyncMetricsDataHandler.getMetricsDataCompleted(currentTimeFileId)); @@ -141,7 +142,8 @@ void shouldThrowGenerateReportExceptionWhenPreviousMetricsStatusIsNull() { String currentTime = Long.toString(currentTimeMillis); GenerateReportException exception = assertThrows(GenerateReportException.class, - () -> asyncMetricsDataHandler.updateMetricsDataCompletedInHandler(currentTime, MetricType.BOARD)); + () -> asyncMetricsDataHandler.updateMetricsDataCompletedInHandler(currentTime, MetricType.BOARD, + false)); assertEquals("Failed to update metrics data completed through this timestamp.", exception.getMessage()); } @@ -155,7 +157,7 @@ void shouldUpdateBoardMetricDataWhenPreviousMetricsStatusIsNotNullAndMetricTypeI .build(); asyncMetricsDataHandler.putMetricsDataCompleted(currentTime, metricsDataCompleted); - asyncMetricsDataHandler.updateMetricsDataCompletedInHandler(currentTime, MetricType.BOARD); + asyncMetricsDataHandler.updateMetricsDataCompletedInHandler(currentTime, MetricType.BOARD, true); MetricsDataCompleted completed = asyncMetricsDataHandler.getMetricsDataCompleted(currentTime); assertTrue(completed.boardMetricsCompleted()); @@ -172,7 +174,7 @@ void shouldUpdateDoraMetricDataWhenPreviousMetricsStatusIsNotNullAndMetricTypeIs .build(); asyncMetricsDataHandler.putMetricsDataCompleted(currentTime, metricsDataCompleted); - asyncMetricsDataHandler.updateMetricsDataCompletedInHandler(currentTime, MetricType.DORA); + asyncMetricsDataHandler.updateMetricsDataCompletedInHandler(currentTime, MetricType.DORA, true); MetricsDataCompleted completed = asyncMetricsDataHandler.getMetricsDataCompleted(currentTime); assertTrue(completed.doraMetricsCompleted()); @@ -180,75 +182,57 @@ void shouldUpdateDoraMetricDataWhenPreviousMetricsStatusIsNotNullAndMetricTypeIs assertNull(asyncMetricsDataHandler.getMetricsDataCompleted(currentTime)); } - } - - @Nested - class GetReportReadyStatusByTimeStamp { - @Test - void shouldGetReadyFalseAndAllMetricsReadyTrueGivenPreviousMetricsStatusIsNull() throws IOException { + void shouldUpdateDoraMetricDataWhenMetricIsDoraAndCreateCsvFileUnsuccessfully() throws IOException { long currentTimeMillis = System.currentTimeMillis(); String currentTime = Long.toString(currentTimeMillis); + MetricsDataCompleted metricsDataCompleted = MetricsDataCompleted.builder() + .doraMetricsCompleted(false) + .isSuccessfulCreateCsvFile(false) + .build(); + asyncMetricsDataHandler.putMetricsDataCompleted(currentTime, metricsDataCompleted); - MetricsDataDTO result = asyncMetricsDataHandler.getReportReadyStatusByTimeStamp(currentTime); + asyncMetricsDataHandler.updateMetricsDataCompletedInHandler(currentTime, MetricType.DORA, false); - assertEquals(false, result.isBoardReady()); - assertEquals(false, result.isDoraReady()); - assertEquals(true, result.isAllMetricsReady()); + MetricsDataCompleted completed = asyncMetricsDataHandler.getMetricsDataCompleted(currentTime); + assertTrue(completed.doraMetricsCompleted()); + assertFalse(completed.isSuccessfulCreateCsvFile()); Files.deleteIfExists(Path.of(APP_OUTPUT_METRICS + "/" + currentTime)); assertNull(asyncMetricsDataHandler.getMetricsDataCompleted(currentTime)); } - @Test - void shouldGetReadyTrueAndAllMetricsReadyFalseGivenPreviousMetricsStatusIsNotAllTrue() throws IOException { - long currentTimeMillis = System.currentTimeMillis(); - String currentTime = Long.toString(currentTimeMillis); - MetricsDataCompleted boardCompleted = MetricsDataCompleted.builder().boardMetricsCompleted(false).build(); - MetricsDataCompleted doraCompleted = MetricsDataCompleted.builder().doraMetricsCompleted(true).build(); - asyncMetricsDataHandler.putMetricsDataCompleted(IdUtil.getBoardReportId(currentTime), boardCompleted); - asyncMetricsDataHandler.putMetricsDataCompleted(IdUtil.getDoraReportId(currentTime), doraCompleted); - - MetricsDataDTO result = asyncMetricsDataHandler.getReportReadyStatusByTimeStamp(currentTime); + } - assertEquals(false, result.isBoardReady()); - assertEquals(true, result.isDoraReady()); - assertEquals(false, result.isAllMetricsReady()); - Files.deleteIfExists(Path.of(APP_OUTPUT_METRICS + "/" + currentTime)); - assertNull(asyncMetricsDataHandler.getMetricsDataCompleted(currentTime)); - } + @Nested + class UpdateAllMetricsCompletedInHandler { @Test - void shouldGetReadyTrueAndAllMetricsReadyTrueGivenPreviousMetricsStatusIsAllTrue() throws IOException { + void shouldThrowGenerateReportExceptionGivenPreviousMetricsCompletedIsNull() { long currentTimeMillis = System.currentTimeMillis(); String currentTime = Long.toString(currentTimeMillis); - MetricsDataCompleted boardCompleted = MetricsDataCompleted.builder().boardMetricsCompleted(true).build(); - MetricsDataCompleted doraCompleted = MetricsDataCompleted.builder().doraMetricsCompleted(true).build(); - asyncMetricsDataHandler.putMetricsDataCompleted(IdUtil.getBoardReportId(currentTime), boardCompleted); - asyncMetricsDataHandler.putMetricsDataCompleted(IdUtil.getDoraReportId(currentTime), doraCompleted); - MetricsDataDTO result = asyncMetricsDataHandler.getReportReadyStatusByTimeStamp(currentTime); + GenerateReportException exception = assertThrows(GenerateReportException.class, + () -> asyncMetricsDataHandler.updateOverallMetricsCompletedInHandler(currentTime)); - assertEquals(true, result.isBoardReady()); - assertEquals(true, result.isDoraReady()); - assertEquals(true, result.isAllMetricsReady()); - Files.deleteIfExists(Path.of(APP_OUTPUT_METRICS + "/" + currentTime)); - assertNull(asyncMetricsDataHandler.getMetricsDataCompleted(currentTime)); + assertEquals("Failed to update metrics data completed through this timestamp.", exception.getMessage()); } @Test - void shouldGetReadyFalseAndAllMetricsReadyFalseGivenPreviousMetricsStatusIsFalse() throws IOException { + void shouldUpdateAllMetricDataWhenPreviousMetricsStatusIsNotNull() throws IOException { long currentTimeMillis = System.currentTimeMillis(); String currentTime = Long.toString(currentTimeMillis); - MetricsDataCompleted boardCompleted = MetricsDataCompleted.builder().boardMetricsCompleted(false).build(); - MetricsDataCompleted doraCompleted = MetricsDataCompleted.builder().doraMetricsCompleted(false).build(); - asyncMetricsDataHandler.putMetricsDataCompleted(IdUtil.getBoardReportId(currentTime), boardCompleted); - asyncMetricsDataHandler.putMetricsDataCompleted(IdUtil.getDoraReportId(currentTime), doraCompleted); + MetricsDataCompleted metricsDataCompleted = MetricsDataCompleted.builder() + .boardMetricsCompleted(true) + .overallMetricCompleted(false) + .build(); + asyncMetricsDataHandler.putMetricsDataCompleted(currentTime, metricsDataCompleted); - MetricsDataDTO result = asyncMetricsDataHandler.getReportReadyStatusByTimeStamp(currentTime); + asyncMetricsDataHandler.updateOverallMetricsCompletedInHandler(currentTime); - assertEquals(false, result.isBoardReady()); - assertEquals(false, result.isDoraReady()); - assertEquals(false, result.isAllMetricsReady()); + MetricsDataCompleted completed = asyncMetricsDataHandler.getMetricsDataCompleted(currentTime); + assertTrue(completed.boardMetricsCompleted()); + assertNull(completed.doraMetricsCompleted()); + assertTrue(completed.allMetricsCompleted()); Files.deleteIfExists(Path.of(APP_OUTPUT_METRICS + "/" + currentTime)); assertNull(asyncMetricsDataHandler.getMetricsDataCompleted(currentTime)); } diff --git a/backend/src/test/java/heartbeat/handler/base/AsyncExceptionDTOTest.java b/backend/src/test/java/heartbeat/handler/base/AsyncExceptionTest.java similarity index 75% rename from backend/src/test/java/heartbeat/handler/base/AsyncExceptionDTOTest.java rename to backend/src/test/java/heartbeat/handler/base/AsyncExceptionTest.java index b0909820f8..4f90d61f5f 100644 --- a/backend/src/test/java/heartbeat/handler/base/AsyncExceptionDTOTest.java +++ b/backend/src/test/java/heartbeat/handler/base/AsyncExceptionTest.java @@ -1,6 +1,5 @@ package heartbeat.handler.base; -import heartbeat.exception.BaseException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -12,7 +11,7 @@ void testAsyncExceptionDTOInheritance() { String expectedMessage = "Test Message"; int expectedStatus = 404; - BaseException baseException = new AsyncExceptionDTO(expectedMessage, expectedStatus); + AsyncExceptionDTO baseException = new AsyncExceptionDTO(expectedMessage, expectedStatus); assertEquals(expectedMessage, baseException.getMessage()); assertEquals(expectedStatus, baseException.getStatus()); diff --git a/backend/src/test/java/heartbeat/service/jira/JiraBoardConfigDTOFixture.java b/backend/src/test/java/heartbeat/service/jira/JiraBoardConfigDTOFixture.java index 149b3fe5de..b535ec61b7 100644 --- a/backend/src/test/java/heartbeat/service/jira/JiraBoardConfigDTOFixture.java +++ b/backend/src/test/java/heartbeat/service/jira/JiraBoardConfigDTOFixture.java @@ -29,6 +29,8 @@ import java.util.HashMap; import java.util.List; +import static heartbeat.tools.TimeUtils.mockTimeStamp; + public class JiraBoardConfigDTOFixture { public static final String BOARD_ID = "unknown"; @@ -71,14 +73,32 @@ public class JiraBoardConfigDTOFixture { public static final String END_TIME = "1676908799000"; - public static final long TIMESTAMP_1 = 1673556350000L; + public static final long TIMESTAMP_1 = mockTimeStamp(2023, 1, 13, 4, 45, 50); + + public static final long TIMESTAMP_2 = mockTimeStamp(2023, 1, 24, 18, 32, 50); + + public static final long TIMESTAMP_3 = mockTimeStamp(2023, 1, 13, 4, 45, 50); - public static final long TIMESTAMP_2 = 1674556350000L; + public static final long TIMESTAMP_4 = mockTimeStamp(2023, 2, 5, 8, 19, 10); - public static final long TIMESTAMP_3 = 1673556350001L; + public static final long TIMESTAMP_5 = mockTimeStamp(2023, 2, 16, 22, 5, 10); + + public static final long TIMESTAMP_6 = mockTimeStamp(2023, 2, 20, 9, 25, 10); public static final String JIRA_CARD_WITH_TWO_SPRINT = "{\"startAt\":0,\"total\":5,\"issues\":[{\"key\":\"TS-1\",\"fields\":{\"assignee\":{\"displayName\":\"Zhang San\"},\"customfield_10020\":[{\"name\":\"TS Sprint 1\",\"completeDate\":\"2024-02-08T03:52:22.395Z\"},{\"name\":\"TS Sprint 2\",\"completeDate\":\"2024-02-07T04:21:14.512Z\"}]}}]}"; + public static final long TIMESTAMP_7 = mockTimeStamp(2023, 2, 18, 1, 52, 10); + + public static final long TIMESTAMP_8 = mockTimeStamp(2023, 2, 18, 16, 26, 10); + + public static final long TIMESTAMP_9 = mockTimeStamp(2023, 2, 19, 20, 13, 10); + + public static final long TIMESTAMP_10 = mockTimeStamp(2023, 1, 20, 1, 33, 10); + + public static final long TIMESTAMP_11 = mockTimeStamp(2023, 2, 20, 5, 33, 10); + + public static final long TIMESTAMP_12 = mockTimeStamp(2023, 2, 20, 6, 39, 10); + public static JiraBoardConfigDTO.JiraBoardConfigDTOBuilder JIRA_BOARD_CONFIG_RESPONSE_BUILDER() { return JiraBoardConfigDTO.builder() @@ -225,107 +245,112 @@ public static AllCardsResponseDTO.AllCardsResponseDTOBuilder ONE_PAGE_NO_DONE_CA public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD_HISTORY_RESPONSE_BUILDER() { return CardHistoryResponseDTO.builder() .isLast(true) - .items(List.of(new HistoryDetail(2, "status", new Status("In Dev"), new Status("To do"), null), - new HistoryDetail(3, "status", new Status(REVIEW), new Status("In Dev"), null), - new HistoryDetail(4, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), null), - new HistoryDetail(5, "status", new Status(TESTING), new Status(WAITING_FOR_TESTING), null), - new HistoryDetail(1662642750003L, "status", new Status("Done"), new Status(TESTING), null))); + .items(List.of(new HistoryDetail(2, "status", new Status("In Dev"), new Status("To do"), null, null), + new HistoryDetail(3, "status", new Status(REVIEW), new Status("In Dev"), null, null), + new HistoryDetail(4, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), null, null), + new HistoryDetail(5, "status", new Status(TESTING), new Status(WAITING_FOR_TESTING), null, null), + new HistoryDetail(1662642750003L, "status", new Status("Done"), new Status(TESTING), null, null))); } public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD_HISTORY_REAL_DONE_RESPONSE_BUILDER() { return CardHistoryResponseDTO.builder() .isLast(true) - .items(List.of(new HistoryDetail(1672556350002L, "status", new Status("In Dev"), new Status("To do"), null), - new HistoryDetail(1672556350003L, "status", new Status(REVIEW), new Status("In Dev"), null), + .items(List.of( + new HistoryDetail(1672556350002L, "status", new Status("In Dev"), new Status("To do"), null, null), + new HistoryDetail(1672556350003L, "status", new Status(REVIEW), new Status("In Dev"), null, null), new HistoryDetail(1672556350004L, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), - null), + null, null), new HistoryDetail(1672556350005L, "status", new Status(TESTING), new Status(WAITING_FOR_TESTING), - null), - new HistoryDetail(1672556350006L, "status", new Status(BLOCK), new Status(TESTING), null), + null, null), + new HistoryDetail(1672556350006L, "status", new Status(BLOCK), new Status(TESTING), null, null), new HistoryDetail(1672556350007L, "status", new Status(WAITING_FOR_TESTING), new Status(BLOCK), - null), + null, null), new HistoryDetail(1672556350008L, "status", new Status(TESTING), new Status(WAITING_FOR_TESTING), - null), - new HistoryDetail(1672556350010L, "status", new Status("Done"), new Status(TESTING), null))); + null, null), + new HistoryDetail(1672556350010L, "status", new Status("Done"), new Status(TESTING), null, null))); } public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD_HISTORY_RESPONSE_BUILDER_TO_DONE() { return CardHistoryResponseDTO.builder() .isLast(true) - .items(List.of(new HistoryDetail(2, "status", new Status("In Dev"), new Status("To do"), null), - new HistoryDetail(3, "status", new Status(REVIEW), new Status("In Dev"), null), - new HistoryDetail(4, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), null), - new HistoryDetail(5, "status", new Status("DONE"), new Status(WAITING_FOR_TESTING), null))); + .items(List.of(new HistoryDetail(2, "status", new Status("In Dev"), new Status("To do"), null, null), + new HistoryDetail(3, "status", new Status(REVIEW), new Status("In Dev"), null, null), + new HistoryDetail(4, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), null, null), + new HistoryDetail(5, "status", new Status("DONE"), new Status(WAITING_FOR_TESTING), null, null))); } public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD_HISTORY_MULTI_RESPONSE_BUILDER() { return CardHistoryResponseDTO.builder() .isLast(true) - .items(List.of(new HistoryDetail(1, "status", new Status("To do"), new Status(BLOCK), null), - new HistoryDetail(2, "assignee", new Status("In Dev"), new Status("To do"), null), - new HistoryDetail(3, "status", new Status(REVIEW), new Status("In Dev"), null), - new HistoryDetail(4, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), null), + .items(List.of(new HistoryDetail(1, "status", new Status("To do"), new Status(BLOCK), null, null), + new HistoryDetail(2, "assignee", new Status("In Dev"), new Status("To do"), null, null), + new HistoryDetail(3, "status", new Status(REVIEW), new Status("In Dev"), null, null), + new HistoryDetail(4, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), null, null), new HistoryDetail(1672642740000L, "status", new Status(TESTING), new Status(WAITING_FOR_TESTING), - null), - new HistoryDetail(1672642740001L, "status", new Status(BLOCK), new Status(TESTING), null), - new HistoryDetail(1672642740002L, "status", new Status(FLAG), new Status(BLOCK), null), + null, null), + new HistoryDetail(1672642740001L, "status", new Status(BLOCK), new Status(TESTING), null, null), + new HistoryDetail(1672642740002L, "status", new Status(FLAG), new Status(BLOCK), null, null), new HistoryDetail(1672642750001L, "customfield_10021", new Status("Impediment"), new Status(FLAG), - null), + null, null), new HistoryDetail(1672642750002L, "flagged", new Status("Impediment"), new Status("removeFlag"), - null), - new HistoryDetail(1672642750003L, "status", new Status("Done"), new Status(TESTING), null), - new HistoryDetail(1672642750004L, "status", new Status("Done"), new Status(TESTING), null), + null, null), + new HistoryDetail(1672642750003L, "status", new Status("Done"), new Status(TESTING), null, null), + new HistoryDetail(1672642750004L, "status", new Status("Done"), new Status(TESTING), null, null), new HistoryDetail(1672642750005L, "customfield_10021", new Status(UNKNOWN), - new Status("removeFlag"), null))); + new Status("removeFlag"), null, null))); } public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD_HISTORY_MULTI_REAL_DONE_RESPONSE_BUILDER() { return CardHistoryResponseDTO.builder() .isLast(true) - .items(List.of(new HistoryDetail(1672642730000L, "status", new Status("To do"), new Status(BLOCK), null), - new HistoryDetail(1672642730000L, "assignee", new Status("In Dev"), new Status("To do"), null), - new HistoryDetail(1672642730000L, "status", new Status(REVIEW), new Status("In Dev"), null), - new HistoryDetail(1672642730000L, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), + .items(List.of( + new HistoryDetail(1672642730000L, "status", new Status("To do"), new Status(BLOCK), null, null), + new HistoryDetail(1672642730000L, "assignee", new Status("In Dev"), new Status("To do"), null, null), + new HistoryDetail(1672642730000L, "status", new Status(REVIEW), new Status("In Dev"), null, null), + new HistoryDetail(1672642730000L, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), + null, null), new HistoryDetail(1672642740000L, "status", new Status(TESTING), new Status(WAITING_FOR_TESTING), - null), - new HistoryDetail(1672642740001L, "status", new Status(BLOCK), new Status(TESTING), null), - new HistoryDetail(1672642740002L, "status", new Status(FLAG), new Status(BLOCK), null), + null, null), + new HistoryDetail(1672642740001L, "status", new Status(BLOCK), new Status(TESTING), null, null), + new HistoryDetail(1672642740002L, "status", new Status(FLAG), new Status(BLOCK), null, null), new HistoryDetail(1672642750001L, "customfield_10021", new Status("Impediment"), new Status(FLAG), - null), + null, null), new HistoryDetail(1672642750002L, "flagged", new Status("Impediment"), new Status("removeFlag"), - null), - new HistoryDetail(1672642750003L, "status", new Status("Done"), new Status(TESTING), null), - new HistoryDetail(1672642750004L, "status", new Status("Done"), new Status(TESTING), null), + null, null), + new HistoryDetail(1672642750003L, "status", new Status("Done"), new Status(TESTING), null, null), + new HistoryDetail(1672642750004L, "status", new Status("Done"), new Status(TESTING), null, null), new HistoryDetail(1672642750005L, "customfield_10021", new Status(UNKNOWN), - new Status("removeFlag"), null))); + new Status("removeFlag"), null, null))); } public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD_HISTORY_DONE_TIME_GREATER_THAN_END_TIME_BUILDER() { return CardHistoryResponseDTO.builder() - .items(List.of(new HistoryDetail(1, "status", new Status("To do"), new Status(BLOCK), null), - new HistoryDetail(2, "assignee", new Status("In Dev"), new Status("To do"), null), - new HistoryDetail(3, "status", new Status(REVIEW), new Status("In Dev"), null), - new HistoryDetail(4, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), null), + .items(List.of(new HistoryDetail(1, "status", new Status("To do"), new Status(BLOCK), null, null), + new HistoryDetail(2, "assignee", new Status("In Dev"), new Status("To do"), null, null), + new HistoryDetail(3, "status", new Status(REVIEW), new Status("In Dev"), null, null), + new HistoryDetail(4, "status", new Status(WAITING_FOR_TESTING), new Status(REVIEW), null, null), new HistoryDetail(1682642740000L, "status", new Status(TESTING), new Status(WAITING_FOR_TESTING), - null), - new HistoryDetail(1682642740001L, "status", new Status(BLOCK), new Status(TESTING), null), - new HistoryDetail(1682642740002L, "status", new Status(FLAG), new Status(BLOCK), null), + null, null), + new HistoryDetail(1682642740001L, "status", new Status(BLOCK), new Status(TESTING), null, null), + new HistoryDetail(1682642740002L, "status", new Status(FLAG), new Status(BLOCK), null, null), new HistoryDetail(1682642750001L, "customfield_10021", new Status("Impediment"), new Status(FLAG), - null), + null, null), new HistoryDetail(1682642750002L, "flagged", new Status("Impediment"), new Status("removeFlag"), - null), - new HistoryDetail(1686908799000L, "status", new Status("Done"), new Status(TESTING), null))); + null, null), + new HistoryDetail(1686908799000L, "status", new Status("Done"), new Status(TESTING), null, null))); } public static FieldResponseDTO.FieldResponseDTOBuilder FIELD_RESPONSE_BUILDER() { IssueField storyPointIssueField = new IssueField("customfield_10016", "Story point estimate"); IssueField sprintIssueField = new IssueField("customfield_10020", "Sprint"); IssueField flaggedIssueField = new IssueField("customfield_10021", "Flagged"); + IssueField summaryIssueField = new IssueField("summary", "customfield_10029"); HashMap issueFieldMap = new HashMap<>(); issueFieldMap.put("customfield_10016", storyPointIssueField); issueFieldMap.put("customfield_10020", sprintIssueField); issueFieldMap.put("customfield_10021", flaggedIssueField); + issueFieldMap.put("customfield_10029", summaryIssueField); return FieldResponseDTO.builder().projects(List.of(new Project(List.of(new Issuetype(issueFieldMap))))); } @@ -594,7 +619,8 @@ public static List CYCLE_TIME_INFO_LIST() { public static JiraBoardSetting.JiraBoardSettingBuilder JIRA_BOARD_SETTING_WITH_HISTORICAL_ASSIGNEE_FILTER_METHOD() { return JiraBoardSetting.builder() .boardId(BOARD_ID) - .boardColumns(List.of(RequestJiraBoardColumnSetting.builder().name(IN_DEV).value(IN_DEV).build(), + .boardColumns(List.of(RequestJiraBoardColumnSetting.builder().name(ANALYSE).value(ANALYSE).build(), + RequestJiraBoardColumnSetting.builder().name(IN_DEV).value(IN_DEV).build(), RequestJiraBoardColumnSetting.builder() .name(WAITING_FOR_TESTING) .value(WAITING_FOR_TESTING) @@ -672,39 +698,39 @@ public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD1_HISTORY return CardHistoryResponseDTO.builder() .items(List.of( new HistoryDetail(TIMESTAMP_1, "status", new Status(TESTING), new Status(REVIEW), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), new HistoryDetail(TIMESTAMP_2, "status", new Status(DONE), new Status(TESTING), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), new HistoryDetail(TIMESTAMP_1, "assignee", new Status("yun"), new Status(null), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), new HistoryDetail(TIMESTAMP_3, "assignee", new Status("song"), new Status("yun"), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)))); + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null))); } public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD2_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER() { return CardHistoryResponseDTO.builder() .items(List.of( new HistoryDetail(TIMESTAMP_1, "status", new Status(TESTING), new Status(REVIEW), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), new HistoryDetail(TIMESTAMP_2, "status", new Status(DONE), new Status(TESTING), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), new HistoryDetail(TIMESTAMP_1, "assignee", new Status("yun"), new Status(null), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), new HistoryDetail(TIMESTAMP_3, "assignee", new Status("kun"), new Status("yun"), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)))); + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null))); } public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD3_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER() { return CardHistoryResponseDTO.builder() .items(List.of( new HistoryDetail(TIMESTAMP_1, "status", new Status(TESTING), new Status(REVIEW), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), new HistoryDetail(TIMESTAMP_2, "status", new Status(DONE), new Status(TESTING), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), new HistoryDetail(TIMESTAMP_1, "assignee", new Status("yun"), new Status(null), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), new HistoryDetail(TIMESTAMP_3, "assignee", new Status(null), new Status("yun"), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)))); + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null))); } public static AllCardsResponseDTO.AllCardsResponseDTOBuilder ALL_DONE_CARDS_RESPONSE_FOR_MULTIPLE_STATUS() { @@ -725,23 +751,81 @@ public static AllCardsResponseDTO.AllCardsResponseDTOBuilder ALL_DONE_CARDS_RESP public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD1_HISTORY_FOR_MULTIPLE_STATUSES() { return CardHistoryResponseDTO.builder() - .items(List.of(new HistoryDetail(TIMESTAMP_1, "status", new Status(IN_DEV), new Status(ANALYSE), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)))); + .isLast(true) + .items(List.of( + new HistoryDetail(TIMESTAMP_1, "status", new Status(IN_DEV), new Status(ANALYSE), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), + new HistoryDetail(TIMESTAMP_2, "status", new Status(TESTING), new Status(IN_DEV), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), + new HistoryDetail(TIMESTAMP_4, "status", new Status(IN_DEV), new Status(TESTING), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), + new HistoryDetail(TIMESTAMP_5, "status", new Status(REVIEW), new Status(IN_DEV), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), + new HistoryDetail(TIMESTAMP_6, "status", new Status(IN_DEV), new Status(REVIEW), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), + new HistoryDetail(TIMESTAMP_7, "status", new Status(IN_DEV), new Status(BLOCK), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), + new HistoryDetail(TIMESTAMP_7, "status", new Status("noExistStatus"), new Status("noExistStatus"), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null))); + } + + public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD1_HISTORY_FOR_MULTIPLE_STATUSES_WITH_FLAG() { + return CardHistoryResponseDTO.builder() + .isLast(true) + .items(List.of( + new HistoryDetail(TIMESTAMP_1, "status", new Status(IN_DEV), new Status(ANALYSE), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), + new HistoryDetail(TIMESTAMP_2, "status", new Status(TESTING), new Status(IN_DEV), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), + new HistoryDetail(TIMESTAMP_4, "customfield_10021", new Status("Impediment"), new Status(null), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), "Flagged"), + new HistoryDetail(TIMESTAMP_5, "status", new Status(IN_DEV), new Status(TESTING), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null), + new HistoryDetail(TIMESTAMP_7, "customfield_10021", new Status(null), new Status("Impediment"), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), "Flagged"), + new HistoryDetail(TIMESTAMP_8, "customfield_10021", new Status("Impediment"), new Status(null), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), "Flagged"), + new HistoryDetail(TIMESTAMP_8, "customfield_10020", new Status("Impediment"), new Status(null), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), "story"), + new HistoryDetail(TIMESTAMP_9, "customfield_10021", new Status(null), new Status("Impediment"), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), "Flagged"), + new HistoryDetail(TIMESTAMP_10, "status", new Status(TESTING), new Status(IN_DEV), null, null), + new HistoryDetail(TIMESTAMP_11, "customfield_10020", new Status("Impediment"), new Status(null), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), "story"), + new HistoryDetail(TIMESTAMP_12, "customfield_10021", new Status(null), new Status("Impediment"), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), "Flagged"))); + } public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD2_HISTORY_FOR_MULTIPLE_STATUSES() { return CardHistoryResponseDTO.builder() .items(List.of(new HistoryDetail(TIMESTAMP_1, "status", new Status(TESTING), new Status(ANALYSE), - new HistoryDetail.Actor(DISPLAY_NAME_ONE)))); + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null))); + } + + public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD3_HISTORY_FOR_MULTIPLE_STATUSES() { + return CardHistoryResponseDTO.builder() + .items(List.of(new HistoryDetail(TIMESTAMP_1, "status", new Status(IN_DEV), new Status(ANALYSE), + new HistoryDetail.Actor(DISPLAY_NAME_ONE), null))); } public static CardHistoryResponseDTO.CardHistoryResponseDTOBuilder CARD_HISTORY_WITH_NO_STATUS_FIELD() { return CardHistoryResponseDTO.builder() - .items(List.of(new HistoryDetail(2, "assignee", new Status("In Dev"), new Status("To do"), null), + .items(List.of(new HistoryDetail(2, "assignee", new Status("In Dev"), new Status("To do"), null, null), new HistoryDetail(TIMESTAMP_1, "customfield_10021", new Status("Impediment"), new Status(FLAG), - null), - new HistoryDetail(TIMESTAMP_2, "flagged", new Status("Impediment"), new Status("removeFlag"), + null, null), + new HistoryDetail(TIMESTAMP_2, "flagged", new Status("Impediment"), new Status("removeFlag"), null, null))); } + public static List MOCK_JIRA_BOARD_COLUMN_SETTING_LIST() { + return List.of(RequestJiraBoardColumnSetting.builder().name(IN_DEV).value(IN_DEV).build(), + RequestJiraBoardColumnSetting.builder().name(ANALYSE).value(ANALYSE).build(), + RequestJiraBoardColumnSetting.builder().name(WAITING_FOR_TESTING).value(WAITING_FOR_TESTING).build(), + RequestJiraBoardColumnSetting.builder().name(BLOCK).value(BLOCK).build(), + RequestJiraBoardColumnSetting.builder().name(TESTING).value(TESTING).build(), + RequestJiraBoardColumnSetting.builder().name(DONE).value(DONE).build(), + RequestJiraBoardColumnSetting.builder().name(REVIEW).value(REVIEW).build()); + } + } diff --git a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java index a2741aaf8b..f404d6525e 100644 --- a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java +++ b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java @@ -15,6 +15,9 @@ import heartbeat.controller.board.dto.request.BoardRequestParam; import heartbeat.controller.board.dto.request.BoardType; import heartbeat.controller.board.dto.request.BoardVerifyRequestParam; +import heartbeat.controller.board.dto.request.CardStepsEnum; +import heartbeat.controller.board.dto.request.RequestJiraBoardColumnSetting; +import heartbeat.controller.board.dto.request.ReworkTimesSetting; import heartbeat.controller.board.dto.request.StoryPointsAndCycleTimeRequest; import heartbeat.controller.board.dto.response.BoardConfigDTO; import heartbeat.controller.board.dto.response.CardCollection; @@ -65,9 +68,11 @@ import static heartbeat.service.jira.JiraBoardConfigDTOFixture.BOARD_ID; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD1_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD1_HISTORY_FOR_MULTIPLE_STATUSES; +import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD1_HISTORY_FOR_MULTIPLE_STATUSES_WITH_FLAG; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD2_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD2_HISTORY_FOR_MULTIPLE_STATUSES; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD3_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER; +import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD3_HISTORY_FOR_MULTIPLE_STATUSES; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD_HISTORY_DONE_TIME_GREATER_THAN_END_TIME_BUILDER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD_HISTORY_MULTI_REAL_DONE_RESPONSE_BUILDER; import static heartbeat.service.jira.JiraBoardConfigDTOFixture.CARD_HISTORY_MULTI_RESPONSE_BUILDER; @@ -122,6 +127,8 @@ class JiraServiceTest { public static final String SITE_ATLASSIAN_NET = "https://site.atlassian.net"; + public static final String INVALID_SITE_ATLASSIAN_NET = "https://unknown.atlassian.net"; + private final BoardType boardTypeJira = BoardType.fromStyle("next-gen"); private final BoardType boardTypeClassicJira = BoardType.fromStyle("classic"); @@ -509,6 +516,20 @@ void shouldCallJiraFeignClientBoardAndThrowNotFoundWhenVerifyJiraBoard() { assertThat(thrown).isInstanceOf(RuntimeException.class).hasMessageContaining("boardId is incorrect"); } + @Test + void shouldCallJiraFeignClientBoardAndThrowNotFoundWhenVerifyJiraBoardSite() { + URI baseUrl = URI.create(INVALID_SITE_ATLASSIAN_NET); + String token = "token"; + BoardVerifyRequestParam boardVerifyRequestParam = BOARD_VERIFY_REQUEST_BUILDER().build(); + + when(urlGenerator.getUri(any())).thenReturn(URI.create(INVALID_SITE_ATLASSIAN_NET)); + doThrow(new NotFoundException("site is incorrect")).when(jiraFeignClient).getDashboard(baseUrl, token); + + Throwable thrown = catchThrowable(() -> jiraService.verify(boardTypeJira, boardVerifyRequestParam)); + assertThat(thrown).isInstanceOf(RuntimeException.class).hasMessageContaining("site is incorrect"); + + } + @Test @Deprecated void shouldCallJiraFeignClientAndThrowNotFoundExceptionWhenGetJiraBoardConfig() throws JsonProcessingException { @@ -553,13 +574,13 @@ void shouldCallJiraFeignClientTwiceGivenTwoPageHistoryDataWhenGetJiraBoardConfig .thenReturn(CARD_HISTORY_MULTI_RESPONSE_BUILDER().build()); when(jiraFeignClient.getJiraCardHistoryByCount(baseUrl, "2", 0, 100, token)) .thenReturn(new CardHistoryResponseDTO(false, new ArrayList<>( - List.of(new HistoryDetail(1, "status", new Status("To do"), new Status("Block"), null))))); + List.of(new HistoryDetail(1, "status", new Status("To do"), new Status("Block"), null, null))))); when(jiraFeignClient.getJiraCardHistoryByCount(baseUrl, "2", 100, 100, token)) .thenReturn(new CardHistoryResponseDTO(true, new ArrayList<>( - List.of(new HistoryDetail(2, "assignee", new Status("In Dev"), new Status("To do"), null))))); + List.of(new HistoryDetail(2, "assignee", new Status("In Dev"), new Status("To do"), null, null))))); when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); // when - jiraService.getStoryPointsAndCycleTimeForDoneCards(storyPointsAndCycleTimeRequest, + jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), ""); // then verify(jiraFeignClient, times(2)).getJiraCardHistoryByCount(any(), eq("2"), anyInt(), anyInt(), any()); @@ -632,9 +653,7 @@ void shouldCallJiraFeignClientAndThrowNonColumnWhenGetJiraBoardConfig() { when(jiraFeignClient.getTargetField(baseUrl, "project key", token)) .thenReturn(FIELD_RESPONSE_BUILDER().build()); - Throwable thrown = catchThrowable(() -> { - jiraService.getJiraConfiguration(boardTypeJira, boardRequestParam); - }); + Throwable thrown = catchThrowable(() -> jiraService.getJiraConfiguration(boardTypeJira, boardRequestParam)); assertThat(thrown).isInstanceOf(InternalServerErrorException.class) .hasMessageContaining("Failed to call Jira to get board config, cause is"); } @@ -693,7 +712,7 @@ void shouldGetCardsWhenCallGetStoryPointsAndCycleTimeGivenStoryPointKeyFromEnvir TargetField.builder().key("").name("Flagged").flag(true).build())); StoryPointsAndCycleTimeRequest storyPointsAndCycleTimeRequest = STORY_POINTS_FORM_ALL_DONE_CARD().build(); storyPointsAndCycleTimeRequest.setOverrideFields(jiraBoardSetting.getOverrideFields()); - CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeForDoneCards( + CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards( storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), ""); assertThat(cardCollection.getStoryPointSum()).isEqualTo(11); @@ -728,7 +747,7 @@ void shouldGetCardsWhenCallGetStoryPointsRealDoneAndCycleTimeGivenStoryPointKeyF StoryPointsAndCycleTimeRequest storyPointsAndCycleTimeRequest = STORY_POINTS_FORM_ALL_REAL_DONE_CARD().build(); JiraBoardSetting jiraBoardSetting = JIRA_BOARD_REAL_DONE_SETTING_BUILD().build(); - CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeForDoneCards( + CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards( storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), ""); assertThat(cardCollection.getStoryPointSum()).isEqualTo(5); @@ -765,7 +784,7 @@ void shouldGetCardsWhenCallGetStoryPointsAndCycleTimeGivenNotEmptyStoryPointKeyA TargetField.builder().key("customfield_10017").name("Flagged").flag(true).build())); StoryPointsAndCycleTimeRequest storyPointsAndCycleTimeRequest = STORY_POINTS_FORM_ALL_DONE_CARD().build(); storyPointsAndCycleTimeRequest.setOverrideFields(jiraBoardSetting.getOverrideFields()); - CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeForDoneCards( + CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards( storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), ""); assertThat(cardCollection.getStoryPointSum()).isEqualTo(11); @@ -1046,7 +1065,7 @@ void shouldGetCardsWhenCallGetStoryPointsAndCycleTime() throws JsonProcessingExc StoryPointsAndCycleTimeRequest storyPointsAndCycleTimeRequest = STORY_POINTS_FORM_ALL_DONE_CARD().build(); JiraBoardSetting jiraBoardSetting = JIRA_BOARD_SETTING_BUILD().build(); - CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeForDoneCards( + CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards( storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), ""); assertThat(cardCollection.getStoryPointSum()).isEqualTo(0); @@ -1076,7 +1095,7 @@ void shouldGetCardsWhenCallGetStoryPointsAndCycleTimeWhenBoardTypeIsClassicJira( .thenReturn(CARD_HISTORY_RESPONSE_BUILDER().build()); when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); - CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeForDoneCards( + CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards( storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), ""); assertThat(cardCollection.getCardsNumber()).isEqualTo(0); @@ -1101,7 +1120,7 @@ void shouldGetCardsWhenCallGetStoryPointsAndCycleTimeWhenDoneTimeGreaterThanSele .thenReturn(CARD_HISTORY_DONE_TIME_GREATER_THAN_END_TIME_BUILDER().build()); when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); - CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeForDoneCards( + CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards( storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), ""); assertThat(cardCollection.getCardsNumber()).isEqualTo(0); @@ -1114,8 +1133,10 @@ void shouldReturnBadRequestExceptionWhenBoardTypeIsNotCorrect() { StoryPointsAndCycleTimeRequest storyPointsAndCycleTimeRequest = INCORRECT_JIRA_STORY_POINTS_FORM_ALL_DONE_CARD() .build(); - assertThatThrownBy(() -> jiraService.getStoryPointsAndCycleTimeForDoneCards(storyPointsAndCycleTimeRequest, - jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), null)) + List boardColumns = jiraBoardSetting.getBoardColumns(); + List users = List.of("Zhang San"); + assertThatThrownBy(() -> jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards( + storyPointsAndCycleTimeRequest, boardColumns, users, null)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Board type does not find!"); } @@ -1147,8 +1168,8 @@ public void shouldProcessCustomFieldsForCardsWhenCallGetStoryPointsAndCycleTime( when(boardUtil.getCycleTimeInfos(any(), any(), any())).thenReturn(CYCLE_TIME_INFO_LIST()); when(boardUtil.getOriginCycleTimeInfos(any())).thenReturn(CYCLE_TIME_INFO_LIST()); - CardCollection doneCards = jiraService.getStoryPointsAndCycleTimeForDoneCards(storyPointsAndCycleTimeRequest, - jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), ""); + CardCollection doneCards = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards( + storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), ""); assertThat(doneCards.getStoryPointSum()).isEqualTo(1); assertThat(doneCards.getCardsNumber()).isEqualTo(1); } @@ -1166,8 +1187,8 @@ public void shouldReturnNullWhenCallGetStoryPointsAndCycleTimeAndHistoryISNull() when(jiraFeignClient.getJiraCardHistoryByCount(any(), any(), anyInt(), anyInt(), any())) .thenReturn(CARD_HISTORY_RESPONSE_BUILDER_TO_DONE().build()); - CardCollection doneCards = jiraService.getStoryPointsAndCycleTimeForDoneCards(storyPointsAndCycleTimeRequest, - jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), null); + CardCollection doneCards = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards( + storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), null); assertThat(doneCards.getStoryPointSum()).isEqualTo(0); assertThat(doneCards.getCardsNumber()).isEqualTo(0); } @@ -1190,8 +1211,10 @@ void shouldReturnIllegalArgumentExceptionWhenHaveUnknownColumn() throws JsonProc .thenReturn(allDoneCards); when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(FIELD_RESPONSE_BUILDER().build()); - assertThatThrownBy(() -> jiraService.getStoryPointsAndCycleTimeForDoneCards(storyPointsAndCycleTimeRequest, - jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), null)) + List boardColumns = jiraBoardSetting.getBoardColumns(); + List users = List.of("Zhang San"); + assertThatThrownBy(() -> jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards( + storyPointsAndCycleTimeRequest, boardColumns, users, null)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Type does not find!"); } @@ -1364,9 +1387,9 @@ void shouldGetRealDoneCardGivenCallGetStoryPointsAndCycleTimeWhenUseHistoricalAs .thenReturn(CARD3_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER().build()); when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); - CardCollection cardCollection1 = jiraService.getStoryPointsAndCycleTimeForDoneCards(request, + CardCollection cardCollection1 = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(request, jiraBoardSetting.getBoardColumns(), List.of("Da Pei"), assigneeFilter); - CardCollection cardCollection2 = jiraService.getStoryPointsAndCycleTimeForDoneCards(request, + CardCollection cardCollection2 = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(request, jiraBoardSetting.getBoardColumns(), List.of("song"), assigneeFilter); assertThat(cardCollection1.getCardsNumber()).isEqualTo(0); @@ -1403,9 +1426,9 @@ void shouldGetRealDoneCardGivenCallGetStoryPointsAndCycleTimeWhenUseLastAssignee .thenReturn(CARD3_HISTORY_FOR_HISTORICAL_ASSIGNEE_FILTER().build()); when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); - CardCollection cardCollection1 = jiraService.getStoryPointsAndCycleTimeForDoneCards(request, + CardCollection cardCollection1 = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(request, jiraBoardSetting.getBoardColumns(), List.of("yun"), assigneeFilter); - CardCollection cardCollection2 = jiraService.getStoryPointsAndCycleTimeForDoneCards(request, + CardCollection cardCollection2 = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(request, jiraBoardSetting.getBoardColumns(), List.of("Da Pei"), assigneeFilter); assertThat(cardCollection1.getCardsNumber()).isEqualTo(1); @@ -1519,7 +1542,7 @@ void shouldGetRealDoneCardsGivenMultipleStatuesMappingToDoneStatusWhenCallGetSto .thenReturn(CARD2_HISTORY_FOR_MULTIPLE_STATUSES().build()); when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); - CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeForDoneCards(request, + CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(request, jiraBoardSetting.getBoardColumns(), List.of("Da Pei"), assigneeFilter); assertThat(cardCollection.getCardsNumber()).isEqualTo(1); @@ -1553,10 +1576,189 @@ void shouldGetRealDoneCardsGivenHistoryWithNoStatusFieldWhenCallGetStoryPointsAn .thenReturn(CARD_HISTORY_WITH_NO_STATUS_FIELD().build()); when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); - CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeForDoneCards(request, + CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(request, jiraBoardSetting.getBoardColumns(), List.of("Da Pei"), assigneeFilter); assertThat(cardCollection.getCardsNumber()).isZero(); } + @Test + void shouldGetRealDoneCardsReworkToInDevTimesGivenNotConsiderFlagIsBlockAndNoExclude() + throws JsonProcessingException { + + URI baseUrl = URI.create(SITE_ATLASSIAN_NET); + String token = "token"; + String assigneeFilter = "lastAssignee"; + + // request param + JiraBoardSetting jiraBoardSetting = JIRA_BOARD_SETTING_WITH_HISTORICAL_ASSIGNEE_FILTER_METHOD().build(); + StoryPointsAndCycleTimeRequest request = STORY_POINTS_REQUEST_WITH_MULTIPLE_REAL_DONE_STATUSES() + .treatFlagCardAsBlock(false) + .reworkTimesSetting(ReworkTimesSetting.builder().reworkState("In Dev").excludedStates(List.of()).build()) + .build(); + + // return value + String allDoneCards = objectMapper.writeValueAsString(ALL_DONE_CARDS_RESPONSE_FOR_MULTIPLE_STATUS().build()) + .replaceAll("sprint", "customfield_10020") + .replaceAll("partner", "customfield_10037") + .replaceAll("flagged", "customfield_10021") + .replaceAll("development", "customfield_10000"); + + when(urlGenerator.getUri(any())).thenReturn(baseUrl); + when(jiraFeignClient.getJiraCards(any(), any(), anyInt(), anyInt(), any(), any())).thenReturn(allDoneCards); + when(jiraFeignClient.getJiraCardHistoryByCount(baseUrl, "ADM-475", 0, 100, token)) + .thenReturn(CARD1_HISTORY_FOR_MULTIPLE_STATUSES().build()); + when(jiraFeignClient.getJiraCardHistoryByCount(baseUrl, "ADM-524", 0, 100, token)) + .thenReturn(CARD3_HISTORY_FOR_MULTIPLE_STATUSES().build()); + when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); + + CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(request, + jiraBoardSetting.getBoardColumns(), + List.of(JiraBoardConfigDTOFixture.DISPLAY_NAME_ONE, JiraBoardConfigDTOFixture.DISPLAY_NAME_TWO), + assigneeFilter); + + assertThat(cardCollection.getReworkCardNumber()).isEqualTo(1); + assertThat(cardCollection.getReworkRatio()).isEqualTo(0.5); + assertThat(cardCollection.getJiraCardDTOList().get(0).getTotalReworkTimes()).isEqualTo(3); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(0).getState()) + .isEqualTo(CardStepsEnum.BLOCK); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(0).getTimes()).isEqualTo(1); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(1).getState()) + .isEqualTo(CardStepsEnum.REVIEW); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(1).getTimes()).isEqualTo(1); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(3).getState()) + .isEqualTo(CardStepsEnum.TESTING); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(3).getTimes()).isEqualTo(1); + } + + @Test + void shouldGetRealDoneCardsReworkTimesToInDevGivenConsiderFlagAsBlock() throws JsonProcessingException { + + URI baseUrl = URI.create(SITE_ATLASSIAN_NET); + String token = "token"; + String assigneeFilter = "lastAssignee"; + + // request param + JiraBoardSetting jiraBoardSetting = JIRA_BOARD_SETTING_WITH_HISTORICAL_ASSIGNEE_FILTER_METHOD().build(); + StoryPointsAndCycleTimeRequest request = STORY_POINTS_REQUEST_WITH_MULTIPLE_REAL_DONE_STATUSES() + .treatFlagCardAsBlock(true) + .reworkTimesSetting(ReworkTimesSetting.builder().reworkState("In Dev").excludedStates(List.of()).build()) + .build(); + + // return value + String allDoneCards = objectMapper.writeValueAsString(ALL_DONE_CARDS_RESPONSE_FOR_MULTIPLE_STATUS().build()) + .replaceAll("sprint", "customfield_10020") + .replaceAll("partner", "customfield_10037") + .replaceAll("flagged", "customfield_10021") + .replaceAll("development", "customfield_10000"); + + when(urlGenerator.getUri(any())).thenReturn(baseUrl); + when(jiraFeignClient.getJiraCards(any(), any(), anyInt(), anyInt(), any(), any())).thenReturn(allDoneCards); + when(jiraFeignClient.getJiraCardHistoryByCount(baseUrl, "ADM-475", 0, 100, token)) + .thenReturn(CARD1_HISTORY_FOR_MULTIPLE_STATUSES_WITH_FLAG().build()); + when(jiraFeignClient.getJiraCardHistoryByCount(baseUrl, "ADM-524", 0, 100, token)) + .thenReturn(CARD2_HISTORY_FOR_MULTIPLE_STATUSES().build()); + when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); + + CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(request, + jiraBoardSetting.getBoardColumns(), List.of(JiraBoardConfigDTOFixture.DISPLAY_NAME_ONE), + assigneeFilter); + + assertThat(cardCollection.getReworkCardNumber()).isEqualTo(1); + assertThat(cardCollection.getReworkRatio()).isEqualTo(1); + assertThat(cardCollection.getJiraCardDTOList().get(0).getTotalReworkTimes()).isEqualTo(2); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(0).getState()) + .isEqualTo(CardStepsEnum.BLOCK); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(0).getTimes()).isEqualTo(2); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(0).getState()) + .isEqualTo(CardStepsEnum.BLOCK); + } + + @Test + void shouldGetRealDoneCardsReworkToInDevTimesGivenNotConsiderFlagIsBlockAndExcludeState() + throws JsonProcessingException { + + URI baseUrl = URI.create(SITE_ATLASSIAN_NET); + String token = "token"; + String assigneeFilter = "lastAssignee"; + + // request param + JiraBoardSetting jiraBoardSetting = JIRA_BOARD_SETTING_WITH_HISTORICAL_ASSIGNEE_FILTER_METHOD().build(); + StoryPointsAndCycleTimeRequest request = STORY_POINTS_REQUEST_WITH_MULTIPLE_REAL_DONE_STATUSES() + .treatFlagCardAsBlock(false) + .reworkTimesSetting( + ReworkTimesSetting.builder().reworkState("In Dev").excludedStates(List.of("Block")).build()) + .build(); + + // return value + String allDoneCards = objectMapper.writeValueAsString(ALL_DONE_CARDS_RESPONSE_FOR_MULTIPLE_STATUS().build()) + .replaceAll("sprint", "customfield_10020") + .replaceAll("partner", "customfield_10037") + .replaceAll("flagged", "customfield_10021") + .replaceAll("development", "customfield_10000"); + + when(urlGenerator.getUri(any())).thenReturn(baseUrl); + when(jiraFeignClient.getJiraCards(any(), any(), anyInt(), anyInt(), any(), any())).thenReturn(allDoneCards); + when(jiraFeignClient.getJiraCardHistoryByCount(baseUrl, "ADM-475", 0, 100, token)) + .thenReturn(CARD1_HISTORY_FOR_MULTIPLE_STATUSES().build()); + when(jiraFeignClient.getJiraCardHistoryByCount(baseUrl, "ADM-524", 0, 100, token)) + .thenReturn(CARD3_HISTORY_FOR_MULTIPLE_STATUSES().build()); + when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); + + CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(request, + jiraBoardSetting.getBoardColumns(), + List.of(JiraBoardConfigDTOFixture.DISPLAY_NAME_ONE, JiraBoardConfigDTOFixture.DISPLAY_NAME_TWO), + assigneeFilter); + + assertThat(cardCollection.getReworkCardNumber()).isEqualTo(1); + assertThat(cardCollection.getReworkRatio()).isEqualTo(0.5); + assertThat(cardCollection.getJiraCardDTOList().get(0).getTotalReworkTimes()).isEqualTo(2); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(0).getState()) + .isEqualTo(CardStepsEnum.REVIEW); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(0).getTimes()).isEqualTo(1); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(1).getState()) + .isEqualTo(CardStepsEnum.WAITING); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(1).getTimes()).isZero(); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(2).getState()) + .isEqualTo(CardStepsEnum.TESTING); + assertThat(cardCollection.getJiraCardDTOList().get(0).getReworkTimesInfos().get(2).getTimes()).isEqualTo(1); + } + + @Test + void shouldGetRealDoneCardsReworkTimesToTestingGivenConsiderFlagAsBlock() throws JsonProcessingException { + + URI baseUrl = URI.create(SITE_ATLASSIAN_NET); + String token = "token"; + String assigneeFilter = "lastAssignee"; + + // request param + JiraBoardSetting jiraBoardSetting = JIRA_BOARD_SETTING_WITH_HISTORICAL_ASSIGNEE_FILTER_METHOD().build(); + StoryPointsAndCycleTimeRequest request = STORY_POINTS_REQUEST_WITH_MULTIPLE_REAL_DONE_STATUSES() + .treatFlagCardAsBlock(true) + .reworkTimesSetting(ReworkTimesSetting.builder().reworkState("Testing").excludedStates(List.of()).build()) + .build(); + + // return value + String allDoneCards = objectMapper.writeValueAsString(ALL_DONE_CARDS_RESPONSE_FOR_MULTIPLE_STATUS().build()) + .replaceAll("sprint", "customfield_10020") + .replaceAll("partner", "customfield_10037") + .replaceAll("flagged", "customfield_10021") + .replaceAll("development", "customfield_10000"); + + when(urlGenerator.getUri(any())).thenReturn(baseUrl); + when(jiraFeignClient.getJiraCards(any(), any(), anyInt(), anyInt(), any(), any())).thenReturn(allDoneCards); + when(jiraFeignClient.getJiraCardHistoryByCount(baseUrl, "ADM-475", 0, 100, token)) + .thenReturn(CARD1_HISTORY_FOR_MULTIPLE_STATUSES_WITH_FLAG().build()); + when(jiraFeignClient.getJiraCardHistoryByCount(baseUrl, "ADM-524", 0, 100, token)) + .thenReturn(CARD2_HISTORY_FOR_MULTIPLE_STATUSES().build()); + when(jiraFeignClient.getTargetField(baseUrl, "PLL", token)).thenReturn(ALL_FIELD_RESPONSE_BUILDER().build()); + + CardCollection cardCollection = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(request, + jiraBoardSetting.getBoardColumns(), List.of(JiraBoardConfigDTOFixture.DISPLAY_NAME_ONE), + assigneeFilter); + + assertThat(cardCollection.getReworkCardNumber()).isZero(); + assertThat(cardCollection.getReworkRatio()).isZero(); + } + } diff --git a/backend/src/test/java/heartbeat/service/pipeline/buildkite/BuildKiteServiceTest.java b/backend/src/test/java/heartbeat/service/pipeline/buildkite/BuildKiteServiceTest.java index ac4b7998b0..a686fd522f 100644 --- a/backend/src/test/java/heartbeat/service/pipeline/buildkite/BuildKiteServiceTest.java +++ b/backend/src/test/java/heartbeat/service/pipeline/buildkite/BuildKiteServiceTest.java @@ -1,25 +1,5 @@ package heartbeat.service.pipeline.buildkite; -import heartbeat.client.dto.pipeline.buildkite.DeployInfo; -import heartbeat.controller.pipeline.dto.request.TokenParam; -import heartbeat.client.dto.pipeline.buildkite.PageStepsInfoDto; -import heartbeat.exception.InternalServerErrorException; -import heartbeat.exception.PermissionDenyException; -import heartbeat.exception.ServiceUnavailableException; -import heartbeat.exception.UnauthorizedException; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.hibernate.validator.internal.util.Contracts.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import heartbeat.client.BuildKiteFeignClient; @@ -28,28 +8,27 @@ import heartbeat.client.dto.pipeline.buildkite.BuildKiteOrganizationsInfo; import heartbeat.client.dto.pipeline.buildkite.BuildKitePipelineDTO; import heartbeat.client.dto.pipeline.buildkite.BuildKiteTokenInfo; +import heartbeat.client.dto.pipeline.buildkite.DeployInfo; import heartbeat.client.dto.pipeline.buildkite.DeployTimes; +import heartbeat.client.dto.pipeline.buildkite.PageBuildKitePipelineInfoDTO; +import heartbeat.client.dto.pipeline.buildkite.PageStepsInfoDto; import heartbeat.controller.pipeline.dto.request.DeploymentEnvironment; import heartbeat.controller.pipeline.dto.request.PipelineStepsParam; +import heartbeat.controller.pipeline.dto.request.TokenParam; import heartbeat.controller.pipeline.dto.response.BuildKiteResponseDTO; import heartbeat.controller.pipeline.dto.response.Pipeline; import heartbeat.controller.pipeline.dto.response.PipelineStepsDTO; +import heartbeat.exception.InternalServerErrorException; import heartbeat.exception.NotFoundException; +import heartbeat.exception.PermissionDenyException; import heartbeat.exception.RequestFailedException; +import heartbeat.exception.ServiceUnavailableException; +import heartbeat.exception.UnauthorizedException; import heartbeat.service.pipeline.buildkite.builder.BuildKiteBuildInfoBuilder; import heartbeat.service.pipeline.buildkite.builder.BuildKiteJobBuilder; import heartbeat.service.pipeline.buildkite.builder.DeployInfoBuilder; import heartbeat.service.pipeline.buildkite.builder.DeployTimesBuilder; import heartbeat.service.pipeline.buildkite.builder.DeploymentEnvironmentBuilder; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CompletionException; - import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -63,6 +42,26 @@ import org.springframework.http.ResponseEntity; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hibernate.validator.internal.util.Contracts.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class BuildKiteServiceTest { @@ -499,10 +498,14 @@ void shouldReturnBuildKiteResponseWhenGetBuildKiteInfo() throws IOException { new File("src/test/java/heartbeat/controller/pipeline/buildKitePipelineInfoData.json"), new TypeReference<>() { }); + var pipelineDTOResponse = PageBuildKitePipelineInfoDTO.builder() + .firstPageInfo(pipelineDTOS) + .totalPage(1) + .build(); when(buildKiteFeignClient.getBuildKiteOrganizationsInfo(any())) .thenReturn(List.of(BuildKiteOrganizationsInfo.builder().name(TEST_ORG_NAME).slug(TEST_ORG_ID).build())); - when(buildKiteFeignClient.getPipelineInfo("Bearer mock_token", TEST_ORG_ID, "1", "100")) - .thenReturn(pipelineDTOS); + when(cachePageService.getPipelineInfoList(TEST_ORG_ID, "Bearer mock_token", "1", "100")) + .thenReturn(pipelineDTOResponse); BuildKiteResponseDTO buildKiteResponseDTO = buildKiteService.getBuildKiteInfo(tokenParam); @@ -517,6 +520,46 @@ void shouldReturnBuildKiteResponseWhenGetBuildKiteInfo() throws IOException { assertThat(pipeline.getSteps().size()).isEqualTo(1); } + @Test + void shouldReturnBuildKiteResponseGivenNonePipelineInfoWhenGetBuildKiteInfo() { + TokenParam tokenParam = TokenParam.builder().token(MOCK_TOKEN).build(); + var pipelineDTOResponse = PageBuildKitePipelineInfoDTO.builder().firstPageInfo(null).totalPage(1).build(); + when(buildKiteFeignClient.getBuildKiteOrganizationsInfo(any())) + .thenReturn(List.of(BuildKiteOrganizationsInfo.builder().name(TEST_ORG_NAME).slug(TEST_ORG_ID).build())); + when(cachePageService.getPipelineInfoList(TEST_ORG_ID, "Bearer mock_token", "1", "100")) + .thenReturn(pipelineDTOResponse); + + BuildKiteResponseDTO buildKiteResponseDTO = buildKiteService.getBuildKiteInfo(tokenParam); + + assertThat(buildKiteResponseDTO.getPipelineList()).isEmpty(); + } + + @Test + void shouldReturnBuildKiteResponseGivenManyPipelinePageWhenGetBuildKiteInfo() throws IOException { + TokenParam tokenParam = TokenParam.builder().token(MOCK_TOKEN).build(); + ObjectMapper mapper = new ObjectMapper(); + List pipelineDTOS = mapper.readValue( + new File("src/test/java/heartbeat/controller/pipeline/buildKitePipelineInfoData.json"), + new TypeReference<>() { + }); + var pipelineDTOResponse = PageBuildKitePipelineInfoDTO.builder() + .firstPageInfo(pipelineDTOS) + .totalPage(3) + .build(); + when(buildKiteFeignClient.getBuildKiteOrganizationsInfo(any())) + .thenReturn(List.of(BuildKiteOrganizationsInfo.builder().name(TEST_ORG_NAME).slug(TEST_ORG_ID).build())); + when(cachePageService.getPipelineInfoList(TEST_ORG_ID, "Bearer mock_token", "1", "100")) + .thenReturn(pipelineDTOResponse); + when(buildKiteFeignClient.getPipelineInfo("Bearer mock_token", TEST_ORG_ID, "2", "100")) + .thenReturn(ResponseEntity.ok(pipelineDTOS)); + when(buildKiteFeignClient.getPipelineInfo("Bearer mock_token", TEST_ORG_ID, "3", "100")) + .thenReturn(ResponseEntity.ok(pipelineDTOS)); + + BuildKiteResponseDTO buildKiteResponseDTO = buildKiteService.getBuildKiteInfo(tokenParam); + + assertThat(buildKiteResponseDTO.getPipelineList()).hasSize(3); + } + @Test void shouldThrowInternalServerErrorExceptionWhenGetBuildKiteInfo500Exception() { TokenParam tokenParam = TokenParam.builder().token(MOCK_TOKEN).build(); diff --git a/backend/src/test/java/heartbeat/service/pipeline/buildkite/CachePageServiceTest.java b/backend/src/test/java/heartbeat/service/pipeline/buildkite/CachePageServiceTest.java index 18722488df..17fe271b57 100644 --- a/backend/src/test/java/heartbeat/service/pipeline/buildkite/CachePageServiceTest.java +++ b/backend/src/test/java/heartbeat/service/pipeline/buildkite/CachePageServiceTest.java @@ -1,8 +1,11 @@ package heartbeat.service.pipeline.buildkite; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import heartbeat.client.BuildKiteFeignClient; import heartbeat.client.dto.pipeline.buildkite.BuildKiteBuildInfo; import heartbeat.client.dto.pipeline.buildkite.BuildKiteJob; +import heartbeat.client.dto.pipeline.buildkite.BuildKitePipelineDTO; import heartbeat.client.dto.pipeline.buildkite.PageStepsInfoDto; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -15,6 +18,8 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -52,6 +57,13 @@ class CachePageServiceTest { ; rel="last" """; + public static final String NONE_PAGE_HEADER = """ + ; rel="first", + ; rel="prev", + ; rel="next", + ; rel="last" + """; + public static final String NONE_TOTAL_PAGE_HEADER = """ ; rel="first", ; rel="prev", @@ -64,7 +76,7 @@ public void setUp() { } @Test - public void shouldReturnPageStepsInfoDtoWhenFetchPageStepsInfoSuccessGivenNullLinkHeader() { + void shouldReturnPageStepsInfoDtoWhenFetchPageStepsInfoSuccessGivenNullLinkHeader() { BuildKiteJob testJob = BuildKiteJob.builder().name(TEST_JOB_NAME).build(); List buildKiteBuildInfoList = new ArrayList<>(); buildKiteBuildInfoList.add(BuildKiteBuildInfo.builder() @@ -86,11 +98,8 @@ public void shouldReturnPageStepsInfoDtoWhenFetchPageStepsInfoSuccessGivenNullLi } @Test - public void shouldReturnPageStepsInfoDtoWhenFetchPageStepsInfoSuccessGivenValidLinkHeader() { - List linkHeader = new ArrayList<>(); - linkHeader.add(TOTAL_PAGE_HEADER); - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.addAll(HttpHeaders.LINK, linkHeader); + void shouldReturnPageStepsInfoDtoWhenFetchPageStepsInfoSuccessGivenValidLinkHeader() { + HttpHeaders httpHeaders = buildHttpHeaders(TOTAL_PAGE_HEADER); List buildKiteBuildInfoList = new ArrayList<>(); BuildKiteJob testJob = BuildKiteJob.builder().name(TEST_JOB_NAME).build(); buildKiteBuildInfoList.add(BuildKiteBuildInfo.builder().jobs(List.of(testJob)).build()); @@ -109,11 +118,8 @@ public void shouldReturnPageStepsInfoDtoWhenFetchPageStepsInfoSuccessGivenValidL } @Test - public void shouldReturnPageStepsInfoDtoWhenFetchPageStepsInfoSuccessGivenExistButNotMatchedLinkHeader() { - List linkHeader = new ArrayList<>(); - linkHeader.add(NONE_TOTAL_PAGE_HEADER); - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.addAll(HttpHeaders.LINK, linkHeader); + void shouldReturnPageStepsInfoDtoWhenFetchPageStepsInfoSuccessGivenExistButNotMatchedLinkHeader() { + HttpHeaders httpHeaders = buildHttpHeaders(NONE_TOTAL_PAGE_HEADER); List buildKiteBuildInfoList = new ArrayList<>(); BuildKiteJob testJob = BuildKiteJob.builder().name(TEST_JOB_NAME).build(); buildKiteBuildInfoList.add(BuildKiteBuildInfo.builder().jobs(List.of(testJob)).build()); @@ -131,4 +137,69 @@ public void shouldReturnPageStepsInfoDtoWhenFetchPageStepsInfoSuccessGivenExistB assertThat(pageStepsInfoDto.getTotalPage()).isEqualTo(1); } + @Test + void shouldReturnPageStepsInfoDtoWhenFetchPageStepsInfoSuccessGivenExistButNotMatchedPageLinkHeader() { + HttpHeaders httpHeaders = buildHttpHeaders(NONE_PAGE_HEADER); + List buildKiteBuildInfoList = new ArrayList<>(); + BuildKiteJob testJob = BuildKiteJob.builder().name(TEST_JOB_NAME).build(); + buildKiteBuildInfoList.add(BuildKiteBuildInfo.builder().jobs(List.of(testJob)).build()); + ResponseEntity> responseEntity = new ResponseEntity<>(buildKiteBuildInfoList, + httpHeaders, HttpStatus.OK); + when(buildKiteFeignClient.getPipelineSteps(anyString(), anyString(), anyString(), anyString(), anyString(), + anyString(), anyString(), any())) + .thenReturn(responseEntity); + + PageStepsInfoDto pageStepsInfoDto = cachePageService.fetchPageStepsInfo(MOCK_TOKEN, TEST_ORG_ID, + TEST_PIPELINE_ID, "1", "100", MOCK_START_TIME, MOCK_END_TIME, List.of("main")); + + assertNotNull(pageStepsInfoDto); + assertThat(pageStepsInfoDto.getFirstPageStepsInfo()).isEqualTo(responseEntity.getBody()); + assertThat(pageStepsInfoDto.getTotalPage()).isEqualTo(1); + } + + @Test + void shouldReturnPagePipelineInfoDtoWhenFetchPageStepsInfoSuccessGivenNullLinkHeader() throws IOException { + HttpHeaders httpHeaders = buildHttpHeaders(TOTAL_PAGE_HEADER); + ResponseEntity> responseEntity = getBuildKitepipelineResponseEntity(httpHeaders); + when(buildKiteFeignClient.getPipelineInfo(MOCK_TOKEN, TEST_ORG_ID, "1", "100")).thenReturn(responseEntity); + + var pageStepsInfoDto = cachePageService.getPipelineInfoList(TEST_ORG_ID, MOCK_TOKEN, "1", "100"); + + assertNotNull(pageStepsInfoDto); + assertThat(pageStepsInfoDto.getFirstPageInfo()).isEqualTo(responseEntity.getBody()); + assertThat(pageStepsInfoDto.getTotalPage()).isEqualTo(3); + } + + @Test + void shouldReturnPagePipelineInfoDtoWhenFetchPagePipelineInfoSuccessGivenExistButNotMatchedLinkHeader() + throws IOException { + HttpHeaders httpHeaders = buildHttpHeaders(NONE_TOTAL_PAGE_HEADER); + ResponseEntity> responseEntity = getBuildKitepipelineResponseEntity(httpHeaders); + when(buildKiteFeignClient.getPipelineInfo(MOCK_TOKEN, TEST_ORG_ID, "1", "100")).thenReturn(responseEntity); + + var PagePipelineInfoDTO = cachePageService.getPipelineInfoList(TEST_ORG_ID, MOCK_TOKEN, "1", "100"); + + assertNotNull(PagePipelineInfoDTO); + assertThat(PagePipelineInfoDTO.getFirstPageInfo()).isEqualTo(responseEntity.getBody()); + assertThat(PagePipelineInfoDTO.getTotalPage()).isEqualTo(1); + } + + private static ResponseEntity> getBuildKitepipelineResponseEntity( + HttpHeaders httpHeaders) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + List pipelineDTOS = mapper.readValue( + new File("src/test/java/heartbeat/controller/pipeline/buildKitePipelineInfoData.json"), + new TypeReference<>() { + }); + return new ResponseEntity<>(pipelineDTOS, httpHeaders, HttpStatus.OK); + } + + private HttpHeaders buildHttpHeaders(String totalPageHeader) { + List linkHeader = new ArrayList<>(); + linkHeader.add(totalPageHeader); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.addAll(HttpHeaders.LINK, linkHeader); + return httpHeaders; + } + } diff --git a/backend/src/test/java/heartbeat/service/report/BoardCsvFixture.java b/backend/src/test/java/heartbeat/service/report/BoardCsvFixture.java index d82283b2d3..8acfed95d3 100644 --- a/backend/src/test/java/heartbeat/service/report/BoardCsvFixture.java +++ b/backend/src/test/java/heartbeat/service/report/BoardCsvFixture.java @@ -9,6 +9,7 @@ import heartbeat.client.dto.board.jira.JiraCardField; import heartbeat.client.dto.board.jira.Sprint; import heartbeat.client.dto.board.jira.Status; +import heartbeat.controller.board.dto.request.CardStepsEnum; import heartbeat.controller.board.dto.response.CardCycleTime; import heartbeat.controller.board.dto.response.CardParent; import heartbeat.controller.board.dto.response.ColumnValue; @@ -21,6 +22,7 @@ import heartbeat.controller.board.dto.response.JiraProject; import heartbeat.controller.board.dto.response.Priority; import heartbeat.controller.board.dto.response.Reporter; +import heartbeat.controller.board.dto.response.ReworkTimesInfo; import heartbeat.controller.board.dto.response.StepsDay; import heartbeat.controller.board.dto.response.TargetField; import heartbeat.controller.report.dto.response.BoardCSVConfig; @@ -510,4 +512,12 @@ public static List MOCK_JIRA_COLUMN_LIST() { .build()); } + public static List MOCK_REWORK_TIMES_INFO_LIST() { + return List.of(ReworkTimesInfo.builder().state(CardStepsEnum.BLOCK).times(2).build(), + ReworkTimesInfo.builder().state(CardStepsEnum.REVIEW).times(0).build(), + ReworkTimesInfo.builder().state(CardStepsEnum.WAITING).times(1).build(), + ReworkTimesInfo.builder().state(CardStepsEnum.TESTING).times(0).build(), + ReworkTimesInfo.builder().state(CardStepsEnum.DONE).times(0).build()); + } + } diff --git a/backend/src/test/java/heartbeat/service/report/CSVFileGeneratorTest.java b/backend/src/test/java/heartbeat/service/report/CSVFileGeneratorTest.java index 77e73ba3bd..839316fc7b 100644 --- a/backend/src/test/java/heartbeat/service/report/CSVFileGeneratorTest.java +++ b/backend/src/test/java/heartbeat/service/report/CSVFileGeneratorTest.java @@ -133,6 +133,30 @@ void shouldConvertPipelineDataToCsvWithoutCreator() throws IOException { file.delete(); } + @Test + void shouldConvertPipelineDataToCsvWithoutCreatorName() throws IOException { + List pipelineCSVInfos = PipelineCsvFixture.MOCK_PIPELINE_CSV_DATA_WITHOUT_CREATOR_NAME(); + String fileName = CSVFileNameEnum.PIPELINE.getValue() + "-" + mockTimeStamp + ".csv"; + File file = new File(fileName); + + csvFileGenerator.convertPipelineDataToCSV(pipelineCSVInfos, mockTimeStamp); + FileInputStream fileInputStream = new FileInputStream(file); + BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream)); + String headers = reader.readLine(); + String firstLine = reader.readLine(); + + assertTrue(file.exists()); + assertEquals( + "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"Code Committed Time\",\"PR Created Time\",\"PR Merged Time\",\"Deployment Completed Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\"", + headers); + assertEquals( + "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"null\",\"880\",\"XXXX\",,\"2023-05-08T07:18:18Z\",\"2023-05-10T06:43:02.653Z\",\"168369327000\",\"1683793037000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\"", + firstLine); + reader.close(); + fileInputStream.close(); + file.delete(); + } + @Test void shouldConvertPipelineDataToCsvGivenNullCommitInfo() throws IOException { @@ -461,12 +485,12 @@ void shouldHasContentWhenGetDataFromCsvGivenDataTypeIsMetric() throws IOExceptio "Lead time for changes","Average / PR Lead Time","0" "Lead time for changes","Average / Pipeline Lead Time","0.05" "Lead time for changes","Average / Total Lead Time","0.05" - "Change failure rate","Heartbeat / Deploy prod / Failure rate","0" - "Change failure rate","Heartbeat / Check Frontend License / Failure rate","0" - "Change failure rate","Average / Failure rate","0" - "Mean Time To Recovery","Heartbeat / Deploy prod / Mean Time To Recovery","0" - "Mean Time To Recovery","Heartbeat / Check Frontend License / Mean Time To Recovery","0" - "Mean Time To Recovery","Average / Mean Time To Recovery","0\""""); + "Dev change failure rate","Heartbeat / Deploy prod / Dev change failure rate","0.0000" + "Dev change failure rate","Heartbeat / Check Frontend License / Dev change failure rate","0.0000" + "Dev change failure rate","Average / Dev change failure rate","0" + "Dev mean time to recovery","Heartbeat / Deploy prod / Dev mean time to recovery","0" + "Dev mean time to recovery","Heartbeat / Check Frontend License / Dev mean time to recovery","0" + "Dev mean time to recovery","Average / Dev mean time to recovery","0\""""); String fileName = CSVFileNameEnum.METRIC.getValue() + "-" + mockTimeStamp + ".csv"; Files.deleteIfExists(Path.of(fileName)); @@ -503,12 +527,15 @@ void shouldHasNoContentForAveragesWhenGetDataFromCsvGivenDataTypeIsMetricAndTheQ Assertions.assertEquals(metricCsvData, """ "Group","Metrics","Value" + "Rework","Total rework times","3" + "Rework","Total rework cards","3" + "Rework","Rework cards ratio(Total rework cards/Throughput)","0.9900" "Deployment frequency","Heartbeat / Deploy prod / Deployment frequency(Deployments/Day)","0.78" "Lead time for changes","Heartbeat / Deploy prod / PR Lead Time","0" "Lead time for changes","Heartbeat / Deploy prod / Pipeline Lead Time","0.02" "Lead time for changes","Heartbeat / Deploy prod / Total Lead Time","0.02" - "Change failure rate","Heartbeat / Deploy prod / Failure rate","0" - "Mean Time To Recovery","Heartbeat / Deploy prod / Mean Time To Recovery","0\""""); + "Dev change failure rate","Heartbeat / Deploy prod / Dev change failure rate","0.0000" + "Dev mean time to recovery","Heartbeat / Deploy prod / Dev mean time to recovery","0\""""); String fileName = CSVFileNameEnum.BOARD.getValue() + "-" + mockTimeStamp + ".csv"; Files.deleteIfExists(Path.of(fileName)); diff --git a/backend/src/test/java/heartbeat/service/report/GenerateReporterServiceTest.java b/backend/src/test/java/heartbeat/service/report/GenerateReporterServiceTest.java index f2ba0c7af0..07fec71249 100644 --- a/backend/src/test/java/heartbeat/service/report/GenerateReporterServiceTest.java +++ b/backend/src/test/java/heartbeat/service/report/GenerateReporterServiceTest.java @@ -1,6 +1,7 @@ package heartbeat.service.report; import com.fasterxml.jackson.databind.ObjectMapper; +import heartbeat.controller.board.dto.request.ReworkTimesSetting; import heartbeat.controller.board.dto.response.CardCollection; import heartbeat.controller.report.dto.request.BuildKiteSetting; import heartbeat.controller.report.dto.request.CodebaseSetting; @@ -8,15 +9,16 @@ import heartbeat.controller.report.dto.request.JiraBoardSetting; import heartbeat.controller.report.dto.request.MetricEnum; import heartbeat.controller.report.dto.request.MetricType; -import heartbeat.controller.report.dto.response.ChangeFailureRate; import heartbeat.controller.report.dto.response.Classification; import heartbeat.controller.report.dto.response.CycleTime; import heartbeat.controller.report.dto.response.DeploymentFrequency; +import heartbeat.controller.report.dto.response.DevChangeFailureRate; +import heartbeat.controller.report.dto.response.DevMeanTimeToRecovery; import heartbeat.controller.report.dto.response.LeadTimeForChanges; -import heartbeat.controller.report.dto.response.MeanTimeToRecovery; import heartbeat.controller.report.dto.response.MetricsDataCompleted; import heartbeat.controller.report.dto.response.PipelineCSVInfo; import heartbeat.controller.report.dto.response.ReportResponse; +import heartbeat.controller.report.dto.response.Rework; import heartbeat.controller.report.dto.response.Velocity; import heartbeat.exception.BadRequestException; import heartbeat.exception.BaseException; @@ -26,18 +28,21 @@ import heartbeat.handler.AsyncExceptionHandler; import heartbeat.handler.AsyncMetricsDataHandler; import heartbeat.handler.AsyncReportRequestHandler; -import heartbeat.service.report.calculator.ChangeFailureRateCalculator; +import heartbeat.handler.base.AsyncExceptionDTO; import heartbeat.service.report.calculator.ClassificationCalculator; import heartbeat.service.report.calculator.CycleTimeCalculator; import heartbeat.service.report.calculator.DeploymentFrequencyCalculator; +import heartbeat.service.report.calculator.DevChangeFailureRateCalculator; import heartbeat.service.report.calculator.LeadTimeForChangesCalculator; import heartbeat.service.report.calculator.MeanToRecoveryCalculator; +import heartbeat.service.report.calculator.ReworkCalculator; import heartbeat.service.report.calculator.VelocityCalculator; import heartbeat.service.report.calculator.model.FetchedData; -import heartbeat.util.ValueUtil; -import lombok.val; +import heartbeat.util.IdUtil; +import org.awaitility.Awaitility; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -54,15 +59,16 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.concurrent.TimeUnit; import static heartbeat.service.report.scheduler.DeleteExpireCSVScheduler.EXPORT_CSV_VALIDITY_TIME; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; @@ -96,6 +102,9 @@ class GenerateReporterServiceTest { @Mock ClassificationCalculator classificationCalculator; + @Mock + ReworkCalculator reworkCalculator; + @Mock DeploymentFrequencyCalculator deploymentFrequency; @@ -103,7 +112,7 @@ class GenerateReporterServiceTest { VelocityCalculator velocityCalculator; @Mock - ChangeFailureRateCalculator changeFailureRate; + DevChangeFailureRateCalculator devChangeFailureRate; @Mock MeanToRecoveryCalculator meanToRecoveryCalculator; @@ -126,6 +135,9 @@ class GenerateReporterServiceTest { @Mock AsyncExceptionHandler asyncExceptionHandler; + @Mock + KanbanCsvService kanbanCsvService; + @Captor ArgumentCaptor responseArgumentCaptor; @@ -147,61 +159,104 @@ static void afterAll() { class GenerateBoardReport { @Test - void shouldSaveReportResponseWithoutMetricDataAndUpdateMetricCompletedWhenMetricsIsEmpty() { + void shouldSaveReportResponseWithReworkInfoWhenReworkInfoTimesIsNotEmpty() { GenerateReportRequest request = GenerateReportRequest.builder() .considerHoliday(false) - .metrics(List.of()) + .metrics(List.of("rework times")) .buildKiteSetting(BuildKiteSetting.builder().build()) + .jiraBoardSetting(JiraBoardSetting.builder() + .reworkTimesSetting( + ReworkTimesSetting.builder().reworkState("In Dev").excludedStates(List.of()).build()) + .build()) .csvTimeStamp(TIMESTAMP) .build(); when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) .thenReturn(MetricsDataCompleted.builder().build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.BOARD); + .updateMetricsDataCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp()), + MetricType.BOARD, true); + when(kanbanService.fetchDataFromKanban(request)).thenReturn(FetchedData.CardCollectionInfo.builder() + .realDoneCardCollection(CardCollection.builder().build()) + .build()); + when(reworkCalculator.calculateRework(any(), any())).thenReturn(Rework.builder() + .reworkState("In Dev") + .reworkCardsRatio(1.1) + .totalReworkTimes(4) + .totalReworkCards(2) + .fromTesting(2) + .fromReview(2) + .build()); + generateReporterService.generateBoardReport(request); - verify(kanbanService, never()).fetchDataFromKanban(eq(request)); - verify(pipelineService, never()).fetchGithubData(any()); + verify(asyncExceptionHandler).remove(request.getBoardReportId()); + verify(kanbanService).fetchDataFromKanban(request); verify(workDay).changeConsiderHolidayMode(false); verify(asyncReportRequestHandler).putReport(eq(request.getBoardReportId()), responseArgumentCaptor.capture()); ReportResponse response = responseArgumentCaptor.getValue(); - assertEquals(1800000L, response.getExportValidityTime()); - assertNull(response.getCycleTime()); - assertNull(response.getVelocity()); - assertNull(response.getClassificationList()); - verify(asyncMetricsDataHandler).updateMetricsDataCompletedInHandler(eq(request.getBoardReportId()), any()); + assertEquals(2, response.getRework().getFromTesting()); + assertEquals(2, response.getRework().getFromReview()); + assertEquals("In Dev", response.getRework().getReworkState()); + assertEquals(1.1, response.getRework().getReworkCardsRatio()); + assertEquals(4, response.getRework().getTotalReworkTimes()); + assertEquals(2, response.getRework().getTotalReworkCards()); + assertNull(response.getRework().getFromDone()); } @Test - void shouldThrowErrorWhenGetMetricDataCompletedIsNull() { + void shouldSaveReportResponseWithReworkInfoWhenReworkSettingIsNullAndMetricsHasReworkTimes() { GenerateReportRequest request = GenerateReportRequest.builder() .considerHoliday(false) - .metrics(List.of()) + .metrics(List.of("rework times")) .buildKiteSetting(BuildKiteSetting.builder().build()) + .jiraBoardSetting(JiraBoardSetting.builder().build()) .csvTimeStamp(TIMESTAMP) .build(); - when(asyncMetricsDataHandler.getMetricsDataCompleted(any())).thenReturn(null); - doAnswer(invocation -> null).when(asyncReportRequestHandler).putReport(any(), any()); - doThrow(new GenerateReportException("Failed to update metrics data completed through this timestamp.")) - .when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(request.getBoardReportId(), MetricType.BOARD); + when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) + .thenReturn(MetricsDataCompleted.builder().build()); + doAnswer(invocation -> null).when(asyncMetricsDataHandler) + .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.BOARD, true); + when(kanbanService.fetchDataFromKanban(request)).thenReturn(FetchedData.CardCollectionInfo.builder() + .realDoneCardCollection(CardCollection.builder().build()) + .build()); generateReporterService.generateBoardReport(request); - verify(asyncExceptionHandler).remove(eq(request.getBoardReportId())); - verify(asyncExceptionHandler).put(eq(request.getBoardReportId()), exceptionCaptor.capture()); - assertEquals("Failed to update metrics data completed through this timestamp.", - exceptionCaptor.getValue().getMessage()); - assertEquals(500, exceptionCaptor.getValue().getStatus()); + verify(asyncExceptionHandler).remove(request.getBoardReportId()); + verify(kanbanService).fetchDataFromKanban(request); + verify(workDay).changeConsiderHolidayMode(false); + verify(asyncReportRequestHandler).putReport(eq(request.getBoardReportId()), + responseArgumentCaptor.capture()); + ReportResponse response = responseArgumentCaptor.getValue(); + assertNull(response.getRework()); + } + + @Test + void shouldSaveReportResponseWithoutMetricDataAndUpdateMetricCompletedWhenMetricsIsEmpty() { + GenerateReportRequest request = GenerateReportRequest.builder() + .considerHoliday(false) + .metrics(List.of()) + .buildKiteSetting(BuildKiteSetting.builder().build()) + .csvTimeStamp(TIMESTAMP) + .build(); + when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) + .thenReturn(MetricsDataCompleted.builder().build()); + doAnswer(invocation -> null).when(asyncMetricsDataHandler) + .updateMetricsDataCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp()), + MetricType.BOARD, true); + generateReporterService.generateBoardReport(request); + verify(kanbanService, never()).fetchDataFromKanban(eq(request)); + verify(pipelineService, never()).fetchGitHubData(any()); verify(workDay).changeConsiderHolidayMode(false); verify(asyncReportRequestHandler).putReport(eq(request.getBoardReportId()), responseArgumentCaptor.capture()); ReportResponse response = responseArgumentCaptor.getValue(); assertEquals(1800000L, response.getExportValidityTime()); assertNull(response.getCycleTime()); - verify(asyncMetricsDataHandler).updateMetricsDataCompletedInHandler(eq(request.getBoardReportId()), any()); + assertNull(response.getVelocity()); + assertNull(response.getClassificationList()); } @Test @@ -215,7 +270,8 @@ void shouldThrowErrorWhenJiraBoardSettingIsNull() { when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) .thenReturn(MetricsDataCompleted.builder().build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.BOARD); + .updateMetricsDataCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp()), + MetricType.BOARD, true); generateReporterService.generateBoardReport(request); @@ -226,8 +282,6 @@ void shouldThrowErrorWhenJiraBoardSettingIsNull() { verify(kanbanService, never()).fetchDataFromKanban(eq(request)); verify(workDay).changeConsiderHolidayMode(false); verify(asyncReportRequestHandler, never()).putReport(eq(request.getBoardReportId()), any()); - verify(asyncMetricsDataHandler, never()).updateMetricsDataCompletedInHandler(eq(request.getBoardReportId()), - any()); } @Test @@ -242,7 +296,8 @@ void shouldSaveReportResponseWithVelocityAndUpdateMetricCompletedWhenVelocityMet when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) .thenReturn(MetricsDataCompleted.builder().build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.BOARD); + .updateMetricsDataCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp()), + MetricType.BOARD, true); when(velocityCalculator.calculateVelocity(any())) .thenReturn(Velocity.builder().velocityForSP(10).velocityForCards(20).build()); when(kanbanService.fetchDataFromKanban(request)).thenReturn(FetchedData.CardCollectionInfo.builder() @@ -252,7 +307,7 @@ void shouldSaveReportResponseWithVelocityAndUpdateMetricCompletedWhenVelocityMet generateReporterService.generateBoardReport(request); verify(asyncExceptionHandler).remove(request.getBoardReportId()); - verify(pipelineService, never()).fetchGithubData(any()); + verify(pipelineService, never()).fetchGitHubData(any()); verify(kanbanService).fetchDataFromKanban(eq(request)); verify(workDay).changeConsiderHolidayMode(false); verify(asyncReportRequestHandler).putReport(eq(request.getBoardReportId()), @@ -261,7 +316,6 @@ void shouldSaveReportResponseWithVelocityAndUpdateMetricCompletedWhenVelocityMet assertEquals(1800000L, response.getExportValidityTime()); assertEquals(10, response.getVelocity().getVelocityForSP()); assertEquals(20, response.getVelocity().getVelocityForCards()); - verify(asyncMetricsDataHandler).updateMetricsDataCompletedInHandler(eq(request.getBoardReportId()), any()); } @Test @@ -276,7 +330,7 @@ void shouldSaveReportResponseWithCycleTimeAndUpdateMetricCompletedWhenCycleTimeM when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) .thenReturn(MetricsDataCompleted.builder().boardMetricsCompleted(true).build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.BOARD); + .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.BOARD, true); when(cycleTimeCalculator.calculateCycleTime(any(), any())).thenReturn(CycleTime.builder() .averageCycleTimePerSP(10) .totalTimeForCards(15) @@ -288,7 +342,7 @@ void shouldSaveReportResponseWithCycleTimeAndUpdateMetricCompletedWhenCycleTimeM generateReporterService.generateBoardReport(request); - verify(pipelineService, never()).fetchGithubData(any()); + verify(pipelineService, never()).fetchGitHubData(any()); verify(kanbanService).fetchDataFromKanban(eq(request)); verify(workDay).changeConsiderHolidayMode(false); verify(asyncReportRequestHandler).putReport(eq(request.getBoardReportId()), @@ -298,7 +352,6 @@ void shouldSaveReportResponseWithCycleTimeAndUpdateMetricCompletedWhenCycleTimeM assertEquals(10, response.getCycleTime().getAverageCycleTimePerSP()); assertEquals(20, response.getCycleTime().getAverageCycleTimePerCard()); assertEquals(15, response.getCycleTime().getTotalTimeForCards()); - verify(asyncMetricsDataHandler).updateMetricsDataCompletedInHandler(eq(request.getBoardReportId()), any()); } @Test @@ -313,7 +366,8 @@ void shouldSaveReportResponseWithClassificationAndUpdateMetricCompletedWhenClass when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) .thenReturn(MetricsDataCompleted.builder().build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.BOARD); + .updateMetricsDataCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp()), + MetricType.BOARD, true); List classifications = List.of(Classification.builder().build()); when(classificationCalculator.calculate(any(), any())).thenReturn(classifications); when(kanbanService.fetchDataFromKanban(request)).thenReturn(FetchedData.CardCollectionInfo.builder() @@ -322,7 +376,7 @@ void shouldSaveReportResponseWithClassificationAndUpdateMetricCompletedWhenClass generateReporterService.generateBoardReport(request); - verify(pipelineService, never()).fetchGithubData(any()); + verify(pipelineService, never()).fetchGitHubData(any()); verify(kanbanService).fetchDataFromKanban(eq(request)); verify(workDay).changeConsiderHolidayMode(false); verify(asyncReportRequestHandler).putReport(eq(request.getBoardReportId()), @@ -330,7 +384,6 @@ void shouldSaveReportResponseWithClassificationAndUpdateMetricCompletedWhenClass ReportResponse response = responseArgumentCaptor.getValue(); assertEquals(1800000L, response.getExportValidityTime()); assertEquals(classifications, response.getClassificationList()); - verify(asyncMetricsDataHandler).updateMetricsDataCompletedInHandler(eq(request.getBoardReportId()), any()); } @Test @@ -346,12 +399,46 @@ void shouldUpdateMetricCompletedWhenExceptionStart4() { when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) .thenReturn(MetricsDataCompleted.builder().build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.BOARD); + .updateMetricsDataCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp()), + MetricType.BOARD, true); generateReporterService.generateBoardReport(request); - verify(asyncExceptionHandler).put(eq(request.getBoardReportId()), any()); - verify(asyncMetricsDataHandler).updateMetricsDataCompletedInHandler(eq(request.getBoardReportId()), any()); + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> verify(asyncExceptionHandler).put(eq(request.getBoardReportId()), any())); + } + + @Test + void shouldAsyncToGenerateCsvAndGenerateReportWhenFetchRight() { + GenerateReportRequest request = GenerateReportRequest.builder() + .considerHoliday(false) + .metrics(List.of("rework times")) + .buildKiteSetting(BuildKiteSetting.builder().build()) + .jiraBoardSetting(JiraBoardSetting.builder() + .reworkTimesSetting( + ReworkTimesSetting.builder().reworkState("To do").excludedStates(List.of("block")).build()) + .build()) + .csvTimeStamp(TIMESTAMP) + .build(); + when(kanbanService.fetchDataFromKanban(request)).thenReturn(FetchedData.CardCollectionInfo.builder() + .realDoneCardCollection(CardCollection.builder().reworkCardNumber(2).build()) + .nonDoneCardCollection(CardCollection.builder().build()) + .build()); + when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) + .thenReturn(MetricsDataCompleted.builder().build()); + when(reworkCalculator.calculateRework(any(), any())) + .thenReturn(Rework.builder().totalReworkCards(2).build()); + doAnswer(invocation -> null).when(asyncMetricsDataHandler) + .updateMetricsDataCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp()), + MetricType.BOARD, true); + + generateReporterService.generateBoardReport(request); + verify(asyncReportRequestHandler).putReport(any(), responseArgumentCaptor.capture()); + assertEquals(2, responseArgumentCaptor.getValue().getRework().getTotalReworkCards()); + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> verify(kanbanCsvService, times(1)).generateCsvInfo(any(), any(), any())); } } @@ -368,9 +455,9 @@ void shouldGenerateCsvFile() { .csvTimeStamp(TIMESTAMP) .build(); when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) - .thenReturn(MetricsDataCompleted.builder().build()); + .thenReturn(MetricsDataCompleted.builder().doraMetricsCompleted(false).build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA); + .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA, true); List pipelineCSVInfos = List.of(); when(pipelineService.generateCSVForPipelineWithCodebase(any(), any(), any(), any(), any())) .thenReturn(pipelineCSVInfos); @@ -380,7 +467,10 @@ void shouldGenerateCsvFile() { verify(asyncExceptionHandler).remove(request.getPipelineReportId()); verify(asyncExceptionHandler).remove(request.getSourceControlReportId()); verify(kanbanService, never()).fetchDataFromKanban(eq(request)); - verify(csvFileGenerator).convertPipelineDataToCSV(eq(pipelineCSVInfos), eq(request.getCsvTimeStamp())); + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> verify(csvFileGenerator).convertPipelineDataToCSV(pipelineCSVInfos, + request.getCsvTimeStamp())); } @Test @@ -392,16 +482,15 @@ void shouldThrowErrorWhenCodeSettingIsNullButSourceControlMetricsIsNotEmpty() { .csvTimeStamp(TIMESTAMP) .build(); when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) - .thenReturn(MetricsDataCompleted.builder().build()); + .thenReturn(MetricsDataCompleted.builder().doraMetricsCompleted(true).build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA); + .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA, true); List pipelineCSVInfos = List.of(); when(pipelineService.generateCSVForPipelineWithCodebase(any(), any(), any(), any(), any())) .thenReturn(pipelineCSVInfos); try { generateReporterService.generateDoraReport(request); - fail(); } catch (BaseException e) { assertEquals(400, e.getStatus()); @@ -422,16 +511,15 @@ void shouldThrowErrorWhenBuildKiteSettingIsNullButPipelineMetricsIsNotEmpty() { .csvTimeStamp(TIMESTAMP) .build(); when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) - .thenReturn(MetricsDataCompleted.builder().build()); + .thenReturn(MetricsDataCompleted.builder().doraMetricsCompleted(true).build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA); + .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA, true); List pipelineCSVInfos = List.of(); when(pipelineService.generateCSVForPipelineWithCodebase(any(), any(), any(), any(), any())) .thenReturn(pipelineCSVInfos); try { generateReporterService.generateDoraReport(request); - fail(); } catch (BaseException e) { assertEquals("Failed to fetch BuildKite info due to BuildKite setting is null.", e.getMessage()); @@ -451,24 +539,24 @@ void shouldGenerateCsvWithPipelineReportWhenPipeLineMetricIsNotEmpty() { .considerHoliday(false) .startTime("10000") .endTime("20000") - .metrics(List.of("deployment frequency", "change failure rate", "mean time to recovery")) + .metrics(List.of("deployment frequency", "dev change failure rate", "dev mean time to recovery")) .buildKiteSetting(BuildKiteSetting.builder().build()) .csvTimeStamp(TIMESTAMP) .build(); when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) - .thenReturn(MetricsDataCompleted.builder().build()); + .thenReturn(MetricsDataCompleted.builder().doraMetricsCompleted(false).build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA); + .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA, true); List pipelineCSVInfos = List.of(); when(pipelineService.generateCSVForPipelineWithCodebase(any(), any(), any(), any(), any())) .thenReturn(pipelineCSVInfos); when(pipelineService.fetchBuildKiteInfo(request)) .thenReturn(FetchedData.BuildKiteData.builder().buildInfosList(List.of()).build()); DeploymentFrequency fakeDeploymentFrequency = DeploymentFrequency.builder().build(); - ChangeFailureRate fakeChangeFailureRate = ChangeFailureRate.builder().build(); - MeanTimeToRecovery fakeMeantime = MeanTimeToRecovery.builder().build(); + DevChangeFailureRate fakeDevChangeFailureRate = DevChangeFailureRate.builder().build(); + DevMeanTimeToRecovery fakeMeantime = DevMeanTimeToRecovery.builder().build(); when(deploymentFrequency.calculate(any(), any(), any())).thenReturn(fakeDeploymentFrequency); - when(changeFailureRate.calculate(any())).thenReturn(fakeChangeFailureRate); + when(devChangeFailureRate.calculate(any())).thenReturn(fakeDevChangeFailureRate); when(meanToRecoveryCalculator.calculate(any())).thenReturn(fakeMeantime); generateReporterService.generateDoraReport(request); @@ -480,12 +568,15 @@ void shouldGenerateCsvWithPipelineReportWhenPipeLineMetricIsNotEmpty() { ReportResponse response = responseArgumentCaptor.getValue(); assertEquals(1800000L, response.getExportValidityTime()); - assertEquals(fakeChangeFailureRate, response.getChangeFailureRate()); - assertEquals(fakeMeantime, response.getMeanTimeToRecovery()); - assertEquals(fakeChangeFailureRate, response.getChangeFailureRate()); + assertEquals(fakeDevChangeFailureRate, response.getDevChangeFailureRate()); + assertEquals(fakeMeantime, response.getDevMeanTimeToRecovery()); + assertEquals(fakeDevChangeFailureRate, response.getDevChangeFailureRate()); assertEquals(fakeDeploymentFrequency, response.getDeploymentFrequency()); - verify(csvFileGenerator).convertPipelineDataToCSV(eq(pipelineCSVInfos), eq(request.getCsvTimeStamp())); + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> verify(csvFileGenerator).convertPipelineDataToCSV(pipelineCSVInfos, + request.getCsvTimeStamp())); } @Test @@ -494,26 +585,26 @@ void shouldUpdateMetricCompletedWhenGenerateCsvWithPipelineReportFailed() { .considerHoliday(false) .startTime("10000") .endTime("20000") - .metrics(List.of("change failure rate")) + .metrics(List.of("dev change failure rate")) .buildKiteSetting(BuildKiteSetting.builder().build()) .csvTimeStamp(TIMESTAMP) .build(); when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) - .thenReturn(MetricsDataCompleted.builder().build()); + .thenReturn(MetricsDataCompleted.builder().doraMetricsCompleted(true).build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA); + .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA, false); List pipelineCSVInfos = List.of(); when(pipelineService.generateCSVForPipelineWithCodebase(any(), any(), any(), any(), any())) .thenReturn(pipelineCSVInfos); when(pipelineService.fetchBuildKiteInfo(request)) .thenReturn(FetchedData.BuildKiteData.builder().buildInfosList(List.of()).build()); - when(changeFailureRate.calculate(any())).thenThrow(new NotFoundException("")); + when(devChangeFailureRate.calculate(any())).thenThrow(new NotFoundException("")); generateReporterService.generateDoraReport(request); verify(asyncExceptionHandler).put(eq(request.getPipelineReportId()), any()); - verify(asyncMetricsDataHandler, times(2)).updateMetricsDataCompletedInHandler(eq(request.getDoraReportId()), - any()); + + verify(asyncMetricsDataHandler, times(1)).updateMetricsDataCompletedInHandler(any(), any(), anyBoolean()); } @Test @@ -528,13 +619,13 @@ void shouldGenerateCsvWithSourceControlReportWhenSourceControlMetricIsNotEmpty() .csvTimeStamp(TIMESTAMP) .build(); when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) - .thenReturn(MetricsDataCompleted.builder().build()); + .thenReturn(MetricsDataCompleted.builder().doraMetricsCompleted(false).build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA); + .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA, true); List pipelineCSVInfos = List.of(); when(pipelineService.generateCSVForPipelineWithCodebase(any(), any(), any(), any(), any())) .thenReturn(pipelineCSVInfos); - when(pipelineService.fetchGithubData(request)) + when(pipelineService.fetchGitHubData(request)) .thenReturn(FetchedData.BuildKiteData.builder().buildInfosList(List.of()).build()); LeadTimeForChanges fakeLeadTimeForChange = LeadTimeForChanges.builder().build(); when(leadTimeForChangesCalculator.calculate(any())).thenReturn(fakeLeadTimeForChange); @@ -547,7 +638,11 @@ void shouldGenerateCsvWithSourceControlReportWhenSourceControlMetricIsNotEmpty() ReportResponse response = responseArgumentCaptor.getValue(); assertEquals(1800000L, response.getExportValidityTime()); assertEquals(fakeLeadTimeForChange, response.getLeadTimeForChanges()); - verify(csvFileGenerator).convertPipelineDataToCSV(eq(pipelineCSVInfos), eq(request.getCsvTimeStamp())); + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> verify(csvFileGenerator).convertPipelineDataToCSV(pipelineCSVInfos, + request.getCsvTimeStamp())); + } @Test @@ -556,20 +651,20 @@ void shouldGenerateCsvWithCachedDataWhenBuildKiteDataAlreadyExisted() { .considerHoliday(false) .startTime("10000") .endTime("20000") - .metrics( - List.of(MetricEnum.LEAD_TIME_FOR_CHANGES.getValue(), MetricEnum.CHANGE_FAILURE_RATE.getValue())) + .metrics(List.of(MetricEnum.LEAD_TIME_FOR_CHANGES.getValue(), + MetricEnum.DEV_CHANGE_FAILURE_RATE.getValue())) .codebaseSetting(CodebaseSetting.builder().build()) .buildKiteSetting(BuildKiteSetting.builder().build()) .csvTimeStamp(TIMESTAMP) .build(); when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) - .thenReturn(MetricsDataCompleted.builder().build()); + .thenReturn(MetricsDataCompleted.builder().doraMetricsCompleted(false).build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA); + .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA, true); List pipelineCSVInfos = List.of(); when(pipelineService.generateCSVForPipelineWithCodebase(any(), any(), any(), any(), any())) .thenReturn(pipelineCSVInfos); - when(pipelineService.fetchGithubData(any())) + when(pipelineService.fetchGitHubData(any())) .thenReturn(FetchedData.BuildKiteData.builder().buildInfosList(List.of()).build()); when(pipelineService.fetchBuildKiteInfo(any())) .thenReturn(FetchedData.BuildKiteData.builder().buildInfosList(List.of()).build()); @@ -584,7 +679,10 @@ void shouldGenerateCsvWithCachedDataWhenBuildKiteDataAlreadyExisted() { ReportResponse response = responseArgumentCaptor.getValue(); assertEquals(1800000L, response.getExportValidityTime()); assertEquals(fakeLeadTimeForChange, response.getLeadTimeForChanges()); - verify(csvFileGenerator).convertPipelineDataToCSV(eq(pipelineCSVInfos), eq(request.getCsvTimeStamp())); + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> verify(csvFileGenerator).convertPipelineDataToCSV(pipelineCSVInfos, + request.getCsvTimeStamp())); } @Test @@ -599,21 +697,20 @@ void shouldUpdateMetricCompletedWhenGenerateCsvWithSourceControlReportFailed() { .csvTimeStamp(TIMESTAMP) .build(); when(asyncMetricsDataHandler.getMetricsDataCompleted(any())) - .thenReturn(MetricsDataCompleted.builder().build()); + .thenReturn(MetricsDataCompleted.builder().doraMetricsCompleted(true).build()); doAnswer(invocation -> null).when(asyncMetricsDataHandler) - .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA); + .updateMetricsDataCompletedInHandler(TIMESTAMP, MetricType.DORA, true); List pipelineCSVInfos = List.of(); when(pipelineService.generateCSVForPipelineWithCodebase(any(), any(), any(), any(), any())) .thenReturn(pipelineCSVInfos); - when(pipelineService.fetchGithubData(request)).thenReturn( + when(pipelineService.fetchGitHubData(request)).thenReturn( FetchedData.BuildKiteData.builder().pipelineLeadTimes(List.of()).buildInfosList(List.of()).build()); doThrow(new NotFoundException("")).when(leadTimeForChangesCalculator).calculate(any()); generateReporterService.generateDoraReport(request); verify(asyncExceptionHandler).put(eq(request.getSourceControlReportId()), any()); - verify(asyncMetricsDataHandler, times(2)).updateMetricsDataCompletedInHandler(eq(request.getDoraReportId()), - any()); + verify(asyncMetricsDataHandler, times(1)).updateMetricsDataCompletedInHandler(any(), any(), anyBoolean()); } } @@ -647,62 +744,57 @@ void shouldThrowErrorWhenTimeStampIsInvalid() { } } - @Test - void shouldReturnMetricStatus() { - String timeStamp = String.valueOf(System.currentTimeMillis() - EXPORT_CSV_VALIDITY_TIME + 200); - MetricsDataDTO metricsDataDTO = new MetricsDataDTO(true, true, true); - when(asyncMetricsDataHandler.getReportReadyStatusByTimeStamp(timeStamp)).thenReturn(metricsDataDTO); - - MetricsDataDTO readyStatus = generateReporterService.checkReportReadyStatus(timeStamp); - - assertEquals(metricsDataDTO, readyStatus); - } - } @Nested class GetComposedReportResponse { - String reportId = String.valueOf(System.currentTimeMillis() - EXPORT_CSV_VALIDITY_TIME + 200); + String reportId; - MetricsDataDTO metricsDataDTO = new MetricsDataDTO(false, true, false); + String dataCompletedId; + + @BeforeEach + void setUp() { + reportId = String.valueOf(System.currentTimeMillis() - EXPORT_CSV_VALIDITY_TIME + 200); + dataCompletedId = IdUtil.getDataCompletedPrefix(reportId); + } @Test void shouldGetDataFromCache() { when(asyncReportRequestHandler.getReport(any())).thenReturn(ReportResponse.builder().build()); - when(asyncMetricsDataHandler.getReportReadyStatusByTimeStamp(reportId)) - .thenReturn(metricsDataDTO); + when(asyncMetricsDataHandler.getMetricsDataCompleted(dataCompletedId)) + .thenReturn(MetricsDataCompleted.builder().boardMetricsCompleted(false).doraMetricsCompleted(true).overallMetricCompleted(false).build()); when(asyncExceptionHandler.get(any())).thenReturn(null); ReportResponse res = generateReporterService.getComposedReportResponse(reportId); assertEquals(EXPORT_CSV_VALIDITY_TIME, res.getExportValidityTime()); - assertEquals(false, res.isBoardMetricsCompleted()); - assertEquals(true, res.isDoraMetricsCompleted()); - assertEquals(false, res.isAllMetricsCompleted()); + assertFalse(res.getBoardMetricsCompleted()); + assertTrue(res.getDoraMetricsCompleted()); + assertFalse(res.getAllMetricsCompleted()); assertNull(res.getReportMetricsError().getBoardMetricsError()); } @Test void shouldReturnErrorDataWhenExceptionIs404Or403Or401() { when(asyncReportRequestHandler.getReport(any())).thenReturn(ReportResponse.builder().build()); - when(asyncMetricsDataHandler.getReportReadyStatusByTimeStamp(reportId)) - .thenReturn(metricsDataDTO); - when(asyncExceptionHandler.get(any())).thenReturn(new NotFoundException("error")); + when(asyncMetricsDataHandler.getMetricsDataCompleted(dataCompletedId)) + .thenReturn(MetricsDataCompleted.builder().boardMetricsCompleted(false).doraMetricsCompleted(true).overallMetricCompleted(false).build()); + when(asyncExceptionHandler.get(any())).thenReturn(new AsyncExceptionDTO(new NotFoundException("error"))); ReportResponse res = generateReporterService.getComposedReportResponse(reportId); assertEquals(EXPORT_CSV_VALIDITY_TIME, res.getExportValidityTime()); - assertEquals(false, res.isAllMetricsCompleted()); + assertFalse(res.getAllMetricsCompleted()); assertEquals(404, res.getReportMetricsError().getBoardMetricsError().getStatus()); } @Test void shouldThrowGenerateReportExceptionWhenErrorIs500() { when(asyncReportRequestHandler.getReport(any())).thenReturn(ReportResponse.builder().build()); - when(asyncMetricsDataHandler.getReportReadyStatusByTimeStamp(reportId)) - .thenReturn(metricsDataDTO); - when(asyncExceptionHandler.get(any())).thenReturn(new GenerateReportException("errorMessage")); + when(asyncMetricsDataHandler.getMetricsDataCompleted(dataCompletedId)) + .thenReturn(MetricsDataCompleted.builder().boardMetricsCompleted(false).doraMetricsCompleted(true).overallMetricCompleted(false).build()); + when(asyncExceptionHandler.get(any())).thenReturn(new AsyncExceptionDTO(new GenerateReportException("errorMessage"))); try { generateReporterService.getComposedReportResponse(reportId); @@ -717,9 +809,9 @@ void shouldThrowGenerateReportExceptionWhenErrorIs500() { @Test void shouldThrowServiceUnavailableExceptionWhenErrorIs503() { when(asyncReportRequestHandler.getReport(any())).thenReturn(ReportResponse.builder().build()); - when(asyncMetricsDataHandler.getReportReadyStatusByTimeStamp(reportId)) - .thenReturn(metricsDataDTO); - when(asyncExceptionHandler.get(any())).thenReturn(new ServiceUnavailableException("errorMessage")); + when(asyncMetricsDataHandler.getMetricsDataCompleted(dataCompletedId)) + .thenReturn(MetricsDataCompleted.builder().boardMetricsCompleted(false).doraMetricsCompleted(true).overallMetricCompleted(false).build()); + when(asyncExceptionHandler.get(any())).thenReturn(new AsyncExceptionDTO(new ServiceUnavailableException("errorMessage"))); try { generateReporterService.getComposedReportResponse(reportId); @@ -734,9 +826,8 @@ void shouldThrowServiceUnavailableExceptionWhenErrorIs503() { @Test void shouldThrowRequestFailedExceptionWhenErrorIsDefault() { when(asyncReportRequestHandler.getReport(any())).thenReturn(ReportResponse.builder().build()); - when(asyncMetricsDataHandler.getReportReadyStatusByTimeStamp(reportId)) - .thenReturn(metricsDataDTO); - when(asyncExceptionHandler.get(any())).thenReturn(new BadRequestException("error")); + when(asyncMetricsDataHandler.getMetricsDataCompleted(dataCompletedId)) + .thenReturn(MetricsDataCompleted.builder().boardMetricsCompleted(false).doraMetricsCompleted(true).overallMetricCompleted(false).build()); when(asyncExceptionHandler.get(any())).thenReturn(new AsyncExceptionDTO(new BadRequestException("error"))); try { generateReporterService.getComposedReportResponse(reportId); @@ -748,6 +839,81 @@ void shouldThrowRequestFailedExceptionWhenErrorIsDefault() { } } + @Test + void shouldGetDataWhenBoardMetricsCompletedIsFalseDoraMetricsCompletedIsNull() { + when(asyncReportRequestHandler.getReport(any())).thenReturn(ReportResponse.builder().build()); + when(asyncMetricsDataHandler.getMetricsDataCompleted(dataCompletedId)) + .thenReturn(MetricsDataCompleted.builder().boardMetricsCompleted(false).overallMetricCompleted(false).build()); + when(asyncExceptionHandler.get(any())).thenReturn(null); + + ReportResponse res = generateReporterService.getComposedReportResponse(reportId); + + assertEquals(EXPORT_CSV_VALIDITY_TIME, res.getExportValidityTime()); + assertFalse(res.getBoardMetricsCompleted()); + assertNull(res.getDoraMetricsCompleted()); + assertFalse(res.getAllMetricsCompleted()); + } + + @Test + void shouldGetDataWhenBoardMetricsCompletedIsNullDoraMetricsCompletedIsFalse() { + when(asyncReportRequestHandler.getReport(any())).thenReturn(ReportResponse.builder().build()); + when(asyncMetricsDataHandler.getMetricsDataCompleted(dataCompletedId)) + .thenReturn(MetricsDataCompleted.builder().doraMetricsCompleted(false).overallMetricCompleted(false).build()); + when(asyncExceptionHandler.get(any())).thenReturn(null); + + ReportResponse res = generateReporterService.getComposedReportResponse(reportId); + + assertEquals(EXPORT_CSV_VALIDITY_TIME, res.getExportValidityTime()); + assertNull(res.getBoardMetricsCompleted()); + assertFalse(res.getDoraMetricsCompleted()); + assertFalse(res.getAllMetricsCompleted()); + } + + @Test + void shouldGetDataWhenBoardMetricsCompletedIsTrueDoraMetricsCompletedIsTrueOverallMetricCompletedIsTrue() { + when(asyncReportRequestHandler.getReport(any())).thenReturn(ReportResponse.builder().build()); + when(asyncMetricsDataHandler.getMetricsDataCompleted(dataCompletedId)) + .thenReturn(MetricsDataCompleted.builder().boardMetricsCompleted(true).doraMetricsCompleted(true).overallMetricCompleted(true).build()); + when(asyncExceptionHandler.get(any())).thenReturn(null); + + ReportResponse res = generateReporterService.getComposedReportResponse(reportId); + + assertEquals(EXPORT_CSV_VALIDITY_TIME, res.getExportValidityTime()); + assertTrue(res.getBoardMetricsCompleted()); + assertTrue(res.getDoraMetricsCompleted()); + assertTrue(res.getAllMetricsCompleted()); + } + + @Test + void shouldGetDataWhenBoardMetricsCompletedIsNullDoraMetricsCompletedIsNullOverallMetricCompletedIsTrue() { + when(asyncReportRequestHandler.getReport(any())).thenReturn(ReportResponse.builder().build()); + when(asyncMetricsDataHandler.getMetricsDataCompleted(dataCompletedId)) + .thenReturn(MetricsDataCompleted.builder().overallMetricCompleted(true).build()); + when(asyncExceptionHandler.get(any())).thenReturn(null); + + ReportResponse res = generateReporterService.getComposedReportResponse(reportId); + + assertEquals(EXPORT_CSV_VALIDITY_TIME, res.getExportValidityTime()); + assertNull(res.getBoardMetricsCompleted()); + assertNull(res.getDoraMetricsCompleted()); + assertTrue(res.getAllMetricsCompleted()); + } + + @Test + void shouldGetDataWhenBoardMetricsCompletedIsNullDoraMetricsCompletedIsNullOverallMetricCompletedIsFalse() { + when(asyncReportRequestHandler.getReport(any())).thenReturn(ReportResponse.builder().build()); + when(asyncMetricsDataHandler.getMetricsDataCompleted(dataCompletedId)) + .thenReturn(MetricsDataCompleted.builder().overallMetricCompleted(false).build()); + when(asyncExceptionHandler.get(any())).thenReturn(null); + + ReportResponse res = generateReporterService.getComposedReportResponse(reportId); + + assertEquals(EXPORT_CSV_VALIDITY_TIME, res.getExportValidityTime()); + assertNull(res.getBoardMetricsCompleted()); + assertNull(res.getDoraMetricsCompleted()); + assertFalse(res.getAllMetricsCompleted()); + } + } @Nested @@ -811,6 +977,21 @@ void shouldDeleteFailWhenDeleteFile() { assertTrue(deleteStatus); } + @Test + void shouldDeleteTempFailWhenDeleteFile() { + File mockFile = mock(File.class); + when(mockFile.getName()).thenReturn("board-1683734399999.tmp"); + when(mockFile.delete()).thenReturn(true); + when(mockFile.exists()).thenReturn(false); + File[] mockFiles = new File[] { mockFile }; + File directory = mock(File.class); + when(directory.listFiles()).thenReturn(mockFiles); + + Boolean deleteStatus = generateReporterService.deleteExpireCSV(System.currentTimeMillis(), directory); + + assertTrue(deleteStatus); + } + @Test void shouldDoConvertMetricDataToCSVWhenCallGenerateCSVForMetrics() throws IOException { String timeStamp = TIMESTAMP; diff --git a/backend/src/test/java/heartbeat/service/report/KanbanCsvServiceTest.java b/backend/src/test/java/heartbeat/service/report/KanbanCsvServiceTest.java index 73bc189f2a..a2bd26c7db 100644 --- a/backend/src/test/java/heartbeat/service/report/KanbanCsvServiceTest.java +++ b/backend/src/test/java/heartbeat/service/report/KanbanCsvServiceTest.java @@ -7,6 +7,7 @@ import heartbeat.client.dto.board.jira.JiraCard; import heartbeat.client.dto.board.jira.JiraCardField; import heartbeat.client.dto.board.jira.Status; +import heartbeat.controller.board.dto.request.ReworkTimesSetting; import heartbeat.controller.board.dto.response.CardCollection; import heartbeat.controller.board.dto.response.ColumnValue; import heartbeat.controller.board.dto.response.CycleTimeInfo; @@ -34,12 +35,15 @@ import java.util.ArrayList; import java.util.List; +import static heartbeat.service.jira.JiraBoardConfigDTOFixture.MOCK_JIRA_BOARD_COLUMN_SETTING_LIST; import static heartbeat.service.report.BoardCsvFixture.MOCK_JIRA_CARD; +import static heartbeat.service.report.BoardCsvFixture.MOCK_REWORK_TIMES_INFO_LIST; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -65,6 +69,9 @@ class KanbanCsvServiceTest { @Captor private ArgumentCaptor> csvFieldsCaptor; + @Captor + private ArgumentCaptor csvSheetCaptor; + @Captor private ArgumentCaptor> csvNewFieldsCaptor; @@ -86,7 +93,7 @@ void shouldSaveCsvWithoutNonDoneCardsWhenNonDoneCardIsNull() throws URISyntaxExc CardCollection.builder().jiraCardDTOList(List.of(jiraCardDTO)).build(), CardCollection.builder().build()); - verify(csvFileGenerator).convertBoardDataToCSV(jiraCardDTOCaptor.capture(), anyList(), any(), any()); + verify(csvFileGenerator).assembleBoardData(jiraCardDTOCaptor.capture(), anyList(), any()); assertEquals(2, jiraCardDTOCaptor.getValue().size()); assertTrue(jiraCardDTOCaptor.getValue().contains(jiraCardDTO)); } @@ -109,7 +116,7 @@ void shouldSaveCsvWithoutNonDoneCardsWhenNonDoneCardIsEmpty() throws URISyntaxEx CardCollection.builder().jiraCardDTOList(List.of(jiraCardDTO)).build(), CardCollection.builder().jiraCardDTOList(Lists.list()).build()); - verify(csvFileGenerator).convertBoardDataToCSV(jiraCardDTOCaptor.capture(), anyList(), any(), any()); + verify(csvFileGenerator).assembleBoardData(jiraCardDTOCaptor.capture(), anyList(), any()); assertEquals(2, jiraCardDTOCaptor.getValue().size()); assertTrue(jiraCardDTOCaptor.getValue().contains(jiraCardDTO)); } @@ -169,7 +176,7 @@ void shouldSaveCsvWithOrderedNonDoneCardsByJiraColumnDescendingWhenNonDoneCardIs CardCollection.builder().jiraCardDTOList(List.of(jiraCardDTO)).build(), CardCollection.builder().jiraCardDTOList(NonDoneJiraCardDTOList).build()); - verify(csvFileGenerator).convertBoardDataToCSV(jiraCardDTOCaptor.capture(), anyList(), any(), any()); + verify(csvFileGenerator).assembleBoardData(jiraCardDTOCaptor.capture(), anyList(), any()); assertEquals(5, jiraCardDTOCaptor.getValue().size()); assertEquals(testingJiraCard, jiraCardDTOCaptor.getValue().get(2)); assertEquals(blockedJiraCard, jiraCardDTOCaptor.getValue().get(3)); @@ -231,7 +238,7 @@ void shouldSaveCsvWithOrderedNonDoneCardsByJiraColumnDescendingWhenNonDoneCardIs CardCollection.builder().jiraCardDTOList(List.of(jiraCardDTO)).build(), CardCollection.builder().jiraCardDTOList(NonDoneJiraCardDTOList).build()); - verify(csvFileGenerator).convertBoardDataToCSV(jiraCardDTOCaptor.capture(), anyList(), any(), any()); + verify(csvFileGenerator).assembleBoardData(jiraCardDTOCaptor.capture(), anyList(), any()); assertEquals(5, jiraCardDTOCaptor.getValue().size()); assertEquals(preDoingJiraCard, jiraCardDTOCaptor.getValue().get(3)); assertEquals(nextDoingJiraCard, jiraCardDTOCaptor.getValue().get(4)); @@ -275,7 +282,7 @@ void shouldSaveCsvWithOrderedDoneCardsByJiraColumnDescendingWhenDoneCardIsNotEmp CardCollection.builder().jiraCardDTOList(doneJiraCardDTOList).build(), CardCollection.builder().jiraCardDTOList(List.of(jiraCardDTO)).build()); - verify(csvFileGenerator).convertBoardDataToCSV(jiraCardDTOCaptor.capture(), anyList(), any(), any()); + verify(csvFileGenerator).assembleBoardData(jiraCardDTOCaptor.capture(), anyList(), any()); assertEquals(4, jiraCardDTOCaptor.getValue().size()); assertEquals(preDoneJiraCard, jiraCardDTOCaptor.getValue().get(0)); assertEquals(nextDoneJiraCard, jiraCardDTOCaptor.getValue().get(1)); @@ -323,7 +330,7 @@ void shouldSaveCsvWithOrderedDoneCardsByJiraColumnDescendingWhenNonDoneCardIsNot CardCollection.builder().jiraCardDTOList(doneJiraCardDTOList).build(), CardCollection.builder().jiraCardDTOList(List.of(jiraCardDTO)).build()); - verify(csvFileGenerator).convertBoardDataToCSV(jiraCardDTOCaptor.capture(), anyList(), any(), any()); + verify(csvFileGenerator).assembleBoardData(jiraCardDTOCaptor.capture(), anyList(), any()); assertEquals(4, jiraCardDTOCaptor.getValue().size()); assertEquals(preDoneJiraCard, jiraCardDTOCaptor.getValue().get(1)); assertEquals(nextDoneJiraCard, jiraCardDTOCaptor.getValue().get(0)); @@ -374,7 +381,7 @@ void shouldSaveCsvWithoutOrderedNonDoneCardsByJiraColumnWhenNonDoneCardIsNotEmpt CardCollection.builder().jiraCardDTOList(List.of(jiraCardDTO)).build(), CardCollection.builder().jiraCardDTOList(NonDoneJiraCardDTOList).build()); - verify(csvFileGenerator).convertBoardDataToCSV(jiraCardDTOCaptor.capture(), anyList(), any(), any()); + verify(csvFileGenerator).assembleBoardData(jiraCardDTOCaptor.capture(), anyList(), any()); assertEquals(5, jiraCardDTOCaptor.getValue().size()); assertEquals(blockedJiraCard, jiraCardDTOCaptor.getValue().get(2)); assertEquals(doingJiraCard, jiraCardDTOCaptor.getValue().get(3)); @@ -423,7 +430,7 @@ void shouldSaveCsvWithOrderedNonDoneCardsByJiraColumnDescendingWhenNonDoneCardIs CardCollection.builder().jiraCardDTOList(List.of(jiraCardDTO)).build(), CardCollection.builder().jiraCardDTOList(NonDoneJiraCardDTOList).build()); - verify(csvFileGenerator).convertBoardDataToCSV(jiraCardDTOCaptor.capture(), anyList(), any(), any()); + verify(csvFileGenerator).assembleBoardData(jiraCardDTOCaptor.capture(), anyList(), any()); assertEquals(5, jiraCardDTOCaptor.getValue().size()); assertEquals(doingJiraCard, jiraCardDTOCaptor.getValue().get(2)); assertEquals(blockedJiraCard, jiraCardDTOCaptor.getValue().get(3)); @@ -462,8 +469,7 @@ void shouldAddFixedFieldsWhenItIsNotInSettingsFields() throws URISyntaxException CardCollection.builder().jiraCardDTOList(List.of(jiraCardDTO)).build(), CardCollection.builder().jiraCardDTOList(NonDoneJiraCardDTOList).build()); - verify(csvFileGenerator).convertBoardDataToCSV(anyList(), csvFieldsCaptor.capture(), - csvNewFieldsCaptor.capture(), any()); + verify(csvFileGenerator).assembleBoardData(anyList(), csvFieldsCaptor.capture(), csvNewFieldsCaptor.capture()); assertEquals(23, csvFieldsCaptor.getValue().size()); BoardCSVConfig targetValue = csvNewFieldsCaptor.getValue().get(0); assertEquals("baseInfo.fields.customFields.key-target1", targetValue.getValue()); @@ -504,7 +510,7 @@ void shouldAddFixedFieldsWhenItIsNotInSettingsFieldsAndCardHasOriginCycleTime() CardCollection.builder().jiraCardDTOList(List.of(jiraCardDTO)).build(), CardCollection.builder().jiraCardDTOList(NonDoneJiraCardDTOList).build()); - verify(csvFileGenerator).convertBoardDataToCSV(anyList(), csvFieldsCaptor.capture(), anyList(), any()); + verify(csvFileGenerator).assembleBoardData(anyList(), csvFieldsCaptor.capture(), anyList()); assertEquals(24, csvFieldsCaptor.getValue().size()); BoardCSVConfig targetValue = csvFieldsCaptor.getValue().get(22); assertEquals("cardCycleTime.steps.review", targetValue.getValue()); @@ -546,7 +552,7 @@ void shouldAddFixedFieldsWhenItIsNotInSettingsFieldsAndCardHasOriginCycleTimeAnd CardCollection.builder().jiraCardDTOList(List.of(jiraCardDTO)).build(), CardCollection.builder().jiraCardDTOList(NonDoneJiraCardDTOList).build()); - verify(csvFileGenerator).convertBoardDataToCSV(anyList(), csvFieldsCaptor.capture(), anyList(), any()); + verify(csvFileGenerator).assembleBoardData(anyList(), csvFieldsCaptor.capture(), anyList()); assertEquals(23, csvFieldsCaptor.getValue().size()); BoardCSVConfig targetValue = csvFieldsCaptor.getValue().get(22); assertEquals("cardCycleTime.steps.review", targetValue.getValue()); @@ -595,8 +601,7 @@ void shouldAddFixedFieldsWithCorrectValueFormatWhenCustomFieldValueInstanceOfLis CardCollection.builder().jiraCardDTOList(List.of(doneJiraCardDTO)).build(), CardCollection.builder().jiraCardDTOList(NonDoneJiraCardDTOList).build()); - verify(csvFileGenerator).convertBoardDataToCSV(anyList(), csvFieldsCaptor.capture(), - csvNewFieldsCaptor.capture(), any()); + verify(csvFileGenerator).assembleBoardData(anyList(), csvFieldsCaptor.capture(), csvNewFieldsCaptor.capture()); assertEquals(28, csvFieldsCaptor.getValue().size()); BoardCSVConfig targetValue1 = csvNewFieldsCaptor.getValue().get(0); @@ -613,4 +618,146 @@ void shouldAddFixedFieldsWithCorrectValueFormatWhenCustomFieldValueInstanceOfLis assertEquals("baseInfo.fields.customFields.json-array[0]", targetValue5.getValue()); } + @Test + void shouldAddReworkFieldsWhenGenerateSheetGivenReworkStateAndExcludedStatesAndConsiderFlagAsBlock() + throws URISyntaxException { + URI uri = new URI("site-uri"); + when(urlGenerator.getUri(any())).thenReturn(uri); + when(jiraService.getJiraBoardConfig(any(), any(), any())).thenReturn(JiraBoardConfigDTO.builder().build()); + when(jiraService.getJiraColumns(any(), any(), any())).thenReturn(JiraColumnResult.builder() + .jiraColumnResponse(List + .of(JiraColumnDTO.builder().value(ColumnValue.builder().statuses(List.of("BLOCKED")).build()).build())) + .build()); + JiraCard jiraCard = JiraCard.builder().fields(MOCK_JIRA_CARD()).build(); + JiraCard jiraCard2 = JiraCard.builder().fields(MOCK_JIRA_CARD()).build(); + jiraCard2.getFields().setLastStatusChangeDate(1701251323000L); + List jiraCardDTOS = new ArrayList<>(List.of( + JiraCardDTO.builder() + .baseInfo(jiraCard) + .reworkTimesInfos(MOCK_REWORK_TIMES_INFO_LIST()) + .totalReworkTimes(3) + .build(), + JiraCardDTO.builder() + .baseInfo(jiraCard2) + .reworkTimesInfos(MOCK_REWORK_TIMES_INFO_LIST()) + .totalReworkTimes(3) + .build())); + JiraCardDTO blockedJiraCard = JiraCardDTO.builder() + .baseInfo(JiraCard.builder().fields(MOCK_JIRA_CARD()).build()) + .build(); + List NonDoneJiraCardDTOList = new ArrayList<>() { + { + add(blockedJiraCard); + } + }; + String[][] fakeSringArray = new String[][] { { "cycle time" }, { "1" }, { "2" }, { "3" }, { "4" } }; + when(csvFileGenerator.assembleBoardData(anyList(), anyList(), anyList())).thenReturn(fakeSringArray); + kanbanCsvService.generateCsvInfo( + GenerateReportRequest.builder() + .jiraBoardSetting(JiraBoardSetting.builder() + .targetFields(List.of( + TargetField.builder().name("assignee").flag(true).key("key-assignee").build(), + TargetField.builder().name("fake-target1").flag(true).key("key-target1").build(), + TargetField.builder().name("fake-target2").flag(false).key("key-target2").build())) + .reworkTimesSetting(ReworkTimesSetting.builder() + .reworkState("In Dev") + .excludedStates(List.of("Review")) + .build()) + .boardColumns(MOCK_JIRA_BOARD_COLUMN_SETTING_LIST()) + .treatFlagCardAsBlock(Boolean.TRUE) + .build()) + .csvTimeStamp("2022-01-01 00:00:00") + .build(), + CardCollection.builder().jiraCardDTOList(jiraCardDTOS).build(), + CardCollection.builder().jiraCardDTOList(NonDoneJiraCardDTOList).build()); + + verify(csvFileGenerator).assembleBoardData(anyList(), csvFieldsCaptor.capture(), anyList()); + verify(csvFileGenerator).writeDataToCSV(anyString(), csvSheetCaptor.capture()); + + assertEquals(23, csvFieldsCaptor.getValue().size()); + BoardCSVConfig targetValue = csvFieldsCaptor.getValue().get(22); + assertEquals("cardCycleTime.steps.review", targetValue.getValue()); + assertEquals("Review Days", targetValue.getLabel()); + assertNull(targetValue.getOriginKey()); + + assertEquals(5, csvSheetCaptor.getValue().length); + assertEquals("cycle time", csvSheetCaptor.getValue()[0][0]); + assertEquals("Rework: total - In dev", csvSheetCaptor.getValue()[0][1]); + assertEquals("Rework: from Block", csvSheetCaptor.getValue()[0][2]); + assertEquals("Rework: from Waiting for testing", csvSheetCaptor.getValue()[0][3]); + assertEquals("Rework: from Testing", csvSheetCaptor.getValue()[0][4]); + assertEquals("Rework: from Done", csvSheetCaptor.getValue()[0][5]); + } + + @Test + void shouldAddReworkFieldsWhenGenerateSheetGivenReworkStateAndExcludedStatesAndNotConsiderFlagAsBlock() + throws URISyntaxException { + URI uri = new URI("site-uri"); + when(urlGenerator.getUri(any())).thenReturn(uri); + when(jiraService.getJiraBoardConfig(any(), any(), any())).thenReturn(JiraBoardConfigDTO.builder().build()); + when(jiraService.getJiraColumns(any(), any(), any())).thenReturn(JiraColumnResult.builder() + .jiraColumnResponse(List + .of(JiraColumnDTO.builder().value(ColumnValue.builder().statuses(List.of("BLOCKED")).build()).build())) + .build()); + JiraCard jiraCard = JiraCard.builder().fields(MOCK_JIRA_CARD()).build(); + JiraCard jiraCard2 = JiraCard.builder().fields(MOCK_JIRA_CARD()).build(); + jiraCard2.getFields().setLastStatusChangeDate(1701251323000L); + List jiraCardDTOS = new ArrayList<>(List.of( + JiraCardDTO.builder() + .baseInfo(jiraCard) + .reworkTimesInfos(MOCK_REWORK_TIMES_INFO_LIST()) + .totalReworkTimes(3) + .build(), + JiraCardDTO.builder() + .baseInfo(jiraCard2) + .reworkTimesInfos(MOCK_REWORK_TIMES_INFO_LIST()) + .totalReworkTimes(3) + .build())); + JiraCardDTO blockedJiraCard = JiraCardDTO.builder() + .baseInfo(JiraCard.builder().fields(MOCK_JIRA_CARD()).build()) + .build(); + List NonDoneJiraCardDTOList = new ArrayList<>() { + { + add(blockedJiraCard); + } + }; + String[][] fakeSringArray = new String[][] { { "cycle time" }, { "1" }, { "2" }, { "3" }, { "4" } }; + when(csvFileGenerator.assembleBoardData(anyList(), anyList(), anyList())).thenReturn(fakeSringArray); + kanbanCsvService.generateCsvInfo( + GenerateReportRequest.builder() + .jiraBoardSetting(JiraBoardSetting.builder() + .targetFields(List.of( + TargetField.builder().name("assignee").flag(true).key("key-assignee").build(), + TargetField.builder().name("fake-target1").flag(true).key("key-target1").build(), + TargetField.builder().name("fake-target2").flag(false).key("key-target2").build())) + .reworkTimesSetting(ReworkTimesSetting.builder() + .reworkState("In Dev") + .excludedStates(List.of("Review")) + .build()) + .boardColumns(MOCK_JIRA_BOARD_COLUMN_SETTING_LIST()) + .treatFlagCardAsBlock(Boolean.FALSE) + .build()) + .csvTimeStamp("2022-01-01 00:00:00") + .build(), + CardCollection.builder().jiraCardDTOList(jiraCardDTOS).build(), + CardCollection.builder().jiraCardDTOList(NonDoneJiraCardDTOList).build()); + + verify(csvFileGenerator).assembleBoardData(anyList(), csvFieldsCaptor.capture(), anyList()); + verify(csvFileGenerator).writeDataToCSV(anyString(), csvSheetCaptor.capture()); + + assertEquals(23, csvFieldsCaptor.getValue().size()); + BoardCSVConfig targetValue = csvFieldsCaptor.getValue().get(22); + assertEquals("cardCycleTime.steps.review", targetValue.getValue()); + assertEquals("Review Days", targetValue.getLabel()); + assertNull(targetValue.getOriginKey()); + + assertEquals(5, csvSheetCaptor.getValue().length); + assertEquals("cycle time", csvSheetCaptor.getValue()[0][0]); + assertEquals("Rework: total - In dev", csvSheetCaptor.getValue()[0][1]); + assertEquals("Rework: from Block", csvSheetCaptor.getValue()[0][2]); + assertEquals("Rework: from Waiting for testing", csvSheetCaptor.getValue()[0][3]); + assertEquals("Rework: from Testing", csvSheetCaptor.getValue()[0][4]); + assertEquals("Rework: from Done", csvSheetCaptor.getValue()[0][5]); + } + } diff --git a/backend/src/test/java/heartbeat/service/report/KanbanServiceTest.java b/backend/src/test/java/heartbeat/service/report/KanbanServiceTest.java index a71c692b7a..1f4c0fb9f2 100644 --- a/backend/src/test/java/heartbeat/service/report/KanbanServiceTest.java +++ b/backend/src/test/java/heartbeat/service/report/KanbanServiceTest.java @@ -13,6 +13,8 @@ import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; @@ -28,9 +30,6 @@ class KanbanServiceTest { @Mock private JiraService jiraService; - @Mock - private KanbanCsvService kanbanCsvService; - @Test void shouldCallCsvServiceToGenerateCSVInfoWhenJiraBoardSettingIsNotNull() { JiraBoardSetting mockJiraBoardSetting = KanbanFixture.MOCK_JIRA_BOARD_SETTING(); @@ -38,24 +37,24 @@ void shouldCallCsvServiceToGenerateCSVInfoWhenJiraBoardSettingIsNotNull() { .jiraBoardSetting(mockJiraBoardSetting) .startTime("startTime") .endTime("endTime") + .metrics(List.of("cycle time", "rework times")) .build(); CardCollection realDoneCardCollection = CardCollection.builder().build(); CardCollection nonDoneCardCollection = CardCollection.builder().build(); when(jiraService.getStoryPointsAndCycleTimeForNonDoneCards(any(), any(), any())) .thenReturn(nonDoneCardCollection); - when(jiraService.getStoryPointsAndCycleTimeForDoneCards(any(), any(), any(), any())) + when(jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards(any(), any(), any(), any())) .thenReturn(realDoneCardCollection); FetchedData.CardCollectionInfo result = kanbanService.fetchDataFromKanban(request); assertEquals(realDoneCardCollection, result.getRealDoneCardCollection()); assertEquals(nonDoneCardCollection, result.getNonDoneCardCollection()); - verify(kanbanCsvService).generateCsvInfo(request, realDoneCardCollection, nonDoneCardCollection); verify(jiraService).getStoryPointsAndCycleTimeForNonDoneCards( KanbanFixture.MOCK_EXPECT_STORY_POINT_AND_CYCLE_TIME_REQUEST(), mockJiraBoardSetting.getBoardColumns(), mockJiraBoardSetting.getUsers()); - verify(jiraService).getStoryPointsAndCycleTimeForDoneCards( + verify(jiraService).getStoryPointsAndCycleTimeAndReworkInfoForDoneCards( KanbanFixture.MOCK_EXPECT_STORY_POINT_AND_CYCLE_TIME_REQUEST(), mockJiraBoardSetting.getBoardColumns(), mockJiraBoardSetting.getUsers(), mockJiraBoardSetting.getAssigneeFilter()); } diff --git a/backend/src/test/java/heartbeat/service/report/MeanToRecoveryCalculatorTest.java b/backend/src/test/java/heartbeat/service/report/MeanToRecoveryCalculatorTest.java index 91dfddef0f..c9dc1afbd2 100644 --- a/backend/src/test/java/heartbeat/service/report/MeanToRecoveryCalculatorTest.java +++ b/backend/src/test/java/heartbeat/service/report/MeanToRecoveryCalculatorTest.java @@ -2,21 +2,22 @@ import heartbeat.client.dto.pipeline.buildkite.DeployInfo; import heartbeat.client.dto.pipeline.buildkite.DeployTimes; -import heartbeat.controller.report.dto.response.AvgMeanTimeToRecovery; -import heartbeat.controller.report.dto.response.MeanTimeToRecovery; -import heartbeat.controller.report.dto.response.MeanTimeToRecoveryOfPipeline; +import heartbeat.controller.report.dto.response.AvgDevMeanTimeToRecovery; +import heartbeat.controller.report.dto.response.DevMeanTimeToRecovery; +import heartbeat.controller.report.dto.response.DevMeanTimeToRecoveryOfPipeline; import heartbeat.service.report.calculator.MeanToRecoveryCalculator; -import java.math.BigDecimal; -import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.junit.jupiter.MockitoExtension; +import java.math.BigDecimal; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + @ExtendWith(MockitoExtension.class) class MeanToRecoveryCalculatorTest { @@ -24,17 +25,17 @@ class MeanToRecoveryCalculatorTest { private MeanToRecoveryCalculator calculator; @Test - public void shouldReturnZeroAvgMeanTimeToRecoveryWhenDeployTimesIsEmpty() { + void shouldReturnZeroAvgDevMeanTimeToRecoveryWhenDeployTimesIsEmpty() { List deployTimes = new ArrayList<>(); - MeanTimeToRecovery result = calculator.calculate(deployTimes); + DevMeanTimeToRecovery result = calculator.calculate(deployTimes); - Assertions.assertEquals(BigDecimal.ZERO, result.getAvgMeanTimeToRecovery().getTimeToRecovery()); - Assertions.assertTrue(result.getMeanTimeRecoveryPipelines().isEmpty()); + Assertions.assertEquals(BigDecimal.ZERO, result.getAvgDevMeanTimeToRecovery().getTimeToRecovery()); + Assertions.assertTrue(result.getDevMeanTimeToRecoveryOfPipelines().isEmpty()); } @Test - void shouldCalculateMeanTimeToRecoveryWhenDeployTimesIsNotEmpty() { + void shouldCalculateDevMeanTimeToRecoveryWhenDeployTimesIsNotEmpty() { DeployTimes deploy1 = createDeployTimes("Pipeline 1", "Step 1", 2, 3); DeployTimes deploy2 = createDeployTimes("Pipeline 2", "Step 2", 1, 2); @@ -46,32 +47,33 @@ void shouldCalculateMeanTimeToRecoveryWhenDeployTimesIsNotEmpty() { deployTimesList.add(deploy2); deployTimesList.add(deploy3); - MeanTimeToRecovery result = calculator.calculate(deployTimesList); + DevMeanTimeToRecovery result = calculator.calculate(deployTimesList); - AvgMeanTimeToRecovery avgMeanTimeToRecovery = result.getAvgMeanTimeToRecovery(); - Assertions.assertEquals(0, avgMeanTimeToRecovery.getTimeToRecovery().compareTo(BigDecimal.valueOf(100000))); + AvgDevMeanTimeToRecovery avgDevMeanTimeToRecovery = result.getAvgDevMeanTimeToRecovery(); + Assertions.assertEquals(0, avgDevMeanTimeToRecovery.getTimeToRecovery().compareTo(BigDecimal.valueOf(100000))); - List meanTimeRecoveryPipelines = result.getMeanTimeRecoveryPipelines(); - Assertions.assertEquals(3, meanTimeRecoveryPipelines.size()); + List devMeanTimeToRecoveryOfPipelines = result + .getDevMeanTimeToRecoveryOfPipelines(); + Assertions.assertEquals(3, devMeanTimeToRecoveryOfPipelines.size()); - MeanTimeToRecoveryOfPipeline deploy1Result = meanTimeRecoveryPipelines.get(0); - Assertions.assertEquals("Pipeline 1", deploy1Result.getPipelineName()); - Assertions.assertEquals("Step 1", deploy1Result.getPipelineStep()); + DevMeanTimeToRecoveryOfPipeline deploy1Result = devMeanTimeToRecoveryOfPipelines.get(0); + Assertions.assertEquals("Pipeline 1", deploy1Result.getName()); + Assertions.assertEquals("Step 1", deploy1Result.getStep()); Assertions.assertEquals(0, deploy1Result.getTimeToRecovery().compareTo(BigDecimal.valueOf(180000))); - MeanTimeToRecoveryOfPipeline deploy2Result = meanTimeRecoveryPipelines.get(1); - Assertions.assertEquals("Pipeline 2", deploy2Result.getPipelineName()); - Assertions.assertEquals("Step 2", deploy2Result.getPipelineStep()); + DevMeanTimeToRecoveryOfPipeline deploy2Result = devMeanTimeToRecoveryOfPipelines.get(1); + Assertions.assertEquals("Pipeline 2", deploy2Result.getName()); + Assertions.assertEquals("Step 2", deploy2Result.getStep()); Assertions.assertEquals(0, deploy2Result.getTimeToRecovery().compareTo(BigDecimal.valueOf(120000))); - MeanTimeToRecoveryOfPipeline deploy3Result = meanTimeRecoveryPipelines.get(2); - Assertions.assertEquals("Pipeline 3", deploy3Result.getPipelineName()); - Assertions.assertEquals("Step 3", deploy3Result.getPipelineStep()); + DevMeanTimeToRecoveryOfPipeline deploy3Result = devMeanTimeToRecoveryOfPipelines.get(2); + Assertions.assertEquals("Pipeline 3", deploy3Result.getName()); + Assertions.assertEquals("Step 3", deploy3Result.getStep()); Assertions.assertEquals(BigDecimal.ZERO, deploy3Result.getTimeToRecovery()); } @Test - void shouldCalculateMeanTimeToRecoveryWhenDeployTimesIsNotEmptyAndHasCanceledJob() { + void shouldCalculateDevMeanTimeToRecoveryWhenDeployTimesIsNotEmptyAndHasCanceledJob() { DeployTimes deploy1 = createDeployTimes("Pipeline 1", "Step 1", 2, 3); deploy1.getPassed().get(0).setPipelineCanceled(true); @@ -85,27 +87,28 @@ void shouldCalculateMeanTimeToRecoveryWhenDeployTimesIsNotEmptyAndHasCanceledJob deployTimesList.add(deploy2); deployTimesList.add(deploy3); - MeanTimeToRecovery result = calculator.calculate(deployTimesList); + DevMeanTimeToRecovery result = calculator.calculate(deployTimesList); - AvgMeanTimeToRecovery avgMeanTimeToRecovery = result.getAvgMeanTimeToRecovery(); - Assertions.assertEquals(0, avgMeanTimeToRecovery.getTimeToRecovery().compareTo(BigDecimal.valueOf(80000))); + AvgDevMeanTimeToRecovery avgDevMeanTimeToRecovery = result.getAvgDevMeanTimeToRecovery(); + Assertions.assertEquals(0, avgDevMeanTimeToRecovery.getTimeToRecovery().compareTo(BigDecimal.valueOf(80000))); - List meanTimeRecoveryPipelines = result.getMeanTimeRecoveryPipelines(); - Assertions.assertEquals(3, meanTimeRecoveryPipelines.size()); + List devMeanTimeToRecoveryOfPipelines = result + .getDevMeanTimeToRecoveryOfPipelines(); + Assertions.assertEquals(3, devMeanTimeToRecoveryOfPipelines.size()); - MeanTimeToRecoveryOfPipeline deploy1Result = meanTimeRecoveryPipelines.get(0); - Assertions.assertEquals("Pipeline 1", deploy1Result.getPipelineName()); - Assertions.assertEquals("Step 1", deploy1Result.getPipelineStep()); + DevMeanTimeToRecoveryOfPipeline deploy1Result = devMeanTimeToRecoveryOfPipelines.get(0); + Assertions.assertEquals("Pipeline 1", deploy1Result.getName()); + Assertions.assertEquals("Step 1", deploy1Result.getStep()); Assertions.assertEquals(0, deploy1Result.getTimeToRecovery().compareTo(BigDecimal.valueOf(240000))); - MeanTimeToRecoveryOfPipeline deploy2Result = meanTimeRecoveryPipelines.get(1); - Assertions.assertEquals("Pipeline 2", deploy2Result.getPipelineName()); - Assertions.assertEquals("Step 2", deploy2Result.getPipelineStep()); + DevMeanTimeToRecoveryOfPipeline deploy2Result = devMeanTimeToRecoveryOfPipelines.get(1); + Assertions.assertEquals("Pipeline 2", deploy2Result.getName()); + Assertions.assertEquals("Step 2", deploy2Result.getStep()); Assertions.assertEquals(BigDecimal.ZERO, deploy2Result.getTimeToRecovery()); - MeanTimeToRecoveryOfPipeline deploy3Result = meanTimeRecoveryPipelines.get(2); - Assertions.assertEquals("Pipeline 3", deploy3Result.getPipelineName()); - Assertions.assertEquals("Step 3", deploy3Result.getPipelineStep()); + DevMeanTimeToRecoveryOfPipeline deploy3Result = devMeanTimeToRecoveryOfPipelines.get(2); + Assertions.assertEquals("Pipeline 3", deploy3Result.getName()); + Assertions.assertEquals("Step 3", deploy3Result.getStep()); Assertions.assertEquals(BigDecimal.ZERO, deploy3Result.getTimeToRecovery()); } diff --git a/backend/src/test/java/heartbeat/service/report/MetricCsvFixture.java b/backend/src/test/java/heartbeat/service/report/MetricCsvFixture.java index 4063da2f10..4573c945f4 100644 --- a/backend/src/test/java/heartbeat/service/report/MetricCsvFixture.java +++ b/backend/src/test/java/heartbeat/service/report/MetricCsvFixture.java @@ -3,6 +3,7 @@ import heartbeat.controller.report.dto.response.Classification; import heartbeat.controller.report.dto.response.ClassificationNameValuePair; import heartbeat.controller.report.dto.response.ReportResponse; +import heartbeat.controller.report.dto.response.Rework; import heartbeat.controller.report.dto.response.Velocity; import heartbeat.controller.report.dto.response.CycleTime; import heartbeat.controller.report.dto.response.CycleTimeForSelectedStepItem; @@ -10,12 +11,12 @@ import heartbeat.controller.report.dto.response.AvgDeploymentFrequency; import heartbeat.controller.report.dto.response.DeploymentFrequencyOfPipeline; import heartbeat.controller.report.dto.response.DailyDeploymentCount; -import heartbeat.controller.report.dto.response.ChangeFailureRate; -import heartbeat.controller.report.dto.response.AvgChangeFailureRate; -import heartbeat.controller.report.dto.response.ChangeFailureRateOfPipeline; -import heartbeat.controller.report.dto.response.MeanTimeToRecovery; -import heartbeat.controller.report.dto.response.AvgMeanTimeToRecovery; -import heartbeat.controller.report.dto.response.MeanTimeToRecoveryOfPipeline; +import heartbeat.controller.report.dto.response.DevChangeFailureRate; +import heartbeat.controller.report.dto.response.AvgDevChangeFailureRate; +import heartbeat.controller.report.dto.response.DevChangeFailureRateOfPipeline; +import heartbeat.controller.report.dto.response.DevMeanTimeToRecovery; +import heartbeat.controller.report.dto.response.AvgDevMeanTimeToRecovery; +import heartbeat.controller.report.dto.response.DevMeanTimeToRecoveryOfPipeline; import heartbeat.controller.report.dto.response.LeadTimeForChanges; import heartbeat.controller.report.dto.response.LeadTimeForChangesOfPipelines; import heartbeat.controller.report.dto.response.AvgLeadTimeForChanges; @@ -23,8 +24,106 @@ import java.math.BigDecimal; import java.util.List; +import static heartbeat.controller.board.dto.request.CardStepsEnum.TODO; +import static heartbeat.service.report.scheduler.DeleteExpireCSVScheduler.EXPORT_CSV_VALIDITY_TIME; + public class MetricCsvFixture { + public static ReportResponse MOCK_COMPOSED_REPORT_RESPONSE() { + return ReportResponse.builder() + .velocity(Velocity.builder().velocityForCards(2).velocityForSP(7).build()) + .classificationList(List.of(Classification.builder().fieldName("Issue Type").build())) + .cycleTime(CycleTime.builder() + .totalTimeForCards(29.26) + .averageCycleTimePerCard(9.75) + .averageCycleTimePerSP(4.18) + .build()) + .rework(Rework.builder() + .totalReworkCards(1) + .reworkCardsRatio(1.0) + .reworkState(TODO.getValue()) + .fromInDev(1) + .build()) + .exportValidityTime(EXPORT_CSV_VALIDITY_TIME) + .deploymentFrequency(DeploymentFrequency.builder() + .avgDeploymentFrequency( + AvgDeploymentFrequency.builder().name("Average").deploymentFrequency(0.67F).build()) + .build()) + .devChangeFailureRate(DevChangeFailureRate.builder() + .avgDevChangeFailureRate(AvgDevChangeFailureRate.builder() + .name("Average") + .totalTimes(12) + .totalFailedTimes(0) + .failureRate(0.0F) + .build()) + .build()) + .devMeanTimeToRecovery(DevMeanTimeToRecovery.builder() + .avgDevMeanTimeToRecovery( + AvgDevMeanTimeToRecovery.builder().timeToRecovery(BigDecimal.valueOf(0)).build()) + .build()) + .leadTimeForChanges(LeadTimeForChanges.builder() + .avgLeadTimeForChanges(AvgLeadTimeForChanges.builder() + .name("Average") + .prLeadTime(0.0) + .pipelineLeadTime(3.0949999999999998) + .totalDelayTime(3.0949999999999998) + .build()) + .build()) + .build(); + } + + public static ReportResponse MOCK_BOARD_REPORT_RESPONSE() { + return ReportResponse.builder() + .velocity(Velocity.builder().velocityForCards(2).velocityForSP(7).build()) + .classificationList(List.of(Classification.builder().fieldName("Issue Type").build())) + .cycleTime(CycleTime.builder() + .totalTimeForCards(29.26) + .averageCycleTimePerCard(9.75) + .averageCycleTimePerSP(4.18) + .build()) + .rework(Rework.builder() + .totalReworkCards(1) + .reworkCardsRatio(1.0) + .reworkState(TODO.getValue()) + .fromInDev(1) + .build()) + .build(); + } + + public static ReportResponse MOCK_PIPELINE_REPORT_RESPONSE() { + return ReportResponse.builder() + .deploymentFrequency(DeploymentFrequency.builder() + .avgDeploymentFrequency( + AvgDeploymentFrequency.builder().name("Average").deploymentFrequency(0.67F).build()) + .build()) + .devChangeFailureRate(DevChangeFailureRate.builder() + .avgDevChangeFailureRate(AvgDevChangeFailureRate.builder() + .name("Average") + .totalTimes(12) + .totalFailedTimes(0) + .failureRate(0.0F) + .build()) + .build()) + .devMeanTimeToRecovery(DevMeanTimeToRecovery.builder() + .avgDevMeanTimeToRecovery( + AvgDevMeanTimeToRecovery.builder().timeToRecovery(BigDecimal.valueOf(0)).build()) + .build()) + .build(); + } + + public static ReportResponse MOCK_SOURCE_CONTROL_REPORT_RESPONSE() { + return ReportResponse.builder() + .leadTimeForChanges(LeadTimeForChanges.builder() + .avgLeadTimeForChanges(AvgLeadTimeForChanges.builder() + .name("Average") + .prLeadTime(0.0) + .pipelineLeadTime(3.0949999999999998) + .totalDelayTime(3.0949999999999998) + .build()) + .build()) + .build(); + } + public static ReportResponse MOCK_METRIC_CSV_DATA() { return ReportResponse.builder() .velocity(Velocity.builder().velocityForCards(2).velocityForSP(7).build()) @@ -88,22 +187,22 @@ public static ReportResponse MOCK_METRIC_CSV_DATA() { List.of(DailyDeploymentCount.builder().date("10/16/2023").count(1).build())) .build())) .build()) - .changeFailureRate(ChangeFailureRate.builder() - .avgChangeFailureRate(AvgChangeFailureRate.builder() + .devChangeFailureRate(DevChangeFailureRate.builder() + .avgDevChangeFailureRate(AvgDevChangeFailureRate.builder() .name("Average") .totalTimes(12) .totalFailedTimes(0) .failureRate(0.0F) .build()) - .changeFailureRateOfPipelines(List.of( - ChangeFailureRateOfPipeline.builder() + .devChangeFailureRateOfPipelines(List.of( + DevChangeFailureRateOfPipeline.builder() .name("Heartbeat") .step(":rocket: Deploy prod") .failedTimesOfPipeline(0) .totalTimesOfPipeline(7) .failureRate(0.0F) .build(), - ChangeFailureRateOfPipeline.builder() + DevChangeFailureRateOfPipeline.builder() .name("Heartbeat") .step(":mag: Check Frontend License") .failedTimesOfPipeline(0) @@ -111,18 +210,19 @@ public static ReportResponse MOCK_METRIC_CSV_DATA() { .failureRate(0.0F) .build())) .build()) - .meanTimeToRecovery(MeanTimeToRecovery.builder() - .avgMeanTimeToRecovery(AvgMeanTimeToRecovery.builder().timeToRecovery(BigDecimal.valueOf(0)).build()) - .meanTimeRecoveryPipelines(List.of( - MeanTimeToRecoveryOfPipeline.builder() + .devMeanTimeToRecovery(DevMeanTimeToRecovery.builder() + .avgDevMeanTimeToRecovery( + AvgDevMeanTimeToRecovery.builder().timeToRecovery(BigDecimal.valueOf(0)).build()) + .devMeanTimeToRecoveryOfPipelines(List.of( + DevMeanTimeToRecoveryOfPipeline.builder() .timeToRecovery(BigDecimal.valueOf(0)) - .pipelineName("Heartbeat") - .pipelineStep(":rocket: Deploy prod") + .name("Heartbeat") + .step(":rocket: Deploy prod") .build(), - MeanTimeToRecoveryOfPipeline.builder() + DevMeanTimeToRecoveryOfPipeline.builder() .timeToRecovery(BigDecimal.valueOf(0)) - .pipelineName("Heartbeat") - .pipelineStep(":mag: Check Frontend License") + .name("Heartbeat") + .step(":mag: Check Frontend License") .build())) .build()) .leadTimeForChanges(LeadTimeForChanges.builder() @@ -157,6 +257,7 @@ public static ReportResponse MOCK_EMPTY_METRIC_CSV_DATA() { public static ReportResponse MOCK_METRIC_CSV_DATA_WITH_ONE_PIPELINE() { return ReportResponse.builder() + .rework(Rework.builder().totalReworkTimes(3).totalReworkCards(3).reworkCardsRatio(0.99).build()) .deploymentFrequency(DeploymentFrequency.builder() .avgDeploymentFrequency( AvgDeploymentFrequency.builder().name("Average").deploymentFrequency(0.67F).build()) @@ -167,14 +268,14 @@ public static ReportResponse MOCK_METRIC_CSV_DATA_WITH_ONE_PIPELINE() { .dailyDeploymentCounts(List.of(DailyDeploymentCount.builder().date("10/16/2023").count(1).build())) .build())) .build()) - .changeFailureRate(ChangeFailureRate.builder() - .avgChangeFailureRate(AvgChangeFailureRate.builder() + .devChangeFailureRate(DevChangeFailureRate.builder() + .avgDevChangeFailureRate(AvgDevChangeFailureRate.builder() .name("Average") .totalTimes(12) .totalFailedTimes(0) .failureRate(0.0F) .build()) - .changeFailureRateOfPipelines(List.of(ChangeFailureRateOfPipeline.builder() + .devChangeFailureRateOfPipelines(List.of(DevChangeFailureRateOfPipeline.builder() .name("Heartbeat") .step(":rocket: Deploy prod") .failedTimesOfPipeline(0) @@ -182,12 +283,13 @@ public static ReportResponse MOCK_METRIC_CSV_DATA_WITH_ONE_PIPELINE() { .failureRate(0.0F) .build())) .build()) - .meanTimeToRecovery(MeanTimeToRecovery.builder() - .avgMeanTimeToRecovery(AvgMeanTimeToRecovery.builder().timeToRecovery(BigDecimal.valueOf(0)).build()) - .meanTimeRecoveryPipelines(List.of(MeanTimeToRecoveryOfPipeline.builder() + .devMeanTimeToRecovery(DevMeanTimeToRecovery.builder() + .avgDevMeanTimeToRecovery( + AvgDevMeanTimeToRecovery.builder().timeToRecovery(BigDecimal.valueOf(0)).build()) + .devMeanTimeToRecoveryOfPipelines(List.of(DevMeanTimeToRecoveryOfPipeline.builder() .timeToRecovery(BigDecimal.valueOf(0)) - .pipelineName("Heartbeat") - .pipelineStep(":rocket: Deploy prod") + .name("Heartbeat") + .step(":rocket: Deploy prod") .build())) .build()) .leadTimeForChanges(LeadTimeForChanges.builder() diff --git a/backend/src/test/java/heartbeat/service/report/PipelineCsvFixture.java b/backend/src/test/java/heartbeat/service/report/PipelineCsvFixture.java index fbfa09fa43..32fbda18ac 100644 --- a/backend/src/test/java/heartbeat/service/report/PipelineCsvFixture.java +++ b/backend/src/test/java/heartbeat/service/report/PipelineCsvFixture.java @@ -121,6 +121,58 @@ public static List MOCK_PIPELINE_CSV_DATA_WITHOUT_CREATOR() { return List.of(pipelineCsvInfo); } + public static List MOCK_PIPELINE_CSV_DATA_WITHOUT_CREATOR_NAME() { + PipelineCSVInfo pipelineCsvInfo = PipelineCSVInfo.builder() + .organizationName("Thoughtworks-Heartbeat") + .pipeLineName("Heartbeat") + .piplineStatus("passed") + .stepName(":rocket: Deploy prod") + .buildInfo(BuildKiteBuildInfo.builder() + .commit("713b31878c756c205a6c03eac5be3ac7c7e6a227") + .creator(BuildKiteBuildInfo.Creator.builder().name(null).email("XXX@test.com").build()) + .pipelineCreateTime("2023-05-10T06:17:21.844Z") + .state("passed") + .number(880) + .jobs(List.of(BuildKiteJob.builder() + .name(":rocket: Deploy prod") + .state("passed") + .startedAt("2023-05-10T06:42:47.498Z") + .finishedAt("2023-05-10T06:43:02.653Z") + .build())) + .branch("branch") + .build()) + .commitInfo(CommitInfo.builder() + .commit(Commit.builder() + .author(Author.builder() + .name("XXXX") + .email("XXX@test.com") + .date("2023-05-10T06:43:02.653Z") + .build()) + .committer(Committer.builder() + .name("XXXX") + .email("XXX@test.com") + .date("2023-05-10T06:43:02.653Z") + .build()) + .build()) + .build()) + .leadTimeInfo(LeadTimeInfo.builder() + .firstCommitTimeInPr("2023-05-08T07:18:18Z") + .totalTime("8379303") + .prMergedTime("1683793037000") + .prLeadTime("16837") + .prCreatedTime("168369327000") + .jobFinishTime("1684793037000") + .pipelineLeadTime("653037000") + .build()) + .deployInfo(DeployInfo.builder() + .state("passed") + .jobFinishTime("1684793037000") + .jobStartTime("168369327000") + .build()) + .build(); + return List.of(pipelineCsvInfo); + } + public static List MOCK_PIPELINE_CSV_DATA_WITH_PIPELINE_STATUS_IS_CANCELED() { PipelineCSVInfo pipelineCsvInfo = PipelineCSVInfo.builder() .organizationName("Thoughtworks-Heartbeat") diff --git a/backend/src/test/java/heartbeat/service/report/PipelineServiceTest.java b/backend/src/test/java/heartbeat/service/report/PipelineServiceTest.java index efbdeb26c6..29b6cb172b 100644 --- a/backend/src/test/java/heartbeat/service/report/PipelineServiceTest.java +++ b/backend/src/test/java/heartbeat/service/report/PipelineServiceTest.java @@ -73,7 +73,7 @@ void shouldReturnEmptyBuildInfosListAndEmptyLeadTimeWhenDeploymentEnvironmentsIs .metrics(new ArrayList<>()) .codebaseSetting(CodebaseSetting.builder().token(MOCK_TOKEN).build()) .build(); - FetchedData.BuildKiteData result = pipelineService.fetchGithubData(request); + FetchedData.BuildKiteData result = pipelineService.fetchGitHubData(request); assertEquals(0, result.getBuildInfosList().size()); verify(buildKiteService, never()).countDeployTimes(any(), any(), any(), any()); @@ -86,7 +86,7 @@ void shouldReturnEmptyPipelineLeadTimeWhenCodebaseSettingIsEmpty() { .buildKiteSetting(BuildKiteSetting.builder().deploymentEnvList(new ArrayList<>()).build()) .metrics(new ArrayList<>()) .build(); - FetchedData.BuildKiteData result = pipelineService.fetchGithubData(request); + FetchedData.BuildKiteData result = pipelineService.fetchGitHubData(request); assertEquals(0, result.getPipelineLeadTimes().size()); verify(gitHubService, never()).fetchPipelinesLeadTime(any(), any(), any()); @@ -114,7 +114,7 @@ void shouldGetPipelineLeadTimeFromGithubServiceAndBuildKiteServiceWhenCodebaseSe when(gitHubService.fetchPipelinesLeadTime(any(), any(), eq(MOCK_TOKEN))) .thenReturn(List.of(PipelineLeadTime.builder().build())); - FetchedData.BuildKiteData result = pipelineService.fetchGithubData(request); + FetchedData.BuildKiteData result = pipelineService.fetchGitHubData(request); assertEquals(1, result.getPipelineLeadTimes().size()); assertEquals(2, result.getBuildInfosList().size()); @@ -146,7 +146,7 @@ void shouldGetSecondValueInRoadMapWhenDeployEnvironmentListHasTwoElementWithSame when(gitHubService.fetchPipelinesLeadTime(any(), any(), eq(MOCK_TOKEN))) .thenReturn(List.of(PipelineLeadTime.builder().build())); - pipelineService.fetchGithubData(request); + pipelineService.fetchGitHubData(request); verify(gitHubService).fetchPipelinesLeadTime(any(), roadMapArgumentCaptor.capture(), any()); assertEquals("repo2", roadMapArgumentCaptor.getValue().get("env1")); @@ -181,7 +181,7 @@ void shouldFilterAuthorByInputCrews() { when(gitHubService.fetchPipelinesLeadTime(any(), any(), eq(MOCK_TOKEN))) .thenReturn(List.of(PipelineLeadTime.builder().build())); - FetchedData.BuildKiteData result = pipelineService.fetchGithubData(request); + FetchedData.BuildKiteData result = pipelineService.fetchGitHubData(request); assertEquals(1, result.getPipelineLeadTimes().size()); assertEquals(1, result.getBuildInfosList().size()); diff --git a/backend/src/test/java/heartbeat/service/report/ReportServiceTest.java b/backend/src/test/java/heartbeat/service/report/ReportServiceTest.java index abbbf194fe..4dd300290c 100644 --- a/backend/src/test/java/heartbeat/service/report/ReportServiceTest.java +++ b/backend/src/test/java/heartbeat/service/report/ReportServiceTest.java @@ -1,14 +1,21 @@ package heartbeat.service.report; import heartbeat.controller.report.dto.request.GenerateReportRequest; -import heartbeat.controller.report.dto.request.MetricType; import heartbeat.controller.report.dto.request.ReportType; +import heartbeat.controller.report.dto.response.ErrorInfo; import heartbeat.controller.report.dto.response.MetricsDataCompleted; +import heartbeat.controller.report.dto.response.ReportMetricsError; +import heartbeat.controller.report.dto.response.ReportResponse; import heartbeat.exception.NotFoundException; import heartbeat.handler.AsyncMetricsDataHandler; +import heartbeat.service.report.calculator.ReportGenerator; +import heartbeat.util.IdUtil; +import org.awaitility.Awaitility; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -23,16 +30,19 @@ import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static heartbeat.controller.report.dto.request.MetricEnum.LEAD_TIME_FOR_CHANGES; -import static heartbeat.controller.report.dto.request.MetricEnum.VELOCITY; +import static heartbeat.controller.report.dto.request.MetricType.BOARD; +import static heartbeat.controller.report.dto.request.MetricType.DORA; import static heartbeat.service.report.scheduler.DeleteExpireCSVScheduler.EXPORT_CSV_VALIDITY_TIME; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -52,6 +62,12 @@ public class ReportServiceTest { @Mock GenerateReporterService generateReporterService; + @Mock + ReportGenerator reportGenerator; + + @Captor + ArgumentCaptor metricsDataCompletedArgumentCaptor; + @Test void exportCsvShouldCallCsvFileGeneratorToGotTheStreamWhenTimestampIsValid() throws IOException { long validTimestamp = System.currentTimeMillis() - EXPORT_CSV_VALIDITY_TIME + 20000L; @@ -81,71 +97,239 @@ class GenerateReportByType { GenerateReportRequest request = GenerateReportRequest.builder() .csvTimeStamp(timeStamp) .metrics(new ArrayList<>()) + .metricTypes(List.of(BOARD)) .build(); @Test - void shouldCallGenerateBoardReportWhenMetricTypeIsBoard() throws InterruptedException { - MetricsDataCompleted expected = MetricsDataCompleted.builder().boardMetricsCompleted(false).build(); + void shouldSuccessfulGenerateBoardReportAndInitializeMetricDataWhenMetricTypesListOnlyHasBoardElement() { + MetricsDataCompleted expected = MetricsDataCompleted.builder() + .boardMetricsCompleted(false) + .overallMetricCompleted(false) + .isSuccessfulCreateCsvFile(false) + .build(); doAnswer(invocation -> null).when(asyncMetricsDataHandler).putMetricsDataCompleted(any(), any()); doAnswer(invocation -> null).when(generateReporterService).generateBoardReport(request); - - reportService.generateReportByType(request, MetricType.BOARD); - Thread.sleep(100); - - verify(asyncMetricsDataHandler).putMetricsDataCompleted(request.getBoardReportId(), expected); - verify(generateReporterService).generateBoardReport(request); - verify(generateReporterService, never()).generateDoraReport(request); + when(generateReporterService.getComposedReportResponse(any())) + .thenReturn(ReportResponse.builder().reportMetricsError(ReportMetricsError.builder().build()).build()); + when(reportGenerator.getReportGenerator(generateReporterService)).thenReturn(Map.of(BOARD, + generateReporterService::generateBoardReport, DORA, generateReporterService::generateDoraReport)); + + reportService.generateReport(request); + + verify(asyncMetricsDataHandler).putMetricsDataCompleted(any(), + metricsDataCompletedArgumentCaptor.capture()); + MetricsDataCompleted metricsDataCompleted = metricsDataCompletedArgumentCaptor.getValue(); + assertEquals(metricsDataCompleted, expected); + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + verify(generateReporterService).generateBoardReport(request); + verify(generateReporterService, never()).generateDoraReport(request); + verify(generateReporterService).getComposedReportResponse(request.getCsvTimeStamp()); + verify(asyncMetricsDataHandler) + .updateOverallMetricsCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp())); + }); } @Test - void shouldCallGenerateDoraReportWhenMetricTypeIsDora() throws InterruptedException { - MetricsDataCompleted expected = MetricsDataCompleted.builder().doraMetricsCompleted(false).build(); + void shouldSuccessfulGenerateDoraReportWhenMetricTypesListOnlyHasDoraMetricType() { + MetricsDataCompleted expected = MetricsDataCompleted.builder() + .doraMetricsCompleted(false) + .overallMetricCompleted(false) + .isSuccessfulCreateCsvFile(false) + .build(); doAnswer(invocation -> null).when(asyncMetricsDataHandler).putMetricsDataCompleted(any(), any()); + request.setMetricTypes(List.of(DORA)); doAnswer(invocation -> null).when(generateReporterService).generateDoraReport(request); - - reportService.generateReportByType(request, MetricType.DORA); - Thread.sleep(100); - - verify(asyncMetricsDataHandler).putMetricsDataCompleted(request.getDoraReportId(), expected); - verify(generateReporterService).generateDoraReport(request); - verify(generateReporterService, never()).generateBoardReport(request); + when(generateReporterService.getComposedReportResponse(any())) + .thenReturn(ReportResponse.builder().reportMetricsError(ReportMetricsError.builder().build()).build()); + when(reportGenerator.getReportGenerator(generateReporterService)).thenReturn(Map.of(BOARD, + generateReporterService::generateBoardReport, DORA, generateReporterService::generateDoraReport)); + + reportService.generateReport(request); + + verify(asyncMetricsDataHandler).putMetricsDataCompleted(any(), + metricsDataCompletedArgumentCaptor.capture()); + MetricsDataCompleted metricsDataCompleted = metricsDataCompletedArgumentCaptor.getValue(); + assertEquals(metricsDataCompleted, expected); + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + verify(generateReporterService).generateDoraReport(request); + verify(generateReporterService, never()).generateBoardReport(request); + verify(generateReporterService).getComposedReportResponse(request.getCsvTimeStamp()); + verify(asyncMetricsDataHandler) + .updateOverallMetricsCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp())); + }); } - } - - @Nested - class InitializeMetricsDataCompletedInHandler { - - String timeStamp = "1683734399999"; - - GenerateReportRequest request = GenerateReportRequest.builder() - .csvTimeStamp(timeStamp) - .metrics(List.of(VELOCITY.getValue(), LEAD_TIME_FOR_CHANGES.getValue())) - .build(); - @Test - void shouldInitializeBoardMetricsCompletedFalseWhenPreviousIsNull() { - MetricsDataCompleted expectMetricsDataResult = MetricsDataCompleted.builder() + void shouldSuccessfulGenerateDoraReportAndBoardReportGivenMetricTypesListHasDoraMetricTypeAndBoardMetricType() { + MetricsDataCompleted expected = MetricsDataCompleted.builder() .boardMetricsCompleted(false) + .doraMetricsCompleted(false) + .overallMetricCompleted(false) + .isSuccessfulCreateCsvFile(false) .build(); doAnswer(invocation -> null).when(asyncMetricsDataHandler).putMetricsDataCompleted(any(), any()); + request.setMetricTypes(List.of(BOARD, DORA)); + doAnswer(invocation -> null).when(generateReporterService).generateDoraReport(request); + doAnswer(invocation -> null).when(generateReporterService).generateBoardReport(request); + when(generateReporterService.getComposedReportResponse(any())) + .thenReturn(ReportResponse.builder().reportMetricsError(ReportMetricsError.builder().build()).build()); + when(reportGenerator.getReportGenerator(generateReporterService)).thenReturn(Map.of(BOARD, + generateReporterService::generateBoardReport, DORA, generateReporterService::generateDoraReport)); + + reportService.generateReport(request); + + verify(asyncMetricsDataHandler).putMetricsDataCompleted(any(), + metricsDataCompletedArgumentCaptor.capture()); + MetricsDataCompleted metricsDataCompleted = metricsDataCompletedArgumentCaptor.getValue(); + assertEquals(metricsDataCompleted, expected); + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + verify(generateReporterService).generateDoraReport(request); + verify(generateReporterService).generateBoardReport(request); + verify(generateReporterService).getComposedReportResponse(request.getCsvTimeStamp()); + verify(asyncMetricsDataHandler) + .updateOverallMetricsCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp())); + }); + } - reportService.initializeMetricsDataCompletedInHandler(timeStamp, MetricType.BOARD); - - verify(asyncMetricsDataHandler).putMetricsDataCompleted(request.getBoardReportId(), - expectMetricsDataResult); + @Test + void shouldNotGenerateMetricCsvWhenBoardMetricsHasError() { + MetricsDataCompleted expected = MetricsDataCompleted.builder() + .boardMetricsCompleted(false) + .doraMetricsCompleted(false) + .overallMetricCompleted(false) + .isSuccessfulCreateCsvFile(false) + .build(); + doAnswer(invocation -> null).when(asyncMetricsDataHandler).putMetricsDataCompleted(any(), any()); + request.setMetricTypes(List.of(BOARD, DORA)); + doAnswer(invocation -> null).when(generateReporterService).generateDoraReport(request); + doAnswer(invocation -> null).when(generateReporterService).generateBoardReport(request); + when(generateReporterService.getComposedReportResponse(any())).thenReturn(ReportResponse.builder() + .reportMetricsError(ReportMetricsError.builder().boardMetricsError(ErrorInfo.builder().build()).build()) + .build()); + when(reportGenerator.getReportGenerator(generateReporterService)).thenReturn(Map.of(BOARD, + generateReporterService::generateBoardReport, DORA, generateReporterService::generateDoraReport)); + + reportService.generateReport(request); + + verify(asyncMetricsDataHandler).putMetricsDataCompleted(any(), + metricsDataCompletedArgumentCaptor.capture()); + MetricsDataCompleted metricsDataCompleted = metricsDataCompletedArgumentCaptor.getValue(); + assertEquals(metricsDataCompleted, expected); + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + verify(generateReporterService).generateDoraReport(request); + verify(generateReporterService).generateBoardReport(request); + verify(generateReporterService).getComposedReportResponse(request.getCsvTimeStamp()); + verify(generateReporterService, never()).generateCSVForMetric(any(), any()); + verify(asyncMetricsDataHandler, times(1)) + .updateOverallMetricsCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp())); + }); } @Test - void shouldInitializeDoraMetricsCompletedFalseWhenPreviousIsNull() { - MetricsDataCompleted expectMetricsDataResult = MetricsDataCompleted.builder() + void shouldNotGenerateMetricCsvWhenPiplineMetricsErrorHasError() { + MetricsDataCompleted expected = MetricsDataCompleted.builder() + .boardMetricsCompleted(false) .doraMetricsCompleted(false) + .overallMetricCompleted(false) + .isSuccessfulCreateCsvFile(false) .build(); doAnswer(invocation -> null).when(asyncMetricsDataHandler).putMetricsDataCompleted(any(), any()); + request.setMetricTypes(List.of(BOARD, DORA)); + doAnswer(invocation -> null).when(generateReporterService).generateDoraReport(request); + doAnswer(invocation -> null).when(generateReporterService).generateBoardReport(request); + when(generateReporterService.getComposedReportResponse(any())).thenReturn(ReportResponse.builder() + .reportMetricsError( + ReportMetricsError.builder().pipelineMetricsError(ErrorInfo.builder().build()).build()) + .build()); + when(reportGenerator.getReportGenerator(generateReporterService)).thenReturn(Map.of(BOARD, + generateReporterService::generateBoardReport, DORA, generateReporterService::generateDoraReport)); + + reportService.generateReport(request); + + verify(asyncMetricsDataHandler).putMetricsDataCompleted(any(), + metricsDataCompletedArgumentCaptor.capture()); + MetricsDataCompleted metricsDataCompleted = metricsDataCompletedArgumentCaptor.getValue(); + assertEquals(metricsDataCompleted, expected); + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + verify(generateReporterService).generateDoraReport(request); + verify(generateReporterService).generateBoardReport(request); + verify(generateReporterService).getComposedReportResponse(request.getCsvTimeStamp()); + verify(generateReporterService, never()).generateCSVForMetric(any(), any()); + verify(asyncMetricsDataHandler, times(1)) + .updateOverallMetricsCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp())); + }); + } - reportService.initializeMetricsDataCompletedInHandler(timeStamp, MetricType.DORA); + @Test + void shouldNotGenerateMetricCsvWhenSourceControlMetricsErrorHasError() { + MetricsDataCompleted expected = MetricsDataCompleted.builder() + .boardMetricsCompleted(false) + .doraMetricsCompleted(false) + .overallMetricCompleted(false) + .isSuccessfulCreateCsvFile(false) + .build(); + doAnswer(invocation -> null).when(asyncMetricsDataHandler).putMetricsDataCompleted(any(), any()); + request.setMetricTypes(List.of(BOARD, DORA)); + doAnswer(invocation -> null).when(generateReporterService).generateDoraReport(request); + doAnswer(invocation -> null).when(generateReporterService).generateBoardReport(request); + when(generateReporterService.getComposedReportResponse(any())).thenReturn(ReportResponse.builder() + .reportMetricsError( + ReportMetricsError.builder().sourceControlMetricsError(ErrorInfo.builder().build()).build()) + .build()); + when(reportGenerator.getReportGenerator(generateReporterService)).thenReturn(Map.of(BOARD, + generateReporterService::generateBoardReport, DORA, generateReporterService::generateDoraReport)); + + reportService.generateReport(request); + + verify(asyncMetricsDataHandler).putMetricsDataCompleted(any(), + metricsDataCompletedArgumentCaptor.capture()); + MetricsDataCompleted metricsDataCompleted = metricsDataCompletedArgumentCaptor.getValue(); + assertEquals(metricsDataCompleted, expected); + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + verify(generateReporterService).generateDoraReport(request); + verify(generateReporterService).generateBoardReport(request); + verify(generateReporterService).getComposedReportResponse(request.getCsvTimeStamp()); + verify(generateReporterService, never()).generateCSVForMetric(any(), any()); + verify(asyncMetricsDataHandler, times(1)) + .updateOverallMetricsCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp())); + }); + } - verify(asyncMetricsDataHandler).putMetricsDataCompleted(request.getDoraReportId(), expectMetricsDataResult); + @Test + void shouldSuccessfulGenerateDoraReportGivenBoardReportHasBeenGeneratedWhenRetryGenerateDoraReport() { + MetricsDataCompleted expected = MetricsDataCompleted.builder() + .boardMetricsCompleted(true) + .doraMetricsCompleted(false) + .overallMetricCompleted(false) + .isSuccessfulCreateCsvFile(false) + .build(); + when(asyncMetricsDataHandler.getMetricsDataCompleted(any())).thenReturn(MetricsDataCompleted.builder() + .boardMetricsCompleted(true) + .doraMetricsCompleted(true) + .overallMetricCompleted(true) + .build()); + when(reportGenerator.getReportGenerator(generateReporterService)).thenReturn(Map.of(BOARD, + generateReporterService::generateBoardReport, DORA, generateReporterService::generateDoraReport)); + doAnswer(invocation -> null).when(asyncMetricsDataHandler).putMetricsDataCompleted(any(), any()); + request.setMetricTypes(List.of(DORA)); + doAnswer(invocation -> null).when(generateReporterService).generateDoraReport(request); + when(generateReporterService.getComposedReportResponse(any())) + .thenReturn(ReportResponse.builder().reportMetricsError(ReportMetricsError.builder().build()).build()); + + reportService.generateReport(request); + + verify(asyncMetricsDataHandler).putMetricsDataCompleted(any(), + metricsDataCompletedArgumentCaptor.capture()); + MetricsDataCompleted metricsDataCompleted = metricsDataCompletedArgumentCaptor.getValue(); + assertEquals(metricsDataCompleted, expected); + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + verify(generateReporterService).generateDoraReport(request); + verify(generateReporterService, never()).generateBoardReport(request); + verify(generateReporterService).getComposedReportResponse(request.getCsvTimeStamp()); + verify(generateReporterService).generateCSVForMetric(any(), any()); + verify(asyncMetricsDataHandler, times(1)) + .updateOverallMetricsCompletedInHandler(IdUtil.getDataCompletedPrefix(request.getCsvTimeStamp())); + }); } } diff --git a/backend/src/test/java/heartbeat/service/report/ReworkCalculatorTest.java b/backend/src/test/java/heartbeat/service/report/ReworkCalculatorTest.java new file mode 100644 index 0000000000..c6784cfcd3 --- /dev/null +++ b/backend/src/test/java/heartbeat/service/report/ReworkCalculatorTest.java @@ -0,0 +1,50 @@ +package heartbeat.service.report; + +import heartbeat.controller.board.dto.response.CardCollection; +import heartbeat.controller.report.dto.response.Rework; +import heartbeat.service.report.calculator.ReworkCalculator; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import static heartbeat.controller.board.dto.request.CardStepsEnum.DEVELOPMENT; +import static heartbeat.service.report.ReworkFixture.MOCK_CARD_COLLECTION; +import static heartbeat.service.report.ReworkFixture.MOCK_CARD_COLLECTION_WITH_TODO; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class ReworkCalculatorTest { + + @InjectMocks + ReworkCalculator reworkCalculator; + + @Test + void shouldReturnAllDoneCardsReworkTimesWhenCallCalculateRework() { + Rework rework = reworkCalculator.calculateRework(MOCK_CARD_COLLECTION(), DEVELOPMENT); + + assertEquals(DEVELOPMENT.getValue(), rework.getReworkState()); + assertEquals(1, rework.getReworkCardsRatio()); + assertEquals(2, rework.getTotalReworkCards()); + assertEquals(2, rework.getFromAnalysis()); + assertEquals(2, rework.getFromInDev()); + assertEquals(2, rework.getFromBlock()); + assertEquals(2, rework.getFromWaitingForTesting()); + assertEquals(2, rework.getFromTesting()); + assertEquals(2, rework.getFromReview()); + assertEquals(2, rework.getFromDone()); + assertEquals(2, rework.getThroughput()); + } + + @Test + void shouldThrowExceptionWhenCallCalculateReworkGivenCardCollectionHasAnalyseStateReworkTimesInfo() { + CardCollection cardCollection = MOCK_CARD_COLLECTION_WITH_TODO(); + assertThrows(IllegalStateException.class, () -> reworkCalculator.calculateRework(cardCollection, DEVELOPMENT), + "Unexpected value: TODO"); + } + +} diff --git a/backend/src/test/java/heartbeat/service/report/ReworkFixture.java b/backend/src/test/java/heartbeat/service/report/ReworkFixture.java new file mode 100644 index 0000000000..0b3f2de3bf --- /dev/null +++ b/backend/src/test/java/heartbeat/service/report/ReworkFixture.java @@ -0,0 +1,50 @@ +package heartbeat.service.report; + +import heartbeat.controller.board.dto.response.CardCollection; +import heartbeat.controller.board.dto.response.JiraCardDTO; +import heartbeat.controller.board.dto.response.ReworkTimesInfo; + +import java.util.List; + +import static heartbeat.controller.board.dto.request.CardStepsEnum.ANALYSE; +import static heartbeat.controller.board.dto.request.CardStepsEnum.BLOCK; +import static heartbeat.controller.board.dto.request.CardStepsEnum.DEVELOPMENT; +import static heartbeat.controller.board.dto.request.CardStepsEnum.DONE; +import static heartbeat.controller.board.dto.request.CardStepsEnum.REVIEW; +import static heartbeat.controller.board.dto.request.CardStepsEnum.TESTING; +import static heartbeat.controller.board.dto.request.CardStepsEnum.TODO; +import static heartbeat.controller.board.dto.request.CardStepsEnum.WAITING; + +public class ReworkFixture { + + public static CardCollection MOCK_CARD_COLLECTION() { + List reworkTimesInfos = List.of(ReworkTimesInfo.builder().state(ANALYSE).times(1).build(), + ReworkTimesInfo.builder().state(DEVELOPMENT).times(1).build(), + ReworkTimesInfo.builder().state(BLOCK).times(1).build(), + ReworkTimesInfo.builder().state(WAITING).times(1).build(), + ReworkTimesInfo.builder().state(REVIEW).times(1).build(), + ReworkTimesInfo.builder().state(TESTING).times(1).build(), + ReworkTimesInfo.builder().state(DONE).times(1).build()); + List jiraCardList = List.of(JiraCardDTO.builder().reworkTimesInfos(reworkTimesInfos).build(), + JiraCardDTO.builder().reworkTimesInfos(reworkTimesInfos).build()); + return CardCollection.builder() + .reworkCardNumber(2) + .cardsNumber(2) + .reworkRatio(1) + .jiraCardDTOList(jiraCardList) + .build(); + } + + public static CardCollection MOCK_CARD_COLLECTION_WITH_TODO() { + List reworkTimesInfos = List.of(ReworkTimesInfo.builder().state(TODO).times(1).build(), + ReworkTimesInfo.builder().state(DEVELOPMENT).times(1).build(), + ReworkTimesInfo.builder().state(BLOCK).times(1).build(), + ReworkTimesInfo.builder().state(WAITING).times(1).build(), + ReworkTimesInfo.builder().state(REVIEW).times(1).build(), + ReworkTimesInfo.builder().state(DONE).times(1).build()); + List jiraCardList = List.of(JiraCardDTO.builder().reworkTimesInfos(reworkTimesInfos).build(), + JiraCardDTO.builder().reworkTimesInfos(reworkTimesInfos).build()); + return CardCollection.builder().reworkCardNumber(2).reworkRatio(1).jiraCardDTOList(jiraCardList).build(); + } + +} diff --git a/backend/src/test/java/heartbeat/service/report/calculator/CalculateChangeFailureRateTest.java b/backend/src/test/java/heartbeat/service/report/calculator/CalculateDevChangeFailureRateTest.java similarity index 56% rename from backend/src/test/java/heartbeat/service/report/calculator/CalculateChangeFailureRateTest.java rename to backend/src/test/java/heartbeat/service/report/calculator/CalculateDevChangeFailureRateTest.java index 3543cee98a..27457f8d66 100644 --- a/backend/src/test/java/heartbeat/service/report/calculator/CalculateChangeFailureRateTest.java +++ b/backend/src/test/java/heartbeat/service/report/calculator/CalculateDevChangeFailureRateTest.java @@ -2,7 +2,7 @@ import heartbeat.client.dto.pipeline.buildkite.DeployInfo; import heartbeat.client.dto.pipeline.buildkite.DeployTimes; -import heartbeat.controller.report.dto.response.ChangeFailureRate; +import heartbeat.controller.report.dto.response.DevChangeFailureRate; import heartbeat.service.pipeline.buildkite.builder.DeployInfoBuilder; import heartbeat.service.pipeline.buildkite.builder.DeployTimesBuilder; import org.junit.jupiter.api.Test; @@ -19,7 +19,7 @@ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -class CalculateChangeFailureRateTest { +class CalculateDevChangeFailureRateTest { private static final String JOB_FINISH_TIME_2022 = "2022-09-08T22:45:33.981Z"; @@ -30,39 +30,39 @@ class CalculateChangeFailureRateTest { private static final String OTHER_JOB_NAME = "JobName"; @InjectMocks - private ChangeFailureRateCalculator changeFailureRate; + private DevChangeFailureRateCalculator devChangeFailureRate; @Test - public void testCalculateChangeFailureRate() { + void testCalculateDevChangeFailureRate() { DeployTimes mockedDeployTimes = DeployTimesBuilder.withDefault() .withPassed(List.of(DeployInfoBuilder.withDefault().withJobFinishTime(JOB_FINISH_TIME_2023).build(), DeployInfoBuilder.withDefault().withJobFinishTime(JOB_FINISH_TIME_2023).build())) .withFailed(List.of(DeployInfo.builder().jobFinishTime(JOB_FINISH_TIME_2022).state(FAILED_STATE).build())) .build(); - ChangeFailureRate changeFailureRate = this.changeFailureRate.calculate(List.of(mockedDeployTimes)); + DevChangeFailureRate devChangeFailureRate = this.devChangeFailureRate.calculate(List.of(mockedDeployTimes)); - assertThat(changeFailureRate.getAvgChangeFailureRate().getFailureRate()).isEqualTo(0.3333F); - assertThat(changeFailureRate.getAvgChangeFailureRate().getTotalFailedTimes()).isEqualTo(1); - assertThat(changeFailureRate.getAvgChangeFailureRate().getTotalTimes()).isEqualTo(3); + assertThat(devChangeFailureRate.getAvgDevChangeFailureRate().getFailureRate()).isEqualTo(0.3333F); + assertThat(devChangeFailureRate.getAvgDevChangeFailureRate().getTotalFailedTimes()).isEqualTo(1); + assertThat(devChangeFailureRate.getAvgDevChangeFailureRate().getTotalTimes()).isEqualTo(3); } @Test - public void testCalculateChangeFailureRateWhenTotalDeployInfosTimesIsZero() { + void testCalculateDevChangeFailureRateWhenTotalDeployInfosTimesIsZero() { DeployTimes mockedDeployTimes = DeployTimesBuilder.withDefault() .withPassed(Collections.emptyList()) .withFailed(Collections.emptyList()) .build(); - ChangeFailureRate changeFailureRate = this.changeFailureRate.calculate(List.of(mockedDeployTimes)); + DevChangeFailureRate devChangeFailureRate = this.devChangeFailureRate.calculate(List.of(mockedDeployTimes)); - assertThat(changeFailureRate.getAvgChangeFailureRate().getFailureRate()).isEqualTo(0.0F); - assertThat(changeFailureRate.getAvgChangeFailureRate().getTotalFailedTimes()).isEqualTo(0); - assertThat(changeFailureRate.getAvgChangeFailureRate().getTotalTimes()).isEqualTo(0); + assertThat(devChangeFailureRate.getAvgDevChangeFailureRate().getFailureRate()).isEqualTo(0.0F); + assertThat(devChangeFailureRate.getAvgDevChangeFailureRate().getTotalFailedTimes()).isZero(); + assertThat(devChangeFailureRate.getAvgDevChangeFailureRate().getTotalTimes()).isZero(); } @Test - public void testCalculateChangeFailureRateWhenHavePassedDeployInfoWhoseJobNameIsNotEqualToPipelineStep() { + void testCalculateDevChangeFailureRateWhenHavePassedDeployInfoWhoseJobNameIsNotEqualToPipelineStep() { DeployTimes mockedDeployTimes = DeployTimesBuilder.withDefault() .withPassed(List.of(DeployInfoBuilder.withDefault() .withJobName(OTHER_JOB_NAME) @@ -71,11 +71,11 @@ public void testCalculateChangeFailureRateWhenHavePassedDeployInfoWhoseJobNameIs .withFailed(List.of(DeployInfo.builder().jobFinishTime(JOB_FINISH_TIME_2022).state(FAILED_STATE).build())) .build(); - ChangeFailureRate changeFailureRate = this.changeFailureRate.calculate(List.of(mockedDeployTimes)); + DevChangeFailureRate devChangeFailureRate = this.devChangeFailureRate.calculate(List.of(mockedDeployTimes)); - assertThat(changeFailureRate.getAvgChangeFailureRate().getFailureRate()).isEqualTo(0.5F); - assertThat(changeFailureRate.getAvgChangeFailureRate().getTotalFailedTimes()).isEqualTo(1); - assertThat(changeFailureRate.getAvgChangeFailureRate().getTotalTimes()).isEqualTo(2); + assertThat(devChangeFailureRate.getAvgDevChangeFailureRate().getFailureRate()).isEqualTo(0.5F); + assertThat(devChangeFailureRate.getAvgDevChangeFailureRate().getTotalFailedTimes()).isEqualTo(1); + assertThat(devChangeFailureRate.getAvgDevChangeFailureRate().getTotalTimes()).isEqualTo(2); } } diff --git a/backend/src/test/java/heartbeat/service/report/calculator/ReportGeneratorTest.java b/backend/src/test/java/heartbeat/service/report/calculator/ReportGeneratorTest.java new file mode 100644 index 0000000000..f1ae781b54 --- /dev/null +++ b/backend/src/test/java/heartbeat/service/report/calculator/ReportGeneratorTest.java @@ -0,0 +1,40 @@ +package heartbeat.service.report.calculator; + +import heartbeat.controller.report.dto.request.GenerateReportRequest; +import heartbeat.controller.report.dto.request.MetricType; +import heartbeat.service.report.GenerateReporterService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import java.util.Map; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class ReportGeneratorTest { + + @InjectMocks + private ReportGenerator reportGenerator; + + @Mock + private GenerateReporterService generateReporterService; + + @Test + void shouldSuccessGetReportGeneratorMapWhenCallGetReportGenerator() { + + Map> generator = reportGenerator + .getReportGenerator(generateReporterService); + + assertTrue(generator.containsKey(MetricType.BOARD)); + assertTrue(generator.containsKey(MetricType.DORA)); + + } + +} diff --git a/backend/src/test/java/heartbeat/service/source/github/GithubServiceTest.java b/backend/src/test/java/heartbeat/service/source/github/GithubServiceTest.java index 707a712344..de611d50fe 100644 --- a/backend/src/test/java/heartbeat/service/source/github/GithubServiceTest.java +++ b/backend/src/test/java/heartbeat/service/source/github/GithubServiceTest.java @@ -10,6 +10,7 @@ import heartbeat.client.dto.codebase.github.PullRequestInfo; import heartbeat.client.dto.pipeline.buildkite.DeployInfo; import heartbeat.client.dto.pipeline.buildkite.DeployTimes; +import heartbeat.exception.BadRequestException; import heartbeat.exception.InternalServerErrorException; import heartbeat.exception.NotFoundException; import heartbeat.exception.PermissionDenyException; @@ -24,12 +25,17 @@ import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletionException; + import static heartbeat.TestFixtures.GITHUB_REPOSITORY; import static heartbeat.TestFixtures.GITHUB_TOKEN; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @@ -39,11 +45,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletionException; - @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class GithubServiceTest { @@ -86,6 +87,7 @@ public void setUp() { .mergedAt("2022-07-23T04:04:00.000+00:00") .createdAt("2022-07-23T04:03:00.000+00:00") .mergeCommitSha("111") + .url("https://api.github.com/repos/XXXX-fs/fs-platform-onboarding/pulls/1") .number(1) .build(); deployInfo = DeployInfo.builder() @@ -168,7 +170,7 @@ void shouldReturnGithubBranchIsVerifyWhenVerifyBranch() { } @Test - void shouldThrowExceptionWhenGithubReturnUnExpectedException() { + void shouldThrowUnauthorizedExceptionGivenGithubReturnUnauthorizedExceptionWhenVerifyToken() { String githubEmptyToken = GITHUB_TOKEN; doThrow(new UnauthorizedException("Failed to get GitHub info_status: 401 UNAUTHORIZED, reason: ...")) .when(gitHubFeignClient) @@ -179,30 +181,42 @@ void shouldThrowExceptionWhenGithubReturnUnExpectedException() { } @Test - void shouldThrowExceptionGivenGithubReturnUnExpectedExceptionWhenVerifyBranch() { + void shouldThrowBadRequestExceptionGivenGithubReturnUnExpectedExceptionWhenVerifyBranch() { String githubEmptyToken = GITHUB_TOKEN; doThrow(new UnauthorizedException("Failed to get GitHub info_status: 401 UNAUTHORIZED, reason: ...")) .when(gitHubFeignClient) .verifyCanReadTargetBranch("fake/repo", "main", "token " + githubEmptyToken); - var exception = assertThrows(UnauthorizedException.class, + var exception = assertThrows(BadRequestException.class, () -> githubService.verifyCanReadTargetBranch(GITHUB_REPOSITORY, "main", githubEmptyToken)); - assertEquals("Failed to get GitHub info_status: 401 UNAUTHORIZED, reason: ...", exception.getMessage()); + assertEquals("Unable to read target branch: main, with token error", exception.getMessage()); } @Test - void shouldThrowExceptionGivenGithubReturnPermissionDenyExceptionWhenVerifyBranch() { + void shouldThrowNotFoundExceptionGivenGithubReturnNotFoundExceptionWhenVerifyBranch() { String githubEmptyToken = GITHUB_TOKEN; doThrow(new NotFoundException("Failed to get GitHub info_status: 404, reason: ...")).when(gitHubFeignClient) .verifyCanReadTargetBranch("fake/repo", "main", "token " + githubEmptyToken); - var exception = assertThrows(PermissionDenyException.class, + var exception = assertThrows(NotFoundException.class, () -> githubService.verifyCanReadTargetBranch(GITHUB_REPOSITORY, "main", githubEmptyToken)); assertEquals("Unable to read target branch: main", exception.getMessage()); } @Test - void shouldThrowExceptionGivenGithubReturnCompletionExceptionExceptionWhenVerifyToken() { + void shouldThrowInternalServerErrorExceptionGivenGithubReturnInternalServerErrorExceptionWhenVerifyBranch() { + String githubEmptyToken = GITHUB_TOKEN; + doThrow(new InternalServerErrorException("Failed to get GitHub info_status: 500, reason: ...")) + .when(gitHubFeignClient) + .verifyCanReadTargetBranch("fake/repo", "main", "token " + githubEmptyToken); + + var exception = assertThrows(InternalServerErrorException.class, + () -> githubService.verifyCanReadTargetBranch(GITHUB_REPOSITORY, "main", githubEmptyToken)); + assertEquals("Failed to get GitHub info_status: 500, reason: ...", exception.getMessage()); + } + + @Test + void shouldThrowInternalServerErrorExceptionGivenGithubReturnCompletionExceptionWhenVerifyToken() { String githubEmptyToken = GITHUB_TOKEN; doThrow(new CompletionException(new Exception("UnExpected Exception"))).when(gitHubFeignClient) .verifyToken("token " + githubEmptyToken); @@ -213,7 +227,7 @@ void shouldThrowExceptionGivenGithubReturnCompletionExceptionExceptionWhenVerify } @Test - void shouldThrowExceptionGivenGithubReturnUnauthorizedExceptionWhenVerifyBranch() { + void shouldThrowInternalServerErrorExceptionGivenGithubReturnCompletionExceptionWhenVerifyBranch() { String githubEmptyToken = GITHUB_TOKEN; doThrow(new CompletionException(new Exception("UnExpected Exception"))).when(gitHubFeignClient) .verifyCanReadTargetBranch("fake/repo", "main", "token " + githubEmptyToken); @@ -223,6 +237,18 @@ void shouldThrowExceptionGivenGithubReturnUnauthorizedExceptionWhenVerifyBranch( assertEquals("Failed to call GitHub branch: main with error: UnExpected Exception", exception.getMessage()); } + @Test + void shouldThrowUnauthorizedExceptionGivenGithubReturnPermissionDenyExceptionWhenVerifyBranch() { + String githubEmptyToken = GITHUB_TOKEN; + doThrow(new PermissionDenyException("Failed to get GitHub info_status: 403 FORBIDDEN...")) + .when(gitHubFeignClient) + .verifyCanReadTargetBranch("fake/repo", "main", "token " + githubEmptyToken); + + var exception = assertThrows(UnauthorizedException.class, + () -> githubService.verifyCanReadTargetBranch(GITHUB_REPOSITORY, "main", githubEmptyToken)); + assertEquals("Unable to read target organization", exception.getMessage()); + } + @Test void shouldReturnNullWhenMergeTimeIsNull() { PullRequestInfo pullRequestInfo = PullRequestInfo.builder().build(); @@ -364,6 +390,38 @@ void shouldReturnEmptyLeadTimeWhenDeployTimesIsEmpty() { assertEquals(expect, result); } + @Test + void shouldReturnEmptyLeadTimeGithubShaIsDifferent() { + String mockToken = "mockToken"; + List expect = List.of(PipelineLeadTime.builder() + .pipelineStep(PIPELINE_STEP) + .pipelineName("Name") + .leadTimes(List.of(LeadTime.builder() + .commitId("111") + .jobFinishTime(1658549160000L) + .pipelineCreateTime(1658549100000L) + .prLeadTime(0L) + .pipelineLeadTime(180000) + .totalTime(180000) + .build())) + .build()); + var pullRequestInfoWithDifferentSha = PullRequestInfo.builder() + .mergedAt("2022-07-23T04:04:00.000+00:00") + .createdAt("2022-07-23T04:03:00.000+00:00") + .mergeCommitSha("222") + .url("https://api.github.com/repos/XXXX-fs/fs-platform-onboarding/pulls/1") + .number(1) + .build(); + when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())) + .thenReturn(List.of(pullRequestInfoWithDifferentSha)); + when(gitHubFeignClient.getPullRequestCommitInfo(any(), any(), any())).thenReturn(List.of(commitInfo)); + when(gitHubFeignClient.getCommitInfo(any(), any(), any())).thenReturn(commitInfo); + + List result = githubService.fetchPipelinesLeadTime(deployTimes, repositoryMap, mockToken); + + assertEquals(expect, result); + } + @Test void shouldReturnEmptyMergeLeadTimeWhenPullRequestInfoIsEmpty() { String mockToken = "mockToken"; @@ -525,6 +583,7 @@ void shouldReturnPipeLineLeadTimeWhenDeployCommitShaIsDifferent() { .mergedAt("2022-07-23T04:04:00.000+00:00") .createdAt("2022-07-23T04:03:00.000+00:00") .mergeCommitSha("222") + .url("") .number(1) .build(); pipelineLeadTimes = List.of(PipelineLeadTime.builder() diff --git a/backend/src/test/java/heartbeat/tools/TimeUtils.java b/backend/src/test/java/heartbeat/tools/TimeUtils.java new file mode 100644 index 0000000000..db195653c1 --- /dev/null +++ b/backend/src/test/java/heartbeat/tools/TimeUtils.java @@ -0,0 +1,12 @@ +package heartbeat.tools; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +public class TimeUtils { + + public static Long mockTimeStamp(int year, int month, int day, int hour, int minute, int second) { + return LocalDateTime.of(year, month, day, hour, minute, second).toInstant(ZoneOffset.UTC).toEpochMilli(); + } + +} diff --git a/backend/src/test/java/heartbeat/util/DecimalUtilTest.java b/backend/src/test/java/heartbeat/util/DecimalUtilTest.java index bb36e41ffe..1f95e6b7c1 100644 --- a/backend/src/test/java/heartbeat/util/DecimalUtilTest.java +++ b/backend/src/test/java/heartbeat/util/DecimalUtilTest.java @@ -1,8 +1,11 @@ package heartbeat.util; +import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.List; + class DecimalUtilTest { @Test @@ -85,4 +88,28 @@ void shouldReturnZeroWhenCallFormatDecimalTwoGivenFloatValue() { Assertions.assertEquals(expected, result); } + @Test + void testFormatDecimalFour_ZeroValue() { + double value = 0; + String expected = "0.0000"; + + String result = DecimalUtil.formatDecimalFour(value); + + Assertions.assertEquals(expected, result); + } + + @Test + void testFormatDecimalFour_NonZeroValue() { + List> value = List.of(Pair.of(10.25671, "10.2567"), Pair.of(10.25, "10.2500"), + Pair.of(0.000006, "0.0000")); + + List result = List.of(DecimalUtil.formatDecimalFour(value.get(0).getLeft()), + DecimalUtil.formatDecimalFour(value.get(1).getLeft()), + DecimalUtil.formatDecimalFour(value.get(2).getLeft())); + + Assertions.assertEquals(value.get(0).getRight(), result.get(0)); + Assertions.assertEquals(value.get(1).getRight(), result.get(1)); + Assertions.assertEquals(value.get(2).getRight(), result.get(2)); + } + } diff --git a/backend/src/test/java/heartbeat/util/MetricsUtilTest.java b/backend/src/test/java/heartbeat/util/MetricsUtilTest.java index 242556c43a..874fc2d3ff 100644 --- a/backend/src/test/java/heartbeat/util/MetricsUtilTest.java +++ b/backend/src/test/java/heartbeat/util/MetricsUtilTest.java @@ -6,18 +6,18 @@ import java.util.List; -public class MetricsUtilTest { +class MetricsUtilTest { @Test void shouldGetRelatedPartMetrics() { - List kanbanMetrics = MetricsUtil.kanbanMetrics; - List buildKiteMetrics = MetricsUtil.buildKiteMetrics; - List codebaseMetrics = MetricsUtil.codebaseMetrics; + List kanbanMetrics = MetricsUtil.KANBAN_METRICS.getValue(); + List buildKiteMetrics = MetricsUtil.BUILDKITE_METRICS.getValue(); + List codebaseMetrics = MetricsUtil.CODEBASE_METRICS.getValue(); List expectedKanbanMetrics = List.of(MetricEnum.VELOCITY.getValue(), MetricEnum.CYCLE_TIME.getValue(), - MetricEnum.CLASSIFICATION.getValue()); - List expectedBuildKiteMetrics = List.of(MetricEnum.CHANGE_FAILURE_RATE.getValue(), - MetricEnum.DEPLOYMENT_FREQUENCY.getValue(), MetricEnum.MEAN_TIME_TO_RECOVERY.getValue()); + MetricEnum.CLASSIFICATION.getValue(), MetricEnum.REWORK_TIMES.getValue()); + List expectedBuildKiteMetrics = List.of(MetricEnum.DEV_CHANGE_FAILURE_RATE.getValue(), + MetricEnum.DEPLOYMENT_FREQUENCY.getValue(), MetricEnum.DEV_MEAN_TIME_TO_RECOVERY.getValue()); List expectedCodebaseMetrics = List.of(MetricEnum.LEAD_TIME_FOR_CHANGES.getValue()); Assertions.assertEquals(expectedKanbanMetrics, kanbanMetrics); diff --git a/contribution.md b/contribution.md new file mode 100644 index 0000000000..0d4be3faca --- /dev/null +++ b/contribution.md @@ -0,0 +1,64 @@ +# Contributing to Heartbeat + +Thank you for your interest in contributing to Heartbeat! + +## Contribution Guidelines + +We welcome contributions to: + +- Add a new feature to the library (guidance below). +- Improve our documentation and add examples to make it clear how to leverage the Heartbeat. +- Report bugs and issues in the project. +- Submit a request for a new feature. +- Improve our test coverage. + +### Contributing Features ✨ + +Heartbeat is designed to provide generic delivery tool to solve problems. Thus, we focus on contributions that can have an impact on a wide range of projects. +Before you contribute a new feature, please submitting an Issue to discuss the feature so that we can weigh in and assist. + +## How to contribute Changes + +### Kick-off + +- GitHub issue: Raise a GitHub issue, describe what you want to develop; Or find existing issues; And we will follow up with you and label the issue you are contributing. +- Roadmap: Need show what will you do in different phase, it's better to draw a image and paste it in the GitHub issue if possible. +- Timeline: Describe your expectation when it could be done. + +### Setup + +#### Local run + +Refer to [Run Heartbeat](README.md#6-run-heartbeat) to setup and run project locally. + +#### Local E2E + +Refer to [Setup E2E locally](https://au-heartbeat.github.io/Heartbeat/en/designs/e2e-testing/) to setup and run E2E locally. + +### Coding Standard + +- 100% Code coverage: whatever you code is about the frontend or backend +- SAST: We are using SonarCloud for static code scanning. +- E2E: Write E2E case for your Code Biz logic +- Swagger: Keep swagger available for any changes +- Small PR: Submit small PR, if we can't repair within 1 hour we will revert it +- Commit message: format should be`[GitHub Issue][number][backend/frontend]: commit message`, e.g. `[GitHub Issue][1135][frontend]: Support for GitHub Actions pipelines` + + +### Before Pull Request + +Make sure below item passed: +- Local [frontend](README.md#612-how-to-run-unit-tests) / [backend](backend/README.md#3-how-to-run-all-tests) UT +- Reviewers: Code must be reviewed by 2 team members at least +- Local E2E screenshot: Upload the latest E2E result screenshot on the PR. +- Github actions: Github actions will be executed automatically once you create a PR. + + +### After PR merged + +- Pipelines: Our Buildkite pipeline will run test check and E2E before deploying into our dev environment +- Revert: We will revert your Pull Request if pipeline failed and we could not fix within 1 hour. We will let you know in details. + +## License + +By contributing, you agree that your contributions will be licensed under an [MIT license](https://opensource.org/licenses/MIT). diff --git a/docs/.prettierrc b/docs/.prettierrc index 34b2ca2821..3934c460fe 100644 --- a/docs/.prettierrc +++ b/docs/.prettierrc @@ -5,6 +5,7 @@ "tabWidth": 2, "trailingComma": "es5", "useTabs": true, + "plugins": ["prettier-plugin-astro"], "overrides": [ { "files": [".*", "*.json", "*.md", "*.mdx", "*.toml", "*.yml"], diff --git a/docs/integrations/expressive-code.ts b/docs/integrations/expressive-code.ts index 3efefd3b88..3b944f9997 100644 --- a/docs/integrations/expressive-code.ts +++ b/docs/integrations/expressive-code.ts @@ -22,23 +22,23 @@ export const astroDocsExpressiveCode = () => scrollbarThumbColor: 'hsl(269deg 20% 90% / 0.25)', scrollbarThumbHoverColor: 'hsl(269deg 20% 90% / 0.5)', }, - frames: { - styleOverrides: { - editorTabBarBackground: 'var(--theme-code-tabs)', - editorActiveTabBackground: 'hsl(269deg 40% 65% / 0.15)', - editorActiveTabBorderBottom: 'hsl(269deg 35% 55%)', - editorTabBarBorderBottom: 'var(--theme-code-tabs)', + // frames: { + // styleOverrides: { + // editorTabBarBackground: 'var(--theme-code-tabs)', + // editorActiveTabBackground: 'hsl(269deg 40% 65% / 0.15)', + // editorActiveTabBorderBottom: 'hsl(269deg 35% 55%)', + // editorTabBarBorderBottom: 'var(--theme-code-tabs)', - terminalTitlebarBackground: 'var(--theme-code-tabs)', - terminalTitlebarBorderBottom: 'transparent', - terminalBackground: 'var(--theme-code-bg)', - }, - }, - textMarkers: { - styleOverrides: { - defaultChroma: '55', - }, - }, + // terminalTitlebarBackground: 'var(--theme-code-tabs)', + // terminalTitlebarBorderBottom: 'transparent', + // terminalBackground: 'var(--theme-code-bg)', + // }, + // }, + // textMarkers: { + // styleOverrides: { + // defaultChroma: '55', + // }, + // }, getBlockLocale: ({ file }) => { // Path format: `src/content/docs/en/getting-started.mdx` // Part indices: 0 1 2 3 4 diff --git a/docs/package.json b/docs/package.json index b0145f1d32..58df912566 100644 --- a/docs/package.json +++ b/docs/package.json @@ -33,87 +33,87 @@ "translation-status": "tsm --require=./scripts/lib/filter-warnings.cjs ./scripts/translation-status.ts" }, "devDependencies": { - "@11ty/eleventy-fetch": "^3.0.0", - "@actions/core": "^1.9.0", - "@astrojs/mdx": "^1.0.0", - "@astrojs/preact": "^3.0.0", - "@astrojs/sitemap": "3.0.0", - "@babel/core": "^7.18.10", - "@docsearch/css": "^3.5.1", - "@docsearch/react": "^3.5.1", - "@types/canvas-confetti": "^1.6.0", - "@types/hast": "^2.3.4", - "@types/html-escaper": "^3.0.0", - "@types/mdast": "^3.0.10", - "@types/node": "^18.6.4", - "@typescript-eslint/eslint-plugin": "^5.46.1", - "@typescript-eslint/parser": "^5.46.1", - "algoliasearch": "^4.20.0", - "astro": "^4.0.4", - "astro-auto-import": "^0.3.1", - "astro-eslint-parser": "^0.9.2", - "astro-expressive-code": "^0.20.0", - "astro-og-canvas": "^0.3.0", - "bcp-47-normalize": "^2.1.0", - "canvaskit-wasm": "^0.37.0", + "@11ty/eleventy-fetch": "^4.0.1", + "@actions/core": "^1.10.1", + "@astrojs/mdx": "^2.2.0", + "@astrojs/preact": "^3.1.1", + "@astrojs/sitemap": "3.1.2", + "@babel/core": "^7.24.3", + "@docsearch/css": "^3.6.0", + "@docsearch/react": "^3.6.0", + "@types/canvas-confetti": "^1.6.4", + "@types/hast": "^3.0.4", + "@types/html-escaper": "^3.0.2", + "@types/mdast": "^4.0.3", + "@types/node": "20.11.30", + "@typescript-eslint/eslint-plugin": "^7.3.1", + "@typescript-eslint/parser": "^7.3.1", + "algoliasearch": "^4.22.1", + "astro": "^4.5.7", + "astro-auto-import": "^0.4.2", + "astro-eslint-parser": "^0.16.3", + "astro-expressive-code": "^0.33.5", + "astro-og-canvas": "^0.4.2", + "bcp-47-normalize": "^2.3.0", + "canvaskit-wasm": "^0.39.1", "dedent-js": "^1.0.1", - "domhandler": "^4.3.1", - "eslint": "^8.29.0", - "eslint-plugin-astro": "^0.21.0", - "eslint-plugin-react": "^7.32.1", - "fast-glob": "^3.2.11", + "domhandler": "^5.0.3", + "eslint": "^8.57.0", + "eslint-plugin-astro": "^0.33.0", + "eslint-plugin-react": "^7.34.1", + "fast-glob": "^3.3.2", "gray-matter": "^4.0.3", - "hast-util-from-html": "^1.0.0", - "hast-util-to-html": "^8.0.4", - "hast-util-to-string": "^2.0.0", - "hastscript": "^7.0.2", + "hast-util-from-html": "^2.0.1", + "hast-util-to-html": "^9.0.0", + "hast-util-to-string": "^3.0.0", + "hastscript": "^9.0.0", "html-escaper": "^3.0.3", - "htmlparser2": "^7.2.0", - "husky": "^8.0.3", + "htmlparser2": "^9.1.0", + "husky": "^9.0.11", "kleur": "^4.1.5", - "lint-staged": "^13.1.0", - "mdast-util-from-markdown": "^1.2.0", - "mdast-util-mdx-jsx": "^2.1.2", - "mdast-util-to-hast": "^12.2.4", - "mdast-util-to-string": "^3.1.1", - "micromark-util-character": "^1.1.0", - "micromark-util-symbol": "^1.0.1", - "node-fetch": "^3.2.10", + "lint-staged": "^15.2.2", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-jsx": "^3.1.2", + "mdast-util-to-hast": "^13.1.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-character": "^2.1.0", + "micromark-util-symbol": "^2.0.0", + "node-fetch": "^3.3.2", "organize-imports-cli": "^0.10.0", - "p-retry": "^5.1.1", + "p-retry": "^6.2.0", "parse-numeric-range": "^1.3.0", - "preact": "^10.16.0", - "prettier": "^3.0.2", - "prettier-plugin-astro": "^0.11.1", + "preact": "^10.19.7", + "prettier": "^3.2.5", + "prettier-plugin-astro": "^0.13.0", "prompts": "^2.4.2", - "rehype": "^12.0.1", - "remark": "^14.0.2", - "remark-directive": "^2.0.1", + "rehype": "^13.0.1", + "remark": "^15.0.1", + "remark-directive": "^3.0.0", "remove-markdown": "^0.5.0", - "simple-git": "^3.11.0", + "simple-git": "^3.23.0", "tsm": "^2.3.0", - "typescript": "^5.0.2", - "unified": "^10.1.2", - "unist-util-remove": "^3.1.0", - "unist-util-visit": "^4.1.0", + "typescript": "^5.4.2", + "unified": "^11.0.4", + "unist-util-remove": "^4.0.0", + "unist-util-visit": "^5.0.0", "unist-util-walker": "^1.0.0", - "vfile": "^5.3.6", - "vitest": "^0.28.5" + "vfile": "^6.0.1", + "vitest": "^1.4.0" }, "dependencies": { "@akebifiky/remark-simple-plantuml": "^1.0.2", - "@astrojs/check": "^0.2.0", - "@fontsource/ibm-plex-mono": "^4.5.10", - "@nanostores/preact": "^0.1.3", - "canvas-confetti": "^1.6.0", - "jsdoc-api": "^7.1.1", + "@astrojs/check": "^0.5.9", + "@fontsource/ibm-plex-mono": "5.0.12", + "@nanostores/preact": "^0.5.1", + "canvas-confetti": "^1.9.2", + "jsdoc-api": "^8.0.0", "minimatch": "^9.0.3", - "nanostores": "^0.5.13", - "rehype-autolink-headings": "^6.1.1", - "rehype-slug": "^5.0.1", - "remark-gfm": "^3.0.1", - "remark-smartypants": "^2.0.0", - "sass": "^1.54.3" + "nanostores": "^0.10.0", + "rehype-autolink-headings": "^7.1.0", + "rehype-slug": "^6.0.0", + "remark-gfm": "^4.0.0", + "remark-smartypants": "^2.1.0", + "sass": "^1.72.0" }, "engines": { "node": ">=18.14.1" diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index 47567f8af4..9441904971 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -9,267 +9,274 @@ dependencies: specifier: ^1.0.2 version: 1.0.2 '@astrojs/check': - specifier: ^0.2.0 - version: 0.2.0(prettier-plugin-astro@0.11.1)(prettier@3.0.2)(typescript@5.0.2) + specifier: ^0.5.9 + version: 0.5.9(prettier-plugin-astro@0.13.0)(prettier@3.2.5)(typescript@5.4.2) '@fontsource/ibm-plex-mono': - specifier: ^4.5.10 - version: 4.5.10 + specifier: 5.0.12 + version: 5.0.12 '@nanostores/preact': - specifier: ^0.1.3 - version: 0.1.3(nanostores@0.5.13)(preact@10.16.0) + specifier: ^0.5.1 + version: 0.5.1(nanostores@0.10.0)(preact@10.19.7) canvas-confetti: - specifier: ^1.6.0 - version: 1.6.0 + specifier: ^1.9.2 + version: 1.9.2 jsdoc-api: - specifier: ^7.1.1 - version: 7.1.1 + specifier: ^8.0.0 + version: 8.0.0 minimatch: specifier: ^9.0.3 version: 9.0.3 nanostores: - specifier: ^0.5.13 - version: 0.5.13 + specifier: ^0.10.0 + version: 0.10.0 rehype-autolink-headings: - specifier: ^6.1.1 - version: 6.1.1 + specifier: ^7.1.0 + version: 7.1.0 rehype-slug: - specifier: ^5.0.1 - version: 5.0.1 + specifier: ^6.0.0 + version: 6.0.0 remark-gfm: - specifier: ^3.0.1 - version: 3.0.1 + specifier: ^4.0.0 + version: 4.0.0 remark-smartypants: - specifier: ^2.0.0 - version: 2.0.0 + specifier: ^2.1.0 + version: 2.1.0 sass: - specifier: ^1.54.3 - version: 1.54.3 + specifier: ^1.72.0 + version: 1.72.0 devDependencies: '@11ty/eleventy-fetch': - specifier: ^3.0.0 - version: 3.0.0 + specifier: ^4.0.1 + version: 4.0.1 '@actions/core': - specifier: ^1.9.0 - version: 1.9.0 + specifier: ^1.10.1 + version: 1.10.1 '@astrojs/mdx': - specifier: ^1.0.0 - version: 1.0.0(astro@4.0.4) + specifier: ^2.2.0 + version: 2.2.2(astro@4.5.7) '@astrojs/preact': - specifier: ^3.0.0 - version: 3.0.0(@babel/core@7.18.10)(preact@10.16.0) + specifier: ^3.1.1 + version: 3.1.1(@babel/core@7.24.3)(preact@10.19.7) '@astrojs/sitemap': - specifier: 3.0.0 - version: 3.0.0 + specifier: 3.1.2 + version: 3.1.2 '@babel/core': - specifier: ^7.18.10 - version: 7.18.10 + specifier: ^7.24.3 + version: 7.24.3 '@docsearch/css': - specifier: ^3.5.1 - version: 3.5.1 + specifier: ^3.6.0 + version: 3.6.0 '@docsearch/react': - specifier: ^3.5.1 - version: 3.5.1(@algolia/client-search@4.20.0)(search-insights@2.8.2) + specifier: ^3.6.0 + version: 3.6.0(@algolia/client-search@4.23.2)(search-insights@2.13.0) '@types/canvas-confetti': - specifier: ^1.6.0 - version: 1.6.0 + specifier: ^1.6.4 + version: 1.6.4 '@types/hast': - specifier: ^2.3.4 - version: 2.3.4 + specifier: ^3.0.4 + version: 3.0.4 '@types/html-escaper': - specifier: ^3.0.0 - version: 3.0.0 + specifier: ^3.0.2 + version: 3.0.2 '@types/mdast': - specifier: ^3.0.10 - version: 3.0.10 + specifier: ^4.0.3 + version: 4.0.3 '@types/node': - specifier: ^18.6.4 - version: 18.6.4 + specifier: 20.11.30 + version: 20.11.30 '@typescript-eslint/eslint-plugin': - specifier: ^5.46.1 - version: 5.46.1(@typescript-eslint/parser@5.46.1)(eslint@8.29.0)(typescript@5.0.2) + specifier: ^7.3.1 + version: 7.4.0(@typescript-eslint/parser@7.4.0)(eslint@8.57.0)(typescript@5.4.2) '@typescript-eslint/parser': - specifier: ^5.46.1 - version: 5.46.1(eslint@8.29.0)(typescript@5.0.2) + specifier: ^7.3.1 + version: 7.4.0(eslint@8.57.0)(typescript@5.4.2) algoliasearch: - specifier: ^4.20.0 - version: 4.20.0 + specifier: ^4.22.1 + version: 4.22.1 astro: - specifier: ^4.0.4 - version: 4.0.4(@types/node@18.6.4)(sass@1.54.3)(typescript@5.0.2) + specifier: ^4.5.7 + version: 4.5.7(@types/node@20.11.30)(sass@1.72.0)(typescript@5.4.2) astro-auto-import: - specifier: ^0.3.1 - version: 0.3.1(astro@4.0.4) + specifier: ^0.4.2 + version: 0.4.2(astro@4.5.7) astro-eslint-parser: - specifier: ^0.9.2 - version: 0.9.2 + specifier: ^0.16.3 + version: 0.16.3 astro-expressive-code: - specifier: ^0.20.0 - version: 0.20.0(astro@4.0.4) + specifier: ^0.33.5 + version: 0.33.5(astro@4.5.7) astro-og-canvas: - specifier: ^0.3.0 - version: 0.3.0(astro@4.0.4) + specifier: ^0.4.2 + version: 0.4.2(astro@4.5.7) bcp-47-normalize: - specifier: ^2.1.0 - version: 2.1.0 + specifier: ^2.3.0 + version: 2.3.0 canvaskit-wasm: - specifier: ^0.37.0 - version: 0.37.0 + specifier: ^0.39.1 + version: 0.39.1 dedent-js: specifier: ^1.0.1 version: 1.0.1 domhandler: - specifier: ^4.3.1 - version: 4.3.1 + specifier: ^5.0.3 + version: 5.0.3 eslint: - specifier: ^8.29.0 - version: 8.29.0 + specifier: ^8.57.0 + version: 8.57.0 eslint-plugin-astro: - specifier: ^0.21.0 - version: 0.21.0(eslint@8.29.0) + specifier: ^0.33.0 + version: 0.33.1(eslint@8.57.0) eslint-plugin-react: - specifier: ^7.32.1 - version: 7.32.1(eslint@8.29.0) + specifier: ^7.34.1 + version: 7.34.1(eslint@8.57.0) fast-glob: - specifier: ^3.2.11 - version: 3.2.11 + specifier: ^3.3.2 + version: 3.3.2 gray-matter: specifier: ^4.0.3 version: 4.0.3 hast-util-from-html: - specifier: ^1.0.0 - version: 1.0.0 + specifier: ^2.0.1 + version: 2.0.1 hast-util-to-html: - specifier: ^8.0.4 - version: 8.0.4 + specifier: ^9.0.0 + version: 9.0.0 hast-util-to-string: - specifier: ^2.0.0 - version: 2.0.0 + specifier: ^3.0.0 + version: 3.0.0 hastscript: - specifier: ^7.0.2 - version: 7.0.2 + specifier: ^9.0.0 + version: 9.0.0 html-escaper: specifier: ^3.0.3 version: 3.0.3 htmlparser2: - specifier: ^7.2.0 - version: 7.2.0 + specifier: ^9.1.0 + version: 9.1.0 husky: - specifier: ^8.0.3 - version: 8.0.3 + specifier: ^9.0.11 + version: 9.0.11 kleur: specifier: ^4.1.5 version: 4.1.5 lint-staged: - specifier: ^13.1.0 - version: 13.3.0 + specifier: ^15.2.2 + version: 15.2.2 mdast-util-from-markdown: - specifier: ^1.2.0 - version: 1.2.0 + specifier: ^2.0.0 + version: 2.0.0 mdast-util-mdx-jsx: - specifier: ^2.1.2 - version: 2.1.2 + specifier: ^3.1.2 + version: 3.1.2 mdast-util-to-hast: - specifier: ^12.2.4 - version: 12.2.4 + specifier: ^13.1.0 + version: 13.1.0 mdast-util-to-string: - specifier: ^3.1.1 - version: 3.1.1 + specifier: ^4.0.0 + version: 4.0.0 micromark-util-character: - specifier: ^1.1.0 - version: 1.1.0 + specifier: ^2.1.0 + version: 2.1.0 micromark-util-symbol: - specifier: ^1.0.1 - version: 1.0.1 + specifier: ^2.0.0 + version: 2.0.0 node-fetch: - specifier: ^3.2.10 - version: 3.2.10 + specifier: ^3.3.2 + version: 3.3.2 organize-imports-cli: specifier: ^0.10.0 version: 0.10.0 p-retry: - specifier: ^5.1.1 - version: 5.1.1 + specifier: ^6.2.0 + version: 6.2.0 parse-numeric-range: specifier: ^1.3.0 version: 1.3.0 preact: - specifier: ^10.16.0 - version: 10.16.0 + specifier: ^10.19.7 + version: 10.19.7 prettier: - specifier: ^3.0.2 - version: 3.0.2 + specifier: ^3.2.5 + version: 3.2.5 prettier-plugin-astro: - specifier: ^0.11.1 - version: 0.11.1 + specifier: ^0.13.0 + version: 0.13.0 prompts: specifier: ^2.4.2 version: 2.4.2 rehype: - specifier: ^12.0.1 - version: 12.0.1 + specifier: ^13.0.1 + version: 13.0.1 remark: - specifier: ^14.0.2 - version: 14.0.2 + specifier: ^15.0.1 + version: 15.0.1 remark-directive: - specifier: ^2.0.1 - version: 2.0.1 + specifier: ^3.0.0 + version: 3.0.0 remove-markdown: specifier: ^0.5.0 version: 0.5.0 simple-git: - specifier: ^3.11.0 - version: 3.11.0 + specifier: ^3.23.0 + version: 3.23.0 tsm: specifier: ^2.3.0 version: 2.3.0 typescript: - specifier: ^5.0.2 - version: 5.0.2 + specifier: ^5.4.2 + version: 5.4.2 unified: - specifier: ^10.1.2 - version: 10.1.2 + specifier: ^11.0.4 + version: 11.0.4 unist-util-remove: - specifier: ^3.1.0 - version: 3.1.0 + specifier: ^4.0.0 + version: 4.0.0 unist-util-visit: - specifier: ^4.1.0 - version: 4.1.0 + specifier: ^5.0.0 + version: 5.0.0 unist-util-walker: specifier: ^1.0.0 version: 1.0.0 vfile: - specifier: ^5.3.6 - version: 5.3.6 + specifier: ^6.0.1 + version: 6.0.1 vitest: - specifier: ^0.28.5 - version: 0.28.5(sass@1.54.3) + specifier: ^1.4.0 + version: 1.4.0(@types/node@20.11.30)(sass@1.72.0) packages: - /@11ty/eleventy-fetch@3.0.0: - resolution: {integrity: sha512-qJvfb331rYQAmlCS71Ygg0/XHUdB4/qXBOLsG0DJ1m61WL5JNha52OtKVeQq34u2J2Nfzim+X4TIL/+QyesB7Q==} - engines: {node: '>=12'} + /@11ty/eleventy-fetch@4.0.1: + resolution: {integrity: sha512-yIiLM5ziBmg86i4TlXpBdcIygJHvh/GgPJyAiFOckO9H4y9cQDM8eIcJCUQ4Mum0NEVui/OjhEut2R08xw0vlQ==} + engines: {node: '>=14'} dependencies: debug: 4.3.4 - flat-cache: 3.0.4 - node-fetch: 2.6.7 + flat-cache: 3.2.0 + node-fetch: 2.7.0 p-queue: 6.6.2 transitivePeerDependencies: - encoding - supports-color dev: true - /@actions/core@1.9.0: - resolution: {integrity: sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA==} + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@actions/core@1.10.1: + resolution: {integrity: sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==} dependencies: - '@actions/http-client': 2.0.1 + '@actions/http-client': 2.2.1 + uuid: 8.3.2 dev: true - /@actions/http-client@2.0.1: - resolution: {integrity: sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==} + /@actions/http-client@2.2.1: + resolution: {integrity: sha512-KhC/cZsq7f8I4LfZSJKgCvEwfkE8o1538VoBeoGzokVLLnbFDEAdFD3UhoMklxo2un9NJVBdANOresx7vTHlHw==} dependencies: tunnel: 0.0.6 + undici: 5.28.4 dev: true /@akebifiky/remark-simple-plantuml@1.0.2: @@ -279,194 +286,212 @@ packages: unist-util-visit: 2.0.3 dev: false - /@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0)(search-insights@2.8.2): + /@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.22.1)(search-insights@2.13.0): resolution: {integrity: sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==} dependencies: - '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0)(search-insights@2.8.2) - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0) + '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.22.1)(search-insights@2.13.0) + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.22.1) transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - search-insights dev: true - /@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0)(search-insights@2.8.2): + /@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.22.1)(search-insights@2.13.0): resolution: {integrity: sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==} peerDependencies: search-insights: '>= 1 < 3' dependencies: - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0) - search-insights: 2.8.2 + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.22.1) + search-insights: 2.13.0 transitivePeerDependencies: - '@algolia/client-search' - algoliasearch dev: true - /@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0): + /@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.22.1): resolution: {integrity: sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==} peerDependencies: '@algolia/client-search': '>= 4.9.1 < 6' algoliasearch: '>= 4.9.1 < 6' dependencies: - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0) - '@algolia/client-search': 4.20.0 - algoliasearch: 4.20.0 + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.22.1) + '@algolia/client-search': 4.23.2 + algoliasearch: 4.22.1 dev: true - /@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0): + /@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.22.1): resolution: {integrity: sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==} peerDependencies: '@algolia/client-search': '>= 4.9.1 < 6' algoliasearch: '>= 4.9.1 < 6' dependencies: - '@algolia/client-search': 4.20.0 - algoliasearch: 4.20.0 + '@algolia/client-search': 4.23.2 + algoliasearch: 4.22.1 dev: true - /@algolia/cache-browser-local-storage@4.20.0: - resolution: {integrity: sha512-uujahcBt4DxduBTvYdwO3sBfHuJvJokiC3BP1+O70fglmE1ShkH8lpXqZBac1rrU3FnNYSUs4pL9lBdTKeRPOQ==} + /@algolia/cache-browser-local-storage@4.22.1: + resolution: {integrity: sha512-Sw6IAmOCvvP6QNgY9j+Hv09mvkvEIDKjYW8ow0UDDAxSXy664RBNQk3i/0nt7gvceOJ6jGmOTimaZoY1THmU7g==} dependencies: - '@algolia/cache-common': 4.20.0 + '@algolia/cache-common': 4.22.1 + dev: true + + /@algolia/cache-common@4.22.1: + resolution: {integrity: sha512-TJMBKqZNKYB9TptRRjSUtevJeQVXRmg6rk9qgFKWvOy8jhCPdyNZV1nB3SKGufzvTVbomAukFR8guu/8NRKBTA==} dev: true - /@algolia/cache-common@4.20.0: - resolution: {integrity: sha512-vCfxauaZutL3NImzB2G9LjLt36vKAckc6DhMp05An14kVo8F1Yofb6SIl6U3SaEz8pG2QOB9ptwM5c+zGevwIQ==} + /@algolia/cache-common@4.23.2: + resolution: {integrity: sha512-OUK/6mqr6CQWxzl/QY0/mwhlGvS6fMtvEPyn/7AHUx96NjqDA4X4+Ju7aXFQKh+m3jW9VPB0B9xvEQgyAnRPNw==} dev: true - /@algolia/cache-in-memory@4.20.0: - resolution: {integrity: sha512-Wm9ak/IaacAZXS4mB3+qF/KCoVSBV6aLgIGFEtQtJwjv64g4ePMapORGmCyulCFwfePaRAtcaTbMcJF+voc/bg==} + /@algolia/cache-in-memory@4.22.1: + resolution: {integrity: sha512-ve+6Ac2LhwpufuWavM/aHjLoNz/Z/sYSgNIXsinGofWOysPilQZPUetqLj8vbvi+DHZZaYSEP9H5SRVXnpsNNw==} dependencies: - '@algolia/cache-common': 4.20.0 + '@algolia/cache-common': 4.22.1 dev: true - /@algolia/client-account@4.20.0: - resolution: {integrity: sha512-GGToLQvrwo7am4zVkZTnKa72pheQeez/16sURDWm7Seyz+HUxKi3BM6fthVVPUEBhtJ0reyVtuK9ArmnaKl10Q==} + /@algolia/client-account@4.22.1: + resolution: {integrity: sha512-k8m+oegM2zlns/TwZyi4YgCtyToackkOpE+xCaKCYfBfDtdGOaVZCM5YvGPtK+HGaJMIN/DoTL8asbM3NzHonw==} dependencies: - '@algolia/client-common': 4.20.0 - '@algolia/client-search': 4.20.0 - '@algolia/transporter': 4.20.0 + '@algolia/client-common': 4.22.1 + '@algolia/client-search': 4.22.1 + '@algolia/transporter': 4.22.1 dev: true - /@algolia/client-analytics@4.20.0: - resolution: {integrity: sha512-EIr+PdFMOallRdBTHHdKI3CstslgLORQG7844Mq84ib5oVFRVASuuPmG4bXBgiDbcsMLUeOC6zRVJhv1KWI0ug==} + /@algolia/client-analytics@4.22.1: + resolution: {integrity: sha512-1ssi9pyxyQNN4a7Ji9R50nSdISIumMFDwKNuwZipB6TkauJ8J7ha/uO60sPJFqQyqvvI+px7RSNRQT3Zrvzieg==} dependencies: - '@algolia/client-common': 4.20.0 - '@algolia/client-search': 4.20.0 - '@algolia/requester-common': 4.20.0 - '@algolia/transporter': 4.20.0 + '@algolia/client-common': 4.22.1 + '@algolia/client-search': 4.22.1 + '@algolia/requester-common': 4.22.1 + '@algolia/transporter': 4.22.1 dev: true - /@algolia/client-common@4.20.0: - resolution: {integrity: sha512-P3WgMdEss915p+knMMSd/fwiHRHKvDu4DYRrCRaBrsfFw7EQHon+EbRSm4QisS9NYdxbS04kcvNoavVGthyfqQ==} + /@algolia/client-common@4.22.1: + resolution: {integrity: sha512-IvaL5v9mZtm4k4QHbBGDmU3wa/mKokmqNBqPj0K7lcR8ZDKzUorhcGp/u8PkPC/e0zoHSTvRh7TRkGX3Lm7iOQ==} dependencies: - '@algolia/requester-common': 4.20.0 - '@algolia/transporter': 4.20.0 + '@algolia/requester-common': 4.22.1 + '@algolia/transporter': 4.22.1 dev: true - /@algolia/client-personalization@4.20.0: - resolution: {integrity: sha512-N9+zx0tWOQsLc3K4PVRDV8GUeOLAY0i445En79Pr3zWB+m67V+n/8w4Kw1C5LlbHDDJcyhMMIlqezh6BEk7xAQ==} + /@algolia/client-common@4.23.2: + resolution: {integrity: sha512-Q2K1FRJBern8kIfZ0EqPvUr3V29ICxCm/q42zInV+VJRjldAD9oTsMGwqUQ26GFMdFYmqkEfCbY4VGAiQhh22g==} dependencies: - '@algolia/client-common': 4.20.0 - '@algolia/requester-common': 4.20.0 - '@algolia/transporter': 4.20.0 + '@algolia/requester-common': 4.23.2 + '@algolia/transporter': 4.23.2 dev: true - /@algolia/client-search@4.20.0: - resolution: {integrity: sha512-zgwqnMvhWLdpzKTpd3sGmMlr4c+iS7eyyLGiaO51zDZWGMkpgoNVmltkzdBwxOVXz0RsFMznIxB9zuarUv4TZg==} + /@algolia/client-personalization@4.22.1: + resolution: {integrity: sha512-sl+/klQJ93+4yaqZ7ezOttMQ/nczly/3GmgZXJ1xmoewP5jmdP/X/nV5U7EHHH3hCUEHeN7X1nsIhGPVt9E1cQ==} dependencies: - '@algolia/client-common': 4.20.0 - '@algolia/requester-common': 4.20.0 - '@algolia/transporter': 4.20.0 + '@algolia/client-common': 4.22.1 + '@algolia/requester-common': 4.22.1 + '@algolia/transporter': 4.22.1 dev: true - /@algolia/logger-common@4.20.0: - resolution: {integrity: sha512-xouigCMB5WJYEwvoWW5XDv7Z9f0A8VoXJc3VKwlHJw/je+3p2RcDXfksLI4G4lIVncFUYMZx30tP/rsdlvvzHQ==} + /@algolia/client-search@4.22.1: + resolution: {integrity: sha512-yb05NA4tNaOgx3+rOxAmFztgMTtGBi97X7PC3jyNeGiwkAjOZc2QrdZBYyIdcDLoI09N0gjtpClcackoTN0gPA==} + dependencies: + '@algolia/client-common': 4.22.1 + '@algolia/requester-common': 4.22.1 + '@algolia/transporter': 4.22.1 dev: true - /@algolia/logger-console@4.20.0: - resolution: {integrity: sha512-THlIGG1g/FS63z0StQqDhT6bprUczBI8wnLT3JWvfAQDZX5P6fCg7dG+pIrUBpDIHGszgkqYEqECaKKsdNKOUA==} + /@algolia/client-search@4.23.2: + resolution: {integrity: sha512-CxSB29OVGSE7l/iyoHvamMonzq7Ev8lnk/OkzleODZ1iBcCs3JC/XgTIKzN/4RSTrJ9QybsnlrN/bYCGufo7qw==} dependencies: - '@algolia/logger-common': 4.20.0 + '@algolia/client-common': 4.23.2 + '@algolia/requester-common': 4.23.2 + '@algolia/transporter': 4.23.2 + dev: true + + /@algolia/logger-common@4.22.1: + resolution: {integrity: sha512-OnTFymd2odHSO39r4DSWRFETkBufnY2iGUZNrMXpIhF5cmFE8pGoINNPzwg02QLBlGSaLqdKy0bM8S0GyqPLBg==} + dev: true + + /@algolia/logger-common@4.23.2: + resolution: {integrity: sha512-jGM49Q7626cXZ7qRAWXn0jDlzvoA1FvN4rKTi1g0hxKsTTSReyYk0i1ADWjChDPl3Q+nSDhJuosM2bBUAay7xw==} dev: true - /@algolia/requester-browser-xhr@4.20.0: - resolution: {integrity: sha512-HbzoSjcjuUmYOkcHECkVTwAelmvTlgs48N6Owt4FnTOQdwn0b8pdht9eMgishvk8+F8bal354nhx/xOoTfwiAw==} + /@algolia/logger-console@4.22.1: + resolution: {integrity: sha512-O99rcqpVPKN1RlpgD6H3khUWylU24OXlzkavUAMy6QZd1776QAcauE3oP8CmD43nbaTjBexZj2nGsBH9Tc0FVA==} dependencies: - '@algolia/requester-common': 4.20.0 + '@algolia/logger-common': 4.22.1 dev: true - /@algolia/requester-common@4.20.0: - resolution: {integrity: sha512-9h6ye6RY/BkfmeJp7Z8gyyeMrmmWsMOCRBXQDs4mZKKsyVlfIVICpcSibbeYcuUdurLhIlrOUkH3rQEgZzonng==} + /@algolia/requester-browser-xhr@4.22.1: + resolution: {integrity: sha512-dtQGYIg6MteqT1Uay3J/0NDqD+UciHy3QgRbk7bNddOJu+p3hzjTRYESqEnoX/DpEkaNYdRHUKNylsqMpgwaEw==} + dependencies: + '@algolia/requester-common': 4.22.1 + dev: true + + /@algolia/requester-common@4.22.1: + resolution: {integrity: sha512-dgvhSAtg2MJnR+BxrIFqlLtkLlVVhas9HgYKMk2Uxiy5m6/8HZBL40JVAMb2LovoPFs9I/EWIoFVjOrFwzn5Qg==} dev: true - /@algolia/requester-node-http@4.20.0: - resolution: {integrity: sha512-ocJ66L60ABSSTRFnCHIEZpNHv6qTxsBwJEPfYaSBsLQodm0F9ptvalFkHMpvj5DfE22oZrcrLbOYM2bdPJRHng==} + /@algolia/requester-common@4.23.2: + resolution: {integrity: sha512-3EfpBS0Hri0lGDB5H/BocLt7Vkop0bTTLVUBB844HH6tVycwShmsV6bDR7yXbQvFP1uNpgePRD3cdBCjeHmk6Q==} + dev: true + + /@algolia/requester-node-http@4.22.1: + resolution: {integrity: sha512-JfmZ3MVFQkAU+zug8H3s8rZ6h0ahHZL/SpMaSasTCGYR5EEJsCc8SI5UZ6raPN2tjxa5bxS13BRpGSBUens7EA==} dependencies: - '@algolia/requester-common': 4.20.0 + '@algolia/requester-common': 4.22.1 dev: true - /@algolia/transporter@4.20.0: - resolution: {integrity: sha512-Lsii1pGWOAISbzeyuf+r/GPhvHMPHSPrTDWNcIzOE1SG1inlJHICaVe2ikuoRjcpgxZNU54Jl+if15SUCsaTUg==} + /@algolia/transporter@4.22.1: + resolution: {integrity: sha512-kzWgc2c9IdxMa3YqA6TN0NW5VrKYYW/BELIn7vnLyn+U/RFdZ4lxxt9/8yq3DKV5snvoDzzO4ClyejZRdV3lMQ==} dependencies: - '@algolia/cache-common': 4.20.0 - '@algolia/logger-common': 4.20.0 - '@algolia/requester-common': 4.20.0 + '@algolia/cache-common': 4.22.1 + '@algolia/logger-common': 4.22.1 + '@algolia/requester-common': 4.22.1 dev: true - /@ampproject/remapping@2.2.0: - resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} - engines: {node: '>=6.0.0'} + /@algolia/transporter@4.23.2: + resolution: {integrity: sha512-GY3aGKBy+8AK4vZh8sfkatDciDVKad5rTY2S10Aefyjh7e7UGBP4zigf42qVXwU8VOPwi7l/L7OACGMOFcjB0Q==} dependencies: - '@jridgewell/gen-mapping': 0.1.1 - '@jridgewell/trace-mapping': 0.3.14 + '@algolia/cache-common': 4.23.2 + '@algolia/logger-common': 4.23.2 + '@algolia/requester-common': 4.23.2 dev: true - /@ampproject/remapping@2.2.1: - resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + /@ampproject/remapping@2.3.0: + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 dev: true - /@astrojs/check@0.2.0(prettier-plugin-astro@0.11.1)(prettier@3.0.2)(typescript@5.0.2): - resolution: {integrity: sha512-QS8TBRNrxBNEKm9hsP4xI+ao2XJ5JZfMZ1GpHqM7HCGPwWe5oWSPtDXGM8zutZ6kH3ilK0k1euqkzxrhcTP1Cg==} + /@astrojs/check@0.5.9(prettier-plugin-astro@0.13.0)(prettier@3.2.5)(typescript@5.4.2): + resolution: {integrity: sha512-+QsQMtYq4oso+gmilJC9HLmdi0glZ+04V/VyyTTPry7n21jqjX9SfgDpLGxMk5cwPC/vwZMkn6ORGPnkZS/L5w==} hasBin: true peerDependencies: typescript: ^5.0.0 dependencies: - '@astrojs/language-server': 2.3.3(prettier-plugin-astro@0.11.1)(prettier@3.0.2)(typescript@5.0.2) - chokidar: 3.5.3 - fast-glob: 3.3.1 + '@astrojs/language-server': 2.8.3(prettier-plugin-astro@0.13.0)(prettier@3.2.5)(typescript@5.4.2) + chokidar: 3.6.0 + fast-glob: 3.3.2 kleur: 4.1.5 - typescript: 5.0.2 + typescript: 5.4.2 yargs: 17.7.2 transitivePeerDependencies: - prettier - prettier-plugin-astro dev: false - /@astrojs/compiler@0.31.0: - resolution: {integrity: sha512-V8/Re/wXgXTZzpfWs4KZBLU5dRhnO6kSd4e3vObGuj+HFGHjaD11wws1zvaC9cXLQyQsM5CSrGagFGYlRZKvVQ==} - dev: true - - /@astrojs/compiler@1.5.5: - resolution: {integrity: sha512-8LEj9nUr5+92ms8/ZUyajcLYqurhXHTNMERctsHZ1JyRrnjseheUHCM+MC34bMP0uOou2JXJFWVJudDTTe4S/A==} - - /@astrojs/compiler@1.5.7: - resolution: {integrity: sha512-dFU7GAMbpTUGPkRoCoMQrGFlTe3qIiQMSOxIXp/nB1Do4My9uogjEmBHdR5Cwr4i6rc5/1R3Od9v8kU/pkHXGQ==} - dev: false + /@astrojs/compiler@1.8.2: + resolution: {integrity: sha512-o/ObKgtMzl8SlpIdzaxFnt7SATKPxu4oIP/1NL+HDJRzxfJcAkOTAb/ZKMRyULbz4q+1t2/DAebs2Z1QairkZw==} - /@astrojs/compiler@2.3.2: - resolution: {integrity: sha512-jkY7bCVxl27KeZsSxIZ+pqACe+g8VQUdTiSJRj/sXYdIaZlW3ZMq4qF2M17P/oDt3LBq0zLNwQr4Cb7fSpRGxQ==} - dev: true + /@astrojs/compiler@2.7.0: + resolution: {integrity: sha512-XpC8MAaWjD1ff6/IfkRq/5k1EFj6zhCNqXRd5J43SVJEBj/Bsmizkm8N0xOYscGcDFQkRgEw6/eKnI5x/1l6aA==} - /@astrojs/internal-helpers@0.2.1: - resolution: {integrity: sha512-06DD2ZnItMwUnH81LBLco3tWjcZ1lGU9rLCCBaeUCGYe9cI0wKyY2W3kDyoW1I6GmcWgt1fu+D1CTvz+FIKf8A==} + /@astrojs/internal-helpers@0.3.0: + resolution: {integrity: sha512-tGmHvrhpzuz0JBHaJX8GywN9g4rldVNHtkoVDC3m/DdzBO70jGoVuc0uuNVglRYnsdwkbG0K02Iw3nOOR3/Y4g==} dev: true - /@astrojs/language-server@2.3.3(prettier-plugin-astro@0.11.1)(prettier@3.0.2)(typescript@5.0.2): - resolution: {integrity: sha512-ObIjAdjKJFHsKCmaFHc6tbpomMkX1580UbxbgnCr6zEpIZuQobH/zlKwIaSDcmQOQtt8ICs921AYTM+kOg8p6w==} + /@astrojs/language-server@2.8.3(prettier-plugin-astro@0.13.0)(prettier@3.2.5)(typescript@5.4.2): + resolution: {integrity: sha512-tO47Lcue7OPXfIDbKVDcshwpC13yaWaTVLyiSOnQ2Yng2Z2SgcJf06Cj4xMpJqGp6s7/o/gcQWYUTl2bpkWKig==} hasBin: true peerDependencies: prettier: ^3.0.0 @@ -477,116 +502,118 @@ packages: prettier-plugin-astro: optional: true dependencies: - '@astrojs/compiler': 1.5.7 + '@astrojs/compiler': 2.7.0 '@jridgewell/sourcemap-codec': 1.4.15 - '@volar/kit': 1.10.1(typescript@5.0.2) - '@volar/language-core': 1.10.1 - '@volar/language-server': 1.10.1 - '@volar/language-service': 1.10.1 - '@volar/source-map': 1.10.1 - '@volar/typescript': 1.10.1 - fast-glob: 3.3.1 - muggle-string: 0.3.1 - prettier: 3.0.2 - prettier-plugin-astro: 0.11.1 - volar-service-css: 0.0.13(@volar/language-service@1.10.1) - volar-service-emmet: 0.0.13(@volar/language-service@1.10.1) - volar-service-html: 0.0.13(@volar/language-service@1.10.1) - volar-service-prettier: 0.0.13(@volar/language-service@1.10.1)(prettier@3.0.2) - volar-service-typescript: 0.0.13(@volar/language-service@1.10.1)(@volar/typescript@1.10.1) - volar-service-typescript-twoslash-queries: 0.0.13(@volar/language-service@1.10.1) - vscode-html-languageservice: 5.0.7 - vscode-uri: 3.0.7 + '@volar/kit': 2.1.2(typescript@5.4.2) + '@volar/language-core': 2.1.2 + '@volar/language-server': 2.1.2 + '@volar/language-service': 2.1.2 + '@volar/typescript': 2.1.2 + fast-glob: 3.3.2 + prettier: 3.2.5 + prettier-plugin-astro: 0.13.0 + volar-service-css: 0.0.34(@volar/language-service@2.1.2) + volar-service-emmet: 0.0.34(@volar/language-service@2.1.2) + volar-service-html: 0.0.34(@volar/language-service@2.1.2) + volar-service-prettier: 0.0.34(@volar/language-service@2.1.2)(prettier@3.2.5) + volar-service-typescript: 0.0.34(@volar/language-service@2.1.2) + volar-service-typescript-twoslash-queries: 0.0.34(@volar/language-service@2.1.2) + vscode-html-languageservice: 5.1.2 + vscode-uri: 3.0.8 transitivePeerDependencies: - typescript dev: false - /@astrojs/markdown-remark@3.0.0(astro@4.0.4): - resolution: {integrity: sha512-s8I49Je4++ImgYAgwL32HgN8m6we2qz3RtBpN4AjObMODPwDylmzUHZksD8Toy31q/P59ED3MuwphqOGm9l03w==} - peerDependencies: - astro: ^3.0.0 + /@astrojs/markdown-remark@4.3.0: + resolution: {integrity: sha512-iZOgYj/yNDvBRfKqkGuAvjeONhjQPq8Uk3HjyIgcTK5valq03NiUgSc5Ovq00yUVBeYJ/5EDx23c8xqtkkBlPw==} dependencies: '@astrojs/prism': 3.0.0 - astro: 4.0.4(@types/node@18.6.4)(sass@1.54.3)(typescript@5.0.2) github-slugger: 2.0.0 - import-meta-resolve: 3.0.0 - rehype-raw: 6.1.1 - rehype-stringify: 9.0.4 - remark-gfm: 3.0.1 - remark-parse: 10.0.2 - remark-rehype: 10.1.0 - remark-smartypants: 2.0.0 - shiki: 0.14.3 - unified: 10.1.2 - unist-util-visit: 4.1.2 - vfile: 5.3.7 + hast-util-from-html: 2.0.1 + hast-util-to-text: 4.0.0 + import-meta-resolve: 4.0.0 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.0 + remark-gfm: 4.0.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.0 + remark-smartypants: 2.1.0 + shiki: 1.2.0 + unified: 11.0.4 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + vfile: 6.0.1 transitivePeerDependencies: - supports-color dev: true - /@astrojs/markdown-remark@4.0.1: - resolution: {integrity: sha512-RU4ESnqvyLpj8WZs0n5elS6idaDdtIIm7mIpMaRNPCebpxMjfcfdwcmBwz83ktAj5d2eO5bC3z92TcGdli+lRw==} + /@astrojs/markdown-remark@4.3.2: + resolution: {integrity: sha512-4Oa4VaYiBd0MatB+rWIU/0A8pZH/sK3c2QkRYb+OO2lPl+qzevJtWaZY8hAQc4qurIOlRdn6B6ofDAGhWw+DSg==} dependencies: '@astrojs/prism': 3.0.0 github-slugger: 2.0.0 + hast-util-from-html: 2.0.1 + hast-util-to-text: 4.0.0 import-meta-resolve: 4.0.0 mdast-util-definitions: 6.0.0 rehype-raw: 7.0.0 rehype-stringify: 10.0.0 remark-gfm: 4.0.0 remark-parse: 11.0.0 - remark-rehype: 11.0.0 - remark-smartypants: 2.0.0 - shikiji: 0.6.13 + remark-rehype: 11.1.0 + remark-smartypants: 2.1.0 + shiki: 1.2.1 unified: 11.0.4 + unist-util-remove-position: 5.0.0 unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 vfile: 6.0.1 transitivePeerDependencies: - supports-color dev: true - /@astrojs/mdx@1.0.0(astro@4.0.4): - resolution: {integrity: sha512-Gmeleci8o4X7dST9E85c1+k273zcKW8cSFgZLTwU5K4dC0qHfY/EaDKHWrtzOB2wjZlT1JDRzTJ68LJYGrF2OA==} + /@astrojs/mdx@2.2.2(astro@4.5.7): + resolution: {integrity: sha512-5SIFtOctC813HFyqJwBf5TBvlT9sbiOOv+bdvzAoiBSab95dC7PZhss22EvUEx+897c81YoIZ4F9fg4ZkxBFIw==} engines: {node: '>=18.14.1'} peerDependencies: - astro: ^3.0.0 - dependencies: - '@astrojs/markdown-remark': 3.0.0(astro@4.0.4) - '@astrojs/prism': 3.0.0 - '@mdx-js/mdx': 2.3.0 - acorn: 8.10.0 - astro: 4.0.4(@types/node@18.6.4)(sass@1.54.3)(typescript@5.0.2) - es-module-lexer: 1.3.0 - estree-util-visit: 1.2.1 + astro: ^4.0.0 + dependencies: + '@astrojs/markdown-remark': 4.3.2 + '@mdx-js/mdx': 3.0.1 + acorn: 8.11.3 + astro: 4.5.7(@types/node@20.11.30)(sass@1.72.0)(typescript@5.4.2) + es-module-lexer: 1.5.0 + estree-util-visit: 2.0.0 github-slugger: 2.0.0 gray-matter: 4.0.3 - hast-util-to-html: 8.0.4 + hast-util-to-html: 9.0.0 kleur: 4.1.5 - rehype-raw: 6.1.1 - remark-frontmatter: 4.0.1 - remark-gfm: 3.0.1 - remark-smartypants: 2.0.0 - shiki: 0.14.3 + rehype-raw: 7.0.0 + remark-gfm: 4.0.0 + remark-smartypants: 2.1.0 source-map: 0.7.4 - unist-util-visit: 4.1.2 - vfile: 5.3.7 + unist-util-visit: 5.0.0 + vfile: 6.0.1 transitivePeerDependencies: - supports-color dev: true - /@astrojs/preact@3.0.0(@babel/core@7.18.10)(preact@10.16.0): - resolution: {integrity: sha512-KlHyozAyQIOorYb3op8MJlktvtKiK/an1nXqHsVYxW5oiOJZjD4fkTEgcDFByl/uRpce93tuK4pdGKKzzyMUVw==} + /@astrojs/preact@3.1.1(@babel/core@7.24.3)(preact@10.19.7): + resolution: {integrity: sha512-ASgmVzh4wLyIyynp5CIfDwE45Vg/tIP+Y+5SnQtURmCP1qZpjdUbsw+bGQ0wCSXtjIbzCBa7Kw7Qn0g6WE2W2w==} engines: {node: '>=18.14.1'} peerDependencies: preact: ^10.6.5 dependencies: - '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.18.10) - '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.18.10) - '@preact/preset-vite': 2.5.0(@babel/core@7.18.10)(preact@10.16.0) - '@preact/signals': 1.2.1(preact@10.16.0) - babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.18.10) - preact: 10.16.0 - preact-render-to-string: 6.2.1(preact@10.16.0) + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.3) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.24.3) + '@preact/preset-vite': 2.8.2(@babel/core@7.24.3)(preact@10.19.7) + '@preact/signals': 1.2.2(preact@10.19.7) + babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.24.3) + preact: 10.19.7 + preact-render-to-string: 6.3.1(preact@10.19.7) + preact-ssr-prepass: 1.2.1(preact@10.19.7) transitivePeerDependencies: - '@babel/core' - supports-color @@ -600,126 +627,63 @@ packages: prismjs: 1.29.0 dev: true - /@astrojs/sitemap@3.0.0: - resolution: {integrity: sha512-qm7npHuUW4q3OOmulqhJ1g69jEQu0Sdc6P8NbOzqIoosj/L+3v4i8dtKBnp6n1UQ4Sx8H8Vdi3Z/On7i9/ZJhw==} + /@astrojs/sitemap@3.1.2: + resolution: {integrity: sha512-FxOJldIl5ltZ5CNjocQxHkAO9orwHBjqtaU28o4smobp9vowS0nbGp+I9CrPxkzWdl1crSDm9vjL9tnvG1DSug==} dependencies: sitemap: 7.1.1 - zod: 3.21.1 + zod: 3.22.4 dev: true /@astrojs/telemetry@3.0.4: resolution: {integrity: sha512-A+0c7k/Xy293xx6odsYZuXiaHO0PL+bnDoXOc47sGDF5ffIKdKQGRPFl2NMlCF4L0NqN4Ynbgnaip+pPF0s7pQ==} engines: {node: '>=18.14.1'} dependencies: - ci-info: 3.8.0 + ci-info: 3.9.0 debug: 4.3.4 dlv: 1.1.3 dset: 3.1.3 is-docker: 3.0.0 - is-wsl: 3.0.0 + is-wsl: 3.1.0 which-pm-runs: 1.1.0 transitivePeerDependencies: - supports-color dev: true - /@babel/code-frame@7.18.6: - resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} + /@babel/code-frame@7.24.1: + resolution: {integrity: sha512-bC49z4spJQR3j8vFtJBLqzyzFV0ciuL5HYX7qfSl3KEqeMVV+eTquRvmXxpvB0AMubRrvv7y5DILiLLPi57Ewg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/highlight': 7.18.6 - dev: true - - /@babel/code-frame@7.22.13: - resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.22.13 - chalk: 2.4.2 - dev: true - - /@babel/code-frame@7.23.5: - resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.23.4 - chalk: 2.4.2 - dev: true - - /@babel/compat-data@7.18.8: - resolution: {integrity: sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/compat-data@7.22.9: - resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/compat-data@7.23.5: - resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} - engines: {node: '>=6.9.0'} + '@babel/highlight': 7.24.1 + picocolors: 1.0.0 dev: true - /@babel/core@7.18.10: - resolution: {integrity: sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==} + /@babel/code-frame@7.24.2: + resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} engines: {node: '>=6.9.0'} dependencies: - '@ampproject/remapping': 2.2.0 - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.18.12 - '@babel/helper-compilation-targets': 7.18.9(@babel/core@7.18.10) - '@babel/helper-module-transforms': 7.18.9 - '@babel/helpers': 7.18.9 - '@babel/parser': 7.18.11 - '@babel/template': 7.18.10 - '@babel/traverse': 7.18.11 - '@babel/types': 7.18.10 - convert-source-map: 1.8.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.1 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color + '@babel/highlight': 7.24.2 + picocolors: 1.0.0 dev: true - /@babel/core@7.22.11: - resolution: {integrity: sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==} + /@babel/compat-data@7.24.1: + resolution: {integrity: sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==} engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.22.13 - '@babel/generator': 7.22.10 - '@babel/helper-compilation-targets': 7.22.10 - '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.11) - '@babel/helpers': 7.22.11 - '@babel/parser': 7.22.14 - '@babel/template': 7.22.5 - '@babel/traverse': 7.22.11 - '@babel/types': 7.22.11 - convert-source-map: 1.9.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color dev: true - /@babel/core@7.23.6: - resolution: {integrity: sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==} + /@babel/core@7.24.3: + resolution: {integrity: sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==} engines: {node: '>=6.9.0'} dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.6 + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.2 + '@babel/generator': 7.24.1 '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.6) - '@babel/helpers': 7.23.6 - '@babel/parser': 7.23.6 - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.6 - '@babel/types': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.3) + '@babel/helpers': 7.24.1 + '@babel/parser': 7.24.1 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.1 + '@babel/types': 7.24.0 convert-source-map: 2.0.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -729,32 +693,13 @@ packages: - supports-color dev: true - /@babel/generator@7.18.12: - resolution: {integrity: sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.18.10 - '@jridgewell/gen-mapping': 0.3.2 - jsesc: 2.5.2 - dev: true - - /@babel/generator@7.22.10: - resolution: {integrity: sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.11 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 - jsesc: 2.5.2 - dev: true - - /@babel/generator@7.23.6: - resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} + /@babel/generator@7.24.1: + resolution: {integrity: sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.6 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 + '@babel/types': 7.24.0 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 dev: true @@ -762,510 +707,223 @@ packages: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.5 - dev: true - - /@babel/helper-compilation-targets@7.18.9(@babel/core@7.18.10): - resolution: {integrity: sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.18.8 - '@babel/core': 7.18.10 - '@babel/helper-validator-option': 7.18.6 - browserslist: 4.21.3 - semver: 6.3.1 - dev: true - - /@babel/helper-compilation-targets@7.22.10: - resolution: {integrity: sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/compat-data': 7.22.9 - '@babel/helper-validator-option': 7.22.5 - browserslist: 4.21.10 - lru-cache: 5.1.1 - semver: 6.3.1 + '@babel/types': 7.24.0 dev: true /@babel/helper-compilation-targets@7.23.6: resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/compat-data': 7.23.5 + '@babel/compat-data': 7.24.1 '@babel/helper-validator-option': 7.23.5 - browserslist: 4.22.2 + browserslist: 4.23.0 lru-cache: 5.1.1 semver: 6.3.1 dev: true - /@babel/helper-environment-visitor@7.18.9: - resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} - engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-environment-visitor@7.22.20: resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-environment-visitor@7.22.5: - resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-function-name@7.18.9: - resolution: {integrity: sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.18.10 - '@babel/types': 7.18.10 - dev: true - - /@babel/helper-function-name@7.22.5: - resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.5 - '@babel/types': 7.22.11 - dev: true - /@babel/helper-function-name@7.23.0: resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.22.15 - '@babel/types': 7.23.6 - dev: true - - /@babel/helper-hoist-variables@7.18.6: - resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.18.10 + '@babel/template': 7.24.0 + '@babel/types': 7.24.0 dev: true /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.11 + '@babel/types': 7.24.0 dev: true - /@babel/helper-module-imports@7.18.6: - resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} + /@babel/helper-module-imports@7.24.1: + resolution: {integrity: sha512-HfEWzysMyOa7xI5uQHc/OcZf67/jc+xe/RZlznWQHhbb8Pg1SkRdbK4yEi61aY8wxQA7PkSfoojtLQP/Kpe3og==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.10 + '@babel/types': 7.24.0 dev: true - /@babel/helper-module-imports@7.22.15: - resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + /@babel/helper-module-imports@7.24.3: + resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.6 + '@babel/types': 7.24.0 dev: true - /@babel/helper-module-imports@7.22.5: - resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.5 - dev: true - - /@babel/helper-module-transforms@7.18.9: - resolution: {integrity: sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-module-imports': 7.18.6 - '@babel/helper-simple-access': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.18.6 - '@babel/template': 7.18.10 - '@babel/traverse': 7.18.11 - '@babel/types': 7.18.10 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helper-module-transforms@7.22.9(@babel/core@7.22.11): - resolution: {integrity: sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-module-imports': 7.22.5 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.5 - dev: true - - /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.6): + /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.3): resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.6 + '@babel/core': 7.24.3 '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.22.15 + '@babel/helper-module-imports': 7.24.3 '@babel/helper-simple-access': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 '@babel/helper-validator-identifier': 7.22.20 dev: true - /@babel/helper-plugin-utils@7.22.5: - resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + /@babel/helper-plugin-utils@7.24.0: + resolution: {integrity: sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-simple-access@7.18.6: - resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.18.10 - dev: true - /@babel/helper-simple-access@7.22.5: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.11 - dev: true - - /@babel/helper-split-export-declaration@7.18.6: - resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.18.10 + '@babel/types': 7.24.0 dev: true /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.11 + '@babel/types': 7.24.0 dev: true - /@babel/helper-string-parser@7.18.10: - resolution: {integrity: sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==} - engines: {node: '>=6.9.0'} - - /@babel/helper-string-parser@7.22.5: - resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-string-parser@7.23.4: - resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-validator-identifier@7.18.6: - resolution: {integrity: sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==} + /@babel/helper-string-parser@7.24.1: + resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} engines: {node: '>=6.9.0'} /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-validator-identifier@7.22.5: - resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-validator-option@7.18.6: - resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-validator-option@7.22.5: - resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} - engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-option@7.23.5: resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} engines: {node: '>=6.9.0'} dev: true - /@babel/helpers@7.18.9: - resolution: {integrity: sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.18.10 - '@babel/traverse': 7.18.11 - '@babel/types': 7.18.10 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helpers@7.22.11: - resolution: {integrity: sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.5 - '@babel/traverse': 7.22.11 - '@babel/types': 7.22.11 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helpers@7.23.6: - resolution: {integrity: sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==} + /@babel/helpers@7.24.1: + resolution: {integrity: sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.6 - '@babel/types': 7.23.6 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.1 + '@babel/types': 7.24.0 transitivePeerDependencies: - supports-color dev: true - /@babel/highlight@7.18.6: - resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.18.6 - chalk: 2.4.2 - js-tokens: 4.0.0 - dev: true - - /@babel/highlight@7.22.13: - resolution: {integrity: sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==} + /@babel/highlight@7.24.1: + resolution: {integrity: sha512-EPmDPxidWe/Ex+HTFINpvXdPHRmgSF3T8hGvzondYjmgzTQ/0EbLpSxyt+w3zzlYSk9cNBQNF9k0dT5Z2NiBjw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 chalk: 2.4.2 js-tokens: 4.0.0 + picocolors: 1.0.0 dev: true - /@babel/highlight@7.23.4: - resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + /@babel/highlight@7.24.2: + resolution: {integrity: sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-validator-identifier': 7.22.20 chalk: 2.4.2 js-tokens: 4.0.0 + picocolors: 1.0.0 dev: true - /@babel/parser@7.18.11: - resolution: {integrity: sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.18.10 - - /@babel/parser@7.22.14: - resolution: {integrity: sha512-1KucTHgOvaw/LzCVrEOAyXkr9rQlp0A1HiHRYnSUE9dmb8PvPW7o5sscg+5169r54n3vGlbx6GevTE/Iw/P3AQ==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.22.11 - dev: true - - /@babel/parser@7.23.6: - resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==} + /@babel/parser@7.24.1: + resolution: {integrity: sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.23.6 - dev: true + '@babel/types': 7.24.0 - /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.18.10): - resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} + /@babel/plugin-syntax-jsx@7.24.1(@babel/core@7.24.3): + resolution: {integrity: sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.3 + '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.6): - resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.18.10): + /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.24.3): resolution: {integrity: sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.18.10 - '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.18.10) - dev: true - - /@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.23.6): - resolution: {integrity: sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.6 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.6) - '@babel/types': 7.23.6 + '@babel/core': 7.24.3 + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.3) dev: true - /@babel/plugin-transform-react-jsx@7.22.5(@babel/core@7.18.10): - resolution: {integrity: sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==} + /@babel/plugin-transform-react-jsx@7.23.4(@babel/core@7.24.3): + resolution: {integrity: sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.18.10 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-module-imports': 7.22.5 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.18.10) - '@babel/types': 7.22.5 - dev: true - - /@babel/template@7.18.10: - resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/parser': 7.18.11 - '@babel/types': 7.18.10 - dev: true - - /@babel/template@7.22.15: - resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.23.5 - '@babel/parser': 7.23.6 - '@babel/types': 7.23.6 - dev: true - - /@babel/template@7.22.5: - resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.22.13 - '@babel/parser': 7.22.14 - '@babel/types': 7.22.11 - dev: true - - /@babel/traverse@7.18.11: - resolution: {integrity: sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.18.12 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.18.9 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.18.11 - '@babel/types': 7.18.10 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-module-imports': 7.24.1 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.3) + '@babel/types': 7.24.0 dev: true - /@babel/traverse@7.22.11: - resolution: {integrity: sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==} + /@babel/template@7.24.0: + resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.22.13 - '@babel/generator': 7.22.10 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-function-name': 7.22.5 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.22.14 - '@babel/types': 7.22.11 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color + '@babel/code-frame': 7.24.2 + '@babel/parser': 7.24.1 + '@babel/types': 7.24.0 dev: true - /@babel/traverse@7.23.6: - resolution: {integrity: sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==} + /@babel/traverse@7.24.1: + resolution: {integrity: sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.6 + '@babel/code-frame': 7.24.1 + '@babel/generator': 7.24.1 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.23.6 - '@babel/types': 7.23.6 + '@babel/parser': 7.24.1 + '@babel/types': 7.24.0 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/types@7.18.10: - resolution: {integrity: sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.18.10 - '@babel/helper-validator-identifier': 7.18.6 - to-fast-properties: 2.0.0 - - /@babel/types@7.22.11: - resolution: {integrity: sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.22.5 - '@babel/helper-validator-identifier': 7.22.5 - to-fast-properties: 2.0.0 - dev: true - - /@babel/types@7.22.5: - resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.22.5 - '@babel/helper-validator-identifier': 7.22.5 - to-fast-properties: 2.0.0 - dev: true - - /@babel/types@7.23.6: - resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} + /@babel/types@7.24.0: + resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-string-parser': 7.23.4 + '@babel/helper-string-parser': 7.24.1 '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 - dev: true - /@ctrl/tinycolor@3.6.0: - resolution: {integrity: sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ==} + /@ctrl/tinycolor@3.6.1: + resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} engines: {node: '>=10'} dev: true - /@docsearch/css@3.5.1: - resolution: {integrity: sha512-2Pu9HDg/uP/IT10rbQ+4OrTQuxIWdKVUEdcw9/w7kZJv9NeHS6skJx1xuRiFyoGKwAzcHXnLp7csE99sj+O1YA==} + /@docsearch/css@3.6.0: + resolution: {integrity: sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==} dev: true - /@docsearch/react@3.5.1(@algolia/client-search@4.20.0)(search-insights@2.8.2): - resolution: {integrity: sha512-t5mEODdLzZq4PTFAm/dvqcvZFdPDMdfPE5rJS5SC8OUq9mPzxEy6b+9THIqNM9P0ocCb4UC5jqBrxKclnuIbzQ==} + /@docsearch/react@3.6.0(@algolia/client-search@4.23.2)(search-insights@2.13.0): + resolution: {integrity: sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==} peerDependencies: '@types/react': '>= 16.8.0 < 19.0.0' react: '>= 16.8.0 < 19.0.0' react-dom: '>= 16.8.0 < 19.0.0' + search-insights: '>= 1 < 3' peerDependenciesMeta: '@types/react': optional: true @@ -1273,14 +931,16 @@ packages: optional: true react-dom: optional: true + search-insights: + optional: true dependencies: - '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0)(search-insights@2.8.2) - '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0) - '@docsearch/css': 3.5.1 - algoliasearch: 4.20.0 + '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.22.1)(search-insights@2.13.0) + '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.22.1) + '@docsearch/css': 3.6.0 + algoliasearch: 4.22.1 + search-insights: 2.13.0 transitivePeerDependencies: - '@algolia/client-search' - - search-insights dev: true /@emmetio/abbreviation@2.3.3: @@ -1299,8 +959,26 @@ packages: resolution: {integrity: sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==} dev: false - /@esbuild/android-arm64@0.18.20: - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + /@esbuild/aix-ppc64@0.19.12: + resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/aix-ppc64@0.20.2: + resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.19.12: + resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} engines: {node: '>=12'} cpu: [arm64] os: [android] @@ -1308,8 +986,8 @@ packages: dev: true optional: true - /@esbuild/android-arm64@0.19.9: - resolution: {integrity: sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==} + /@esbuild/android-arm64@0.20.2: + resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} engines: {node: '>=12'} cpu: [arm64] os: [android] @@ -1326,8 +1004,8 @@ packages: dev: true optional: true - /@esbuild/android-arm@0.18.20: - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + /@esbuild/android-arm@0.19.12: + resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} engines: {node: '>=12'} cpu: [arm] os: [android] @@ -1335,8 +1013,8 @@ packages: dev: true optional: true - /@esbuild/android-arm@0.19.9: - resolution: {integrity: sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA==} + /@esbuild/android-arm@0.20.2: + resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} engines: {node: '>=12'} cpu: [arm] os: [android] @@ -1344,8 +1022,8 @@ packages: dev: true optional: true - /@esbuild/android-x64@0.18.20: - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + /@esbuild/android-x64@0.19.12: + resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} engines: {node: '>=12'} cpu: [x64] os: [android] @@ -1353,8 +1031,8 @@ packages: dev: true optional: true - /@esbuild/android-x64@0.19.9: - resolution: {integrity: sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA==} + /@esbuild/android-x64@0.20.2: + resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} engines: {node: '>=12'} cpu: [x64] os: [android] @@ -1362,8 +1040,8 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64@0.18.20: - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + /@esbuild/darwin-arm64@0.19.12: + resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] @@ -1371,8 +1049,8 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64@0.19.9: - resolution: {integrity: sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw==} + /@esbuild/darwin-arm64@0.20.2: + resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] @@ -1380,8 +1058,8 @@ packages: dev: true optional: true - /@esbuild/darwin-x64@0.18.20: - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + /@esbuild/darwin-x64@0.19.12: + resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} engines: {node: '>=12'} cpu: [x64] os: [darwin] @@ -1389,8 +1067,8 @@ packages: dev: true optional: true - /@esbuild/darwin-x64@0.19.9: - resolution: {integrity: sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ==} + /@esbuild/darwin-x64@0.20.2: + resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} engines: {node: '>=12'} cpu: [x64] os: [darwin] @@ -1398,8 +1076,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64@0.18.20: - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + /@esbuild/freebsd-arm64@0.19.12: + resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] @@ -1407,8 +1085,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64@0.19.9: - resolution: {integrity: sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g==} + /@esbuild/freebsd-arm64@0.20.2: + resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] @@ -1416,8 +1094,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64@0.18.20: - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + /@esbuild/freebsd-x64@0.19.12: + resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] @@ -1425,8 +1103,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64@0.19.9: - resolution: {integrity: sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA==} + /@esbuild/freebsd-x64@0.20.2: + resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] @@ -1434,8 +1112,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm64@0.18.20: - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + /@esbuild/linux-arm64@0.19.12: + resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} engines: {node: '>=12'} cpu: [arm64] os: [linux] @@ -1443,8 +1121,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm64@0.19.9: - resolution: {integrity: sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ==} + /@esbuild/linux-arm64@0.20.2: + resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} engines: {node: '>=12'} cpu: [arm64] os: [linux] @@ -1452,8 +1130,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm@0.18.20: - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + /@esbuild/linux-arm@0.19.12: + resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} engines: {node: '>=12'} cpu: [arm] os: [linux] @@ -1461,8 +1139,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm@0.19.9: - resolution: {integrity: sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw==} + /@esbuild/linux-arm@0.20.2: + resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} engines: {node: '>=12'} cpu: [arm] os: [linux] @@ -1470,8 +1148,8 @@ packages: dev: true optional: true - /@esbuild/linux-ia32@0.18.20: - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + /@esbuild/linux-ia32@0.19.12: + resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} engines: {node: '>=12'} cpu: [ia32] os: [linux] @@ -1479,8 +1157,8 @@ packages: dev: true optional: true - /@esbuild/linux-ia32@0.19.9: - resolution: {integrity: sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q==} + /@esbuild/linux-ia32@0.20.2: + resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} engines: {node: '>=12'} cpu: [ia32] os: [linux] @@ -1497,8 +1175,8 @@ packages: dev: true optional: true - /@esbuild/linux-loong64@0.18.20: - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + /@esbuild/linux-loong64@0.19.12: + resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -1506,8 +1184,8 @@ packages: dev: true optional: true - /@esbuild/linux-loong64@0.19.9: - resolution: {integrity: sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA==} + /@esbuild/linux-loong64@0.20.2: + resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -1515,8 +1193,8 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el@0.18.20: - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + /@esbuild/linux-mips64el@0.19.12: + resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] @@ -1524,8 +1202,8 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el@0.19.9: - resolution: {integrity: sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw==} + /@esbuild/linux-mips64el@0.20.2: + resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] @@ -1533,8 +1211,8 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64@0.18.20: - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + /@esbuild/linux-ppc64@0.19.12: + resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] @@ -1542,8 +1220,8 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64@0.19.9: - resolution: {integrity: sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ==} + /@esbuild/linux-ppc64@0.20.2: + resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] @@ -1551,8 +1229,8 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64@0.18.20: - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + /@esbuild/linux-riscv64@0.19.12: + resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] @@ -1560,8 +1238,8 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64@0.19.9: - resolution: {integrity: sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg==} + /@esbuild/linux-riscv64@0.20.2: + resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] @@ -1569,8 +1247,8 @@ packages: dev: true optional: true - /@esbuild/linux-s390x@0.18.20: - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + /@esbuild/linux-s390x@0.19.12: + resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} engines: {node: '>=12'} cpu: [s390x] os: [linux] @@ -1578,8 +1256,8 @@ packages: dev: true optional: true - /@esbuild/linux-s390x@0.19.9: - resolution: {integrity: sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw==} + /@esbuild/linux-s390x@0.20.2: + resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} engines: {node: '>=12'} cpu: [s390x] os: [linux] @@ -1587,8 +1265,8 @@ packages: dev: true optional: true - /@esbuild/linux-x64@0.18.20: - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + /@esbuild/linux-x64@0.19.12: + resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} engines: {node: '>=12'} cpu: [x64] os: [linux] @@ -1596,8 +1274,8 @@ packages: dev: true optional: true - /@esbuild/linux-x64@0.19.9: - resolution: {integrity: sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A==} + /@esbuild/linux-x64@0.20.2: + resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} engines: {node: '>=12'} cpu: [x64] os: [linux] @@ -1605,8 +1283,8 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64@0.18.20: - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + /@esbuild/netbsd-x64@0.19.12: + resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] @@ -1614,8 +1292,8 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64@0.19.9: - resolution: {integrity: sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug==} + /@esbuild/netbsd-x64@0.20.2: + resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] @@ -1623,8 +1301,8 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64@0.18.20: - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + /@esbuild/openbsd-x64@0.19.12: + resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] @@ -1632,8 +1310,8 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64@0.19.9: - resolution: {integrity: sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw==} + /@esbuild/openbsd-x64@0.20.2: + resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] @@ -1641,8 +1319,8 @@ packages: dev: true optional: true - /@esbuild/sunos-x64@0.18.20: - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + /@esbuild/sunos-x64@0.19.12: + resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} engines: {node: '>=12'} cpu: [x64] os: [sunos] @@ -1650,8 +1328,8 @@ packages: dev: true optional: true - /@esbuild/sunos-x64@0.19.9: - resolution: {integrity: sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw==} + /@esbuild/sunos-x64@0.20.2: + resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} engines: {node: '>=12'} cpu: [x64] os: [sunos] @@ -1659,8 +1337,8 @@ packages: dev: true optional: true - /@esbuild/win32-arm64@0.18.20: - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + /@esbuild/win32-arm64@0.19.12: + resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] @@ -1668,8 +1346,8 @@ packages: dev: true optional: true - /@esbuild/win32-arm64@0.19.9: - resolution: {integrity: sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg==} + /@esbuild/win32-arm64@0.20.2: + resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} engines: {node: '>=12'} cpu: [arm64] os: [win32] @@ -1677,8 +1355,8 @@ packages: dev: true optional: true - /@esbuild/win32-ia32@0.18.20: - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + /@esbuild/win32-ia32@0.19.12: + resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -1686,8 +1364,8 @@ packages: dev: true optional: true - /@esbuild/win32-ia32@0.19.9: - resolution: {integrity: sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg==} + /@esbuild/win32-ia32@0.20.2: + resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -1695,8 +1373,8 @@ packages: dev: true optional: true - /@esbuild/win32-x64@0.18.20: - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + /@esbuild/win32-x64@0.19.12: + resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -1704,8 +1382,8 @@ packages: dev: true optional: true - /@esbuild/win32-x64@0.19.9: - resolution: {integrity: sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ==} + /@esbuild/win32-x64@0.20.2: + resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -1713,15 +1391,30 @@ packages: dev: true optional: true - /@eslint/eslintrc@1.3.3: - resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==} + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 debug: 4.3.4 - espree: 9.4.1 - globals: 13.15.0 - ignore: 5.2.0 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.1 import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -1730,47 +1423,57 @@ packages: - supports-color dev: true - /@expressive-code/core@0.20.0: - resolution: {integrity: sha512-fWBQ4qNjQNCkOv8D/tfeOMnNRGZg7OqNcLoqVXduqsTvTL/IvPH0tcL2B3moZaQ2jbqGvIl73d/Exz3sioplvQ==} + /@eslint/js@8.57.0: + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@expressive-code/core@0.33.5: + resolution: {integrity: sha512-KL0EkKAvd7SSIQL3ZIP19xqe4xNjBaQYNvcJC6RmoBUnQpvxaJNFwRxCBEF/X0ftJEMaSG7WTrabZ9c/zFeqmA==} dependencies: - '@ctrl/tinycolor': 3.6.0 + '@ctrl/tinycolor': 3.6.1 hast-util-to-html: 8.0.4 hastscript: 7.2.0 - postcss: 8.4.32 - postcss-nested: 6.0.1(postcss@8.4.32) + postcss: 8.4.37 + postcss-nested: 6.0.1(postcss@8.4.37) dev: true - /@expressive-code/plugin-frames@0.20.0: - resolution: {integrity: sha512-TjdxOobHuTyUxwqEEZ/nEk290x7tYUukIjZdOJRTrXVomUpjMbIEmSeqm2K0S1zqtm4YvFkUVG1l48Ze20jESg==} + /@expressive-code/plugin-frames@0.33.5: + resolution: {integrity: sha512-lFt/gbnZscBSxHovg4XiWohp5nrxk4McS6RGABdj6+0gJcX8/YrFTM23GKBIkaDePxdDidVY0jQYGYDL/RrQHw==} dependencies: - '@expressive-code/core': 0.20.0 + '@expressive-code/core': 0.33.5 hastscript: 7.2.0 dev: true - /@expressive-code/plugin-shiki@0.20.0: - resolution: {integrity: sha512-xR/XvobgQ2mrikTp2qraF6pM7zafZ15F2VcjSZXQFB7ttMqJFE8Cjpt1v0BS9UDRGnPwn95hYNsbbw0KySgNyA==} + /@expressive-code/plugin-shiki@0.33.5: + resolution: {integrity: sha512-LWgttQTUrIPE1X+Lya1qFWiX47tH2AS2hkbj/cZoWkdiSjn6zUvtTypK/2Xn6Rgn6z6ClzpgHvkXRqFn7nAB4A==} dependencies: - '@expressive-code/core': 0.20.0 - shiki: 0.14.3 + '@expressive-code/core': 0.33.5 + shiki: 1.2.1 dev: true - /@expressive-code/plugin-text-markers@0.20.0: - resolution: {integrity: sha512-TVDgxVjsZzkT5LtU/gBpKISvEEgwkBlUhQe6tDJWgwZXQ9BttTsCgTXJiw24z7dGhLsx5UtX3jOsXoWBc/5NGw==} + /@expressive-code/plugin-text-markers@0.33.5: + resolution: {integrity: sha512-JxSHL1MGrJAPNaUMjFXex3K+9NJDbfew9H6PmX8LQ+fm9VNQdtBYTAz/x7nqOk7bkTrtAZK5RfDqUfb8U5M+2A==} dependencies: - '@expressive-code/core': 0.20.0 + '@expressive-code/core': 0.33.5 hastscript: 7.2.0 unist-util-visit-parents: 5.1.3 dev: true - /@fontsource/ibm-plex-mono@4.5.10: - resolution: {integrity: sha512-YmyewD+qHgo4ylcNSg6T9zpneRS34HhFd8cpF2TrJMbM3iKOW59XwmZqzdKyctzU0UQoHUFzjAAzDJ4THElFRA==} + /@fastify/busboy@2.1.1: + resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} + engines: {node: '>=14'} + dev: true + + /@fontsource/ibm-plex-mono@5.0.12: + resolution: {integrity: sha512-RamYYYUQk7FX/yVbQqGxyMR+AfX5hfCZsLo5pr5BBUBNf2i3N4AjJ4AWfieqLx1Mdwt2ukzXYojlf9J0G/gaZQ==} dev: false - /@humanwhocodes/config-array@0.11.7: - resolution: {integrity: sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==} + /@humanwhocodes/config-array@0.11.14: + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} dependencies: - '@humanwhocodes/object-schema': 1.2.1 + '@humanwhocodes/object-schema': 2.0.2 debug: 4.3.4 minimatch: 3.1.2 transitivePeerDependencies: @@ -1782,71 +1485,52 @@ packages: engines: {node: '>=12.22'} dev: true - /@humanwhocodes/object-schema@1.2.1: - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} - dev: true - - /@jridgewell/gen-mapping@0.1.1: - resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 + /@humanwhocodes/object-schema@2.0.2: + resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} dev: true - /@jridgewell/gen-mapping@0.3.2: - resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} - engines: {node: '>=6.0.0'} + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping': 0.3.14 + '@sinclair/typebox': 0.27.8 dev: true - /@jridgewell/gen-mapping@0.3.3: - resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + /@jridgewell/gen-mapping@0.3.5: + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} dependencies: - '@jridgewell/set-array': 1.1.2 + '@jridgewell/set-array': 1.2.1 '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.19 - dev: true - - /@jridgewell/resolve-uri@3.1.0: - resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} - engines: {node: '>=6.0.0'} + '@jridgewell/trace-mapping': 0.3.25 dev: true - /@jridgewell/resolve-uri@3.1.1: - resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + /@jridgewell/resolve-uri@3.1.2: + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/set-array@1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + /@jridgewell/set-array@1.2.1: + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/sourcemap-codec@1.4.14: - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - dev: true - /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - /@jridgewell/trace-mapping@0.3.14: - resolution: {integrity: sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==} + /@jridgewell/trace-mapping@0.3.25: + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@jridgewell/trace-mapping@0.3.19: - resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + /@jsdoc/salty@0.2.7: + resolution: {integrity: sha512-mh8LbS9d4Jq84KLw8pzho7XC2q2/IJGiJss3xwRoLD1A+EE16SjN4PfaG4jRCzKegTFLlN0Zd8SdUPE6XdoPFg==} + engines: {node: '>=v12.0.0'} dependencies: - '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true + lodash: 4.17.21 + dev: false /@kwsites/file-exists@1.1.1: resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} @@ -1860,39 +1544,45 @@ packages: resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} dev: true - /@mdx-js/mdx@2.3.0: - resolution: {integrity: sha512-jLuwRlz8DQfQNiUCJR50Y09CGPq3fLtmtUQfVrj79E0JWu3dvsVcxVIcfhR5h0iXu+/z++zDrYeiJqifRynJkA==} + /@mdx-js/mdx@3.0.1: + resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==} dependencies: - '@types/estree-jsx': 1.0.0 - '@types/mdx': 2.0.5 - estree-util-build-jsx: 2.2.2 - estree-util-is-identifier-name: 2.1.0 - estree-util-to-js: 1.2.0 + '@types/estree': 1.0.5 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdx': 2.0.12 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-build-jsx: 3.0.1 + estree-util-is-identifier-name: 3.0.0 + estree-util-to-js: 2.0.0 estree-walker: 3.0.3 - hast-util-to-estree: 2.3.3 - markdown-extensions: 1.1.1 + hast-util-to-estree: 3.1.0 + hast-util-to-jsx-runtime: 2.3.0 + markdown-extensions: 2.0.0 periscopic: 3.1.0 - remark-mdx: 2.3.0 - remark-parse: 10.0.2 - remark-rehype: 10.1.0 - unified: 10.1.2 - unist-util-position-from-estree: 1.1.2 - unist-util-stringify-position: 3.0.3 - unist-util-visit: 4.1.2 - vfile: 5.3.7 + remark-mdx: 3.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.0 + source-map: 0.7.4 + unified: 11.0.4 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.1 transitivePeerDependencies: - supports-color dev: true - /@nanostores/preact@0.1.3(nanostores@0.5.13)(preact@10.16.0): - resolution: {integrity: sha512-uiX1ned0LrzASot+sPUjyJzr8Js3pX075omazgsSdLf0zPp4ss8xwTiuNh5FSKigTSQEVqZFiS+W8CnHIrX62A==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + /@nanostores/preact@0.5.1(nanostores@0.10.0)(preact@10.19.7): + resolution: {integrity: sha512-kofyeDwzM3TrOd37ay+Xxgk3Cn6jih23dxELc7Mr9IJV55jmWATfNP9b7O/awwCL7CE5z5PfzFnNk/W+tMaWGw==} + engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: - nanostores: ^0.5.2 + nanostores: ^0.9.0 || ^0.10.0 preact: '>=10.0.0' dependencies: - nanostores: 0.5.13 - preact: 10.16.0 + nanostores: 0.10.0 + preact: 10.19.7 dev: false /@nodelib/fs.scandir@2.1.5: @@ -1911,74 +1601,71 @@ packages: engines: {node: '>= 8'} dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.13.0 + fastq: 1.17.1 - /@pkgr/utils@2.4.2: - resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==} + /@pkgr/core@0.1.1: + resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - dependencies: - cross-spawn: 7.0.3 - fast-glob: 3.3.0 - is-glob: 4.0.3 - open: 9.1.0 - picocolors: 1.0.0 - tslib: 2.6.0 dev: true - /@preact/preset-vite@2.5.0(@babel/core@7.18.10)(preact@10.16.0): - resolution: {integrity: sha512-BUhfB2xQ6ex0yPkrT1Z3LbfPzjpJecOZwQ/xJrXGFSZD84+ObyS//41RdEoQCMWsM0t7UHGaujUxUBub7WM1Jw==} + /@preact/preset-vite@2.8.2(@babel/core@7.24.3)(preact@10.19.7): + resolution: {integrity: sha512-m3tl+M8IO8jgiHnk+7LSTFl8axdPXloewi7iGVLdmCwf34XOzEUur0bZVewW4DUbUipFjTS2CXu27+5f/oexBA==} peerDependencies: '@babel/core': 7.x - vite: 2.x || 3.x || 4.x + vite: 2.x || 3.x || 4.x || 5.x peerDependenciesMeta: vite: optional: true dependencies: - '@babel/core': 7.18.10 - '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.18.10) - '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.18.10) - '@prefresh/vite': 2.4.1(preact@10.16.0) + '@babel/core': 7.24.3 + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.3) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.24.3) + '@prefresh/vite': 2.4.5(preact@10.19.7) '@rollup/pluginutils': 4.2.1 - babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.18.10) + babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.24.3) debug: 4.3.4 kolorist: 1.8.0 - resolve: 1.22.4 + magic-string: 0.30.5 + node-html-parser: 6.1.12 + resolve: 1.22.8 + source-map: 0.7.4 + stack-trace: 1.0.0-pre2 transitivePeerDependencies: - preact - supports-color dev: true - /@preact/signals-core@1.4.0: - resolution: {integrity: sha512-5iYoZBhELLIhUQceZI7sDTQWPb+xcVSn2qk8T/aNl/VMh+A4AiPX9YRSh4XO7fZ6pncrVxl1Iln82poVqYVbbw==} + /@preact/signals-core@1.5.1: + resolution: {integrity: sha512-dE6f+WCX5ZUDwXzUIWNMhhglmuLpqJhuy3X3xHrhZYI0Hm2LyQwOu0l9mdPiWrVNsE+Q7txOnJPgtIqHCYoBVA==} dev: true - /@preact/signals@1.2.1(preact@10.16.0): - resolution: {integrity: sha512-hRPvp1C2ooDzOHqfnhdpHgoIFDbYFAXLhoid3+jSItuPPD/J0r/UsiWKv/8ZO/oEhjRaP0M5niuRYsWqmY2GEA==} + /@preact/signals@1.2.2(preact@10.19.7): + resolution: {integrity: sha512-ColCqdo4cRP18bAuIR4Oik5rDpiyFtPIJIygaYPMEAwTnl4buWkBOflGBSzhYyPyJfKpkwlekrvK+1pzQ2ldWw==} peerDependencies: preact: 10.x dependencies: - '@preact/signals-core': 1.4.0 - preact: 10.16.0 + '@preact/signals-core': 1.5.1 + preact: 10.19.7 dev: true - /@prefresh/babel-plugin@0.5.0: - resolution: {integrity: sha512-joAwpkUDwo7ZqJnufXRGzUb+udk20RBgfA8oLPBh5aJH2LeStmV1luBfeJTztPdyCscC2j2SmZ/tVxFRMIxAEw==} + /@prefresh/babel-plugin@0.5.1: + resolution: {integrity: sha512-uG3jGEAysxWoyG3XkYfjYHgaySFrSsaEb4GagLzYaxlydbuREtaX+FTxuIidp241RaLl85XoHg9Ej6E4+V1pcg==} dev: true - /@prefresh/core@1.5.1(preact@10.16.0): - resolution: {integrity: sha512-e0mB0Oxtog6ZpKPDBYbzFniFJDIktuKMzOHp7sguntU+ot0yi6dbhJRE9Css1qf0u16wdSZjpL2W2ODWuU05Cw==} + /@prefresh/core@1.5.2(preact@10.19.7): + resolution: {integrity: sha512-A/08vkaM1FogrCII5PZKCrygxSsc11obExBScm3JF1CryK2uDS3ZXeni7FeKCx1nYdUkj4UcJxzPzc1WliMzZA==} peerDependencies: preact: ^10.0.0 dependencies: - preact: 10.16.0 + preact: 10.19.7 dev: true /@prefresh/utils@1.2.0: resolution: {integrity: sha512-KtC/fZw+oqtwOLUFM9UtiitB0JsVX0zLKNyRTA332sqREqSALIIQQxdUCS1P3xR/jT1e2e8/5rwH6gdcMLEmsQ==} dev: true - /@prefresh/vite@2.4.1(preact@10.16.0): - resolution: {integrity: sha512-vthWmEqu8TZFeyrBNc9YE5SiC3DVSzPgsOCp/WQ7FqdHpOIJi7Z8XvCK06rBPOtG4914S52MjG9Ls22eVAiuqQ==} + /@prefresh/vite@2.4.5(preact@10.19.7): + resolution: {integrity: sha512-iForDVJ2M8gQYnm5pHumvTEJjGGc7YNYC0GVKnHFL+GvFfKHfH9Rpq67nUAzNbjuLEpqEOUuQVQajMazWu2ZNQ==} peerDependencies: preact: ^10.4.0 vite: '>=2.0.0' @@ -1986,12 +1673,12 @@ packages: vite: optional: true dependencies: - '@babel/core': 7.22.11 - '@prefresh/babel-plugin': 0.5.0 - '@prefresh/core': 1.5.1(preact@10.16.0) + '@babel/core': 7.24.3 + '@prefresh/babel-plugin': 0.5.1 + '@prefresh/core': 1.5.2(preact@10.19.7) '@prefresh/utils': 1.2.0 '@rollup/pluginutils': 4.2.1 - preact: 10.16.0 + preact: 10.19.7 transitivePeerDependencies: - supports-color dev: true @@ -2004,114 +1691,126 @@ packages: picomatch: 2.3.1 dev: true - /@rollup/rollup-android-arm-eabi@4.8.0: - resolution: {integrity: sha512-zdTObFRoNENrdPpnTNnhOljYIcOX7aI7+7wyrSpPFFIOf/nRdedE6IYsjaBE7tjukphh1tMTojgJ7p3lKY8x6Q==} + /@rollup/rollup-android-arm-eabi@4.13.0: + resolution: {integrity: sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==} cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-android-arm64@4.8.0: - resolution: {integrity: sha512-aiItwP48BiGpMFS9Znjo/xCNQVwTQVcRKkFKsO81m8exrGjHkCBDvm9PHay2kpa8RPnZzzKcD1iQ9KaLY4fPQQ==} + /@rollup/rollup-android-arm64@4.13.0: + resolution: {integrity: sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==} cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-arm64@4.8.0: - resolution: {integrity: sha512-zhNIS+L4ZYkYQUjIQUR6Zl0RXhbbA0huvNIWjmPc2SL0cB1h5Djkcy+RZ3/Bwszfb6vgwUvcVJYD6e6Zkpsi8g==} + /@rollup/rollup-darwin-arm64@4.13.0: + resolution: {integrity: sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-x64@4.8.0: - resolution: {integrity: sha512-A/FAHFRNQYrELrb/JHncRWzTTXB2ticiRFztP4ggIUAfa9Up1qfW8aG2w/mN9jNiZ+HB0t0u0jpJgFXG6BfRTA==} + /@rollup/rollup-darwin-x64@4.13.0: + resolution: {integrity: sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.8.0: - resolution: {integrity: sha512-JsidBnh3p2IJJA4/2xOF2puAYqbaczB3elZDT0qHxn362EIoIkq7hrR43Xa8RisgI6/WPfvb2umbGsuvf7E37A==} + /@rollup/rollup-linux-arm-gnueabihf@4.13.0: + resolution: {integrity: sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.8.0: - resolution: {integrity: sha512-hBNCnqw3EVCkaPB0Oqd24bv8SklETptQWcJz06kb9OtiShn9jK1VuTgi7o4zPSt6rNGWQOTDEAccbk0OqJmS+g==} + /@rollup/rollup-linux-arm64-gnu@4.13.0: + resolution: {integrity: sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-musl@4.8.0: - resolution: {integrity: sha512-Fw9ChYfJPdltvi9ALJ9wzdCdxGw4wtq4t1qY028b2O7GwB5qLNSGtqMsAel1lfWTZvf4b6/+4HKp0GlSYg0ahA==} + /@rollup/rollup-linux-arm64-musl@4.13.0: + resolution: {integrity: sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-riscv64-gnu@4.8.0: - resolution: {integrity: sha512-BH5xIh7tOzS9yBi8dFrCTG8Z6iNIGWGltd3IpTSKp6+pNWWO6qy8eKoRxOtwFbMrid5NZaidLYN6rHh9aB8bEw==} + /@rollup/rollup-linux-riscv64-gnu@4.13.0: + resolution: {integrity: sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==} cpu: [riscv64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-gnu@4.8.0: - resolution: {integrity: sha512-PmvAj8k6EuWiyLbkNpd6BLv5XeYFpqWuRvRNRl80xVfpGXK/z6KYXmAgbI4ogz7uFiJxCnYcqyvZVD0dgFog7Q==} + /@rollup/rollup-linux-x64-gnu@4.13.0: + resolution: {integrity: sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-musl@4.8.0: - resolution: {integrity: sha512-mdxnlW2QUzXwY+95TuxZ+CurrhgrPAMveDWI97EQlA9bfhR8tw3Pt7SUlc/eSlCNxlWktpmT//EAA8UfCHOyXg==} + /@rollup/rollup-linux-x64-musl@4.13.0: + resolution: {integrity: sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.8.0: - resolution: {integrity: sha512-ge7saUz38aesM4MA7Cad8CHo0Fyd1+qTaqoIo+Jtk+ipBi4ATSrHWov9/S4u5pbEQmLjgUjB7BJt+MiKG2kzmA==} + /@rollup/rollup-win32-arm64-msvc@4.13.0: + resolution: {integrity: sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.8.0: - resolution: {integrity: sha512-p9E3PZlzurhlsN5h9g7zIP1DnqKXJe8ZUkFwAazqSvHuWfihlIISPxG9hCHCoA+dOOspL/c7ty1eeEVFTE0UTw==} + /@rollup/rollup-win32-ia32-msvc@4.13.0: + resolution: {integrity: sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==} cpu: [ia32] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-x64-msvc@4.8.0: - resolution: {integrity: sha512-kb4/auKXkYKqlUYTE8s40FcJIj5soOyRLHKd4ugR0dCq0G2EfcF54eYcfQiGkHzjidZ40daB4ulsFdtqNKZtBg==} + /@rollup/rollup-win32-x64-msvc@4.13.0: + resolution: {integrity: sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==} cpu: [x64] os: [win32] requiresBuild: true dev: true optional: true + /@shikijs/core@1.2.0: + resolution: {integrity: sha512-OlFvx+nyr5C8zpcMBnSGir0YPD6K11uYhouqhNmm1qLiis4GA7SsGtu07r9gKS9omks8RtQqHrJL4S+lqWK01A==} + dev: true + + /@shikijs/core@1.2.1: + resolution: {integrity: sha512-KaIS0H4EQ3KI2d++TjYqRNgwp8E3M/68e9veR4QtInzA7kKFgcjeiJqb80fuXW+blDy5fmd11PN9g9soz/3ANQ==} + dev: true + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + /@ts-morph/common@0.16.0: resolution: {integrity: sha512-SgJpzkTgZKLKqQniCjLaE3c2L2sdL7UShvmTmPBejAKd2OKV/yfMpQ2IWpAuA+VY5wy7PkSUaEObIqEK6afFuw==} dependencies: - fast-glob: 3.3.0 + fast-glob: 3.3.2 minimatch: 5.1.6 mkdirp: 1.0.4 path-browserify: 1.0.1 @@ -2120,152 +1819,140 @@ packages: /@types/acorn@4.0.6: resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} dependencies: - '@types/estree': 1.0.1 + '@types/estree': 1.0.5 dev: true /@types/babel__core@7.20.5: resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} dependencies: - '@babel/parser': 7.23.6 - '@babel/types': 7.23.6 - '@types/babel__generator': 7.6.4 - '@types/babel__template': 7.4.1 - '@types/babel__traverse': 7.20.1 - dev: true - - /@types/babel__generator@7.6.4: - resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} - dependencies: - '@babel/types': 7.23.6 + '@babel/parser': 7.24.1 + '@babel/types': 7.24.0 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.5 dev: true - /@types/babel__template@7.4.1: - resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} + /@types/babel__generator@7.6.8: + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} dependencies: - '@babel/parser': 7.23.6 - '@babel/types': 7.23.6 + '@babel/types': 7.24.0 dev: true - /@types/babel__traverse@7.20.1: - resolution: {integrity: sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==} + /@types/babel__template@7.4.4: + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} dependencies: - '@babel/types': 7.23.6 + '@babel/parser': 7.24.1 + '@babel/types': 7.24.0 dev: true - /@types/canvas-confetti@1.6.0: - resolution: {integrity: sha512-Yq6rIccwcco0TLD5SMUrIM7Fk7Fe/C0jmNRxJJCLtAF6gebDkPuUjK5EHedxecm69Pi/aA+It39Ux4OHmFhjRw==} - dev: true - - /@types/chai-subset@1.3.3: - resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} + /@types/babel__traverse@7.20.5: + resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==} dependencies: - '@types/chai': 4.3.4 + '@babel/types': 7.24.0 dev: true - /@types/chai@4.3.4: - resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} + /@types/canvas-confetti@1.6.4: + resolution: {integrity: sha512-fNyZ/Fdw/Y92X0vv7B+BD6ysHL4xVU5dJcgzgxLdGbn8O3PezZNIJpml44lKM0nsGur+o/6+NZbZeNTt00U1uA==} dev: true - /@types/debug@4.1.7: - resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} + /@types/debug@4.1.12: + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} dependencies: - '@types/ms': 0.7.31 + '@types/ms': 0.7.34 - /@types/estree-jsx@1.0.0: - resolution: {integrity: sha512-3qvGd0z8F2ENTGr/GG1yViqfiKmRfrXVx5sJyHGFu3z7m5g5utCQtGp/g29JnjflhtQJBv1WDQukHiT58xPcYQ==} + /@types/estree-jsx@1.0.5: + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} dependencies: - '@types/estree': 1.0.0 - dev: true - - /@types/estree@1.0.0: - resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} + '@types/estree': 1.0.5 dev: true - /@types/estree@1.0.1: - resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: true - /@types/hast@2.3.4: - resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==} + /@types/hast@2.3.10: + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} dependencies: - '@types/unist': 2.0.6 + '@types/unist': 2.0.10 + dev: true - /@types/hast@3.0.3: - resolution: {integrity: sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==} + /@types/hast@3.0.4: + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} dependencies: - '@types/unist': 2.0.8 - dev: true + '@types/unist': 3.0.2 - /@types/html-escaper@3.0.0: - resolution: {integrity: sha512-OcJcvP3Yk8mjYwf/IdXZtTE1tb/u0WF0qa29ER07ZHCYUBZXSN29Z1mBS+/96+kNMGTFUAbSz9X+pHmHpZrTCw==} + /@types/html-escaper@3.0.2: + resolution: {integrity: sha512-A8vk09eyYzk8J/lFO4OUMKCmRN0rRzfZf4n3Olwapgox/PtTiU8zPYlL1UEkJ/WeHvV6v9Xnj3o/705PKz9r4Q==} dev: true - /@types/json-schema@7.0.11: - resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true - /@types/linkify-it@3.0.2: - resolution: {integrity: sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==} + /@types/linkify-it@3.0.5: + resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} dev: false /@types/markdown-it@12.2.3: resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==} dependencies: - '@types/linkify-it': 3.0.2 - '@types/mdurl': 1.0.2 + '@types/linkify-it': 3.0.5 + '@types/mdurl': 1.0.5 dev: false - /@types/mdast@3.0.10: - resolution: {integrity: sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==} - dependencies: - '@types/unist': 2.0.6 - /@types/mdast@4.0.3: resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} dependencies: - '@types/unist': 2.0.8 - dev: true + '@types/unist': 3.0.2 - /@types/mdurl@1.0.2: - resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==} + /@types/mdurl@1.0.5: + resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} dev: false - /@types/mdx@2.0.5: - resolution: {integrity: sha512-76CqzuD6Q7LC+AtbPqrvD9AqsN0k8bsYo2bM2J8pmNldP1aIPAbzUQ7QbobyXL4eLr1wK5x8FZFe8eF/ubRuBg==} + /@types/mdx@2.0.12: + resolution: {integrity: sha512-H9VZ9YqE+H28FQVchC83RCs5xQ2J7mAAv6qdDEaWmXEVl3OpdH+xfrSUzQ1lp7U7oSTRZ0RvW08ASPJsYBi7Cw==} dev: true - /@types/ms@0.7.31: - resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} + /@types/ms@0.7.34: + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - /@types/nlcst@1.0.0: - resolution: {integrity: sha512-3TGCfOcy8R8mMQ4CNSNOe3PG66HttvjcLzCoOpvXvDtfWOTi+uT/rxeOKm/qEwbM4SNe1O/PjdiBK2YcTjU4OQ==} + /@types/nlcst@1.0.4: + resolution: {integrity: sha512-ABoYdNQ/kBSsLvZAekMhIPMQ3YUZvavStpKYs7BjLLuKVmIMA0LUgZ7b54zzuWJRbHF80v1cNf4r90Vd6eMQDg==} dependencies: - '@types/unist': 2.0.6 + '@types/unist': 2.0.10 /@types/node@17.0.45: resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} dev: true - /@types/node@18.6.4: - resolution: {integrity: sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg==} + /@types/node@18.19.26: + resolution: {integrity: sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/node@20.11.30: + resolution: {integrity: sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==} + dependencies: + undici-types: 5.26.5 dev: true /@types/parse5@6.0.3: resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==} dev: true - /@types/retry@0.12.1: - resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==} + /@types/retry@0.12.2: + resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} dev: true - /@types/sax@1.2.4: - resolution: {integrity: sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==} + /@types/sax@1.2.7: + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} dependencies: - '@types/node': 18.6.4 + '@types/node': 20.11.30 dev: true - /@types/semver@7.3.13: - resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} dev: true /@types/strip-bom@3.0.0: @@ -2276,300 +1963,308 @@ packages: resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} dev: true - /@types/unist@2.0.6: - resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} - - /@types/unist@2.0.8: - resolution: {integrity: sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==} + /@types/unist@2.0.10: + resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} /@types/unist@3.0.2: resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} - dev: true - /@typescript-eslint/eslint-plugin@5.46.1(@typescript-eslint/parser@5.46.1)(eslint@8.29.0)(typescript@5.0.2): - resolution: {integrity: sha512-YpzNv3aayRBwjs4J3oz65eVLXc9xx0PDbIRisHj+dYhvBn02MjYOD96P8YGiWEIFBrojaUjxvkaUpakD82phsA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/eslint-plugin@7.4.0(@typescript-eslint/parser@7.4.0)(eslint@8.57.0)(typescript@5.4.2): + resolution: {integrity: sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.46.1(eslint@8.29.0)(typescript@5.0.2) - '@typescript-eslint/scope-manager': 5.46.1 - '@typescript-eslint/type-utils': 5.46.1(eslint@8.29.0)(typescript@5.0.2) - '@typescript-eslint/utils': 5.46.1(eslint@8.29.0)(typescript@5.0.2) + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 7.4.0(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/scope-manager': 7.4.0 + '@typescript-eslint/type-utils': 7.4.0(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/utils': 7.4.0(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/visitor-keys': 7.4.0 debug: 4.3.4 - eslint: 8.29.0 - ignore: 5.2.0 - natural-compare-lite: 1.4.0 - regexpp: 3.2.0 - semver: 7.3.7 - tsutils: 3.21.0(typescript@5.0.2) - typescript: 5.0.2 + eslint: 8.57.0 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare: 1.4.0 + semver: 7.6.0 + ts-api-utils: 1.3.0(typescript@5.4.2) + typescript: 5.4.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@5.46.1(eslint@8.29.0)(typescript@5.0.2): - resolution: {integrity: sha512-RelQ5cGypPh4ySAtfIMBzBGyrNerQcmfA1oJvPj5f+H4jI59rl9xxpn4bonC0tQvUKOEN7eGBFWxFLK3Xepneg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/parser@7.4.0(eslint@8.57.0)(typescript@5.4.2): + resolution: {integrity: sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.46.1 - '@typescript-eslint/types': 5.46.1 - '@typescript-eslint/typescript-estree': 5.46.1(typescript@5.0.2) + '@typescript-eslint/scope-manager': 7.4.0 + '@typescript-eslint/types': 7.4.0 + '@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.2) + '@typescript-eslint/visitor-keys': 7.4.0 debug: 4.3.4 - eslint: 8.29.0 - typescript: 5.0.2 + eslint: 8.57.0 + typescript: 5.4.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@5.46.1: - resolution: {integrity: sha512-iOChVivo4jpwUdrJZyXSMrEIM/PvsbbDOX1y3UCKjSgWn+W89skxWaYXACQfxmIGhPVpRWK/VWPYc+bad6smIA==} + /@typescript-eslint/scope-manager@5.62.0: + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.46.1 - '@typescript-eslint/visitor-keys': 5.46.1 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 dev: true - /@typescript-eslint/type-utils@5.46.1(eslint@8.29.0)(typescript@5.0.2): - resolution: {integrity: sha512-V/zMyfI+jDmL1ADxfDxjZ0EMbtiVqj8LUGPAGyBkXXStWmCUErMpW873zEHsyguWCuq2iN4BrlWUkmuVj84yng==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/scope-manager@7.4.0: + resolution: {integrity: sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==} + engines: {node: ^18.18.0 || >=20.0.0} + dependencies: + '@typescript-eslint/types': 7.4.0 + '@typescript-eslint/visitor-keys': 7.4.0 + dev: true + + /@typescript-eslint/type-utils@7.4.0(eslint@8.57.0)(typescript@5.4.2): + resolution: {integrity: sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - eslint: '*' + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.46.1(typescript@5.0.2) - '@typescript-eslint/utils': 5.46.1(eslint@8.29.0)(typescript@5.0.2) + '@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.2) + '@typescript-eslint/utils': 7.4.0(eslint@8.57.0)(typescript@5.4.2) debug: 4.3.4 - eslint: 8.29.0 - tsutils: 3.21.0(typescript@5.0.2) - typescript: 5.0.2 + eslint: 8.57.0 + ts-api-utils: 1.3.0(typescript@5.4.2) + typescript: 5.4.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@5.32.0: - resolution: {integrity: sha512-EBUKs68DOcT/EjGfzywp+f8wG9Zw6gj6BjWu7KV/IYllqKJFPlZlLSYw/PTvVyiRw50t6wVbgv4p9uE2h6sZrQ==} + /@typescript-eslint/types@5.62.0: + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/types@5.46.1: - resolution: {integrity: sha512-Z5pvlCaZgU+93ryiYUwGwLl9AQVB/PQ1TsJ9NZ/gHzZjN7g9IAn6RSDkpCV8hqTwAiaj6fmCcKSQeBPlIpW28w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/types@7.4.0: + resolution: {integrity: sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==} + engines: {node: ^18.18.0 || >=20.0.0} dev: true - /@typescript-eslint/typescript-estree@5.46.1(typescript@5.0.2): - resolution: {integrity: sha512-j9W4t67QiNp90kh5Nbr1w92wzt+toiIsaVPnEblB2Ih2U9fqBTyqV9T3pYWZBRt6QoMh/zVWP59EpuCjc4VRBg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/typescript-estree@7.4.0(typescript@5.4.2): + resolution: {integrity: sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.46.1 - '@typescript-eslint/visitor-keys': 5.46.1 + '@typescript-eslint/types': 7.4.0 + '@typescript-eslint/visitor-keys': 7.4.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.5.4 - tsutils: 3.21.0(typescript@5.0.2) - typescript: 5.0.2 + minimatch: 9.0.3 + semver: 7.6.0 + ts-api-utils: 1.3.0(typescript@5.4.2) + typescript: 5.4.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@5.46.1(eslint@8.29.0)(typescript@5.0.2): - resolution: {integrity: sha512-RBdBAGv3oEpFojaCYT4Ghn4775pdjvwfDOfQ2P6qzNVgQOVrnSPe5/Pb88kv7xzYQjoio0eKHKB9GJ16ieSxvA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/utils@7.4.0(eslint@8.57.0)(typescript@5.4.2): + resolution: {integrity: sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@types/json-schema': 7.0.11 - '@types/semver': 7.3.13 - '@typescript-eslint/scope-manager': 5.46.1 - '@typescript-eslint/types': 5.46.1 - '@typescript-eslint/typescript-estree': 5.46.1(typescript@5.0.2) - eslint: 8.29.0 - eslint-scope: 5.1.1 - eslint-utils: 3.0.0(eslint@8.29.0) - semver: 7.5.4 + eslint: ^8.56.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 7.4.0 + '@typescript-eslint/types': 7.4.0 + '@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.2) + eslint: 8.57.0 + semver: 7.6.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@5.46.1: - resolution: {integrity: sha512-jczZ9noovXwy59KjRTk1OftT78pwygdcmCuBf8yMoWt/8O8l+6x2LSEze0E4TeepXK4MezW3zGSyoDRZK7Y9cg==} + /@typescript-eslint/visitor-keys@5.62.0: + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.46.1 - eslint-visitor-keys: 3.3.0 + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@typescript-eslint/visitor-keys@7.4.0: + resolution: {integrity: sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==} + engines: {node: ^18.18.0 || >=20.0.0} + dependencies: + '@typescript-eslint/types': 7.4.0 + eslint-visitor-keys: 3.4.3 dev: true /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + + /@vitest/expect@1.4.0: + resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==} + dependencies: + '@vitest/spy': 1.4.0 + '@vitest/utils': 1.4.0 + chai: 4.4.1 dev: true - /@vitest/expect@0.28.5: - resolution: {integrity: sha512-gqTZwoUTwepwGIatnw4UKpQfnoyV0Z9Czn9+Lo2/jLIt4/AXLTn+oVZxlQ7Ng8bzcNkR+3DqLJ08kNr8jRmdNQ==} + /@vitest/runner@1.4.0: + resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==} dependencies: - '@vitest/spy': 0.28.5 - '@vitest/utils': 0.28.5 - chai: 4.3.7 + '@vitest/utils': 1.4.0 + p-limit: 5.0.0 + pathe: 1.1.2 dev: true - /@vitest/runner@0.28.5: - resolution: {integrity: sha512-NKkHtLB+FGjpp5KmneQjTcPLWPTDfB7ie+MmF1PnUBf/tGe2OjGxWyB62ySYZ25EYp9krR5Bw0YPLS/VWh1QiA==} + /@vitest/snapshot@1.4.0: + resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==} dependencies: - '@vitest/utils': 0.28.5 - p-limit: 4.0.0 - pathe: 1.1.0 + magic-string: 0.30.8 + pathe: 1.1.2 + pretty-format: 29.7.0 dev: true - /@vitest/spy@0.28.5: - resolution: {integrity: sha512-7if6rsHQr9zbmvxN7h+gGh2L9eIIErgf8nSKYDlg07HHimCxp4H6I/X/DPXktVPPLQfiZ1Cw2cbDIx9fSqDjGw==} + /@vitest/spy@1.4.0: + resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==} dependencies: - tinyspy: 1.1.1 + tinyspy: 2.2.1 dev: true - /@vitest/utils@0.28.5: - resolution: {integrity: sha512-UyZdYwdULlOa4LTUSwZ+Paz7nBHGTT72jKwdFSV4IjHF1xsokp+CabMdhjvVhYwkLfO88ylJT46YMilnkSARZA==} + /@vitest/utils@1.4.0: + resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==} dependencies: - cli-truncate: 3.1.0 - diff: 5.1.0 - loupe: 2.3.6 - picocolors: 1.0.0 - pretty-format: 27.5.1 + diff-sequences: 29.6.3 + estree-walker: 3.0.3 + loupe: 2.3.7 + pretty-format: 29.7.0 dev: true - /@volar/kit@1.10.1(typescript@5.0.2): - resolution: {integrity: sha512-+aR3rvgER14VfjFflhD6k161uLdshpuK1tQUrnl8phpKtSGJDXHkTl/WkNk2xCEuE4goShS9nTvruTyrI9gGBw==} + /@volar/kit@2.1.2(typescript@5.4.2): + resolution: {integrity: sha512-u20R1lCWCgFYBCHC+FR/e9J+P61vUNQpyWt4keAY+zpVHEHsSXVA2xWMJV1l1Iq5Dd0jBUSqrb1zsEya455AzA==} peerDependencies: typescript: '*' dependencies: - '@volar/language-service': 1.10.1 + '@volar/language-service': 2.1.2 + '@volar/typescript': 2.1.2 typesafe-path: 0.2.2 - typescript: 5.0.2 - vscode-languageserver-textdocument: 1.0.8 - vscode-uri: 3.0.7 + typescript: 5.4.2 + vscode-languageserver-textdocument: 1.0.11 + vscode-uri: 3.0.8 dev: false - /@volar/language-core@1.10.1: - resolution: {integrity: sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==} + /@volar/language-core@2.1.2: + resolution: {integrity: sha512-5qsDp0Gf6fE09UWCeK7bkVn6NxMwC9OqFWQkMMkeej8h8XjyABPdRygC2RCrqDrfVdGijqlMQeXs6yRS+vfZYA==} dependencies: - '@volar/source-map': 1.10.1 + '@volar/source-map': 2.1.2 dev: false - /@volar/language-server@1.10.1: - resolution: {integrity: sha512-UXgRMAPKoy4EZBcBT1SFp8YIb5AJqe7Is1/TnqRUq0LBBV2M7HpEeHHI8E4fy05Eg4TlSVRcrlZtiTrY9fRjJg==} + /@volar/language-server@2.1.2: + resolution: {integrity: sha512-5NR5Ztg+OxvDI4oRrjS0/4ZVPumWwhVq5acuK2BJbakG1kJXViYI9NOWiWITMjnliPvf12TEcSrVDBmIq54DOg==} dependencies: - '@volar/language-core': 1.10.1 - '@volar/language-service': 1.10.1 - '@volar/typescript': 1.10.1 - '@vscode/l10n': 0.0.11 + '@volar/language-core': 2.1.2 + '@volar/language-service': 2.1.2 + '@volar/snapshot-document': 2.1.2 + '@volar/typescript': 2.1.2 + '@vscode/l10n': 0.0.16 + path-browserify: 1.0.1 request-light: 0.7.0 - typesafe-path: 0.2.2 - vscode-languageserver: 8.1.0 - vscode-languageserver-protocol: 3.17.3 - vscode-languageserver-textdocument: 1.0.8 - vscode-uri: 3.0.7 + vscode-languageserver: 9.0.1 + vscode-languageserver-protocol: 3.17.5 + vscode-languageserver-textdocument: 1.0.11 + vscode-uri: 3.0.8 + dev: false + + /@volar/language-service@2.1.2: + resolution: {integrity: sha512-CmVbbKdqzVq+0FT67hfELdHpboqXhKXh6EjypypuFX5ptIRftHZdkaq3/lCCa46EHxS5tvE44jn+s7faN4iRDA==} + dependencies: + '@volar/language-core': 2.1.2 + vscode-languageserver-protocol: 3.17.5 + vscode-languageserver-textdocument: 1.0.11 + vscode-uri: 3.0.8 dev: false - /@volar/language-service@1.10.1: - resolution: {integrity: sha512-9AfMd8DeBuiw5twyXCL4Dw/9dg7djs2GAMQ5YY6LlN4v6u5IwU+foM/un5F7yzE94v2cuI+NN9LtHeR87AXpRA==} + /@volar/snapshot-document@2.1.2: + resolution: {integrity: sha512-ZpJIBZrdm/Gx4jC/zn8H+O6H5vZZwY7B5CMTxl9y8HvcqlePOyDi+VkX8pjQz1VFG9Z5Z+Bau/RL6exqkoVDDA==} dependencies: - '@volar/language-core': 1.10.1 - '@volar/source-map': 1.10.1 - vscode-languageserver-protocol: 3.17.3 - vscode-languageserver-textdocument: 1.0.8 - vscode-uri: 3.0.7 + vscode-languageserver-protocol: 3.17.5 + vscode-languageserver-textdocument: 1.0.11 dev: false - /@volar/source-map@1.10.1: - resolution: {integrity: sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==} + /@volar/source-map@2.1.2: + resolution: {integrity: sha512-yFJqsuLm1OaWrsz9E3yd3bJcYIlHqdZ8MbmIoZLrAzMYQDcoF26/INIhgziEXSdyHc8xd7rd/tJdSnUyh0gH4Q==} dependencies: - muggle-string: 0.3.1 + muggle-string: 0.4.1 dev: false - /@volar/typescript@1.10.1: - resolution: {integrity: sha512-+iiO9yUSRHIYjlteT+QcdRq8b44qH19/eiUZtjNtuh6D9ailYM7DVR0zO2sEgJlvCaunw/CF9Ov2KooQBpR4VQ==} + /@volar/typescript@2.1.2: + resolution: {integrity: sha512-lhTancZqamvaLvoz0u/uth8dpudENNt2LFZOWCw9JZiX14xRFhdhfzmphiCRb7am9E6qAJSbdS/gMt1utXAoHQ==} dependencies: - '@volar/language-core': 1.10.1 + '@volar/language-core': 2.1.2 + path-browserify: 1.0.1 dev: false /@vscode/emmet-helper@2.9.2: resolution: {integrity: sha512-MaGuyW+fa13q3aYsluKqclmh62Hgp0BpKIqS66fCxfOaBcVQ1OnMQxRRgQUYnCkxFISAQlkJ0qWWPyXjro1Qrg==} dependencies: - emmet: 2.4.6 + emmet: 2.4.7 jsonc-parser: 2.3.1 - vscode-languageserver-textdocument: 1.0.8 - vscode-languageserver-types: 3.17.3 + vscode-languageserver-textdocument: 1.0.11 + vscode-languageserver-types: 3.17.5 vscode-uri: 2.1.2 dev: false - /@vscode/l10n@0.0.11: - resolution: {integrity: sha512-ukOMWnCg1tCvT7WnDfsUKQOFDQGsyR5tNgRpwmqi+5/vzU3ghdDXzvIM4IOPdSb3OeSsBNvmSL8nxIVOqi2WXA==} - dev: false - /@vscode/l10n@0.0.16: resolution: {integrity: sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg==} dev: false - /acorn-jsx@5.3.2(acorn@8.10.0): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.10.0 + /@vscode/l10n@0.0.18: + resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} + dev: false + + /@webgpu/types@0.1.21: + resolution: {integrity: sha512-pUrWq3V5PiSGFLeLxoGqReTZmiiXwY3jRkIG5sLLKjyqNxrwm/04b4nw7LSmGWJcKk59XOM/YRTUwOzo4MMlow==} dev: true - /acorn-jsx@5.3.2(acorn@8.8.0): + /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - acorn: 8.8.0 - dev: true - - /acorn-walk@8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} - engines: {node: '>=0.4.0'} - dev: true - - /acorn@8.10.0: - resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /acorn@8.11.2: - resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} - engines: {node: '>=0.4.0'} - hasBin: true + acorn: 8.11.3 dev: true - /acorn@8.8.0: - resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==} + /acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} - hasBin: true dev: true - /acorn@8.8.2: - resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} hasBin: true dev: true @@ -2583,23 +2278,23 @@ packages: uri-js: 4.4.1 dev: true - /algoliasearch@4.20.0: - resolution: {integrity: sha512-y+UHEjnOItoNy0bYO+WWmLWBlPwDjKHW6mNHrPi0NkuhpQOOEbrkwQH/wgKFDLh7qlKjzoKeiRtlpewDPDG23g==} + /algoliasearch@4.22.1: + resolution: {integrity: sha512-jwydKFQJKIx9kIZ8Jm44SdpigFwRGPESaxZBaHSV0XWN2yBJAOT4mT7ppvlrpA4UGzz92pqFnVKr/kaZXrcreg==} dependencies: - '@algolia/cache-browser-local-storage': 4.20.0 - '@algolia/cache-common': 4.20.0 - '@algolia/cache-in-memory': 4.20.0 - '@algolia/client-account': 4.20.0 - '@algolia/client-analytics': 4.20.0 - '@algolia/client-common': 4.20.0 - '@algolia/client-personalization': 4.20.0 - '@algolia/client-search': 4.20.0 - '@algolia/logger-common': 4.20.0 - '@algolia/logger-console': 4.20.0 - '@algolia/requester-browser-xhr': 4.20.0 - '@algolia/requester-common': 4.20.0 - '@algolia/requester-node-http': 4.20.0 - '@algolia/transporter': 4.20.0 + '@algolia/cache-browser-local-storage': 4.22.1 + '@algolia/cache-common': 4.22.1 + '@algolia/cache-in-memory': 4.22.1 + '@algolia/client-account': 4.22.1 + '@algolia/client-analytics': 4.22.1 + '@algolia/client-common': 4.22.1 + '@algolia/client-personalization': 4.22.1 + '@algolia/client-search': 4.22.1 + '@algolia/logger-common': 4.22.1 + '@algolia/logger-console': 4.22.1 + '@algolia/requester-browser-xhr': 4.22.1 + '@algolia/requester-common': 4.22.1 + '@algolia/requester-node-http': 4.22.1 + '@algolia/transporter': 4.22.1 dev: true /ansi-align@3.0.1: @@ -2608,11 +2303,11 @@ packages: string-width: 4.2.3 dev: true - /ansi-escapes@5.0.0: - resolution: {integrity: sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==} - engines: {node: '>=12'} + /ansi-escapes@6.2.0: + resolution: {integrity: sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==} + engines: {node: '>=14.16'} dependencies: - type-fest: 1.4.0 + type-fest: 3.13.1 dev: true /ansi-regex@5.0.1: @@ -2624,10 +2319,6 @@ packages: engines: {node: '>=12'} dev: true - /ansi-sequence-parser@1.1.0: - resolution: {integrity: sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==} - dev: true - /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -2671,8 +2362,14 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + /aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: true + /array-back@1.0.4: - resolution: {integrity: sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=} + resolution: {integrity: sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==} engines: {node: '>=0.12.0'} dependencies: typical: 2.6.1 @@ -2693,43 +2390,95 @@ packages: engines: {node: '>=12.17'} dev: false - /array-includes@3.1.6: - resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + /array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + is-array-buffer: 3.0.4 + dev: true + + /array-includes@3.1.7: + resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.21.1 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + get-intrinsic: 1.2.4 is-string: 1.0.7 dev: true - /array-iterate@1.1.4: - resolution: {integrity: sha512-sNRaPGh9nnmdC8Zf+pT3UqP8rnWj5Hf9wiFGsX3wUQ2yVSIhO2ShFwCoceIPpB41QF6i2OEmrHmCo36xronCVA==} + /array-iterate@2.0.1: + resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} dev: true - /array.prototype.flatmap@1.3.1: - resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + /array.prototype.findlast@1.2.4: + resolution: {integrity: sha512-BMtLxpV+8BD+6ZPFIWmnUBpQoy+A+ujcg4rhp2iwCRJYA7PEh2MS4NL3lz8EiDlLrJPp2hg9qWihr5pd//jcGw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.21.1 - es-shim-unscopables: 1.0.0 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.toreversed@1.1.2: + resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.tosorted@1.1.3: + resolution: {integrity: sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 dev: true - /array.prototype.tosorted@1.1.1: - resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} + /arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.21.1 - es-shim-unscopables: 1.0.0 - get-intrinsic: 1.2.0 + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 dev: true /assertion-error@1.1.0: @@ -2741,119 +2490,123 @@ packages: hasBin: true dev: true - /astro-auto-import@0.3.1(astro@4.0.4): - resolution: {integrity: sha512-4kXZMlZFiq3dqT6fcfPbCjHTABQ279eKbIqZAb6qktBhGlmHwpHr1spOUFj/RQFilaWVgfjzOBmuZnoydZb5Vg==} + /astro-auto-import@0.4.2(astro@4.5.7): + resolution: {integrity: sha512-ZgWZQ58+EhbEym1+aoUnNyECOy0wsG5uRUs+rVp/7BzHtj1V76J2qkhjaTWLplgNb+8WrzhvTQNxytmXRCW+Ow==} engines: {node: '>=16.0.0'} peerDependencies: - astro: ^2.0.0 || ^3.0.0-beta + astro: ^2.0.0 || ^3.0.0-beta || ^4.0.0-beta dependencies: - '@types/node': 18.6.4 - acorn: 8.10.0 - astro: 4.0.4(@types/node@18.6.4)(sass@1.54.3)(typescript@5.0.2) + '@types/node': 18.19.26 + acorn: 8.11.3 + astro: 4.5.7(@types/node@20.11.30)(sass@1.72.0)(typescript@5.4.2) dev: true - /astro-eslint-parser@0.9.2: - resolution: {integrity: sha512-Ppd1Y0PcGnR9ijwNbU7GdRR6r5mR1I38Nk7soYKWuw7WzbSK1u80nxwfDSshmAkgcBZatPboywbfXRcaYwSb3A==} + /astro-eslint-parser@0.16.3: + resolution: {integrity: sha512-CGaBseNtunAV2DCpwBXqTKq8+9Tw65XZetMaC0FsMoZuLj0gxNIkbCf2QyKYScVrNOU7/ayfNdVw8ZCSHBiqCg==} engines: {node: ^14.18.0 || >=16.0.0} dependencies: - '@astrojs/compiler': 0.31.0 - '@typescript-eslint/types': 5.32.0 - astrojs-compiler-sync: 0.3.1(@astrojs/compiler@0.31.0) + '@astrojs/compiler': 2.7.0 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + astrojs-compiler-sync: 0.3.5(@astrojs/compiler@2.7.0) debug: 4.3.4 - eslint-scope: 7.1.1 - eslint-visitor-keys: 3.3.0 - espree: 9.3.3 + entities: 4.5.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.6.0 transitivePeerDependencies: - supports-color dev: true - /astro-expressive-code@0.20.0(astro@4.0.4): - resolution: {integrity: sha512-SOt4OL3qE2cXHgN6ZVS1+Qjv69+c6or57ifClNE/MkQMTmHWpfLxv06+KdRrEe/EUTaJtW72IiYQVE2S91OU0g==} + /astro-expressive-code@0.33.5(astro@4.5.7): + resolution: {integrity: sha512-9JAyllueMUN8JTl/h/yTdbKinNmfalEWcV11s3lSf/UJQbAZfWJuy+IlGcArZDI/CmD21GXhFHLqYthpdY33ug==} peerDependencies: - astro: ^2.0.0 || ^3.0.0-beta + astro: ^4.0.0-beta || ^3.3.0 dependencies: - astro: 4.0.4(@types/node@18.6.4)(sass@1.54.3)(typescript@5.0.2) - remark-expressive-code: 0.20.0 + astro: 4.5.7(@types/node@20.11.30)(sass@1.72.0)(typescript@5.4.2) + hast-util-to-html: 8.0.4 + remark-expressive-code: 0.33.5 dev: true - /astro-og-canvas@0.3.0(astro@4.0.4): - resolution: {integrity: sha512-UVT7layzhxdiIuBrxwQoMAXq814d6AKMMUO4iC8IhZGssYW+ZDIKN8NEI4dn1WKgYsNv813w6Im3eSER01t2ZA==} + /astro-og-canvas@0.4.2(astro@4.5.7): + resolution: {integrity: sha512-OQsH6Gr2HX9ZRHdVy2OcXVBIPI65WvEtLG/60krnphh8d3ldhuAFunymYaNGcrdSZcYgXkHWejbPt//3qaRidA==} engines: {node: '>=18.14.1'} peerDependencies: - astro: ^3.0.0 + astro: ^3.0.0 || ^4.0.0 dependencies: - astro: 4.0.4(@types/node@18.6.4)(sass@1.54.3)(typescript@5.0.2) - canvaskit-wasm: 0.37.0 - deterministic-object-hash: 1.3.1 + astro: 4.5.7(@types/node@20.11.30)(sass@1.72.0)(typescript@5.4.2) + canvaskit-wasm: 0.37.2 + deterministic-object-hash: 2.0.2 entities: 4.5.0 dev: true - /astro@4.0.4(@types/node@18.6.4)(sass@1.54.3)(typescript@5.0.2): - resolution: {integrity: sha512-KhwoF5//kFusczKN46ZG185uk9BQt1OH5949wsrFKs0z7Oo/o31XyxKyqIW8ZSL0fWU4XYKSdYlAo1RaPF9TtA==} + /astro@4.5.7(@types/node@20.11.30)(sass@1.72.0)(typescript@5.4.2): + resolution: {integrity: sha512-Ioeg3TV42dOJvf6GlmykeR3EKZ8+JcnZyJ/X9qDPzVf2OREmtvW0182YCDXQBqwXFRHndZRcHLqinAWjzZYh/A==} engines: {node: '>=18.14.1', npm: '>=6.14.0'} hasBin: true dependencies: - '@astrojs/compiler': 2.3.2 - '@astrojs/internal-helpers': 0.2.1 - '@astrojs/markdown-remark': 4.0.1 + '@astrojs/compiler': 2.7.0 + '@astrojs/internal-helpers': 0.3.0 + '@astrojs/markdown-remark': 4.3.0 '@astrojs/telemetry': 3.0.4 - '@babel/core': 7.23.6 - '@babel/generator': 7.23.6 - '@babel/parser': 7.23.6 - '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.6) - '@babel/traverse': 7.23.6 - '@babel/types': 7.23.6 + '@babel/core': 7.24.3 + '@babel/generator': 7.24.1 + '@babel/parser': 7.24.1 + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.3) + '@babel/traverse': 7.24.1 + '@babel/types': 7.24.0 '@types/babel__core': 7.20.5 - acorn: 8.11.2 + acorn: 8.11.3 + aria-query: 5.3.0 + axobject-query: 4.0.0 boxen: 7.1.1 - chokidar: 3.5.3 + chokidar: 3.6.0 ci-info: 4.0.0 - clsx: 2.0.0 + clsx: 2.1.0 common-ancestor-path: 1.0.1 cookie: 0.6.0 + cssesc: 3.0.0 debug: 4.3.4 deterministic-object-hash: 2.0.2 devalue: 4.3.2 - diff: 5.1.0 + diff: 5.2.0 dlv: 1.1.3 dset: 3.1.3 - es-module-lexer: 1.4.1 - esbuild: 0.19.9 + es-module-lexer: 1.4.2 + esbuild: 0.19.12 estree-walker: 3.0.3 execa: 8.0.1 fast-glob: 3.3.2 - flattie: 1.1.0 + flattie: 1.1.1 github-slugger: 2.0.0 gray-matter: 4.0.3 html-escaper: 3.0.3 http-cache-semantics: 4.1.1 js-yaml: 4.1.0 kleur: 4.1.5 - magic-string: 0.30.3 - mdast-util-to-hast: 13.0.2 + magic-string: 0.30.8 mime: 3.0.0 ora: 7.0.1 p-limit: 5.0.0 - p-queue: 7.4.1 + p-queue: 8.0.1 path-to-regexp: 6.2.1 - preferred-pm: 3.1.2 - probe-image-size: 7.2.3 + preferred-pm: 3.1.3 prompts: 2.4.2 rehype: 13.0.1 - resolve: 1.22.4 - semver: 7.5.4 - server-destroy: 1.0.1 - shikiji: 0.6.13 - string-width: 7.0.0 + resolve: 1.22.8 + semver: 7.6.0 + shiki: 1.2.0 + string-width: 7.1.0 strip-ansi: 7.1.0 - tsconfck: 3.0.0(typescript@5.0.2) + tsconfck: 3.0.3(typescript@5.4.2) unist-util-visit: 5.0.0 vfile: 6.0.1 - vite: 5.0.8(@types/node@18.6.4)(sass@1.54.3) - vitefu: 0.2.5(vite@5.0.8) + vite: 5.2.8(@types/node@20.11.30)(sass@1.72.0) + vitefu: 0.2.5(vite@5.2.8) which-pm: 2.1.1 yargs-parser: 21.1.1 zod: 3.22.4 + zod-to-json-schema: 3.22.4(zod@3.22.4) optionalDependencies: sharp: 0.32.6 transitivePeerDependencies: @@ -2868,33 +2621,41 @@ packages: - typescript dev: true - /astrojs-compiler-sync@0.3.1(@astrojs/compiler@0.31.0): - resolution: {integrity: sha512-IzPuzkwdiRIZoBhCTuFhuBMWVESXgthTdwQ24QS8LvLargcWAA4E21KmZo4wimsmOG5vj4KKs9QFpy9zhXuo9Q==} + /astrojs-compiler-sync@0.3.5(@astrojs/compiler@2.7.0): + resolution: {integrity: sha512-y420rhIIJ2HHDkYeqKArBHSdJNIIGMztLH90KGIX3zjcJyt/cr9Z2wYA8CP5J1w6KE7xqMh0DAkhfjhNDpQb2Q==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@astrojs/compiler': '>=0.27.0' dependencies: - '@astrojs/compiler': 0.31.0 - synckit: 0.8.5 + '@astrojs/compiler': 2.7.0 + synckit: 0.9.0 dev: true - /available-typed-arrays@1.0.5: - resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + /available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + dependencies: + possible-typed-array-names: 1.0.0 + dev: true + + /axobject-query@4.0.0: + resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} + dependencies: + dequal: 2.0.3 dev: true - /b4a@1.6.4: - resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} + /b4a@1.6.6: + resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} requiresBuild: true dev: true optional: true - /babel-plugin-transform-hook-names@1.0.2(@babel/core@7.18.10): + /babel-plugin-transform-hook-names@1.0.2(@babel/core@7.24.3): resolution: {integrity: sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==} peerDependencies: '@babel/core': ^7.12.10 dependencies: - '@babel/core': 7.18.10 + '@babel/core': 7.24.3 dev: true /bail@2.0.2: @@ -2903,6 +2664,37 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + /bare-events@2.2.1: + resolution: {integrity: sha512-9GYPpsPFvrWBkelIhOhTWtkeZxVxZOdb3VnFTCzlOo3OjvmTvzLoZFUT8kNFACx0vJej6QPney1Cf9BvzCNE/A==} + requiresBuild: true + dev: true + optional: true + + /bare-fs@2.2.2: + resolution: {integrity: sha512-X9IqgvyB0/VA5OZJyb5ZstoN62AzD7YxVGog13kkfYWYqJYcK0kcqLZ6TrmH5qr4/8//ejVcX4x/a0UvaogXmA==} + requiresBuild: true + dependencies: + bare-events: 2.2.1 + bare-os: 2.2.1 + bare-path: 2.1.0 + streamx: 2.16.1 + dev: true + optional: true + + /bare-os@2.2.1: + resolution: {integrity: sha512-OwPyHgBBMkhC29Hl3O4/YfxW9n7mdTr2+SsO29XBWKKJsbgj3mnorDB80r5TiCQgQstgE5ga1qNYrpes6NvX2w==} + requiresBuild: true + dev: true + optional: true + + /bare-path@2.1.0: + resolution: {integrity: sha512-DIIg7ts8bdRKwJRJrUMy/PICEaQZaPGZ26lsSx9MJSwIhSrcdHn7/C8W+XmnG/rKi6BaRcz+JO00CjZteybDtw==} + requiresBuild: true + dependencies: + bare-os: 2.2.1 + dev: true + optional: true + /base-64@1.0.0: resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} dev: true @@ -2911,15 +2703,15 @@ packages: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true - /bcp-47-match@2.0.2: - resolution: {integrity: sha512-zy5swVXwQ25ttElhoN9Dgnqm6VFlMkeDNljvHSGqGNr4zClUosdFzxD+fQHJVmx3g3KY+r//wV/fmBHsa1ErnA==} + /bcp-47-match@2.0.3: + resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==} dev: true - /bcp-47-normalize@2.1.0: - resolution: {integrity: sha512-wa7PmQUfSSkbH9xRnnv4l77SlR8pbTdGnXeEiUsJ4cUNnxVpvGPwY0UKTg7NVOBrnHa/zQXJhCdEKsC5b3B3oA==} + /bcp-47-normalize@2.3.0: + resolution: {integrity: sha512-8I/wfzqQvttUFz7HVJgIZ7+dj3vUaIyIxYXaTRP1YWoSDfzt6TUmxaKZeuXR62qBmYr+nvuWINFRl6pZ5DlN4Q==} dependencies: bcp-47: 2.1.0 - bcp-47-match: 2.0.2 + bcp-47-match: 2.0.3 dev: true /bcp-47@2.1.0: @@ -2930,13 +2722,8 @@ packages: is-decimal: 2.0.1 dev: true - /big-integer@1.6.51: - resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} - engines: {node: '>=0.6'} - dev: true - - /binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + /binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} /bl@4.1.0: @@ -2961,6 +2748,10 @@ packages: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} dev: false + /boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: true + /boxen@7.1.1: resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} engines: {node: '>=14.16'} @@ -2975,13 +2766,6 @@ packages: wrap-ansi: 8.1.0 dev: true - /bplist-parser@0.2.0: - resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} - engines: {node: '>= 5.10.0'} - dependencies: - big-integer: 1.6.51 - dev: true - /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -2999,41 +2783,15 @@ packages: dependencies: fill-range: 7.0.1 - /browserslist@4.21.10: - resolution: {integrity: sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001524 - electron-to-chromium: 1.4.506 - node-releases: 2.0.13 - update-browserslist-db: 1.0.11(browserslist@4.21.10) - dev: true - - /browserslist@4.21.3: - resolution: {integrity: sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==} + /browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001374 - electron-to-chromium: 1.4.211 - node-releases: 2.0.6 - update-browserslist-db: 1.0.5(browserslist@4.21.3) - dev: true - - /browserslist@4.22.2: - resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001568 - electron-to-chromium: 1.4.610 + caniuse-lite: 1.0.30001599 + electron-to-chromium: 1.4.711 node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.22.2) - dev: true - - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + update-browserslist-db: 1.0.13(browserslist@4.23.0) dev: true /buffer@5.7.1: @@ -3052,13 +2810,6 @@ packages: ieee754: 1.2.1 dev: true - /bundle-name@3.0.0: - resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} - engines: {node: '>=12'} - dependencies: - run-applescript: 5.0.0 - dev: true - /cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -3073,11 +2824,15 @@ packages: mkdirp2: 1.0.5 dev: false - /call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + /call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.2.0 + 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.2 dev: true /callsites@3.1.0: @@ -3090,24 +2845,22 @@ packages: engines: {node: '>=14.16'} dev: true - /caniuse-lite@1.0.30001374: - resolution: {integrity: sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==} + /caniuse-lite@1.0.30001599: + resolution: {integrity: sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==} dev: true - /caniuse-lite@1.0.30001524: - resolution: {integrity: sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA==} - dev: true + /canvas-confetti@1.9.2: + resolution: {integrity: sha512-6Xi7aHHzKwxZsem4mCKoqP6YwUG3HamaHHAlz1hTNQPCqXhARFpSXnkC9TWlahHY5CG6hSL5XexNjxK8irVErg==} + dev: false - /caniuse-lite@1.0.30001568: - resolution: {integrity: sha512-vSUkH84HontZJ88MiNrOau1EBrCqEQYgkC5gIySiDlpsm8sGVrhU7Kx4V6h0tnqaHzIHZv08HlJIwPbL4XL9+A==} + /canvaskit-wasm@0.37.2: + resolution: {integrity: sha512-212imazRF98gLOTiU4JAXM7xDvaknI7jaPtAg4ETXGW5rLQs6pomgIvVPUSfoKnQVTdGgzj+B4e+/u0Da20aGg==} dev: true - /canvas-confetti@1.6.0: - resolution: {integrity: sha512-ej+w/m8Jzpv9Z7W7uJZer14Ke8P2ogsjg4ZMGIuq4iqUOqY2Jq8BNW42iGmNfRwREaaEfFIczLuZZiEVSYNHAA==} - dev: false - - /canvaskit-wasm@0.37.0: - resolution: {integrity: sha512-hegK3gdVQJwgOre02kFHJDEo9b+VHY/bA/N+12C7fECSosGL9Q7gwcz4vnDdOnzSoeLCRH26oTNK3gEO+dm+dg==} + /canvaskit-wasm@0.39.1: + resolution: {integrity: sha512-Gy3lCmhUdKq+8bvDrs9t8+qf7RvcjuQn+we7vTVVyqgOVO1UVfHpsnBxkTZw+R4ApEJ3D5fKySl9TU11hmjl/A==} + dependencies: + '@webgpu/types': 0.1.21 dev: true /catharsis@0.9.0: @@ -3120,15 +2873,15 @@ packages: /ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - /chai@4.3.7: - resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} + /chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} engines: {node: '>=4'} dependencies: assertion-error: 1.1.0 - check-error: 1.0.2 + check-error: 1.0.3 deep-eql: 4.1.3 - get-func-name: 2.0.0 - loupe: 2.3.6 + get-func-name: 2.0.2 + loupe: 2.3.7 pathval: 1.1.1 type-detect: 4.0.8 dev: true @@ -3170,12 +2923,14 @@ packages: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} dev: true - /check-error@1.0.2: - resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 dev: true - /chokidar@3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} dependencies: anymatch: 3.1.3 @@ -3194,8 +2949,8 @@ packages: dev: true optional: true - /ci-info@3.8.0: - resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} dev: true @@ -3216,17 +2971,17 @@ packages: restore-cursor: 4.0.0 dev: true - /cli-spinners@2.9.0: - resolution: {integrity: sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==} + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} dev: true - /cli-truncate@3.1.0: - resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + /cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} dependencies: slice-ansi: 5.0.0 - string-width: 5.1.2 + string-width: 7.1.0 dev: true /cliui@8.0.1: @@ -3238,8 +2993,8 @@ packages: wrap-ansi: 7.0.0 dev: false - /clsx@2.0.0: - resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} + /clsx@2.1.0: + resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==} engines: {node: '>=6'} dev: true @@ -3247,6 +3002,10 @@ packages: resolution: {integrity: sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==} dev: true + /collapse-white-space@2.1.0: + resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + dev: true + /collect-all@1.0.4: resolution: {integrity: sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==} engines: {node: '>=0.10.0'} @@ -3298,16 +3057,12 @@ packages: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} dev: true - /comma-separated-tokens@2.0.2: - resolution: {integrity: sha512-G5yTt3KQN4Yn7Yk4ed73hlZ1evrFKXeUW3086p3PRFNp7m2vIjI6Pg+Kgb+oyzhd9F2qdcoj67+y3SdxL5XWsg==} - dev: true - /comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} dev: true - /commander@11.0.0: - resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==} + /commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} dev: true @@ -3316,21 +3071,11 @@ packages: dev: true /common-ancestor-path@1.0.1: - resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} - dev: true - - /concat-map@0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} - - /convert-source-map@1.8.0: - resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==} - dependencies: - safe-buffer: 5.1.2 + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} dev: true - /convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - dev: true + /concat-map@0.0.1: + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -3350,37 +3095,57 @@ packages: which: 2.0.2 dev: true + /css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + dev: true + + /css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: true + /cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} hasBin: true dev: true - /data-uri-to-buffer@4.0.0: - resolution: {integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==} + /data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} dev: true - /debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + /data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} dependencies: - ms: 2.0.0 + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 dev: true - /debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + /data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} dependencies: - ms: 2.1.2 + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + dev: true + + /data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 dev: true /debug@4.3.4: @@ -3409,7 +3174,7 @@ packages: optional: true /dedent-js@1.0.1: - resolution: {integrity: sha1-vuX7fJ5yfYXf+iRZDRDsGrElUwU=} + resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==} dev: true /deep-eql@4.1.3: @@ -3430,57 +3195,35 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /default-browser-id@3.0.0: - resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} - engines: {node: '>=12'} - dependencies: - bplist-parser: 0.2.0 - untildify: 4.0.0 - dev: true - - /default-browser@4.0.0: - resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==} - engines: {node: '>=14.16'} + /define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} dependencies: - bundle-name: 3.0.0 - default-browser-id: 3.0.0 - execa: 7.1.1 - titleize: 3.0.0 - dev: true - - /define-lazy-prop@3.0.0: - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} - engines: {node: '>=12'} + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 dev: true - /define-properties@1.1.4: - resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} dependencies: - has-property-descriptors: 1.0.0 + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 object-keys: 1.1.1 dev: true - /dequal@2.0.2: - resolution: {integrity: sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==} - engines: {node: '>=6'} - /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - dev: true - /detect-libc@2.0.2: - resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} + /detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} requiresBuild: true dev: true optional: true - /deterministic-object-hash@1.3.1: - resolution: {integrity: sha512-kQDIieBUreEgY+akq0N7o4FzZCr27dPG1xr3wq267vPwDlSXQ3UMcBXHqTGUBaM/5WDS1jwTYjxRhUzHeuiAvw==} - dev: true - /deterministic-object-hash@2.0.2: resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} engines: {node: '>=18'} @@ -3496,16 +3239,16 @@ packages: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} dependencies: dequal: 2.0.3 - dev: true - /diff@5.0.0: - resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} - engines: {node: '>=0.3.1'} + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true - /diff@5.1.0: - resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} + /diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} engines: {node: '>=0.3.1'} + dev: true /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} @@ -3532,31 +3275,31 @@ packages: esutils: 2.0.3 dev: true - /dom-serializer@1.4.1: - resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + /dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} dependencies: domelementtype: 2.3.0 - domhandler: 4.3.1 - entities: 2.2.0 + domhandler: 5.0.3 + entities: 4.5.0 dev: true /domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} dev: true - /domhandler@4.3.1: - resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + /domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} dependencies: domelementtype: 2.3.0 dev: true - /domutils@2.8.0: - resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + /domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} dependencies: - dom-serializer: 1.4.1 + dom-serializer: 2.0.0 domelementtype: 2.3.0 - domhandler: 4.3.1 + domhandler: 5.0.3 dev: true /dset@3.1.3: @@ -3574,33 +3317,21 @@ packages: dependencies: commander: 2.20.3 lru-cache: 4.1.5 - semver: 5.7.1 + semver: 5.7.2 sigmund: 1.0.1 dev: true - /electron-to-chromium@1.4.211: - resolution: {integrity: sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==} - dev: true - - /electron-to-chromium@1.4.506: - resolution: {integrity: sha512-xxGct4GPAKSRlrLBtJxJFYy74W11zX6PO9GyHgl/U+2s3Dp0ZEwAklDfNHXOWcvH7zWMpsmgbR0ggEuaYAVvHA==} + /electron-to-chromium@1.4.711: + resolution: {integrity: sha512-hRg81qzvUEibX2lDxnFlVCHACa+LtrCPIsWAxo161LDYIB3jauf57RGsMZV9mvGwE98yGH06icj3zBEoOkxd/w==} dev: true - /electron-to-chromium@1.4.610: - resolution: {integrity: sha512-mqi2oL1mfeHYtOdCxbPQYV/PL7YrQlxbvFEZ0Ee8GbDdShimqt2/S6z2RWqysuvlwdOrQdqvE0KZrBTipAeJzg==} - dev: true - - /emmet@2.4.6: - resolution: {integrity: sha512-dJfbdY/hfeTyf/Ef7Y7ubLYzkBvPQ912wPaeVYpAxvFxkEBf/+hJu4H6vhAvFN6HlxqedlfVn2x1S44FfQ97pg==} + /emmet@2.4.7: + resolution: {integrity: sha512-O5O5QNqtdlnQM2bmKHtJgyChcrFMgQuulI+WdiOw2NArzprUqqxUW6bgYtKvzKgrsYpuLWalOkdhNP+1jluhCA==} dependencies: '@emmetio/abbreviation': 2.3.3 '@emmetio/css-abbreviation': 2.1.8 dev: false - /emoji-regex@10.2.1: - resolution: {integrity: sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==} - dev: true - /emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} dev: true @@ -3624,80 +3355,170 @@ packages: resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==} dev: false - /entities@2.2.0: - resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} - dev: true - - /entities@3.0.1: - resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} - engines: {node: '>=0.12'} - dev: true - /entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} dev: true - /es-abstract@1.21.1: - resolution: {integrity: sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==} + /es-abstract@1.22.5: + resolution: {integrity: sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.3 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.5 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.15 + dev: true + + /es-abstract@1.23.2: + resolution: {integrity: sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==} engines: {node: '>= 0.4'} dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - es-set-tostringtag: 2.0.1 + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 es-to-primitive: 1.2.1 - function-bind: 1.1.1 - function.prototype.name: 1.1.5 - get-intrinsic: 1.2.0 - get-symbol-description: 1.0.0 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 globalthis: 1.0.3 gopd: 1.0.1 - has: 1.0.3 - has-property-descriptors: 1.0.0 - has-proto: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 has-symbols: 1.0.3 - internal-slot: 1.0.4 - is-array-buffer: 3.0.1 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 is-callable: 1.2.7 - is-negative-zero: 2.0.2 + is-data-view: 1.0.1 + is-negative-zero: 2.0.3 is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 + is-shared-array-buffer: 1.0.3 is-string: 1.0.7 - is-typed-array: 1.1.10 + is-typed-array: 1.1.13 is-weakref: 1.0.2 - object-inspect: 1.12.3 + object-inspect: 1.13.1 object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.4.3 - safe-regex-test: 1.0.0 - string.prototype.trimend: 1.0.6 - string.prototype.trimstart: 1.0.6 - typed-array-length: 1.0.4 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.5 unbox-primitive: 1.0.2 - which-typed-array: 1.1.9 + which-typed-array: 1.1.15 + dev: true + + /es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + dev: true + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + dev: true + + /es-iterator-helpers@1.0.18: + resolution: {integrity: sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.2 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.3 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + globalthis: 1.0.3 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + internal-slot: 1.0.7 + iterator.prototype: 1.1.2 + safe-array-concat: 1.1.2 dev: true - /es-module-lexer@1.3.0: - resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==} + /es-module-lexer@1.4.2: + resolution: {integrity: sha512-7nOqkomXZEaxUDJw21XZNtRk739QvrPSoZoRtbsEfcii00vdzZUh6zh1CQwHhrib8MdEtJfv5rJiGeb4KuV/vw==} dev: true - /es-module-lexer@1.4.1: - resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==} + /es-module-lexer@1.5.0: + resolution: {integrity: sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==} + dev: true + + /es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 dev: true - /es-set-tostringtag@2.0.1: - resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + /es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} engines: {node: '>= 0.4'} dependencies: - get-intrinsic: 1.2.0 - has: 1.0.3 - has-tostringtag: 1.0.0 + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.2 dev: true - /es-shim-unscopables@1.0.0: - resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} dependencies: - has: 1.0.3 + hasown: 2.0.2 dev: true /es-to-primitive@1.2.1: @@ -3919,68 +3740,70 @@ packages: esbuild-windows-arm64: 0.15.18 dev: true - /esbuild@0.18.20: - resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + /esbuild@0.19.12: + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.18.20 - '@esbuild/android-arm64': 0.18.20 - '@esbuild/android-x64': 0.18.20 - '@esbuild/darwin-arm64': 0.18.20 - '@esbuild/darwin-x64': 0.18.20 - '@esbuild/freebsd-arm64': 0.18.20 - '@esbuild/freebsd-x64': 0.18.20 - '@esbuild/linux-arm': 0.18.20 - '@esbuild/linux-arm64': 0.18.20 - '@esbuild/linux-ia32': 0.18.20 - '@esbuild/linux-loong64': 0.18.20 - '@esbuild/linux-mips64el': 0.18.20 - '@esbuild/linux-ppc64': 0.18.20 - '@esbuild/linux-riscv64': 0.18.20 - '@esbuild/linux-s390x': 0.18.20 - '@esbuild/linux-x64': 0.18.20 - '@esbuild/netbsd-x64': 0.18.20 - '@esbuild/openbsd-x64': 0.18.20 - '@esbuild/sunos-x64': 0.18.20 - '@esbuild/win32-arm64': 0.18.20 - '@esbuild/win32-ia32': 0.18.20 - '@esbuild/win32-x64': 0.18.20 - dev: true - - /esbuild@0.19.9: - resolution: {integrity: sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==} + '@esbuild/aix-ppc64': 0.19.12 + '@esbuild/android-arm': 0.19.12 + '@esbuild/android-arm64': 0.19.12 + '@esbuild/android-x64': 0.19.12 + '@esbuild/darwin-arm64': 0.19.12 + '@esbuild/darwin-x64': 0.19.12 + '@esbuild/freebsd-arm64': 0.19.12 + '@esbuild/freebsd-x64': 0.19.12 + '@esbuild/linux-arm': 0.19.12 + '@esbuild/linux-arm64': 0.19.12 + '@esbuild/linux-ia32': 0.19.12 + '@esbuild/linux-loong64': 0.19.12 + '@esbuild/linux-mips64el': 0.19.12 + '@esbuild/linux-ppc64': 0.19.12 + '@esbuild/linux-riscv64': 0.19.12 + '@esbuild/linux-s390x': 0.19.12 + '@esbuild/linux-x64': 0.19.12 + '@esbuild/netbsd-x64': 0.19.12 + '@esbuild/openbsd-x64': 0.19.12 + '@esbuild/sunos-x64': 0.19.12 + '@esbuild/win32-arm64': 0.19.12 + '@esbuild/win32-ia32': 0.19.12 + '@esbuild/win32-x64': 0.19.12 + dev: true + + /esbuild@0.20.2: + resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.19.9 - '@esbuild/android-arm64': 0.19.9 - '@esbuild/android-x64': 0.19.9 - '@esbuild/darwin-arm64': 0.19.9 - '@esbuild/darwin-x64': 0.19.9 - '@esbuild/freebsd-arm64': 0.19.9 - '@esbuild/freebsd-x64': 0.19.9 - '@esbuild/linux-arm': 0.19.9 - '@esbuild/linux-arm64': 0.19.9 - '@esbuild/linux-ia32': 0.19.9 - '@esbuild/linux-loong64': 0.19.9 - '@esbuild/linux-mips64el': 0.19.9 - '@esbuild/linux-ppc64': 0.19.9 - '@esbuild/linux-riscv64': 0.19.9 - '@esbuild/linux-s390x': 0.19.9 - '@esbuild/linux-x64': 0.19.9 - '@esbuild/netbsd-x64': 0.19.9 - '@esbuild/openbsd-x64': 0.19.9 - '@esbuild/sunos-x64': 0.19.9 - '@esbuild/win32-arm64': 0.19.9 - '@esbuild/win32-ia32': 0.19.9 - '@esbuild/win32-x64': 0.19.9 - dev: true - - /escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + '@esbuild/aix-ppc64': 0.20.2 + '@esbuild/android-arm': 0.20.2 + '@esbuild/android-arm64': 0.20.2 + '@esbuild/android-x64': 0.20.2 + '@esbuild/darwin-arm64': 0.20.2 + '@esbuild/darwin-x64': 0.20.2 + '@esbuild/freebsd-arm64': 0.20.2 + '@esbuild/freebsd-x64': 0.20.2 + '@esbuild/linux-arm': 0.20.2 + '@esbuild/linux-arm64': 0.20.2 + '@esbuild/linux-ia32': 0.20.2 + '@esbuild/linux-loong64': 0.20.2 + '@esbuild/linux-mips64el': 0.20.2 + '@esbuild/linux-ppc64': 0.20.2 + '@esbuild/linux-riscv64': 0.20.2 + '@esbuild/linux-s390x': 0.20.2 + '@esbuild/linux-x64': 0.20.2 + '@esbuild/netbsd-x64': 0.20.2 + '@esbuild/openbsd-x64': 0.20.2 + '@esbuild/sunos-x64': 0.20.2 + '@esbuild/win32-arm64': 0.20.2 + '@esbuild/win32-ia32': 0.20.2 + '@esbuild/win32-x64': 0.20.2 + dev: true + + /escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} /escape-string-regexp@1.0.5: @@ -4002,147 +3825,129 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - /eslint-plugin-astro@0.21.0(eslint@8.29.0): - resolution: {integrity: sha512-7pEhTfYT+tlOMOSmQV77TNgCeuFZgqSAqJPTHh6LYlwLqYyBQLURc5RRtlQqCJkufSh4Fan4nsV3LXCT/vjpCA==} + /eslint-compat-utils@0.5.0(eslint@8.57.0): + resolution: {integrity: sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + eslint: 8.57.0 + semver: 7.6.0 + dev: true + + /eslint-plugin-astro@0.33.1(eslint@8.57.0): + resolution: {integrity: sha512-wVyxAf8Ulmljv5qJQLgspWe17LR4hLXcksIENtUlEC3W7rleBVEKXS+hIqzBfCbpkBLZpl1tsYes1AGpYHd13w==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '>=7.0.0' dependencies: - '@typescript-eslint/types': 5.32.0 - astro-eslint-parser: 0.9.2 - eslint: 8.29.0 - eslint-utils: 3.0.0(eslint@8.29.0) - postcss: 8.4.18 - postcss-selector-parser: 6.0.10 - sourcemap-codec: 1.4.8 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@jridgewell/sourcemap-codec': 1.4.15 + '@typescript-eslint/types': 5.62.0 + astro-eslint-parser: 0.16.3 + eslint: 8.57.0 + eslint-compat-utils: 0.5.0(eslint@8.57.0) + globals: 13.24.0 + postcss: 8.4.37 + postcss-selector-parser: 6.0.16 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-react@7.32.1(eslint@8.29.0): - resolution: {integrity: sha512-vOjdgyd0ZHBXNsmvU+785xY8Bfe57EFbTYYk8XrROzWpr9QBvpjITvAXt9xqcE6+8cjR/g1+mfumPToxsl1www==} + /eslint-plugin-react@7.34.1(eslint@8.57.0): + resolution: {integrity: sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 dependencies: - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - array.prototype.tosorted: 1.1.1 + array-includes: 3.1.7 + array.prototype.findlast: 1.2.4 + array.prototype.flatmap: 1.3.2 + array.prototype.toreversed: 1.1.2 + array.prototype.tosorted: 1.1.3 doctrine: 2.1.0 - eslint: 8.29.0 + es-iterator-helpers: 1.0.18 + eslint: 8.57.0 estraverse: 5.3.0 - jsx-ast-utils: 3.3.3 + jsx-ast-utils: 3.3.5 minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.hasown: 1.1.2 - object.values: 1.1.6 + object.entries: 1.1.8 + object.fromentries: 2.0.8 + object.hasown: 1.1.3 + object.values: 1.2.0 prop-types: 15.8.1 - resolve: 2.0.0-next.4 - semver: 6.3.0 - string.prototype.matchall: 4.0.8 - dev: true - - /eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.10 dev: true - /eslint-scope@7.1.1: - resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 dev: true - /eslint-utils@3.0.0(eslint@8.29.0): - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - dependencies: - eslint: 8.29.0 - eslint-visitor-keys: 2.1.0 - dev: true - - /eslint-visitor-keys@2.1.0: - resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} - engines: {node: '>=10'} - dev: true - - /eslint-visitor-keys@3.3.0: - resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.29.0: - resolution: {integrity: sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==} + /eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint/eslintrc': 1.3.3 - '@humanwhocodes/config-array': 0.11.7 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 debug: 4.3.4 doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.1.1 - eslint-utils: 3.0.0(eslint@8.29.0) - eslint-visitor-keys: 3.3.0 - espree: 9.4.1 - esquery: 1.4.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.15.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.0 - import-fresh: 3.3.0 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 - js-sdsl: 4.2.0 js-yaml: 4.1.0 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.4.1 lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 - optionator: 0.9.1 - regexpp: 3.2.0 + optionator: 0.9.3 strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 text-table: 0.2.0 transitivePeerDependencies: - supports-color dev: true - /espree@9.3.3: - resolution: {integrity: sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.8.0 - acorn-jsx: 5.3.2(acorn@8.8.0) - eslint-visitor-keys: 3.3.0 - dev: true - - /espree@9.4.1: - resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - acorn: 8.10.0 - acorn-jsx: 5.3.2(acorn@8.10.0) - eslint-visitor-keys: 3.3.0 + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) + eslint-visitor-keys: 3.4.3 dev: true /esprima@4.0.1: @@ -4151,8 +3956,8 @@ packages: hasBin: true dev: true - /esquery@1.4.0: - resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} dependencies: estraverse: 5.3.0 @@ -4165,47 +3970,43 @@ packages: estraverse: 5.3.0 dev: true - /estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - dev: true - /estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} dev: true - /estree-util-attach-comments@2.1.1: - resolution: {integrity: sha512-+5Ba/xGGS6mnwFbXIuQiDPTbuTxuMCooq3arVv7gPZtYpjp+VXH/NkHAP35OOefPhNG/UGqU3vt/LTABwcHX0w==} + /estree-util-attach-comments@3.0.0: + resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} dependencies: - '@types/estree': 1.0.1 + '@types/estree': 1.0.5 dev: true - /estree-util-build-jsx@2.2.2: - resolution: {integrity: sha512-m56vOXcOBuaF+Igpb9OPAy7f9w9OIkb5yhjsZuaPm7HoGi4oTOQi0h2+yZ+AtKklYFZ+rPC4n0wYCJCEU1ONqg==} + /estree-util-build-jsx@3.0.1: + resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} dependencies: - '@types/estree-jsx': 1.0.0 - estree-util-is-identifier-name: 2.1.0 + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 estree-walker: 3.0.3 dev: true - /estree-util-is-identifier-name@2.1.0: - resolution: {integrity: sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==} + /estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} dev: true - /estree-util-to-js@1.2.0: - resolution: {integrity: sha512-IzU74r1PK5IMMGZXUVZbmiu4A1uhiPgW5hm1GjcOfr4ZzHaMPpLNJjR7HjXiIOzi25nZDrgFTobHTkV5Q6ITjA==} + /estree-util-to-js@2.0.0: + resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} dependencies: - '@types/estree-jsx': 1.0.0 + '@types/estree-jsx': 1.0.5 astring: 1.8.6 source-map: 0.7.4 dev: true - /estree-util-visit@1.2.1: - resolution: {integrity: sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==} + /estree-util-visit@2.0.0: + resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} dependencies: - '@types/estree-jsx': 1.0.0 - '@types/unist': 2.0.8 + '@types/estree-jsx': 1.0.5 + '@types/unist': 3.0.2 dev: true /estree-walker@2.0.2: @@ -4215,7 +4016,7 @@ packages: /estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} dependencies: - '@types/estree': 1.0.1 + '@types/estree': 1.0.5 dev: true /esutils@2.0.3: @@ -4231,51 +4032,6 @@ packages: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} dev: true - /execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - dev: true - - /execa@7.1.1: - resolution: {integrity: sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==} - engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 4.3.1 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.1.0 - onetime: 6.0.0 - signal-exit: 3.0.7 - strip-final-newline: 3.0.0 - dev: true - - /execa@7.2.0: - resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} - engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 4.3.1 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.1.0 - onetime: 6.0.0 - signal-exit: 3.0.7 - strip-final-newline: 3.0.0 - dev: true - /execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -4285,7 +4041,7 @@ packages: human-signals: 5.0.0 is-stream: 3.0.0 merge-stream: 2.0.0 - npm-run-path: 5.1.0 + npm-run-path: 5.3.0 onetime: 6.0.0 signal-exit: 4.1.0 strip-final-newline: 3.0.0 @@ -4298,13 +4054,13 @@ packages: dev: true optional: true - /expressive-code@0.20.0: - resolution: {integrity: sha512-P4KHAVpECDLBOWQWJNkNsDwYdVxrntIzFnSoIaeh3xBRNo/Fdc+xhlJHdqODJwRS8bSqoGt2lgBUOe5s5PfxwA==} + /expressive-code@0.33.5: + resolution: {integrity: sha512-UPg2jSvZEfXPiCa4MKtMoMQ5Hwiv7In5/LSCa/ukhjzZqPO48iVsCcEBgXWEUmEAQ02P0z00/xFfBmVnUKH+Zw==} dependencies: - '@expressive-code/core': 0.20.0 - '@expressive-code/plugin-frames': 0.20.0 - '@expressive-code/plugin-shiki': 0.20.0 - '@expressive-code/plugin-text-markers': 0.20.0 + '@expressive-code/core': 0.33.5 + '@expressive-code/plugin-frames': 0.33.5 + '@expressive-code/plugin-shiki': 0.33.5 + '@expressive-code/plugin-text-markers': 0.33.5 dev: true /extend-shallow@2.0.1: @@ -4327,39 +4083,6 @@ packages: dev: true optional: true - /fast-glob@3.2.11: - resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} - engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.5 - dev: true - - /fast-glob@3.3.0: - resolution: {integrity: sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==} - engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.5 - dev: true - - /fast-glob@3.3.1: - resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} - engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.5 - dev: false - /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -4369,7 +4092,6 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 - dev: true /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -4379,30 +4101,24 @@ packages: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true - /fastq@1.13.0: - resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} + /fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} dependencies: reusify: 1.0.4 - /fault@2.0.1: - resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} - dependencies: - format: 0.2.2 - dev: true - - /fetch-blob@3.1.5: - resolution: {integrity: sha512-N64ZpKqoLejlrwkIAnb9iLSA3Vx/kjgzpcDhygcqJ2KKjky8nCgUQ+dzXtbrLaWZGZNmNfQTsiQ0weZ1svglHg==} + /fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} dependencies: node-domexception: 1.0.0 - web-streams-polyfill: 3.2.0 + web-streams-polyfill: 3.3.3 dev: true /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flat-cache: 3.0.4 + flat-cache: 3.2.0 dev: true /file-set@4.0.2: @@ -4442,20 +4158,21 @@ packages: pkg-dir: 4.2.0 dev: true - /flat-cache@3.0.4: - resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + /flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flatted: 3.2.6 + flatted: 3.3.1 + keyv: 4.5.4 rimraf: 3.0.2 dev: true - /flatted@3.2.6: - resolution: {integrity: sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==} + /flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true - /flattie@1.1.0: - resolution: {integrity: sha512-xU99gDEnciIwJdGcBmNHnzTJ/w5AT+VFJOu6sTB6WM8diOYNA3Sa+K1DiEBQ7XH4QikQq3iFW1U+jRVcotQnBw==} + /flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} engines: {node: '>=8'} dev: true @@ -4465,16 +4182,11 @@ packages: is-callable: 1.2.7 dev: true - /format@0.2.2: - resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} - engines: {node: '>=0.4.x'} - dev: true - /formdata-polyfill@4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} dependencies: - fetch-blob: 3.1.5 + fetch-blob: 3.2.0 dev: true /fs-constants@1.0.0: @@ -4484,7 +4196,7 @@ packages: optional: true /fs-then-native@2.0.0: - resolution: {integrity: sha1-GaEk2U2QwiyOBF8ujdbr6jbUjGc=} + resolution: {integrity: sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==} engines: {node: '>=4.0.0'} dev: false @@ -4498,17 +4210,17 @@ packages: requiresBuild: true optional: true - /function-bind@1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} dev: true - /function.prototype.name@1.1.5: - resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.21.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 functions-have-names: 1.2.3 dev: true @@ -4531,21 +4243,19 @@ packages: engines: {node: '>=18'} dev: true - /get-func-name@2.0.0: - resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} dev: true - /get-intrinsic@1.2.0: - resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} dependencies: - function-bind: 1.1.1 - has: 1.0.3 + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 has-symbols: 1.0.3 - dev: true - - /get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} + hasown: 2.0.2 dev: true /get-stream@8.0.1: @@ -4553,12 +4263,13 @@ packages: engines: {node: '>=16'} dev: true - /get-symbol-description@1.0.0: - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + /get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 dev: true /github-from-package@0.0.0: @@ -4567,13 +4278,8 @@ packages: dev: true optional: true - /github-slugger@1.5.0: - resolution: {integrity: sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==} - dev: false - /github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} - dev: true /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -4603,8 +4309,8 @@ packages: engines: {node: '>=4'} dev: true - /globals@13.15.0: - resolution: {integrity: sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==} + /globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 @@ -4614,7 +4320,7 @@ packages: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} engines: {node: '>= 0.4'} dependencies: - define-properties: 1.1.4 + define-properties: 1.2.1 dev: true /globby@11.1.0: @@ -4623,8 +4329,8 @@ packages: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.0 - ignore: 5.2.0 + fast-glob: 3.3.2 + ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 dev: true @@ -4632,15 +4338,14 @@ packages: /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: - get-intrinsic: 1.2.0 + get-intrinsic: 1.2.4 dev: true /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: true - /grapheme-splitter@1.0.4: - resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true /gray-matter@4.0.3: @@ -4667,14 +4372,14 @@ packages: engines: {node: '>=8'} dev: true - /has-property-descriptors@1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + /has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} dependencies: - get-intrinsic: 1.2.0 + es-define-property: 1.0.0 dev: true - /has-proto@1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} engines: {node: '>= 0.4'} dev: true @@ -4683,45 +4388,24 @@ packages: engines: {node: '>= 0.4'} dev: true - /has-tostringtag@1.0.0: - resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + /has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.3 dev: true - /has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 - dev: true - - /hast-to-hyperscript@10.0.1: - resolution: {integrity: sha512-dhIVGoKCQVewFi+vz3Vt567E4ejMppS1haBRL6TEmeLeJVB1i/FJIIg/e6s1Bwn0g5qtYojHEKvyGA+OZuyifw==} - dependencies: - '@types/unist': 2.0.6 - comma-separated-tokens: 2.0.2 - property-information: 6.1.1 - space-separated-tokens: 2.0.1 - style-to-object: 0.3.0 - unist-util-is: 5.1.1 - web-namespaces: 2.0.1 - dev: true - - /hast-util-from-html@1.0.0: - resolution: {integrity: sha512-tXYPhk28aMtDjGb4xNDaxtGKqlyZAEPGjN12jPCjczWppdLSrG/0r604a3FMvrd+9nV1HclILQiVqMnuPxN0WQ==} + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} dependencies: - '@types/hast': 2.3.4 - hast-util-from-parse5: 7.1.0 - parse5: 7.1.2 - vfile: 5.3.6 + function-bind: 1.1.2 dev: true /hast-util-from-html@2.0.1: resolution: {integrity: sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==} dependencies: - '@types/hast': 3.0.3 + '@types/hast': 3.0.4 devlop: 1.1.0 hast-util-from-parse5: 8.0.1 parse5: 7.1.2 @@ -4729,26 +4413,13 @@ packages: vfile-message: 4.0.2 dev: true - /hast-util-from-parse5@7.1.0: - resolution: {integrity: sha512-m8yhANIAccpU4K6+121KpPP55sSl9/samzQSQGpb0mTExcNh2WlvjtMwSWFhg6uqD4Rr6Nfa8N6TMypQM51rzQ==} - dependencies: - '@types/hast': 2.3.4 - '@types/parse5': 6.0.3 - '@types/unist': 2.0.6 - hastscript: 7.0.2 - property-information: 6.1.1 - vfile: 5.3.6 - vfile-location: 4.0.1 - web-namespaces: 2.0.1 - dev: true - /hast-util-from-parse5@7.1.2: resolution: {integrity: sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==} dependencies: - '@types/hast': 2.3.4 - '@types/unist': 2.0.8 + '@types/hast': 2.3.10 + '@types/unist': 2.0.10 hastscript: 7.2.0 - property-information: 6.2.0 + property-information: 6.4.1 vfile: 5.3.7 vfile-location: 4.1.0 web-namespaces: 2.0.1 @@ -4757,71 +4428,43 @@ packages: /hast-util-from-parse5@8.0.1: resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==} dependencies: - '@types/hast': 3.0.3 + '@types/hast': 3.0.4 '@types/unist': 3.0.2 devlop: 1.1.0 hastscript: 8.0.0 - property-information: 6.2.0 + property-information: 6.4.1 vfile: 6.0.1 vfile-location: 5.0.2 web-namespaces: 2.0.1 dev: true - /hast-util-has-property@2.0.0: - resolution: {integrity: sha512-4Qf++8o5v14us4Muv3HRj+Er6wTNGA/N9uCaZMty4JWvyFKLdhULrv4KE1b65AthsSO9TXSZnjuxS8ecIyhb0w==} - dev: false - - /hast-util-heading-rank@2.1.0: - resolution: {integrity: sha512-w+Rw20Q/iWp2Bcnr6uTrYU6/ftZLbHKhvc8nM26VIWpDqDMlku2iXUVTeOlsdoih/UKQhY7PHQ+vZ0Aqq8bxtQ==} - dependencies: - '@types/hast': 2.3.4 - dev: false - - /hast-util-is-element@2.1.2: - resolution: {integrity: sha512-thjnlGAnwP8ef/GSO1Q8BfVk2gundnc2peGQqEg2kUt/IqesiGg/5mSwN2fE7nLzy61pg88NG6xV+UrGOrx9EA==} + /hast-util-heading-rank@3.0.0: + resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==} dependencies: - '@types/hast': 2.3.4 - '@types/unist': 2.0.6 + '@types/hast': 3.0.4 dev: false - /hast-util-parse-selector@3.1.0: - resolution: {integrity: sha512-AyjlI2pTAZEOeu7GeBPZhROx0RHBnydkQIXlhnFzDi0qfXTmGUWoCYZtomHbrdrheV4VFUlPcfJ6LMF5T6sQzg==} + /hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} dependencies: - '@types/hast': 2.3.4 - dev: true + '@types/hast': 3.0.4 /hast-util-parse-selector@3.1.1: resolution: {integrity: sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==} dependencies: - '@types/hast': 2.3.4 + '@types/hast': 2.3.10 dev: true /hast-util-parse-selector@4.0.0: resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} dependencies: - '@types/hast': 3.0.3 - dev: true - - /hast-util-raw@7.2.1: - resolution: {integrity: sha512-wgtppqXVdXzkDXDFclLLdAyVUJSKMYYi6LWIAbA8oFqEdwksYIcPGM3RkKV1Dfn5GElvxhaOCs0jmCOMayxd3A==} - dependencies: - '@types/hast': 2.3.4 - '@types/parse5': 6.0.3 - hast-util-from-parse5: 7.1.0 - hast-util-to-parse5: 7.0.0 - html-void-elements: 2.0.1 - parse5: 6.0.1 - unist-util-position: 4.0.3 - unist-util-visit: 4.1.0 - vfile: 5.3.6 - web-namespaces: 2.0.1 - zwitch: 2.0.4 + '@types/hast': 3.0.4 dev: true /hast-util-raw@7.2.3: resolution: {integrity: sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==} dependencies: - '@types/hast': 2.3.4 + '@types/hast': 2.3.10 '@types/parse5': 6.0.3 hast-util-from-parse5: 7.1.2 hast-util-to-parse5: 7.1.0 @@ -4834,16 +4477,16 @@ packages: zwitch: 2.0.4 dev: true - /hast-util-raw@9.0.1: - resolution: {integrity: sha512-5m1gmba658Q+lO5uqL5YNGQWeh1MYWZbZmWrM5lncdcuiXuo5E2HT/CIOp0rLF8ksfSwiCVJ3twlgVRyTGThGA==} + /hast-util-raw@9.0.2: + resolution: {integrity: sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA==} dependencies: - '@types/hast': 3.0.3 + '@types/hast': 3.0.4 '@types/unist': 3.0.2 '@ungap/structured-clone': 1.2.0 hast-util-from-parse5: 8.0.1 hast-util-to-parse5: 8.0.0 html-void-elements: 3.0.0 - mdast-util-to-hast: 13.0.2 + mdast-util-to-hast: 13.1.0 parse5: 7.1.2 unist-util-position: 5.0.0 unist-util-visit: 5.0.0 @@ -4852,23 +4495,24 @@ packages: zwitch: 2.0.4 dev: true - /hast-util-to-estree@2.3.3: - resolution: {integrity: sha512-ihhPIUPxN0v0w6M5+IiAZZrn0LH2uZomeWwhn7uP7avZC6TE7lIiEh2yBMPr5+zi1aUCXq6VoYRgs2Bw9xmycQ==} + /hast-util-to-estree@3.1.0: + resolution: {integrity: sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==} dependencies: - '@types/estree': 1.0.1 - '@types/estree-jsx': 1.0.0 - '@types/hast': 2.3.4 - '@types/unist': 2.0.8 + '@types/estree': 1.0.5 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 comma-separated-tokens: 2.0.3 - estree-util-attach-comments: 2.1.1 - estree-util-is-identifier-name: 2.1.0 - hast-util-whitespace: 2.0.1 - mdast-util-mdx-expression: 1.3.2 - mdast-util-mdxjs-esm: 1.3.1 - property-information: 6.2.0 + devlop: 1.1.0 + estree-util-attach-comments: 3.0.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.0 + mdast-util-mdx-jsx: 3.1.2 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 6.4.1 space-separated-tokens: 2.0.2 - style-to-object: 0.4.1 - unist-util-position: 4.0.4 + style-to-object: 0.4.4 + unist-util-position: 5.0.0 zwitch: 2.0.4 transitivePeerDependencies: - supports-color @@ -4877,15 +4521,15 @@ packages: /hast-util-to-html@8.0.4: resolution: {integrity: sha512-4tpQTUOr9BMjtYyNlt0P50mH7xj0Ks2xpo8M943Vykljf99HW6EzulIoJP1N3eKOSScEHzyzi9dm7/cn0RfGwA==} dependencies: - '@types/hast': 2.3.4 - '@types/unist': 2.0.6 + '@types/hast': 2.3.10 + '@types/unist': 2.0.10 ccount: 2.0.1 - comma-separated-tokens: 2.0.2 - hast-util-raw: 7.2.1 - hast-util-whitespace: 2.0.0 + comma-separated-tokens: 2.0.3 + hast-util-raw: 7.2.3 + hast-util-whitespace: 2.0.1 html-void-elements: 2.0.1 - property-information: 6.1.1 - space-separated-tokens: 2.0.1 + property-information: 6.4.1 + space-separated-tokens: 2.0.2 stringify-entities: 4.0.3 zwitch: 2.0.4 dev: true @@ -4893,37 +4537,48 @@ packages: /hast-util-to-html@9.0.0: resolution: {integrity: sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw==} dependencies: - '@types/hast': 3.0.3 + '@types/hast': 3.0.4 '@types/unist': 3.0.2 ccount: 2.0.1 comma-separated-tokens: 2.0.3 - hast-util-raw: 9.0.1 + hast-util-raw: 9.0.2 hast-util-whitespace: 3.0.0 html-void-elements: 3.0.0 - mdast-util-to-hast: 13.0.2 - property-information: 6.2.0 + mdast-util-to-hast: 13.1.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 stringify-entities: 4.0.3 zwitch: 2.0.4 dev: true - /hast-util-to-parse5@7.0.0: - resolution: {integrity: sha512-YHiS6aTaZ3N0Q3nxaY/Tj98D6kM8QX5Q8xqgg8G45zR7PvWnPGPP0vcKCgb/moIydEJ/QWczVrX0JODCVeoV7A==} + /hast-util-to-jsx-runtime@2.3.0: + resolution: {integrity: sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==} dependencies: - '@types/hast': 2.3.4 - '@types/parse5': 6.0.3 - hast-to-hyperscript: 10.0.1 - property-information: 6.1.1 - web-namespaces: 2.0.1 - zwitch: 2.0.4 + '@types/estree': 1.0.5 + '@types/hast': 3.0.4 + '@types/unist': 3.0.2 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.0 + mdast-util-mdx-jsx: 3.1.2 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 6.4.1 + space-separated-tokens: 2.0.2 + style-to-object: 1.0.6 + unist-util-position: 5.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color dev: true /hast-util-to-parse5@7.1.0: resolution: {integrity: sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==} dependencies: - '@types/hast': 2.3.4 + '@types/hast': 2.3.10 comma-separated-tokens: 2.0.3 - property-information: 6.2.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 web-namespaces: 2.0.1 zwitch: 2.0.4 @@ -4932,22 +4587,27 @@ packages: /hast-util-to-parse5@8.0.0: resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} dependencies: - '@types/hast': 3.0.3 + '@types/hast': 3.0.4 comma-separated-tokens: 2.0.3 devlop: 1.1.0 - property-information: 6.2.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 web-namespaces: 2.0.1 zwitch: 2.0.4 dev: true - /hast-util-to-string@2.0.0: - resolution: {integrity: sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==} + /hast-util-to-string@3.0.0: + resolution: {integrity: sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA==} dependencies: - '@types/hast': 2.3.4 + '@types/hast': 3.0.4 - /hast-util-whitespace@2.0.0: - resolution: {integrity: sha512-Pkw+xBHuV6xFeJprJe2BBEoDV+AvQySaz3pPDRUs5PNZEMQjpXJJueqrpcHIXxnWTcAGi/UOCgVShlkY6kLoqg==} + /hast-util-to-text@4.0.0: + resolution: {integrity: sha512-EWiE1FSArNBPUo1cKWtzqgnuRQwEeQbQtnFJRYV1hb1BWDgrAlBU0ExptvZMM/KSA82cDpm2sFGf3Dmc5Mza3w==} + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.2 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 dev: true /hast-util-whitespace@2.0.1: @@ -4957,39 +4617,44 @@ packages: /hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} dependencies: - '@types/hast': 3.0.3 - dev: true - - /hastscript@7.0.2: - resolution: {integrity: sha512-uA8ooUY4ipaBvKcMuPehTAB/YfFLSSzCwFSwT6ltJbocFUKH/GDHLN+tflq7lSRf9H86uOuxOFkh1KgIy3Gg2g==} - dependencies: - '@types/hast': 2.3.4 - comma-separated-tokens: 2.0.2 - hast-util-parse-selector: 3.1.0 - property-information: 6.1.1 - space-separated-tokens: 2.0.1 + '@types/hast': 3.0.4 dev: true /hastscript@7.2.0: resolution: {integrity: sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==} dependencies: - '@types/hast': 2.3.4 + '@types/hast': 2.3.10 comma-separated-tokens: 2.0.3 hast-util-parse-selector: 3.1.1 - property-information: 6.2.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 dev: true /hastscript@8.0.0: resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} dependencies: - '@types/hast': 3.0.3 + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 6.4.1 + space-separated-tokens: 2.0.2 + dev: true + + /hastscript@9.0.0: + resolution: {integrity: sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw==} + dependencies: + '@types/hast': 3.0.4 comma-separated-tokens: 2.0.3 hast-util-parse-selector: 4.0.0 - property-information: 6.2.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 dev: true + /he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: true + /html-escaper@3.0.3: resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} dev: true @@ -5002,27 +4667,17 @@ packages: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} dev: true - /htmlparser2@7.2.0: - resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==} + /htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} dependencies: domelementtype: 2.3.0 - domhandler: 4.3.1 - domutils: 2.8.0 - entities: 3.0.1 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 dev: true /http-cache-semantics@4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - dev: true - - /human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - dev: true - - /human-signals@4.3.1: - resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} - engines: {node: '>=14.18.0'} + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} dev: true /human-signals@5.0.0: @@ -5030,30 +4685,23 @@ packages: engines: {node: '>=16.17.0'} dev: true - /husky@8.0.3: - resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} - engines: {node: '>=14'} + /husky@9.0.11: + resolution: {integrity: sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==} + engines: {node: '>=18'} hasBin: true dev: true - /iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - dependencies: - safer-buffer: 2.1.2 - dev: true - /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} dev: true - /ignore@5.2.0: - resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} + /ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} dev: true - /immutable@4.1.0: - resolution: {integrity: sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==} + /immutable@4.3.5: + resolution: {integrity: sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==} /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -5063,10 +4711,6 @@ packages: resolve-from: 4.0.0 dev: true - /import-meta-resolve@3.0.0: - resolution: {integrity: sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg==} - dev: true - /import-meta-resolve@4.0.0: resolution: {integrity: sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==} dev: true @@ -5095,13 +4739,17 @@ packages: resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} dev: true - /internal-slot@1.0.4: - resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==} + /inline-style-parser@0.2.3: + resolution: {integrity: sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g==} + dev: true + + /internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} dependencies: - get-intrinsic: 1.2.0 - has: 1.0.3 - side-channel: 1.0.4 + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.0.6 dev: true /is-alphabetical@2.0.1: @@ -5115,12 +4763,12 @@ packages: is-decimal: 2.0.1 dev: true - /is-array-buffer@3.0.1: - resolution: {integrity: sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==} + /is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - is-typed-array: 1.1.10 + call-bind: 1.0.7 + get-intrinsic: 1.2.4 dev: true /is-arrayish@0.3.2: @@ -5129,6 +4777,13 @@ packages: dev: true optional: true + /is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + /is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: @@ -5139,14 +4794,14 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} dependencies: - binary-extensions: 2.2.0 + binary-extensions: 2.3.0 /is-boolean-object@1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 + call-bind: 1.0.7 + has-tostringtag: 1.0.2 dev: true /is-buffer@2.0.5: @@ -5158,35 +4813,30 @@ packages: engines: {node: '>= 0.4'} dev: true - /is-core-module@2.13.0: - resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: - has: 1.0.3 + hasown: 2.0.2 dev: true - /is-core-module@2.9.0: - resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==} + /is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} dependencies: - has: 1.0.3 + is-typed-array: 1.1.13 dev: true /is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} dependencies: - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 dev: true /is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} dev: true - /is-docker@2.2.1: - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} - engines: {node: '>=8'} - hasBin: true - dev: true - /is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -5202,6 +4852,12 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + /is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + dependencies: + call-bind: 1.0.7 + dev: true + /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -5211,6 +4867,20 @@ packages: engines: {node: '>=12'} dev: true + /is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + dependencies: + get-east-asian-width: 1.2.0 + dev: true + + /is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -5234,16 +4904,26 @@ packages: engines: {node: '>=12'} dev: true - /is-negative-zero@2.0.2: - resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + /is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + dev: true + + /is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} dev: true + /is-network-error@1.1.0: + resolution: {integrity: sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==} + engines: {node: '>=16'} + dev: true + /is-number-object@1.0.7: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} dependencies: - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 dev: true /is-number@7.0.0: @@ -5255,33 +4935,34 @@ packages: engines: {node: '>=8'} dev: true - /is-plain-obj@4.0.0: - resolution: {integrity: sha512-NXRbBtUdBioI73y/HmOhogw/U5msYPC9DAtGkJXeFcFWSFZw0mCUsPxk/snTuJHzNKA8kLBK4rH97RMB1BfCXw==} + /is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - /is-reference@3.0.1: - resolution: {integrity: sha512-baJJdQLiYaJdvFbJqXrcGv3WU3QCzBlUcI5QhbesIm6/xPsvmO+2CDoi/GMOFBQEQm+PXkwOPrp9KK5ozZsp2w==} + /is-reference@3.0.2: + resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} dependencies: - '@types/estree': 1.0.1 + '@types/estree': 1.0.5 dev: true /is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 + call-bind: 1.0.7 + has-tostringtag: 1.0.2 dev: true - /is-shared-array-buffer@1.0.2: - resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} - dependencies: - call-bind: 1.0.2 + /is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} dev: true - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} + /is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 dev: true /is-stream@3.0.0: @@ -5293,7 +4974,7 @@ packages: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} dependencies: - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 dev: true /is-symbol@1.0.4: @@ -5303,15 +4984,11 @@ packages: has-symbols: 1.0.3 dev: true - /is-typed-array@1.1.10: - resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} + /is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.0 + which-typed-array: 1.1.15 dev: true /is-unicode-supported@1.3.0: @@ -5319,38 +4996,58 @@ packages: engines: {node: '>=12'} dev: true + /is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + dev: true + /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 dev: true - /is-wsl@2.2.0: - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} - engines: {node: '>=8'} + /is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} dependencies: - is-docker: 2.2.1 + call-bind: 1.0.7 + get-intrinsic: 1.2.4 dev: true - /is-wsl@3.0.0: - resolution: {integrity: sha512-TQ7xXW/fTBaz/HhGSV779AC99ocpvb9qJPuPwyIea+F+Z+htcQ1wouAA0xEQaa4saVqyP8mwkoYp5efeM/4Gbg==} + /is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} engines: {node: '>=16'} dependencies: - is-docker: 3.0.0 + is-inside-container: 1.0.0 + dev: true + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} dev: true /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /js-sdsl@4.2.0: - resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==} + /iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + dependencies: + define-properties: 1.2.1 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + reflect.getprototypeof: 1.0.6 + set-function-name: 2.0.2 dev: true /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true + /js-tokens@8.0.3: + resolution: {integrity: sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==} + dev: true + /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true @@ -5372,8 +5069,8 @@ packages: xmlcreate: 2.0.4 dev: false - /jsdoc-api@7.1.1: - resolution: {integrity: sha512-0pkuPCzVXiqsDAsVrNFXCkHzlyNepBIDVtwwehry4RJAnZmXtlAz7rh8F9FRz53u3NeynGbex+bpYWwi8lE66A==} + /jsdoc-api@8.0.0: + resolution: {integrity: sha512-Rnhor0suB1Ds1abjmFkFfKeD+kSMRN9oHMTMZoJVUrmtCGDwXty+sWMA9sa4xbe4UyxuPjhC7tavZ40mDKK6QQ==} engines: {node: '>=12.17'} dependencies: array-back: 6.2.2 @@ -5381,32 +5078,32 @@ packages: collect-all: 1.0.4 file-set: 4.0.2 fs-then-native: 2.0.0 - jsdoc: 3.6.10 + jsdoc: 4.0.2 object-to-spawn-args: 2.0.1 temp-path: 1.0.0 walk-back: 5.1.0 dev: false - /jsdoc@3.6.10: - resolution: {integrity: sha512-IdQ8ppSo5LKZ9o3M+LKIIK8i00DIe5msDvG3G81Km+1dhy0XrOWD0Ji8H61ElgyEj/O9KRLokgKbAM9XX9CJAg==} - engines: {node: '>=8.15.0'} + /jsdoc@4.0.2: + resolution: {integrity: sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==} + engines: {node: '>=12.0.0'} hasBin: true dependencies: - '@babel/parser': 7.18.11 + '@babel/parser': 7.24.1 + '@jsdoc/salty': 0.2.7 '@types/markdown-it': 12.2.3 bluebird: 3.7.2 catharsis: 0.9.0 escape-string-regexp: 2.0.0 js2xmlparser: 4.0.2 - klaw: 4.0.1 + klaw: 3.0.0 markdown-it: 12.3.2 - markdown-it-anchor: 8.4.1(@types/markdown-it@12.2.3)(markdown-it@12.3.2) - marked: 4.0.12 + markdown-it-anchor: 8.6.7(@types/markdown-it@12.2.3)(markdown-it@12.3.2) + marked: 4.3.0 mkdirp: 1.0.4 - requizzle: 0.2.3 + requizzle: 0.2.4 strip-json-comments: 3.1.1 - taffydb: 2.6.2 - underscore: 1.13.2 + underscore: 1.13.6 dev: false /jsesc@2.5.2: @@ -5415,6 +5112,10 @@ packages: hasBin: true dev: true + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true @@ -5423,12 +5124,6 @@ packages: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true - /json5@2.2.1: - resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} - engines: {node: '>=6'} - hasBin: true - dev: true - /json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -5439,16 +5134,24 @@ packages: resolution: {integrity: sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==} dev: false - /jsonc-parser@3.2.0: - resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + /jsonc-parser@3.2.1: + resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} dev: true - /jsx-ast-utils@3.3.3: - resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==} + /jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} dependencies: - array-includes: 3.1.6 - object.assign: 4.1.4 + array-includes: 3.1.7 + array.prototype.flat: 1.3.2 + object.assign: 4.1.5 + object.values: 1.2.0 + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 dev: true /kind-of@6.0.3: @@ -5456,9 +5159,10 @@ packages: engines: {node: '>=0.10.0'} dev: true - /klaw@4.0.1: - resolution: {integrity: sha512-pgsE40/SvC7st04AHiISNewaIMUbY5V/K8b21ekiPiFoYs/EYSdsGa+FJArB1d441uq4Q8zZyIxvAzkGNlBdRw==} - engines: {node: '>=14.14.0'} + /klaw@3.0.0: + resolution: {integrity: sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==} + dependencies: + graceful-fs: 4.2.11 dev: false /kleur@3.0.3: @@ -5482,9 +5186,9 @@ packages: type-check: 0.4.0 dev: true - /lilconfig@2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} - engines: {node: '>=10'} + /lilconfig@3.0.0: + resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==} + engines: {node: '>=14'} dev: true /linkify-it@3.0.3: @@ -5493,41 +5197,35 @@ packages: uc.micro: 1.0.6 dev: false - /lint-staged@13.3.0: - resolution: {integrity: sha512-mPRtrYnipYYv1FEE134ufbWpeggNTo+O/UPzngoaKzbzHAthvR55am+8GfHTnqNRQVRRrYQLGW9ZyUoD7DsBHQ==} - engines: {node: ^16.14.0 || >=18.0.0} + /lint-staged@15.2.2: + resolution: {integrity: sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==} + engines: {node: '>=18.12.0'} hasBin: true dependencies: chalk: 5.3.0 - commander: 11.0.0 + commander: 11.1.0 debug: 4.3.4 - execa: 7.2.0 - lilconfig: 2.1.0 - listr2: 6.6.1 + execa: 8.0.1 + lilconfig: 3.0.0 + listr2: 8.0.1 micromatch: 4.0.5 pidtree: 0.6.0 string-argv: 0.3.2 - yaml: 2.3.1 + yaml: 2.3.4 transitivePeerDependencies: - - enquirer - supports-color dev: true - /listr2@6.6.1: - resolution: {integrity: sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==} - engines: {node: '>=16.0.0'} - peerDependencies: - enquirer: '>= 2.3.0 < 3' - peerDependenciesMeta: - enquirer: - optional: true + /listr2@8.0.1: + resolution: {integrity: sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==} + engines: {node: '>=18.0.0'} dependencies: - cli-truncate: 3.1.0 + cli-truncate: 4.0.0 colorette: 2.0.20 eventemitter3: 5.0.1 - log-update: 5.0.1 - rfdc: 1.3.0 - wrap-ansi: 8.1.0 + log-update: 6.0.0 + rfdc: 1.3.1 + wrap-ansi: 9.0.0 dev: true /load-yaml-file@0.2.0: @@ -5540,9 +5238,12 @@ packages: strip-bom: 3.0.0 dev: true - /local-pkg@0.4.3: - resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + /local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} engines: {node: '>=14'} + dependencies: + mlly: 1.6.1 + pkg-types: 1.0.3 dev: true /locate-path@5.0.0: @@ -5575,23 +5276,19 @@ packages: is-unicode-supported: 1.3.0 dev: true - /log-update@5.0.1: - resolution: {integrity: sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + /log-update@6.0.0: + resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==} + engines: {node: '>=18'} dependencies: - ansi-escapes: 5.0.0 + ansi-escapes: 6.2.0 cli-cursor: 4.0.0 - slice-ansi: 5.0.0 + slice-ansi: 7.1.0 strip-ansi: 7.1.0 - wrap-ansi: 8.1.0 + wrap-ansi: 9.0.0 dev: true - /longest-streak@3.0.1: - resolution: {integrity: sha512-cHlYSUpL2s7Fb3394mYxwTYj8niTaNHUCLr0qdiCXQfSjfuA7CKofpX2uSwEfFDQ0EB7JcnMnm+GjbqqoinYYg==} - /longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - dev: true /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} @@ -5600,10 +5297,10 @@ packages: js-tokens: 4.0.0 dev: true - /loupe@2.3.6: - resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} dependencies: - get-func-name: 2.0.0 + get-func-name: 2.0.2 dev: true /lru-cache@4.1.5: @@ -5625,20 +5322,27 @@ packages: dependencies: yallist: 4.0.0 - /magic-string@0.30.3: - resolution: {integrity: sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==} + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} engines: {node: '>=12'} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /markdown-extensions@1.1.1: - resolution: {integrity: sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==} - engines: {node: '>=0.10.0'} + /magic-string@0.30.8: + resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /markdown-extensions@2.0.0: + resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} + engines: {node: '>=16'} dev: true - /markdown-it-anchor@8.4.1(@types/markdown-it@12.2.3)(markdown-it@12.3.2): - resolution: {integrity: sha512-sLODeRetZ/61KkKLJElaU3NuU2z7MhXf12Ml1WJMSdwpngeofneCRF+JBbat8HiSqhniOMuTemXMrsI7hA6XyA==} + /markdown-it-anchor@8.6.7(@types/markdown-it@12.2.3)(markdown-it@12.3.2): + resolution: {integrity: sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==} peerDependencies: '@types/markdown-it': '*' markdown-it: '*' @@ -5658,23 +5362,15 @@ packages: uc.micro: 1.0.6 dev: false - /markdown-table@3.0.2: - resolution: {integrity: sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==} + /markdown-table@3.0.3: + resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} - /marked@4.0.12: - resolution: {integrity: sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==} + /marked@4.3.0: + resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} engines: {node: '>= 12'} hasBin: true dev: false - /mdast-util-definitions@5.1.1: - resolution: {integrity: sha512-rQ+Gv7mHttxHOBx2dkF4HWTg+EE+UR78ptQWDylzPKaQuVGdG4HIoY3SrS/pCp80nZ04greFvXbVFHT+uf0JVQ==} - dependencies: - '@types/mdast': 3.0.10 - '@types/unist': 2.0.6 - unist-util-visit: 4.1.0 - dev: true - /mdast-util-definitions@6.0.0: resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} dependencies: @@ -5683,24 +5379,21 @@ packages: unist-util-visit: 5.0.0 dev: true - /mdast-util-directive@2.2.1: - resolution: {integrity: sha512-yZRRuaulzc6bM4IOyZfkOrVs+9Sf1BC+rldRXJyl/Ej6S/6ewQQ9jt75HvEoqZZ4m9ealVTHiS4MP2GRUE7INA==} + /mdast-util-directive@3.0.0: + resolution: {integrity: sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==} dependencies: - '@types/mdast': 3.0.10 - '@types/unist': 2.0.6 - mdast-util-to-markdown: 1.3.0 - parse-entities: 4.0.0 + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + parse-entities: 4.0.1 stringify-entities: 4.0.3 - unist-util-visit-parents: 5.1.0 + unist-util-visit-parents: 6.0.1 + transitivePeerDependencies: + - supports-color dev: true - /mdast-util-find-and-replace@2.1.0: - resolution: {integrity: sha512-1w1jbqAd13oU78QPBf5223+xB+37ecNtQ1JElq2feWols5oEYAl+SgNDnOZipe7NfLemoEt362yUS15/wip4mw==} - dependencies: - escape-string-regexp: 5.0.0 - unist-util-is: 5.1.1 - unist-util-visit-parents: 4.1.1 - /mdast-util-find-and-replace@3.0.1: resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} dependencies: @@ -5708,25 +5401,6 @@ packages: escape-string-regexp: 5.0.0 unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 - dev: true - - /mdast-util-from-markdown@1.2.0: - resolution: {integrity: sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==} - dependencies: - '@types/mdast': 3.0.10 - '@types/unist': 2.0.6 - decode-named-character-reference: 1.0.2 - mdast-util-to-string: 3.1.1 - micromark: 3.0.10 - micromark-util-decode-numeric-character-reference: 1.0.0 - micromark-util-decode-string: 1.0.2 - micromark-util-normalize-identifier: 1.0.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - unist-util-stringify-position: 3.0.2 - uvu: 0.5.4 - transitivePeerDependencies: - - supports-color /mdast-util-from-markdown@2.0.0: resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==} @@ -5745,23 +5419,6 @@ packages: unist-util-stringify-position: 4.0.0 transitivePeerDependencies: - supports-color - dev: true - - /mdast-util-frontmatter@1.0.1: - resolution: {integrity: sha512-JjA2OjxRqAa8wEG8hloD0uTU0kdn8kbtOWpPP94NBkfAlbxn4S8gCGf/9DwFtEeGPXrDcNXdiDjVaRdUFqYokw==} - dependencies: - '@types/mdast': 3.0.10 - mdast-util-to-markdown: 1.5.0 - micromark-extension-frontmatter: 1.1.1 - dev: true - - /mdast-util-gfm-autolink-literal@1.0.2: - resolution: {integrity: sha512-FzopkOd4xTTBeGXhXSBU0OCDDh5lUj2rd+HQqG92Ld+jL4lpUfgX2AT2OHAVP9aEeDKp7G92fuooSZcYJA3cRg==} - dependencies: - '@types/mdast': 3.0.10 - ccount: 2.0.1 - mdast-util-find-and-replace: 2.1.0 - micromark-util-character: 1.1.0 /mdast-util-gfm-autolink-literal@2.0.0: resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==} @@ -5770,15 +5427,7 @@ packages: ccount: 2.0.1 devlop: 1.1.0 mdast-util-find-and-replace: 3.0.1 - micromark-util-character: 2.0.1 - dev: true - - /mdast-util-gfm-footnote@1.0.1: - resolution: {integrity: sha512-p+PrYlkw9DeCRkTVw1duWqPRHX6Ywh2BNKJQcZbCwAuP/59B0Lk9kakuAd7KbQprVO4GzdW8eS5++A9PUSqIyw==} - dependencies: - '@types/mdast': 3.0.10 - mdast-util-to-markdown: 1.3.0 - micromark-util-normalize-identifier: 1.0.0 + micromark-util-character: 2.1.0 /mdast-util-gfm-footnote@2.0.0: resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} @@ -5790,13 +5439,6 @@ packages: micromark-util-normalize-identifier: 2.0.0 transitivePeerDependencies: - supports-color - dev: true - - /mdast-util-gfm-strikethrough@1.0.1: - resolution: {integrity: sha512-zKJbEPe+JP6EUv0mZ0tQUyLQOC+FADt0bARldONot/nefuISkaZFlmVK4tU6JgfyZGrky02m/I6PmehgAgZgqg==} - dependencies: - '@types/mdast': 3.0.10 - mdast-util-to-markdown: 1.3.0 /mdast-util-gfm-strikethrough@2.0.0: resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} @@ -5806,34 +5448,17 @@ packages: mdast-util-to-markdown: 2.1.0 transitivePeerDependencies: - supports-color - dev: true - - /mdast-util-gfm-table@1.0.4: - resolution: {integrity: sha512-aEuoPwZyP4iIMkf2cLWXxx3EQ6Bmh2yKy9MVCg4i6Sd3cX80dcLEfXO/V4ul3pGH9czBK4kp+FAl+ZHmSUt9/w==} - dependencies: - markdown-table: 3.0.2 - mdast-util-from-markdown: 1.2.0 - mdast-util-to-markdown: 1.3.0 - transitivePeerDependencies: - - supports-color /mdast-util-gfm-table@2.0.0: resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} dependencies: '@types/mdast': 4.0.3 devlop: 1.1.0 - markdown-table: 3.0.2 + markdown-table: 3.0.3 mdast-util-from-markdown: 2.0.0 mdast-util-to-markdown: 2.1.0 transitivePeerDependencies: - supports-color - dev: true - - /mdast-util-gfm-task-list-item@1.0.1: - resolution: {integrity: sha512-KZ4KLmPdABXOsfnM6JHUIjxEvcx2ulk656Z/4Balw071/5qgnhz+H1uGtf2zIGnrnvDC8xR4Fj9uKbjAFGNIeA==} - dependencies: - '@types/mdast': 3.0.10 - mdast-util-to-markdown: 1.3.0 /mdast-util-gfm-task-list-item@2.0.0: resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} @@ -5844,20 +5469,6 @@ packages: mdast-util-to-markdown: 2.1.0 transitivePeerDependencies: - supports-color - dev: true - - /mdast-util-gfm@2.0.1: - resolution: {integrity: sha512-42yHBbfWIFisaAfV1eixlabbsa6q7vHeSPY+cg+BBjX51M8xhgMacqH9g6TftB/9+YkcI0ooV4ncfrJslzm/RQ==} - dependencies: - mdast-util-from-markdown: 1.2.0 - mdast-util-gfm-autolink-literal: 1.0.2 - mdast-util-gfm-footnote: 1.0.1 - mdast-util-gfm-strikethrough: 1.0.1 - mdast-util-gfm-table: 1.0.4 - mdast-util-gfm-task-list-item: 1.0.1 - mdast-util-to-markdown: 1.3.0 - transitivePeerDependencies: - - supports-color /mdast-util-gfm@3.0.0: resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==} @@ -5871,95 +5482,75 @@ packages: mdast-util-to-markdown: 2.1.0 transitivePeerDependencies: - supports-color - dev: true - /mdast-util-mdx-expression@1.3.2: - resolution: {integrity: sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA==} + /mdast-util-mdx-expression@2.0.0: + resolution: {integrity: sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==} dependencies: - '@types/estree-jsx': 1.0.0 - '@types/hast': 2.3.4 - '@types/mdast': 3.0.10 - mdast-util-from-markdown: 1.2.0 - mdast-util-to-markdown: 1.5.0 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 transitivePeerDependencies: - supports-color dev: true - /mdast-util-mdx-jsx@2.1.2: - resolution: {integrity: sha512-o9vBCYQK5ZLGEj3tCGISJGjvafyHRVJlZmfJzSE7xjiogSzIeph/Z4zMY65q4WGRMezQBeAwPlrdymDYYYx0tA==} + /mdast-util-mdx-jsx@3.1.2: + resolution: {integrity: sha512-eKMQDeywY2wlHc97k5eD8VC+9ASMjN8ItEZQNGwJ6E0XWKiW/Z0V5/H8pvoXUf+y+Mj0VIgeRRbujBmFn4FTyA==} dependencies: - '@types/estree-jsx': 1.0.0 - '@types/hast': 2.3.4 - '@types/mdast': 3.0.10 - '@types/unist': 2.0.6 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 ccount: 2.0.1 - mdast-util-from-markdown: 1.2.0 - mdast-util-to-markdown: 1.3.0 - parse-entities: 4.0.0 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + parse-entities: 4.0.1 stringify-entities: 4.0.3 - unist-util-remove-position: 4.0.1 - unist-util-stringify-position: 3.0.2 - vfile-message: 3.1.2 + unist-util-remove-position: 5.0.0 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 transitivePeerDependencies: - supports-color dev: true - /mdast-util-mdx@2.0.1: - resolution: {integrity: sha512-38w5y+r8nyKlGvNjSEqWrhG0w5PmnRA+wnBvm+ulYCct7nsGYhFVb0lljS9bQav4psDAS1eGkP2LMVcZBi/aqw==} + /mdast-util-mdx@3.0.0: + resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} dependencies: - mdast-util-from-markdown: 1.2.0 - mdast-util-mdx-expression: 1.3.2 - mdast-util-mdx-jsx: 2.1.2 - mdast-util-mdxjs-esm: 1.3.1 - mdast-util-to-markdown: 1.5.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-mdx-expression: 2.0.0 + mdast-util-mdx-jsx: 3.1.2 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.0 transitivePeerDependencies: - supports-color dev: true - /mdast-util-mdxjs-esm@1.3.1: - resolution: {integrity: sha512-SXqglS0HrEvSdUEfoXFtcg7DRl7S2cwOXc7jkuusG472Mmjag34DUDeOJUZtl+BVnyeO1frIgVpHlNRWc2gk/w==} + /mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} dependencies: - '@types/estree-jsx': 1.0.0 - '@types/hast': 2.3.4 - '@types/mdast': 3.0.10 - mdast-util-from-markdown: 1.2.0 - mdast-util-to-markdown: 1.5.0 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 transitivePeerDependencies: - supports-color dev: true - /mdast-util-phrasing@3.0.1: - resolution: {integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==} - dependencies: - '@types/mdast': 3.0.10 - unist-util-is: 5.2.1 - dev: true - - /mdast-util-phrasing@4.0.0: - resolution: {integrity: sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==} + /mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} dependencies: '@types/mdast': 4.0.3 unist-util-is: 6.0.0 - dev: true - - /mdast-util-to-hast@12.2.4: - resolution: {integrity: sha512-a21xoxSef1l8VhHxS1Dnyioz6grrJkoaCUgGzMD/7dWHvboYX3VW53esRUfB5tgTyz4Yos1n25SPcj35dJqmAg==} - dependencies: - '@types/hast': 2.3.4 - '@types/mdast': 3.0.10 - mdast-util-definitions: 5.1.1 - micromark-util-sanitize-uri: 1.1.0 - trim-lines: 3.0.1 - unist-builder: 3.0.0 - unist-util-generated: 2.0.0 - unist-util-position: 4.0.3 - unist-util-visit: 4.1.0 - dev: true - /mdast-util-to-hast@13.0.2: - resolution: {integrity: sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og==} + /mdast-util-to-hast@13.1.0: + resolution: {integrity: sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==} dependencies: - '@types/hast': 3.0.3 + '@types/hast': 3.0.4 '@types/mdast': 4.0.3 '@ungap/structured-clone': 1.2.0 devlop: 1.1.0 @@ -5967,30 +5558,7 @@ packages: trim-lines: 3.0.1 unist-util-position: 5.0.0 unist-util-visit: 5.0.0 - dev: true - - /mdast-util-to-markdown@1.3.0: - resolution: {integrity: sha512-6tUSs4r+KK4JGTTiQ7FfHmVOaDrLQJPmpjD6wPMlHGUVXoG9Vjc3jIeP+uyBWRf8clwB2blM+W7+KrlMYQnftA==} - dependencies: - '@types/mdast': 3.0.10 - '@types/unist': 2.0.6 - longest-streak: 3.0.1 - mdast-util-to-string: 3.1.1 - micromark-util-decode-string: 1.0.2 - unist-util-visit: 4.1.0 - zwitch: 2.0.2 - - /mdast-util-to-markdown@1.5.0: - resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==} - dependencies: - '@types/mdast': 3.0.10 - '@types/unist': 2.0.8 - longest-streak: 3.1.0 - mdast-util-phrasing: 3.0.1 - mdast-util-to-string: 3.1.1 - micromark-util-decode-string: 1.1.0 - unist-util-visit: 4.1.2 - zwitch: 2.0.4 + vfile: 6.0.1 dev: true /mdast-util-to-markdown@2.1.0: @@ -5999,26 +5567,19 @@ packages: '@types/mdast': 4.0.3 '@types/unist': 3.0.2 longest-streak: 3.1.0 - mdast-util-phrasing: 4.0.0 + mdast-util-phrasing: 4.1.0 mdast-util-to-string: 4.0.0 micromark-util-decode-string: 2.0.0 unist-util-visit: 5.0.0 zwitch: 2.0.4 - dev: true - - /mdast-util-to-string@3.1.1: - resolution: {integrity: sha512-tGvhT94e+cVnQt8JWE9/b3cUQZWS732TJxXHktvP+BYo62PpYD53Ls/6cC60rW21dW+txxiM4zMdc6abASvZKA==} - dependencies: - '@types/mdast': 3.0.10 /mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} dependencies: '@types/mdast': 4.0.3 - dev: true /mdurl@1.0.1: - resolution: {integrity: sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=} + resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} dev: false /merge-stream@2.0.0: @@ -6029,47 +5590,6 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - /micromark-core-commonmark@1.0.6: - resolution: {integrity: sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==} - dependencies: - decode-named-character-reference: 1.0.2 - micromark-factory-destination: 1.0.0 - micromark-factory-label: 1.0.2 - micromark-factory-space: 1.0.0 - micromark-factory-title: 1.0.2 - micromark-factory-whitespace: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-chunked: 1.0.0 - micromark-util-classify-character: 1.0.0 - micromark-util-html-tag-name: 1.0.0 - micromark-util-normalize-identifier: 1.0.0 - micromark-util-resolve-all: 1.0.0 - micromark-util-subtokenize: 1.0.2 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.4 - - /micromark-core-commonmark@1.1.0: - resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==} - dependencies: - decode-named-character-reference: 1.0.2 - micromark-factory-destination: 1.1.0 - micromark-factory-label: 1.1.0 - micromark-factory-space: 1.1.0 - micromark-factory-title: 1.1.0 - micromark-factory-whitespace: 1.1.0 - micromark-util-character: 1.1.0 - micromark-util-chunked: 1.1.0 - micromark-util-classify-character: 1.1.0 - micromark-util-html-tag-name: 1.2.0 - micromark-util-normalize-identifier: 1.1.0 - micromark-util-resolve-all: 1.1.0 - micromark-util-subtokenize: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - dev: true - /micromark-core-commonmark@2.0.0: resolution: {integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==} dependencies: @@ -6080,7 +5600,7 @@ packages: micromark-factory-space: 2.0.0 micromark-factory-title: 2.0.0 micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-chunked: 2.0.0 micromark-util-classify-character: 2.0.0 micromark-util-html-tag-name: 2.0.0 @@ -6089,58 +5609,26 @@ packages: micromark-util-subtokenize: 2.0.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-extension-directive@2.1.1: - resolution: {integrity: sha512-+7MYZ3a10cpPrQRg3530srFMSBx0EL7gQaJ3ekguOQFSlJHLikW15AphBmNxvCNdRSWTX1R8RepzjKQra8INQw==} - dependencies: - micromark-factory-space: 1.0.0 - micromark-factory-whitespace: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - parse-entities: 4.0.0 - uvu: 0.5.3 - dev: true - /micromark-extension-frontmatter@1.1.1: - resolution: {integrity: sha512-m2UH9a7n3W8VAH9JO9y01APpPKmNNNs71P0RbknEmYSaZU5Ghogv38BYO94AI5Xw6OYfxZRdHZZ2nYjs/Z+SZQ==} + /micromark-extension-directive@3.0.0: + resolution: {integrity: sha512-61OI07qpQrERc+0wEysLHMvoiO3s2R56x5u7glHq2Yqq6EHbH4dW25G9GfDdGCDYqA21KE6DWgNSzxSwHc2hSg==} dependencies: - fault: 2.0.1 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-factory-whitespace: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + parse-entities: 4.0.1 dev: true - /micromark-extension-gfm-autolink-literal@1.0.3: - resolution: {integrity: sha512-i3dmvU0htawfWED8aHMMAzAVp/F0Z+0bPh3YrbTPPL1v4YAlCZpy5rBO5p0LPYiZo0zFVkoYh7vDU7yQSiCMjg==} - dependencies: - micromark-util-character: 1.1.0 - micromark-util-sanitize-uri: 1.0.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.4 - /micromark-extension-gfm-autolink-literal@2.0.0: resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==} dependencies: - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-sanitize-uri: 2.0.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-extension-gfm-footnote@1.0.4: - resolution: {integrity: sha512-E/fmPmDqLiMUP8mLJ8NbJWJ4bTw6tS+FEQS8CcuDtZpILuOb2kjLqPEeAePF1djXROHXChM/wPJw0iS4kHCcIg==} - dependencies: - micromark-core-commonmark: 1.0.6 - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-normalize-identifier: 1.0.0 - micromark-util-sanitize-uri: 1.0.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.4 /micromark-extension-gfm-footnote@2.0.0: resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==} @@ -6148,22 +5636,11 @@ packages: devlop: 1.1.0 micromark-core-commonmark: 2.0.0 micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-normalize-identifier: 2.0.0 micromark-util-sanitize-uri: 2.0.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-extension-gfm-strikethrough@1.0.4: - resolution: {integrity: sha512-/vjHU/lalmjZCT5xt7CcHVJGq8sYRm80z24qAKXzaHzem/xsDYb2yLL+NNVbYvmpLx3O7SYPuGL5pzusL9CLIQ==} - dependencies: - micromark-util-chunked: 1.0.0 - micromark-util-classify-character: 1.0.0 - micromark-util-resolve-all: 1.0.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.4 /micromark-extension-gfm-strikethrough@2.0.0: resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==} @@ -6174,68 +5651,29 @@ packages: micromark-util-resolve-all: 2.0.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-extension-gfm-table@1.0.5: - resolution: {integrity: sha512-xAZ8J1X9W9K3JTJTUL7G6wSKhp2ZYHrFk5qJgY/4B33scJzE2kpfRL6oiw/veJTbt7jiM/1rngLlOKPWr1G+vg==} - dependencies: - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.4 /micromark-extension-gfm-table@2.0.0: resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==} dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-extension-gfm-tagfilter@1.0.1: - resolution: {integrity: sha512-Ty6psLAcAjboRa/UKUbbUcwjVAv5plxmpUTy2XC/3nJFL37eHej8jrHrRzkqcpipJliuBH30DTs7+3wqNcQUVA==} - dependencies: - micromark-util-types: 1.0.2 /micromark-extension-gfm-tagfilter@2.0.0: resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} dependencies: micromark-util-types: 2.0.0 - dev: true - - /micromark-extension-gfm-task-list-item@1.0.3: - resolution: {integrity: sha512-PpysK2S1Q/5VXi72IIapbi/jliaiOFzv7THH4amwXeYXLq3l1uo8/2Be0Ac1rEwK20MQEsGH2ltAZLNY2KI/0Q==} - dependencies: - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.4 /micromark-extension-gfm-task-list-item@2.0.1: resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==} dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-extension-gfm@2.0.1: - resolution: {integrity: sha512-p2sGjajLa0iYiGQdT0oelahRYtMWvLjy8J9LOCxzIQsllMCGLbsLW+Nc+N4vi02jcRJvedVJ68cjelKIO6bpDA==} - dependencies: - micromark-extension-gfm-autolink-literal: 1.0.3 - micromark-extension-gfm-footnote: 1.0.4 - micromark-extension-gfm-strikethrough: 1.0.4 - micromark-extension-gfm-table: 1.0.5 - micromark-extension-gfm-tagfilter: 1.0.1 - micromark-extension-gfm-task-list-item: 1.0.3 - micromark-util-combine-extensions: 1.0.0 - micromark-util-types: 1.0.2 /micromark-extension-gfm@3.0.0: resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} @@ -6248,423 +5686,190 @@ packages: micromark-extension-gfm-task-list-item: 2.0.1 micromark-util-combine-extensions: 2.0.0 micromark-util-types: 2.0.0 - dev: true - /micromark-extension-mdx-expression@1.0.8: - resolution: {integrity: sha512-zZpeQtc5wfWKdzDsHRBY003H2Smg+PUi2REhqgIhdzAa5xonhP03FcXxqFSerFiNUr5AWmHpaNPQTBVOS4lrXw==} + /micromark-extension-mdx-expression@3.0.0: + resolution: {integrity: sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==} dependencies: - '@types/estree': 1.0.1 - micromark-factory-mdx-expression: 1.0.9 - micromark-factory-space: 1.1.0 - micromark-util-character: 1.1.0 - micromark-util-events-to-acorn: 1.2.3 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 - uvu: 0.5.6 + '@types/estree': 1.0.5 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.1 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 dev: true - /micromark-extension-mdx-jsx@1.0.5: - resolution: {integrity: sha512-gPH+9ZdmDflbu19Xkb8+gheqEDqkSpdCEubQyxuz/Hn8DOXiXvrXeikOoBA71+e8Pfi0/UYmU3wW3H58kr7akA==} + /micromark-extension-mdx-jsx@3.0.0: + resolution: {integrity: sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w==} dependencies: '@types/acorn': 4.0.6 - '@types/estree': 1.0.1 - estree-util-is-identifier-name: 2.1.0 - micromark-factory-mdx-expression: 1.0.9 - micromark-factory-space: 1.1.0 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - vfile-message: 3.1.4 - dev: true - - /micromark-extension-mdx-md@1.0.1: - resolution: {integrity: sha512-7MSuj2S7xjOQXAjjkbjBsHkMtb+mDGVW6uI2dBL9snOBCbZmoNgDAeZ0nSn9j3T42UE/g2xVNMn18PJxZvkBEA==} - dependencies: - micromark-util-types: 1.1.0 + '@types/estree': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.1 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + vfile-message: 4.0.2 dev: true - /micromark-extension-mdxjs-esm@1.0.5: - resolution: {integrity: sha512-xNRBw4aoURcyz/S69B19WnZAkWJMxHMT5hE36GtDAyhoyn/8TuAeqjFJQlwk+MKQsUD7b3l7kFX+vlfVWgcX1w==} + /micromark-extension-mdx-md@2.0.0: + resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} dependencies: - '@types/estree': 1.0.1 - micromark-core-commonmark: 1.1.0 - micromark-util-character: 1.1.0 - micromark-util-events-to-acorn: 1.2.3 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 - unist-util-position-from-estree: 1.1.2 - uvu: 0.5.6 - vfile-message: 3.1.4 + micromark-util-types: 2.0.0 dev: true - /micromark-extension-mdxjs@1.0.1: - resolution: {integrity: sha512-7YA7hF6i5eKOfFUzZ+0z6avRG52GpWR8DL+kN47y3f2KhxbBZMhmxe7auOeaTBrW2DenbbZTf1ea9tA2hDpC2Q==} + /micromark-extension-mdxjs-esm@3.0.0: + resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} dependencies: - acorn: 8.10.0 - acorn-jsx: 5.3.2(acorn@8.10.0) - micromark-extension-mdx-expression: 1.0.8 - micromark-extension-mdx-jsx: 1.0.5 - micromark-extension-mdx-md: 1.0.1 - micromark-extension-mdxjs-esm: 1.0.5 - micromark-util-combine-extensions: 1.1.0 - micromark-util-types: 1.1.0 + '@types/estree': 1.0.5 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 dev: true - /micromark-factory-destination@1.0.0: - resolution: {integrity: sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==} - dependencies: - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - - /micromark-factory-destination@1.1.0: - resolution: {integrity: sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==} + /micromark-extension-mdxjs@3.0.0: + resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} dependencies: - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) + micromark-extension-mdx-expression: 3.0.0 + micromark-extension-mdx-jsx: 3.0.0 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.0 + micromark-util-types: 2.0.0 dev: true /micromark-factory-destination@2.0.0: resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} dependencies: - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-factory-label@1.0.2: - resolution: {integrity: sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==} - dependencies: - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.4 - - /micromark-factory-label@1.1.0: - resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==} - dependencies: - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - dev: true /micromark-factory-label@2.0.0: - resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} - dependencies: - devlop: 1.1.0 - micromark-util-character: 2.0.1 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: true - - /micromark-factory-mdx-expression@1.0.9: - resolution: {integrity: sha512-jGIWzSmNfdnkJq05c7b0+Wv0Kfz3NJ3N4cBjnbO4zjXIlxJr+f8lk+5ZmwFvqdAbUy2q6B5rCY//g0QAAaXDWA==} - dependencies: - '@types/estree': 1.0.1 - micromark-util-character: 1.1.0 - micromark-util-events-to-acorn: 1.2.3 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 - unist-util-position-from-estree: 1.1.2 - uvu: 0.5.6 - vfile-message: 3.1.4 - dev: true - - /micromark-factory-space@1.0.0: - resolution: {integrity: sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==} + resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} dependencies: - micromark-util-character: 1.1.0 - micromark-util-types: 1.0.2 + devlop: 1.1.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 - /micromark-factory-space@1.1.0: - resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==} + /micromark-factory-mdx-expression@2.0.1: + resolution: {integrity: sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg==} dependencies: - micromark-util-character: 1.1.0 - micromark-util-types: 1.1.0 + '@types/estree': 1.0.5 + devlop: 1.1.0 + micromark-util-character: 2.1.0 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 dev: true /micromark-factory-space@2.0.0: resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==} dependencies: - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-factory-title@1.0.2: - resolution: {integrity: sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==} - dependencies: - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.4 - - /micromark-factory-title@1.1.0: - resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==} - dependencies: - micromark-factory-space: 1.1.0 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 - dev: true /micromark-factory-title@2.0.0: resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==} dependencies: micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-factory-whitespace@1.0.0: - resolution: {integrity: sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==} - dependencies: - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - - /micromark-factory-whitespace@1.1.0: - resolution: {integrity: sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==} - dependencies: - micromark-factory-space: 1.1.0 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 - dev: true /micromark-factory-whitespace@2.0.0: resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==} dependencies: micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-util-character@1.1.0: - resolution: {integrity: sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==} - dependencies: - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - /micromark-util-character@2.0.1: - resolution: {integrity: sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==} + /micromark-util-character@2.1.0: + resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} dependencies: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-util-chunked@1.0.0: - resolution: {integrity: sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==} - dependencies: - micromark-util-symbol: 1.0.1 - - /micromark-util-chunked@1.1.0: - resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==} - dependencies: - micromark-util-symbol: 1.0.1 - dev: true /micromark-util-chunked@2.0.0: resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} dependencies: micromark-util-symbol: 2.0.0 - dev: true - - /micromark-util-classify-character@1.0.0: - resolution: {integrity: sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==} - dependencies: - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - - /micromark-util-classify-character@1.1.0: - resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==} - dependencies: - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 - dev: true /micromark-util-classify-character@2.0.0: resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==} dependencies: - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-util-combine-extensions@1.0.0: - resolution: {integrity: sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==} - dependencies: - micromark-util-chunked: 1.0.0 - micromark-util-types: 1.0.2 - - /micromark-util-combine-extensions@1.1.0: - resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==} - dependencies: - micromark-util-chunked: 1.1.0 - micromark-util-types: 1.1.0 - dev: true /micromark-util-combine-extensions@2.0.0: resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==} dependencies: micromark-util-chunked: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-util-decode-numeric-character-reference@1.0.0: - resolution: {integrity: sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==} - dependencies: - micromark-util-symbol: 1.0.1 - - /micromark-util-decode-numeric-character-reference@1.1.0: - resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==} - dependencies: - micromark-util-symbol: 1.0.1 - dev: true /micromark-util-decode-numeric-character-reference@2.0.1: resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==} dependencies: micromark-util-symbol: 2.0.0 - dev: true - - /micromark-util-decode-string@1.0.2: - resolution: {integrity: sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==} - dependencies: - decode-named-character-reference: 1.0.2 - micromark-util-character: 1.1.0 - micromark-util-decode-numeric-character-reference: 1.0.0 - micromark-util-symbol: 1.0.1 - - /micromark-util-decode-string@1.1.0: - resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==} - dependencies: - decode-named-character-reference: 1.0.2 - micromark-util-character: 1.1.0 - micromark-util-decode-numeric-character-reference: 1.1.0 - micromark-util-symbol: 1.0.1 - dev: true /micromark-util-decode-string@2.0.0: resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==} dependencies: decode-named-character-reference: 1.0.2 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-decode-numeric-character-reference: 2.0.1 micromark-util-symbol: 2.0.0 - dev: true - - /micromark-util-encode@1.0.1: - resolution: {integrity: sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==} /micromark-util-encode@2.0.0: resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} - dev: true - /micromark-util-events-to-acorn@1.2.3: - resolution: {integrity: sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==} + /micromark-util-events-to-acorn@2.0.2: + resolution: {integrity: sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==} dependencies: '@types/acorn': 4.0.6 - '@types/estree': 1.0.1 - '@types/unist': 2.0.8 - estree-util-visit: 1.2.1 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - vfile-message: 3.1.4 - dev: true - - /micromark-util-html-tag-name@1.0.0: - resolution: {integrity: sha512-NenEKIshW2ZI/ERv9HtFNsrn3llSPZtY337LID/24WeLqMzeZhBEE6BQ0vS2ZBjshm5n40chKtJ3qjAbVV8S0g==} - - /micromark-util-html-tag-name@1.2.0: - resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==} + '@types/estree': 1.0.5 + '@types/unist': 3.0.2 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + vfile-message: 4.0.2 dev: true /micromark-util-html-tag-name@2.0.0: resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} - dev: true - - /micromark-util-normalize-identifier@1.0.0: - resolution: {integrity: sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==} - dependencies: - micromark-util-symbol: 1.0.1 - - /micromark-util-normalize-identifier@1.1.0: - resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==} - dependencies: - micromark-util-symbol: 1.0.1 - dev: true /micromark-util-normalize-identifier@2.0.0: resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==} dependencies: micromark-util-symbol: 2.0.0 - dev: true - - /micromark-util-resolve-all@1.0.0: - resolution: {integrity: sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==} - dependencies: - micromark-util-types: 1.0.2 - - /micromark-util-resolve-all@1.1.0: - resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==} - dependencies: - micromark-util-types: 1.1.0 - dev: true /micromark-util-resolve-all@2.0.0: resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==} dependencies: micromark-util-types: 2.0.0 - dev: true - - /micromark-util-sanitize-uri@1.0.0: - resolution: {integrity: sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==} - dependencies: - micromark-util-character: 1.1.0 - micromark-util-encode: 1.0.1 - micromark-util-symbol: 1.0.1 - - /micromark-util-sanitize-uri@1.1.0: - resolution: {integrity: sha512-RoxtuSCX6sUNtxhbmsEFQfWzs8VN7cTctmBPvYivo98xb/kDEoTCtJQX5wyzIYEmk/lvNFTat4hL8oW0KndFpg==} - dependencies: - micromark-util-character: 1.1.0 - micromark-util-encode: 1.0.1 - micromark-util-symbol: 1.0.1 /micromark-util-sanitize-uri@2.0.0: resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} dependencies: - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-encode: 2.0.0 micromark-util-symbol: 2.0.0 - dev: true - - /micromark-util-subtokenize@1.0.2: - resolution: {integrity: sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==} - dependencies: - micromark-util-chunked: 1.0.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.4 - - /micromark-util-subtokenize@1.1.0: - resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==} - dependencies: - micromark-util-chunked: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - dev: true /micromark-util-subtokenize@2.0.0: resolution: {integrity: sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==} @@ -6673,59 +5878,23 @@ packages: micromark-util-chunked: 2.0.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: true - - /micromark-util-symbol@1.0.1: - resolution: {integrity: sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==} /micromark-util-symbol@2.0.0: resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} - dev: true - - /micromark-util-types@1.0.2: - resolution: {integrity: sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==} - - /micromark-util-types@1.1.0: - resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==} - dev: true /micromark-util-types@2.0.0: resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} - dev: true - - /micromark@3.0.10: - resolution: {integrity: sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg==} - dependencies: - '@types/debug': 4.1.7 - debug: 4.3.4 - decode-named-character-reference: 1.0.2 - micromark-core-commonmark: 1.0.6 - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-chunked: 1.0.0 - micromark-util-combine-extensions: 1.0.0 - micromark-util-decode-numeric-character-reference: 1.0.0 - micromark-util-encode: 1.0.1 - micromark-util-normalize-identifier: 1.0.0 - micromark-util-resolve-all: 1.0.0 - micromark-util-sanitize-uri: 1.1.0 - micromark-util-subtokenize: 1.0.2 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.4 - transitivePeerDependencies: - - supports-color /micromark@4.0.0: resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} dependencies: - '@types/debug': 4.1.7 + '@types/debug': 4.1.12 debug: 4.3.4 decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.0 micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-chunked: 2.0.0 micromark-util-combine-extensions: 2.0.0 micromark-util-decode-numeric-character-reference: 2.0.1 @@ -6738,7 +5907,6 @@ packages: micromark-util-types: 2.0.0 transitivePeerDependencies: - supports-color - dev: true /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} @@ -6787,7 +5955,6 @@ packages: engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 - dev: false /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -6810,28 +5977,20 @@ packages: engines: {node: '>=10'} hasBin: true - /mlly@1.1.1: - resolution: {integrity: sha512-Jnlh4W/aI4GySPo6+DyTN17Q75KKbLTyFK8BrGhjNP4rxuUjbRWhE6gHg3bs33URWAF44FRm7gdQA348i3XxRw==} + /mlly@1.6.1: + resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==} dependencies: - acorn: 8.10.0 - pathe: 1.1.0 - pkg-types: 1.0.2 - ufo: 1.1.0 - dev: true - - /mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - - /ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + acorn: 8.11.3 + pathe: 1.1.2 + pkg-types: 1.0.3 + ufo: 1.5.2 dev: true /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - /muggle-string@0.3.1: - resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} + /muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} dev: false /nanoid@3.3.7: @@ -6840,9 +5999,9 @@ packages: hasBin: true dev: true - /nanostores@0.5.13: - resolution: {integrity: sha512-UxC2eZsCbbcHZ1Z/hwFQ1C4gy8Tkhzskvmu6wOsAhOMAOd72HmVX1Nxs96DSDUcd7V1x0IdRtsl+zAm7rg7slA==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + /nanostores@0.10.0: + resolution: {integrity: sha512-Poy5+9wFXOD0jAstn4kv9n686U2BFw48z/W8lms8cS8lcbRz7BU20JxZ3e/kkKQVfRrkm4yLWCUA6GQINdvJCQ==} + engines: {node: ^18.0.0 || >=20.0.0} dev: false /napi-build-utils@1.0.2: @@ -6851,40 +6010,21 @@ packages: dev: true optional: true - /natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - dev: true - /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /needle@2.9.1: - resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==} - engines: {node: '>= 4.4.x'} - hasBin: true - dependencies: - debug: 3.2.7 - iconv-lite: 0.4.24 - sax: 1.2.4 - transitivePeerDependencies: - - supports-color - dev: true - - /nlcst-to-string@2.0.4: - resolution: {integrity: sha512-3x3jwTd6UPG7vi5k4GEzvxJ5rDA7hVUIRNHPblKuMVP9Z3xmlsd9cgLcpAMkc5uPOBna82EeshROFhsPkbnTZg==} - - /nlcst-to-string@3.1.0: - resolution: {integrity: sha512-Y8HQWKw/zrHTCnu2zcFBN1dV6vN0NUG7s5fkEj380G8tF3R+vA2KG+tDl2QoHVQCTHGHVXwoni2RQkDSFQb1PA==} + /nlcst-to-string@3.1.1: + resolution: {integrity: sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw==} dependencies: - '@types/nlcst': 1.0.0 + '@types/nlcst': 1.0.4 - /node-abi@3.47.0: - resolution: {integrity: sha512-2s6B2CWZM//kPgwnuI0KrYwNjfdByE25zvAaEpq9IH4zcNsarH8Ihu/UuX6XMPEogDAxkuUFeZn60pXNHAqn3A==} + /node-abi@3.56.0: + resolution: {integrity: sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==} engines: {node: '>=10'} requiresBuild: true dependencies: - semver: 7.5.4 + semver: 7.6.0 dev: true optional: true @@ -6899,8 +6039,8 @@ packages: engines: {node: '>=10.5.0'} dev: true - /node-fetch@2.6.7: - resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} peerDependencies: encoding: ^0.1.0 @@ -6911,43 +6051,41 @@ packages: whatwg-url: 5.0.0 dev: true - /node-fetch@3.2.10: - resolution: {integrity: sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==} + /node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: - data-uri-to-buffer: 4.0.0 - fetch-blob: 3.1.5 + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 dev: true - /node-releases@2.0.13: - resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + /node-html-parser@6.1.12: + resolution: {integrity: sha512-/bT/Ncmv+fbMGX96XG9g05vFt43m/+SYKIs9oAemQVYyVcZmDAI2Xq/SbNcpOA35eF0Zk2av3Ksf+Xk8Vt8abA==} + dependencies: + css-select: 5.1.0 + he: 1.2.0 dev: true /node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} dev: true - /node-releases@2.0.6: - resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} - dev: true - /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - /npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} + /npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: - path-key: 3.1.1 + path-key: 4.0.0 dev: true - /npm-run-path@5.1.0: - resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + /nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} dependencies: - path-key: 4.0.0 + boolbase: 1.0.0 dev: true /object-assign@4.1.1: @@ -6955,8 +6093,8 @@ packages: engines: {node: '>=0.10.0'} dev: true - /object-inspect@1.12.3: - resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} dev: true /object-keys@1.1.1: @@ -6969,48 +6107,49 @@ packages: engines: {node: '>=8.0.0'} dev: false - /object.assign@4.1.4: - resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + /object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 + call-bind: 1.0.7 + define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 dev: true - /object.entries@1.1.6: - resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} + /object.entries@1.1.8: + resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.21.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 dev: true - /object.fromentries@2.0.6: - resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} + /object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.21.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.2 + es-object-atoms: 1.0.0 dev: true - /object.hasown@1.1.2: - resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} + /object.hasown@1.1.3: + resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} dependencies: - define-properties: 1.1.4 - es-abstract: 1.21.1 + define-properties: 1.2.1 + es-abstract: 1.22.5 dev: true - /object.values@1.1.6: - resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} + /object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.21.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 dev: true /once@1.4.0: @@ -7032,26 +6171,16 @@ packages: mimic-fn: 4.0.0 dev: true - /open@9.1.0: - resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} - engines: {node: '>=14.16'} - dependencies: - default-browser: 4.0.0 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - is-wsl: 2.2.0 - dev: true - - /optionator@0.9.1: - resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 deep-is: 0.1.4 fast-levenshtein: 2.0.6 levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 - word-wrap: 1.2.3 dev: true /ora@7.0.1: @@ -7060,7 +6189,7 @@ packages: dependencies: chalk: 5.3.0 cli-cursor: 4.0.0 - cli-spinners: 2.9.0 + cli-spinners: 2.9.2 is-interactive: 2.0.0 is-unicode-supported: 1.3.0 log-symbols: 5.1.0 @@ -7098,13 +6227,6 @@ packages: yocto-queue: 0.1.0 dev: true - /p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - yocto-queue: 1.0.0 - dev: true - /p-limit@5.0.0: resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} engines: {node: '>=18'} @@ -7134,19 +6256,20 @@ packages: p-timeout: 3.2.0 dev: true - /p-queue@7.4.1: - resolution: {integrity: sha512-vRpMXmIkYF2/1hLBKisKeVYJZ8S2tZ0zEAmIJgdVKP2nq0nh4qCdf8bgw+ZgKrkh71AOCaqzwbJJk1WtdcF3VA==} - engines: {node: '>=12'} + /p-queue@8.0.1: + resolution: {integrity: sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA==} + engines: {node: '>=18'} dependencies: eventemitter3: 5.0.1 - p-timeout: 5.1.0 + p-timeout: 6.1.2 dev: true - /p-retry@5.1.1: - resolution: {integrity: sha512-i69WkEU5ZAL8mrmdmVviWwU+DN+IUF8f4sSJThoJ3z5A7Nn5iuO5ROX3Boye0u+uYQLOSfgFl7SuFZCjlAVbQA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + /p-retry@6.2.0: + resolution: {integrity: sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA==} + engines: {node: '>=16.17'} dependencies: - '@types/retry': 0.12.1 + '@types/retry': 0.12.2 + is-network-error: 1.1.0 retry: 0.13.1 dev: true @@ -7157,9 +6280,9 @@ packages: p-finally: 1.0.0 dev: true - /p-timeout@5.1.0: - resolution: {integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==} - engines: {node: '>=12'} + /p-timeout@6.1.2: + resolution: {integrity: sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==} + engines: {node: '>=14.16'} dev: true /p-try@2.2.0: @@ -7174,10 +6297,10 @@ packages: callsites: 3.1.0 dev: true - /parse-entities@4.0.0: - resolution: {integrity: sha512-5nk9Fn03x3rEhGaX1FU6IDwG/k+GxLXlFAkgrbM1asuAFl3BhdQWvASaIsmwWypRNcZKHPYnIuOSfIWEyEQnPQ==} + /parse-entities@4.0.1: + resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} dependencies: - '@types/unist': 2.0.6 + '@types/unist': 2.0.10 character-entities: 2.0.2 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 @@ -7187,12 +6310,12 @@ packages: is-hexadecimal: 2.0.1 dev: true - /parse-latin@5.0.0: - resolution: {integrity: sha512-Ht+4/+AUySMS5HKGAiQpBmkFsHSoGrj6Y83flLCa5OIBdtsVkO3UD4OtboJ0O0vZiOznH02x8qlwg9KLUVXuNg==} + /parse-latin@5.0.1: + resolution: {integrity: sha512-b/K8ExXaWC9t34kKeDV8kGXBkXZ1HCSAZRYE7HR14eA1GlXX5L8iWhs8USJNhQU9q5ci413jCKF0gOyovvyRBg==} dependencies: - nlcst-to-string: 2.0.4 - unist-util-modify-children: 2.0.0 - unist-util-visit-children: 1.1.4 + nlcst-to-string: 3.1.1 + unist-util-modify-children: 3.1.1 + unist-util-visit-children: 2.0.2 /parse-numeric-range@1.3.0: resolution: {integrity: sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==} @@ -7210,7 +6333,6 @@ packages: /path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - dev: true /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -7244,8 +6366,8 @@ packages: engines: {node: '>=8'} dev: true - /pathe@1.1.0: - resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==} + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} dev: true /pathval@1.1.1: @@ -7255,9 +6377,9 @@ packages: /periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} dependencies: - '@types/estree': 1.0.1 + '@types/estree': 1.0.5 estree-walker: 3.0.3 - is-reference: 3.0.1 + is-reference: 3.0.2 dev: true /picocolors@1.0.0: @@ -7286,87 +6408,92 @@ packages: find-up: 4.1.0 dev: true - /pkg-types@1.0.2: - resolution: {integrity: sha512-hM58GKXOcj8WTqUXnsQyJYXdeAPbythQgEF3nTcEo+nkD49chjQ9IKm/QJy9xf6JakXptz86h7ecP2024rrLaQ==} + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} dependencies: - jsonc-parser: 3.2.0 - mlly: 1.1.1 - pathe: 1.1.0 + jsonc-parser: 3.2.1 + mlly: 1.6.1 + pathe: 1.1.2 dev: true /plantuml-encoder@1.4.0: resolution: {integrity: sha512-sxMwpDw/ySY1WB2CE3+IdMuEcWibJ72DDOsXLkSmEaSzwEUaYBT6DWgOfBiHGCux4q433X6+OEFWjlVqp7gL6g==} dev: false - /postcss-nested@6.0.1(postcss@8.4.32): + /possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + dev: true + + /postcss-nested@6.0.1(postcss@8.4.37): resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 dependencies: - postcss: 8.4.32 - postcss-selector-parser: 6.0.13 - dev: true - - /postcss-selector-parser@6.0.10: - resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} - engines: {node: '>=4'} - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 + postcss: 8.4.37 + postcss-selector-parser: 6.0.16 dev: true - /postcss-selector-parser@6.0.13: - resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + /postcss-selector-parser@6.0.16: + resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} engines: {node: '>=4'} dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 dev: true - /postcss@8.4.18: - resolution: {integrity: sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==} + /postcss@8.4.37: + resolution: {integrity: sha512-7iB/v/r7Woof0glKLH8b1SPHrsX7uhdO+Geb41QpF/+mWZHU3uxxSlN+UXGVit1PawOYDToO+AbZzhBzWRDwbQ==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.7 picocolors: 1.0.0 - source-map-js: 1.0.2 + source-map-js: 1.2.0 dev: true - /postcss@8.4.32: - resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==} + /postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.7 picocolors: 1.0.0 - source-map-js: 1.0.2 + source-map-js: 1.2.0 dev: true - /preact-render-to-string@6.2.1(preact@10.16.0): - resolution: {integrity: sha512-5t7nFeMUextd53igL3GAakAAMaUD+dVWDHaRYaeh1tbPIjQIBtgJnMw6vf8VS/lviV0ggFtkgebatPxvtJsXyQ==} + /preact-render-to-string@6.3.1(preact@10.19.7): + resolution: {integrity: sha512-NQ28WrjLtWY6lKDlTxnFpKHZdpjfF+oE6V4tZ0rTrunHrtZp6Dm0oFrcJalt/5PNeqJz4j1DuZDS0Y6rCBoqDA==} peerDependencies: preact: '>=10' dependencies: - preact: 10.16.0 + preact: 10.19.7 pretty-format: 3.8.0 dev: true - /preact@10.16.0: - resolution: {integrity: sha512-XTSj3dJ4roKIC93pald6rWuB2qQJO9gO2iLLyTe87MrjQN+HklueLsmskbywEWqCHlclgz3/M4YLL2iBr9UmMA==} + /preact-ssr-prepass@1.2.1(preact@10.19.7): + resolution: {integrity: sha512-bLgbUfy8nL+PZghAPpyk9MF+cmXjdwEnxYPaJBmwbzFQqzIz8dQVBqjwB60RqZ9So/vIf6BRfHCiwFGuMCyfbQ==} + peerDependencies: + preact: '>=10 || ^10.0.0-beta.0 || ^10.0.0-alpha.0' + dependencies: + preact: 10.19.7 + dev: true + + /preact@10.19.7: + resolution: {integrity: sha512-IJOW6cQN1fwfC17HfNOqUtAGyB8wAYshuC+jG1JiL/1+sC4yVyuA3IcF0N9vdodMJjW/lbuEF5qFsJqGNcbHbw==} - /prebuild-install@7.1.1: - resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} + /prebuild-install@7.1.2: + resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} engines: {node: '>=10'} hasBin: true requiresBuild: true dependencies: - detect-libc: 2.0.2 + detect-libc: 2.0.3 expand-template: 2.0.3 github-from-package: 0.0.0 minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 1.0.2 - node-abi: 3.47.0 + node-abi: 3.56.0 pump: 3.0.0 rc: 1.2.8 simple-get: 4.0.1 @@ -7375,8 +6502,8 @@ packages: dev: true optional: true - /preferred-pm@3.1.2: - resolution: {integrity: sha512-nk7dKrcW8hfCZ4H6klWcdRknBOXWzNQByJ0oJyX97BOupsYD+FzLS4hflgEu/uPUEHZCuRfMxzCBsuWd7OzT8Q==} + /preferred-pm@3.1.3: + resolution: {integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w==} engines: {node: '>=10'} dependencies: find-up: 5.0.0 @@ -7390,26 +6517,26 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier-plugin-astro@0.11.1: - resolution: {integrity: sha512-28sf624jQz9uP4hkQiRPRVuG1/4XJpnS6DfoXPgeDAeQ+eQ1o21bpioUbxze57y2EN+BCHeEw6x3a1MhM08Liw==} + /prettier-plugin-astro@0.13.0: + resolution: {integrity: sha512-5HrJNnPmZqTUNoA97zn4gNQv9BgVhv+et03314WpQ9H9N8m2L9OSV798olwmG2YLXPl1iSstlJCR1zB3x5xG4g==} engines: {node: ^14.15.0 || >=16.0.0} dependencies: - '@astrojs/compiler': 1.5.5 - prettier: 3.0.2 - sass-formatter: 0.7.6 + '@astrojs/compiler': 1.8.2 + prettier: 3.2.5 + sass-formatter: 0.7.9 - /prettier@3.0.2: - resolution: {integrity: sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==} + /prettier@3.2.5: + resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} engines: {node: '>=14'} hasBin: true - /pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - ansi-regex: 5.0.1 + '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 - react-is: 17.0.2 + react-is: 18.2.0 dev: true /pretty-format@3.8.0: @@ -7421,16 +6548,6 @@ packages: engines: {node: '>=6'} dev: true - /probe-image-size@7.2.3: - resolution: {integrity: sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==} - dependencies: - lodash.merge: 4.6.2 - needle: 2.9.1 - stream-parser: 0.3.1 - transitivePeerDependencies: - - supports-color - dev: true - /prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -7447,12 +6564,8 @@ packages: react-is: 16.13.1 dev: true - /property-information@6.1.1: - resolution: {integrity: sha512-hrzC564QIl0r0vy4l6MvRLhafmUowhO/O3KgVSoXIbbA2Sz4j8HGpJc6T2cubRVwMwpdiG/vKGfhT4IixmKN9w==} - dev: true - - /property-information@6.2.0: - resolution: {integrity: sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==} + /property-information@6.4.1: + resolution: {integrity: sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==} dev: true /pseudomap@1.0.2: @@ -7468,8 +6581,8 @@ packages: dev: true optional: true - /punycode@2.1.1: - resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} dev: true @@ -7498,8 +6611,8 @@ packages: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true - /react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true /readable-stream@3.6.2: @@ -7517,155 +6630,102 @@ packages: dependencies: picomatch: 2.3.1 - /regexp.prototype.flags@1.4.3: - resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} + /reflect.getprototypeof@1.0.6: + resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - functions-have-names: 1.2.3 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.2 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + globalthis: 1.0.3 + which-builtin-type: 1.1.3 dev: true - /regexpp@3.2.0: - resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} - engines: {node: '>=8'} + /regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 dev: true - /rehype-autolink-headings@6.1.1: - resolution: {integrity: sha512-NMYzZIsHM3sA14nC5rAFuUPIOfg+DFmf9EY1YMhaNlB7+3kK/ZlE6kqPfuxr1tsJ1XWkTrMtMoyHosU70d35mA==} + /rehype-autolink-headings@7.1.0: + resolution: {integrity: sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==} dependencies: - '@types/hast': 2.3.4 - extend: 3.0.2 - hast-util-has-property: 2.0.0 - hast-util-heading-rank: 2.1.0 - hast-util-is-element: 2.1.2 - unified: 10.1.2 - unist-util-visit: 4.1.0 + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.2.0 + hast-util-heading-rank: 3.0.0 + hast-util-is-element: 3.0.0 + unified: 11.0.4 + unist-util-visit: 5.0.0 dev: false - /rehype-parse@8.0.4: - resolution: {integrity: sha512-MJJKONunHjoTh4kc3dsM1v3C9kGrrxvA3U8PxZlP2SjH8RNUSrb+lF7Y0KVaUDnGH2QZ5vAn7ulkiajM9ifuqg==} - dependencies: - '@types/hast': 2.3.4 - hast-util-from-parse5: 7.1.0 - parse5: 6.0.1 - unified: 10.1.2 - dev: true - /rehype-parse@9.0.0: resolution: {integrity: sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw==} dependencies: - '@types/hast': 3.0.3 + '@types/hast': 3.0.4 hast-util-from-html: 2.0.1 unified: 11.0.4 dev: true - /rehype-raw@6.1.1: - resolution: {integrity: sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==} - dependencies: - '@types/hast': 2.3.4 - hast-util-raw: 7.2.3 - unified: 10.1.2 - dev: true - /rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} dependencies: - '@types/hast': 3.0.3 - hast-util-raw: 9.0.1 + '@types/hast': 3.0.4 + hast-util-raw: 9.0.2 vfile: 6.0.1 dev: true - /rehype-slug@5.0.1: - resolution: {integrity: sha512-X5v3wV/meuOX9NFcGhJvUpEjIvQl2gDvjg3z40RVprYFt7q3th4qMmYLULiu3gXvbNX1ppx+oaa6JyY1W67pTA==} + /rehype-slug@6.0.0: + resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==} dependencies: - '@types/hast': 2.3.4 - github-slugger: 1.5.0 - hast-util-has-property: 2.0.0 - hast-util-heading-rank: 2.1.0 - hast-util-to-string: 2.0.0 - unified: 10.1.2 - unist-util-visit: 4.1.0 + '@types/hast': 3.0.4 + github-slugger: 2.0.0 + hast-util-heading-rank: 3.0.0 + hast-util-to-string: 3.0.0 + unist-util-visit: 5.0.0 dev: false /rehype-stringify@10.0.0: resolution: {integrity: sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ==} dependencies: - '@types/hast': 3.0.3 + '@types/hast': 3.0.4 hast-util-to-html: 9.0.0 unified: 11.0.4 dev: true - /rehype-stringify@9.0.3: - resolution: {integrity: sha512-kWiZ1bgyWlgOxpqD5HnxShKAdXtb2IUljn3hQAhySeak6IOQPPt6DeGnsIh4ixm7yKJWzm8TXFuC/lPfcWHJqw==} - dependencies: - '@types/hast': 2.3.4 - hast-util-to-html: 8.0.4 - unified: 10.1.2 - dev: true - - /rehype-stringify@9.0.4: - resolution: {integrity: sha512-Uk5xu1YKdqobe5XpSskwPvo1XeHUUucWEQSl8hTrXt5selvca1e8K1EZ37E6YoZ4BT8BCqCdVfQW7OfHfthtVQ==} - dependencies: - '@types/hast': 2.3.4 - hast-util-to-html: 8.0.4 - unified: 10.1.2 - dev: true - - /rehype@12.0.1: - resolution: {integrity: sha512-ey6kAqwLM3X6QnMDILJthGvG1m1ULROS9NT4uG9IDCuv08SFyLlreSuvOa//DgEvbXx62DS6elGVqusWhRUbgw==} - dependencies: - '@types/hast': 2.3.4 - rehype-parse: 8.0.4 - rehype-stringify: 9.0.3 - unified: 10.1.2 - dev: true - /rehype@13.0.1: resolution: {integrity: sha512-AcSLS2mItY+0fYu9xKxOu1LhUZeBZZBx8//5HKzF+0XP+eP8+6a5MXn2+DW2kfXR6Dtp1FEXMVrjyKAcvcU8vg==} dependencies: - '@types/hast': 3.0.3 + '@types/hast': 3.0.4 rehype-parse: 9.0.0 rehype-stringify: 10.0.0 unified: 11.0.4 dev: true - /remark-directive@2.0.1: - resolution: {integrity: sha512-oosbsUAkU/qmUE78anLaJePnPis4ihsE7Agp0T/oqTzvTea8pOiaYEtfInU/+xMOVTS9PN5AhGOiaIVe4GD8gw==} + /remark-directive@3.0.0: + resolution: {integrity: sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA==} dependencies: - '@types/mdast': 3.0.10 - mdast-util-directive: 2.2.1 - micromark-extension-directive: 2.1.1 - unified: 10.1.2 + '@types/mdast': 4.0.3 + mdast-util-directive: 3.0.0 + micromark-extension-directive: 3.0.0 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color dev: true - /remark-expressive-code@0.20.0: - resolution: {integrity: sha512-exiNlq/Y39TLYO25sh9ed+gRJ6EYqGeDj1gmqhzu9ufecJJbcf+Av57AHG0N0gpyA1gQ2o42G47f3Fsfwpa5Ew==} + /remark-expressive-code@0.33.5: + resolution: {integrity: sha512-E4CZq3AuUXLu6or0AaDKkgsHYqmnm4ZL8/+1/8YgwtKcogHwTMRJfQtxkZpth90QQoNUpsapvm5x5n3Np2OC9w==} dependencies: - expressive-code: 0.20.0 + expressive-code: 0.33.5 hast-util-to-html: 8.0.4 unist-util-visit: 4.1.2 dev: true - /remark-frontmatter@4.0.1: - resolution: {integrity: sha512-38fJrB0KnmD3E33a5jZC/5+gGAC2WKNiPw1/fdXJvijBlhA7RCsvJklrYJakS0HedninvaCYW8lQGf9C918GfA==} - dependencies: - '@types/mdast': 3.0.10 - mdast-util-frontmatter: 1.0.1 - micromark-extension-frontmatter: 1.1.1 - unified: 10.1.2 - dev: true - - /remark-gfm@3.0.1: - resolution: {integrity: sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==} - dependencies: - '@types/mdast': 3.0.10 - mdast-util-gfm: 2.0.1 - micromark-extension-gfm: 2.0.1 - unified: 10.1.2 - transitivePeerDependencies: - - supports-color - /remark-gfm@4.0.0: resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==} dependencies: @@ -7677,33 +6737,12 @@ packages: unified: 11.0.4 transitivePeerDependencies: - supports-color - dev: true - - /remark-mdx@2.3.0: - resolution: {integrity: sha512-g53hMkpM0I98MU266IzDFMrTD980gNF3BJnkyFcmN+dD873mQeD5rdMO3Y2X+x8umQfbSE0PcoEDl7ledSA+2g==} - dependencies: - mdast-util-mdx: 2.0.1 - micromark-extension-mdxjs: 1.0.1 - transitivePeerDependencies: - - supports-color - dev: true - - /remark-parse@10.0.1: - resolution: {integrity: sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==} - dependencies: - '@types/mdast': 3.0.10 - mdast-util-from-markdown: 1.2.0 - unified: 10.1.2 - transitivePeerDependencies: - - supports-color - dev: true - /remark-parse@10.0.2: - resolution: {integrity: sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==} + /remark-mdx@3.0.1: + resolution: {integrity: sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==} dependencies: - '@types/mdast': 3.0.10 - mdast-util-from-markdown: 1.2.0 - unified: 10.1.2 + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 transitivePeerDependencies: - supports-color dev: true @@ -7717,42 +6756,24 @@ packages: unified: 11.0.4 transitivePeerDependencies: - supports-color - dev: true - - /remark-rehype@10.1.0: - resolution: {integrity: sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==} - dependencies: - '@types/hast': 2.3.4 - '@types/mdast': 3.0.10 - mdast-util-to-hast: 12.2.4 - unified: 10.1.2 - dev: true - /remark-rehype@11.0.0: - resolution: {integrity: sha512-vx8x2MDMcxuE4lBmQ46zYUDfcFMmvg80WYX+UNLeG6ixjdCCLcw1lrgAukwBTuOFsS78eoAedHGn9sNM0w7TPw==} + /remark-rehype@11.1.0: + resolution: {integrity: sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==} dependencies: - '@types/hast': 3.0.3 + '@types/hast': 3.0.4 '@types/mdast': 4.0.3 - mdast-util-to-hast: 13.0.2 + mdast-util-to-hast: 13.1.0 unified: 11.0.4 vfile: 6.0.1 dev: true - /remark-smartypants@2.0.0: - resolution: {integrity: sha512-Rc0VDmr/yhnMQIz8n2ACYXlfw/P/XZev884QU1I5u+5DgJls32o97Vc1RbK3pfumLsJomS2yy8eT4Fxj/2MDVA==} + /remark-smartypants@2.1.0: + resolution: {integrity: sha512-qoF6Vz3BjU2tP6OfZqHOvCU0ACmu/6jhGaINSQRI9mM7wCxNQTKB3JUAN4SVoN2ybElEDTxBIABRep7e569iJw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: retext: 8.1.0 - retext-smartypants: 5.1.0 - unist-util-visit: 4.1.0 - - /remark-stringify@10.0.2: - resolution: {integrity: sha512-6wV3pvbPvHkbNnWB0wdDvVFHOe1hBRAx1Q/5g/EpH4RppAII6J8Gnwe7VbHuXaoKIF6LAg6ExTel/+kNqSQ7lw==} - dependencies: - '@types/mdast': 3.0.10 - mdast-util-to-markdown: 1.3.0 - unified: 10.1.2 - dev: true + retext-smartypants: 5.2.0 + unist-util-visit: 5.0.0 /remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} @@ -7760,15 +6781,14 @@ packages: '@types/mdast': 4.0.3 mdast-util-to-markdown: 2.1.0 unified: 11.0.4 - dev: true - /remark@14.0.2: - resolution: {integrity: sha512-A3ARm2V4BgiRXaUo5K0dRvJ1lbogrbXnhkJRmD0yw092/Yl0kOCZt1k9ZeElEwkZsWGsMumz6qL5MfNJH9nOBA==} + /remark@15.0.1: + resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} dependencies: - '@types/mdast': 3.0.10 - remark-parse: 10.0.1 - remark-stringify: 10.0.2 - unified: 10.1.2 + '@types/mdast': 4.0.3 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.4 transitivePeerDependencies: - supports-color dev: true @@ -7786,8 +6806,8 @@ packages: engines: {node: '>=0.10.0'} dev: false - /requizzle@0.2.3: - resolution: {integrity: sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==} + /requizzle@0.2.4: + resolution: {integrity: sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==} dependencies: lodash: 4.17.21 dev: false @@ -7797,20 +6817,20 @@ packages: engines: {node: '>=4'} dev: true - /resolve@1.22.4: - resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true dependencies: - is-core-module: 2.13.0 + is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 dev: true - /resolve@2.0.0-next.4: - resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} + /resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true dependencies: - is-core-module: 2.9.0 + is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 dev: true @@ -7826,30 +6846,30 @@ packages: /retext-latin@3.1.0: resolution: {integrity: sha512-5MrD1tuebzO8ppsja5eEu+ZbBeUNCjoEarn70tkXOS7Bdsdf6tNahsv2bY0Z8VooFF6cw7/6S+d3yI/TMlMVVQ==} dependencies: - '@types/nlcst': 1.0.0 - parse-latin: 5.0.0 - unherit: 3.0.0 + '@types/nlcst': 1.0.4 + parse-latin: 5.0.1 + unherit: 3.0.1 unified: 10.1.2 - /retext-smartypants@5.1.0: - resolution: {integrity: sha512-P+VS0YlE96T2MRAlFHaTUhPrq1Rls+1GCvIytBvbo7wcgmRxC9xHle0/whTYpRqWirV9WaUm5mXmh1dKnskGWQ==} + /retext-smartypants@5.2.0: + resolution: {integrity: sha512-Do8oM+SsjrbzT2UNIKgheP0hgUQTDDQYyZaIY3kfq0pdFzoPk+ZClYJ+OERNXveog4xf1pZL4PfRxNoVL7a/jw==} dependencies: - '@types/nlcst': 1.0.0 - nlcst-to-string: 3.1.0 + '@types/nlcst': 1.0.4 + nlcst-to-string: 3.1.1 unified: 10.1.2 - unist-util-visit: 4.1.0 + unist-util-visit: 4.1.2 /retext-stringify@3.1.0: resolution: {integrity: sha512-767TLOaoXFXyOnjx/EggXlb37ZD2u4P1n0GJqVdpipqACsQP+20W+BNpMYrlJkq7hxffnFk+jc6mAK9qrbuB8w==} dependencies: - '@types/nlcst': 1.0.0 - nlcst-to-string: 3.1.0 + '@types/nlcst': 1.0.4 + nlcst-to-string: 3.1.1 unified: 10.1.2 /retext@8.1.0: resolution: {integrity: sha512-N9/Kq7YTn6ZpzfiGW45WfEGJqFf1IM1q8OsRa1CGzIebCJBNCANDRmOrholiDRGKo/We7ofKR4SEvcGAWEMD3Q==} dependencies: - '@types/nlcst': 1.0.0 + '@types/nlcst': 1.0.4 retext-latin: 3.1.0 retext-stringify: 3.1.0 unified: 10.1.2 @@ -7863,53 +6883,40 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - /rfdc@1.3.0: - resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} + /rfdc@1.3.1: + resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} dev: true /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true - dependencies: - glob: 7.2.3 - dev: true - - /rollup@3.29.0: - resolution: {integrity: sha512-nszM8DINnx1vSS+TpbWKMkxem0CDWk3cSit/WWCBVs9/JZ1I/XLwOsiUglYuYReaeWWSsW9kge5zE5NZtf/a4w==} - engines: {node: '>=14.18.0', npm: '>=8.0.0'} - hasBin: true - optionalDependencies: - fsevents: 2.3.3 + dependencies: + glob: 7.2.3 dev: true - /rollup@4.8.0: - resolution: {integrity: sha512-NpsklK2fach5CdI+PScmlE5R4Ao/FSWtF7LkoIrHDxPACY/xshNasPsbpG0VVHxUTbf74tJbVT4PrP8JsJ6ZDA==} + /rollup@4.13.0: + resolution: {integrity: sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + dependencies: + '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.8.0 - '@rollup/rollup-android-arm64': 4.8.0 - '@rollup/rollup-darwin-arm64': 4.8.0 - '@rollup/rollup-darwin-x64': 4.8.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.8.0 - '@rollup/rollup-linux-arm64-gnu': 4.8.0 - '@rollup/rollup-linux-arm64-musl': 4.8.0 - '@rollup/rollup-linux-riscv64-gnu': 4.8.0 - '@rollup/rollup-linux-x64-gnu': 4.8.0 - '@rollup/rollup-linux-x64-musl': 4.8.0 - '@rollup/rollup-win32-arm64-msvc': 4.8.0 - '@rollup/rollup-win32-ia32-msvc': 4.8.0 - '@rollup/rollup-win32-x64-msvc': 4.8.0 + '@rollup/rollup-android-arm-eabi': 4.13.0 + '@rollup/rollup-android-arm64': 4.13.0 + '@rollup/rollup-darwin-arm64': 4.13.0 + '@rollup/rollup-darwin-x64': 4.13.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.13.0 + '@rollup/rollup-linux-arm64-gnu': 4.13.0 + '@rollup/rollup-linux-arm64-musl': 4.13.0 + '@rollup/rollup-linux-riscv64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-musl': 4.13.0 + '@rollup/rollup-win32-arm64-msvc': 4.13.0 + '@rollup/rollup-win32-ia32-msvc': 4.13.0 + '@rollup/rollup-win32-x64-msvc': 4.13.0 fsevents: 2.3.3 dev: true - /run-applescript@5.0.0: - resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} - engines: {node: '>=12'} - dependencies: - execa: 5.1.1 - dev: true - /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -7918,52 +6925,50 @@ packages: /s.color@0.0.15: resolution: {integrity: sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==} - /sade@1.8.1: - resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} - engines: {node: '>=6'} + /safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} dependencies: - mri: 1.2.0 - - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 dev: true /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + requiresBuild: true dev: true - /safe-regex-test@1.0.0: - resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + /safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + es-errors: 1.3.0 is-regex: 1.1.4 dev: true - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: true - - /sass-formatter@0.7.6: - resolution: {integrity: sha512-hXdxU6PCkiV3XAiSnX+XLqz2ohHoEnVUlrd8LEVMAI80uB1+OTScIkH9n6qQwImZpTye1r1WG1rbGUteHNhoHg==} + /sass-formatter@0.7.9: + resolution: {integrity: sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw==} dependencies: suf-log: 2.5.3 - /sass@1.54.3: - resolution: {integrity: sha512-fLodey5Qd41Pxp/Tk7Al97sViYwF/TazRc5t6E65O7JOk4XF8pzwIW7CvCxYVOfJFFI/1x5+elDyBIixrp+zrw==} - engines: {node: '>=12.0.0'} + /sass@1.72.0: + resolution: {integrity: sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==} + engines: {node: '>=14.0.0'} hasBin: true dependencies: - chokidar: 3.5.3 - immutable: 4.1.0 - source-map-js: 1.0.2 + chokidar: 3.6.0 + immutable: 4.3.5 + source-map-js: 1.1.0 - /sax@1.2.4: - resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + /sax@1.3.0: + resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} dev: true - /search-insights@2.8.2: - resolution: {integrity: sha512-PxA9M5Q2bpBelVvJ3oDZR8nuY00Z6qwOxL53wNpgzV28M/D6u9WUbImDckjLSILBF8F1hn/mgyuUaOPtjow4Qw==} + /search-insights@2.13.0: + resolution: {integrity: sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==} dev: true /section-matter@1.0.0: @@ -7974,13 +6979,8 @@ packages: kind-of: 6.0.3 dev: true - /semver@5.7.1: - resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} - hasBin: true - dev: true - - /semver@6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true dev: true @@ -7989,23 +6989,33 @@ packages: hasBin: true dev: true - /semver@7.3.7: - resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} + /semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} engines: {node: '>=10'} hasBin: true dependencies: lru-cache: 6.0.0 - dev: true - /semver@7.5.4: - resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} - engines: {node: '>=10'} - hasBin: true + /set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} dependencies: - lru-cache: 6.0.0 + 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 + dev: true - /server-destroy@1.0.1: - resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} + /set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 dev: true /sharp@0.32.6: @@ -8014,12 +7024,12 @@ packages: requiresBuild: true dependencies: color: 4.2.3 - detect-libc: 2.0.2 + detect-libc: 2.0.3 node-addon-api: 6.1.0 - prebuild-install: 7.1.1 - semver: 7.5.4 + prebuild-install: 7.1.2 + semver: 7.6.0 simple-get: 4.0.1 - tar-fs: 3.0.4 + tar-fs: 3.0.5 tunnel-agent: 0.6.0 dev: true optional: true @@ -8036,27 +7046,26 @@ packages: engines: {node: '>=8'} dev: true - /shiki@0.14.3: - resolution: {integrity: sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g==} + /shiki@1.2.0: + resolution: {integrity: sha512-xLhiTMOIUXCv5DqJ4I70GgQCtdlzsTqFLZWcMHHG3TAieBUbvEGthdrlPDlX4mL/Wszx9C6rEcxU6kMlg4YlxA==} dependencies: - ansi-sequence-parser: 1.1.0 - jsonc-parser: 3.2.0 - vscode-oniguruma: 1.7.0 - vscode-textmate: 8.0.0 + '@shikijs/core': 1.2.0 dev: true - /shikiji@0.6.13: - resolution: {integrity: sha512-4T7X39csvhT0p7GDnq9vysWddf2b6BeioiN3Ymhnt3xcy9tXmDcnsEFVxX18Z4YcQgEE/w48dLJ4pPPUcG9KkA==} + /shiki@1.2.1: + resolution: {integrity: sha512-u+XW6o0vCkUNlneZb914dLO+AayEIwK5tI62WeS//R5HIXBFiYaj/Hc5xcq27Yh83Grr4JbNtUBV8W6zyK4hWg==} dependencies: - hast-util-to-html: 9.0.0 + '@shikijs/core': 1.2.1 dev: true - /side-channel@1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + /side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - object-inspect: 1.12.3 + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 dev: true /siginfo@2.0.0: @@ -8092,8 +7101,8 @@ packages: dev: true optional: true - /simple-git@3.11.0: - resolution: {integrity: sha512-XULamN/hxviH/ABjDbxJqUTpH59Pn3fHRtwZZZ6v7KWTLE3wKl6CLB0SPXFfzjalQ5hUp+R5DWX2X8rKm4crvw==} + /simple-git@3.23.0: + resolution: {integrity: sha512-P9ggTW8vb/21CAL/AmnACAhqBDfnqSSZVpV7WuFtsFR9HLunf5IqQvk+OXAQTfkcZep8pKnt3DV3o7w3TegEkQ==} dependencies: '@kwsites/file-exists': 1.1.1 '@kwsites/promise-deferred': 1.1.1 @@ -8120,9 +7129,9 @@ packages: hasBin: true dependencies: '@types/node': 17.0.45 - '@types/sax': 1.2.4 + '@types/sax': 1.2.7 arg: 5.0.2 - sax: 1.2.4 + sax: 1.3.0 dev: true /slash@3.0.0: @@ -8138,19 +7147,20 @@ packages: is-fullwidth-code-point: 4.0.0 dev: true - /source-map-js@1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} - engines: {node: '>=0.10.0'} - - /source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + /slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 dev: true - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + /source-map-js@1.1.0: + resolution: {integrity: sha512-9vC2SfsJzlej6MAaMPLu8HiBSHGdRAJ9hVFYN1ibZoNkeanmDmLUcIrj6G9DGL7XMJ54AKg/G75akXl1/izTOw==} + engines: {node: '>=0.10.0'} + + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} dev: true @@ -8159,15 +7169,6 @@ packages: engines: {node: '>= 8'} dev: true - /sourcemap-codec@1.4.8: - resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} - deprecated: Please use @jridgewell/sourcemap-codec instead - dev: true - - /space-separated-tokens@2.0.1: - resolution: {integrity: sha512-ekwEbFp5aqSPKaqeY1PGrlGQxPNaq+Cnx4+bE2D8sciBQrHpbwoBbawqTN2+6jPs9IdWxxiUcN0K2pkczD3zmw==} - dev: true - /space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} dev: true @@ -8176,12 +7177,17 @@ packages: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true + /stack-trace@1.0.0-pre2: + resolution: {integrity: sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==} + engines: {node: '>=16'} + dev: true + /stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true - /std-env@3.3.2: - resolution: {integrity: sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA==} + /std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} dev: true /stdin-discarder@0.1.0: @@ -8192,31 +7198,25 @@ packages: dev: true /stream-connect@1.0.2: - resolution: {integrity: sha1-GLyB8u2zW4tdmoAJIAqYUxRCipc=} + resolution: {integrity: sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==} engines: {node: '>=0.10.0'} dependencies: array-back: 1.0.4 dev: false - /stream-parser@0.3.1: - resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==} - dependencies: - debug: 2.6.9 - transitivePeerDependencies: - - supports-color - dev: true - /stream-via@1.0.4: resolution: {integrity: sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==} engines: {node: '>=0.10.0'} dev: false - /streamx@2.15.1: - resolution: {integrity: sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==} + /streamx@2.16.1: + resolution: {integrity: sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==} requiresBuild: true dependencies: fast-fifo: 1.3.2 queue-tick: 1.0.1 + optionalDependencies: + bare-events: 2.2.1 dev: true optional: true @@ -8247,12 +7247,12 @@ packages: engines: {node: '>=16'} dependencies: eastasianwidth: 0.2.0 - emoji-regex: 10.2.1 + emoji-regex: 10.3.0 strip-ansi: 7.1.0 dev: true - /string-width@7.0.0: - resolution: {integrity: sha512-GPQHj7row82Hjo9hKZieKcHIhaAIKOJvFSIZXuCU9OASVZrMNUaZuz++SPVrBjnLsnk4k+z9f2EIypgxf2vNFw==} + /string-width@7.1.0: + resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} engines: {node: '>=18'} dependencies: emoji-regex: 10.3.0 @@ -8260,37 +7260,49 @@ packages: strip-ansi: 7.1.0 dev: true - /string.prototype.matchall@4.0.8: - resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} + /string.prototype.matchall@4.0.10: + resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.21.1 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + get-intrinsic: 1.2.4 has-symbols: 1.0.3 - internal-slot: 1.0.4 - regexp.prototype.flags: 1.4.3 - side-channel: 1.0.4 + internal-slot: 1.0.7 + regexp.prototype.flags: 1.5.2 + set-function-name: 2.0.2 + side-channel: 1.0.6 + dev: true + + /string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.2 + es-object-atoms: 1.0.0 dev: true - /string.prototype.trimend@1.0.6: - resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + /string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.21.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 dev: true - /string.prototype.trimstart@1.0.6: - resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.21.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 dev: true /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + requiresBuild: true dependencies: safe-buffer: 5.2.1 dev: true @@ -8325,11 +7337,6 @@ packages: engines: {node: '>=4'} dev: true - /strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - dev: true - /strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} @@ -8344,22 +7351,22 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - /strip-literal@1.0.1: - resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==} + /strip-literal@2.0.0: + resolution: {integrity: sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==} dependencies: - acorn: 8.10.0 + js-tokens: 8.0.3 dev: true - /style-to-object@0.3.0: - resolution: {integrity: sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==} + /style-to-object@0.4.4: + resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} dependencies: inline-style-parser: 0.1.1 dev: true - /style-to-object@0.4.1: - resolution: {integrity: sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==} + /style-to-object@1.0.6: + resolution: {integrity: sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==} dependencies: - inline-style-parser: 0.1.1 + inline-style-parser: 0.2.3 dev: true /suf-log@2.5.3: @@ -8386,18 +7393,14 @@ packages: engines: {node: '>= 0.4'} dev: true - /synckit@0.8.5: - resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==} + /synckit@0.9.0: + resolution: {integrity: sha512-7RnqIMq572L8PeEzKeBINYEJDDxpcH8JEgLwUqBd3TkofhFRbkq4QLR0u+36avGAhCRbk2nnmjcW9SE531hPDg==} engines: {node: ^14.18.0 || >=16.0.0} dependencies: - '@pkgr/utils': 2.4.2 - tslib: 2.6.0 + '@pkgr/core': 0.1.1 + tslib: 2.6.2 dev: true - /taffydb@2.6.2: - resolution: {integrity: sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=} - dev: false - /tar-fs@2.1.1: resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} requiresBuild: true @@ -8409,13 +7412,15 @@ packages: dev: true optional: true - /tar-fs@3.0.4: - resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} + /tar-fs@3.0.5: + resolution: {integrity: sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==} requiresBuild: true dependencies: - mkdirp-classic: 0.5.3 pump: 3.0.0 - tar-stream: 3.1.6 + tar-stream: 3.1.7 + optionalDependencies: + bare-fs: 2.2.2 + bare-path: 2.1.0 dev: true optional: true @@ -8432,43 +7437,38 @@ packages: dev: true optional: true - /tar-stream@3.1.6: - resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} + /tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} requiresBuild: true dependencies: - b4a: 1.6.4 + b4a: 1.6.6 fast-fifo: 1.3.2 - streamx: 2.15.1 + streamx: 2.16.1 dev: true optional: true /temp-path@1.0.0: - resolution: {integrity: sha1-JLFUOXOrRCiW2a02fdnL2/r+kYs=} + resolution: {integrity: sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==} dev: false /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true - /tinybench@2.3.1: - resolution: {integrity: sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==} + /tinybench@2.6.0: + resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==} dev: true - /tinypool@0.3.1: - resolution: {integrity: sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ==} + /tinypool@0.8.2: + resolution: {integrity: sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==} engines: {node: '>=14.0.0'} dev: true - /tinyspy@1.1.1: - resolution: {integrity: sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==} + /tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} engines: {node: '>=14.0.0'} dev: true - /titleize@3.0.0: - resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} - engines: {node: '>=12'} - dev: true - /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -8487,8 +7487,17 @@ packages: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} dev: true - /trough@2.1.0: - resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==} + /trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + /ts-api-utils@1.3.0(typescript@5.4.2): + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.4.2 + dev: true /ts-morph@15.1.0: resolution: {integrity: sha512-RBsGE2sDzUXFTnv8Ba22QfeuKbgvAGJFuTN7HfmIRUkgT/NaVLfDM/8OFm2NlFkGlWEXdpW5OaFIp1jvqdDuOg==} @@ -8497,8 +7506,8 @@ packages: code-block-writer: 11.0.3 dev: true - /tsconfck@3.0.0(typescript@5.0.2): - resolution: {integrity: sha512-w3wnsIrJNi7avf4Zb0VjOoodoO0woEqGgZGQm+LHH9przdUI+XDKsWAXwxHA1DaRTjeuZNcregSzr7RaA8zG9A==} + /tsconfck@3.0.3(typescript@5.4.2): + resolution: {integrity: sha512-4t0noZX9t6GcPTfBAbIbbIU4pfpCwh0ueq3S4O/5qXI1VwK1outmxhe9dOiEWqMz3MW2LKgDTpqWV+37IWuVbA==} engines: {node: ^18 || >=20} hasBin: true peerDependencies: @@ -8507,7 +7516,7 @@ packages: typescript: optional: true dependencies: - typescript: 5.0.2 + typescript: 5.4.2 dev: true /tsconfig@7.0.0: @@ -8519,12 +7528,8 @@ packages: strip-json-comments: 2.0.1 dev: true - /tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - dev: true - - /tslib@2.6.0: - resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==} + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: true /tsm@2.3.0: @@ -8535,16 +7540,6 @@ packages: esbuild: 0.15.18 dev: true - /tsutils@3.21.0(typescript@5.0.2): - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - dependencies: - tslib: 1.14.1 - typescript: 5.0.2 - dev: true - /tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} requiresBuild: true @@ -8575,77 +7570,124 @@ packages: engines: {node: '>=10'} dev: true - /type-fest@1.4.0: - resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} - engines: {node: '>=10'} - dev: true - /type-fest@2.19.0: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} dev: true - /typed-array-length@1.0.4: - resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + /type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + dev: true + + /typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 + dev: true + + /typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + dev: true + + /typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + dev: true + + /typed-array-length@1.0.5: + resolution: {integrity: sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==} + engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 for-each: 0.3.3 - is-typed-array: 1.1.10 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 dev: true /typesafe-path@0.2.2: resolution: {integrity: sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==} dev: false - /typescript-auto-import-cache@0.3.0: - resolution: {integrity: sha512-Rq6/q4O9iyqUdjvOoyas7x/Qf9nWUMeqpP3YeTaLA+uECgfy5wOhfOS+SW/+fZ/uI/ZcKaf+2/ZhFzXh8xfofQ==} + /typescript-auto-import-cache@0.3.2: + resolution: {integrity: sha512-+laqe5SFL1vN62FPOOJSUDTZxtgsoOXjneYOXIpx5rQ4UMiN89NAtJLpqLqyebv9fgQ/IMeeTX+mQyRnwvJzvg==} dependencies: - semver: 7.5.4 + semver: 7.6.0 dev: false - /typescript@5.0.2: - resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==} - engines: {node: '>=12.20'} + /typescript@5.4.2: + resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + engines: {node: '>=14.17'} hasBin: true /typical@2.6.1: - resolution: {integrity: sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=} + resolution: {integrity: sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==} dev: false /uc.micro@1.0.6: resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} dev: false - /ufo@1.1.0: - resolution: {integrity: sha512-LQc2s/ZDMaCN3QLpa+uzHUOQ7SdV0qgv3VBXOolQGXTaaZpIur6PwUclF5nN2hNkiTRcUugXd1zFOW3FLJ135Q==} + /ufo@1.5.2: + resolution: {integrity: sha512-eiutMaL0J2MKdhcOM1tUy13pIrYnyR87fEd8STJQFrrAwImwvlXkxlZEjaKah8r2viPohld08lt73QfLG1NxMg==} dev: true /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 dev: true - /underscore@1.13.2: - resolution: {integrity: sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==} + /underscore@1.13.6: + resolution: {integrity: sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==} dev: false - /unherit@3.0.0: - resolution: {integrity: sha512-UmvIQZGEc9qdLIQ8mv8/61n6PiMgfbOoASPKHpCvII5srShCQSa6jSjBjlZOR4bxt2XnT6uo6csmPKRi+zQ0Jg==} + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /undici@5.28.4: + resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} + engines: {node: '>=14.0'} + dependencies: + '@fastify/busboy': 2.1.1 + dev: true + + /unherit@3.0.1: + resolution: {integrity: sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg==} /unified@10.1.2: resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==} dependencies: - '@types/unist': 2.0.6 + '@types/unist': 2.0.10 bail: 2.0.2 extend: 3.0.2 is-buffer: 2.0.5 - is-plain-obj: 4.0.0 - trough: 2.1.0 - vfile: 5.3.6 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 5.3.7 /unified@11.0.4: resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} @@ -8654,61 +7696,47 @@ packages: bail: 2.0.2 devlop: 1.1.0 extend: 3.0.2 - is-plain-obj: 4.0.0 - trough: 2.1.0 + is-plain-obj: 4.1.0 + trough: 2.2.0 vfile: 6.0.1 - dev: true - /unist-builder@3.0.0: - resolution: {integrity: sha512-GFxmfEAa0vi9i5sd0R2kcrI9ks0r82NasRq5QHh2ysGngrc6GiqD5CDf1FjPenY4vApmFASBIIlk/jj5J5YbmQ==} + /unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} dependencies: - '@types/unist': 2.0.6 - dev: true - - /unist-util-generated@2.0.0: - resolution: {integrity: sha512-TiWE6DVtVe7Ye2QxOVW9kqybs6cZexNwTwSMVgkfjEReqy/xwGpAXb99OxktoWwmL+Z+Epb0Dn8/GNDYP1wnUw==} + '@types/unist': 3.0.2 + unist-util-is: 6.0.0 dev: true /unist-util-is@4.1.0: resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} dev: false - /unist-util-is@5.1.1: - resolution: {integrity: sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==} - /unist-util-is@5.2.1: resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} dependencies: - '@types/unist': 2.0.6 - dev: true + '@types/unist': 2.0.10 /unist-util-is@6.0.0: resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} dependencies: '@types/unist': 3.0.2 - dev: true - - /unist-util-modify-children@2.0.0: - resolution: {integrity: sha512-HGrj7JQo9DwZt8XFsX8UD4gGqOsIlCih9opG6Y+N11XqkBGKzHo8cvDi+MfQQgiZ7zXRUiQREYHhjOBHERTMdg==} - dependencies: - array-iterate: 1.1.4 - /unist-util-position-from-estree@1.1.2: - resolution: {integrity: sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==} + /unist-util-modify-children@3.1.1: + resolution: {integrity: sha512-yXi4Lm+TG5VG+qvokP6tpnk+r1EPwyYL04JWDxLvgvPV40jANh7nm3udk65OOWquvbMDe+PL9+LmkxDpTv/7BA==} dependencies: - '@types/unist': 2.0.8 - dev: true + '@types/unist': 2.0.10 + array-iterate: 2.0.1 - /unist-util-position@4.0.3: - resolution: {integrity: sha512-p/5EMGIa1qwbXjA+QgcBXaPWjSnZfQ2Sc3yBEEfgPwsEmJd8Qh+DSk3LGnmOM4S1bY2C0AjmMnB8RuEYxpPwXQ==} + /unist-util-position-from-estree@2.0.0: + resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} dependencies: - '@types/unist': 2.0.6 + '@types/unist': 3.0.2 dev: true /unist-util-position@4.0.4: resolution: {integrity: sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==} dependencies: - '@types/unist': 2.0.8 + '@types/unist': 2.0.10 dev: true /unist-util-position@5.0.0: @@ -8717,96 +7745,69 @@ packages: '@types/unist': 3.0.2 dev: true - /unist-util-remove-position@4.0.1: - resolution: {integrity: sha512-0yDkppiIhDlPrfHELgB+NLQD5mfjup3a8UYclHruTJWmY74je8g+CIFr79x5f6AkmzSwlvKLbs63hC0meOMowQ==} + /unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} dependencies: - '@types/unist': 2.0.6 - unist-util-visit: 4.1.0 + '@types/unist': 3.0.2 + unist-util-visit: 5.0.0 dev: true - /unist-util-remove@3.1.0: - resolution: {integrity: sha512-rO/sIghl13eN8irs5OBN2a4RC10MsJdiePCfwrvnzGtgIbHcDXr2REr0qi9F2r/CIb1r9FyyFmcMRIGs+EyUFw==} + /unist-util-remove@4.0.0: + resolution: {integrity: sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==} dependencies: - '@types/unist': 2.0.6 - unist-util-is: 5.1.1 - unist-util-visit-parents: 5.1.0 + '@types/unist': 3.0.2 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 dev: true - /unist-util-stringify-position@3.0.2: - resolution: {integrity: sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==} - dependencies: - '@types/unist': 2.0.6 - /unist-util-stringify-position@3.0.3: resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} dependencies: - '@types/unist': 2.0.6 - dev: true + '@types/unist': 2.0.10 /unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} dependencies: '@types/unist': 3.0.2 - dev: true - /unist-util-visit-children@1.1.4: - resolution: {integrity: sha512-sA/nXwYRCQVRwZU2/tQWUqJ9JSFM1X3x7JIOsIgSzrFHcfVt6NkzDtKzyxg2cZWkCwGF9CO8x4QNZRJRMK8FeQ==} + /unist-util-visit-children@2.0.2: + resolution: {integrity: sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q==} + dependencies: + '@types/unist': 2.0.10 /unist-util-visit-parents@3.1.1: resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} dependencies: - '@types/unist': 2.0.8 + '@types/unist': 2.0.10 unist-util-is: 4.1.0 dev: false - /unist-util-visit-parents@4.1.1: - resolution: {integrity: sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw==} - dependencies: - '@types/unist': 2.0.6 - unist-util-is: 5.1.1 - - /unist-util-visit-parents@5.1.0: - resolution: {integrity: sha512-y+QVLcY5eR/YVpqDsLf/xh9R3Q2Y4HxkZTp7ViLDU6WtJCEcPmRzW1gpdWDCDIqIlhuPDXOgttqPlykrHYDekg==} - dependencies: - '@types/unist': 2.0.6 - unist-util-is: 5.1.1 - /unist-util-visit-parents@5.1.3: resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==} dependencies: - '@types/unist': 2.0.6 - unist-util-is: 5.1.1 - dev: true + '@types/unist': 2.0.10 + unist-util-is: 5.2.1 /unist-util-visit-parents@6.0.1: resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} dependencies: '@types/unist': 3.0.2 unist-util-is: 6.0.0 - dev: true /unist-util-visit@2.0.3: resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==} dependencies: - '@types/unist': 2.0.8 + '@types/unist': 2.0.10 unist-util-is: 4.1.0 unist-util-visit-parents: 3.1.1 dev: false - /unist-util-visit@4.1.0: - resolution: {integrity: sha512-n7lyhFKJfVZ9MnKtqbsqkQEk5P1KShj0+//V7mAcoI6bpbUjh3C/OG8HVD+pBihfh6Ovl01m8dkcv9HNqYajmQ==} - dependencies: - '@types/unist': 2.0.6 - unist-util-is: 5.1.1 - unist-util-visit-parents: 5.1.0 - /unist-util-visit@4.1.2: resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==} dependencies: - '@types/unist': 2.0.6 + '@types/unist': 2.0.10 unist-util-is: 5.2.1 unist-util-visit-parents: 5.1.3 - dev: true /unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} @@ -8814,106 +7815,44 @@ packages: '@types/unist': 3.0.2 unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 - dev: true /unist-util-walker@1.0.0: resolution: {integrity: sha512-XxadVB7qdSH6LBwhyHozj1VltpnK9m3/Zt/E/WFLaEt9eRQ0RkbsUb0lP9e1anQCEOXxf4X3NYtZQSpzqzTptw==} dependencies: - '@types/unist': 2.0.6 + '@types/unist': 2.0.10 unified: 10.1.2 dev: true - /untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - dev: true - - /update-browserslist-db@1.0.11(browserslist@4.21.10): - resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.10 - escalade: 3.1.1 - picocolors: 1.0.0 - dev: true - - /update-browserslist-db@1.0.13(browserslist@4.22.2): + /update-browserslist-db@1.0.13(browserslist@4.23.0): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: - browserslist: 4.22.2 - escalade: 3.1.1 - picocolors: 1.0.0 - dev: true - - /update-browserslist-db@1.0.5(browserslist@4.21.3): - resolution: {integrity: sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.3 - escalade: 3.1.1 + browserslist: 4.23.0 + escalade: 3.1.2 picocolors: 1.0.0 dev: true /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: - punycode: 2.1.1 + punycode: 2.3.1 dev: true /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /uvu@0.5.3: - resolution: {integrity: sha512-brFwqA3FXzilmtnIyJ+CxdkInkY/i4ErvP7uV0DnUVxQcQ55reuHphorpF+tZoVHK2MniZ/VJzI7zJQoc9T9Yw==} - engines: {node: '>=8'} - hasBin: true - dependencies: - dequal: 2.0.2 - diff: 5.0.0 - kleur: 4.1.5 - sade: 1.8.1 - dev: true - - /uvu@0.5.4: - resolution: {integrity: sha512-x1CyUjcP9VKaNPhjeB3FIc/jqgLsz2Q9LFhRzUTu/jnaaHILEGNuE0XckQonl8ISLcwyk9I2EZvWlYsQnwxqvQ==} - engines: {node: '>=8'} - hasBin: true - dependencies: - dequal: 2.0.2 - diff: 5.1.0 - kleur: 4.1.5 - sade: 1.8.1 - - /uvu@0.5.6: - resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} - engines: {node: '>=8'} + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true - dependencies: - dequal: 2.0.3 - diff: 5.1.0 - kleur: 4.1.5 - sade: 1.8.1 - dev: true - - /vfile-location@4.0.1: - resolution: {integrity: sha512-JDxPlTbZrZCQXogGheBHjbRWjESSPEak770XwWPfw5mTc1v1nWGLB/apzZxsx8a0SJVfF8HK8ql8RD308vXRUw==} - dependencies: - '@types/unist': 2.0.6 - vfile: 5.3.6 dev: true /vfile-location@4.1.0: resolution: {integrity: sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==} dependencies: - '@types/unist': 2.0.8 + '@types/unist': 2.0.10 vfile: 5.3.7 dev: true @@ -8924,42 +7863,25 @@ packages: vfile: 6.0.1 dev: true - /vfile-message@3.1.2: - resolution: {integrity: sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==} - dependencies: - '@types/unist': 2.0.6 - unist-util-stringify-position: 3.0.2 - /vfile-message@3.1.4: resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==} dependencies: - '@types/unist': 2.0.6 + '@types/unist': 2.0.10 unist-util-stringify-position: 3.0.3 - dev: true /vfile-message@4.0.2: resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} dependencies: '@types/unist': 3.0.2 unist-util-stringify-position: 4.0.0 - dev: true - - /vfile@5.3.6: - resolution: {integrity: sha512-ADBsmerdGBs2WYckrLBEmuETSPyTD4TuLxTrw0DvjirxW1ra4ZwkbzG8ndsv3Q57smvHxo677MHaQrY9yxH8cA==} - dependencies: - '@types/unist': 2.0.6 - is-buffer: 2.0.5 - unist-util-stringify-position: 3.0.2 - vfile-message: 3.1.2 /vfile@5.3.7: resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==} dependencies: - '@types/unist': 2.0.8 + '@types/unist': 2.0.10 is-buffer: 2.0.5 unist-util-stringify-position: 3.0.3 vfile-message: 3.1.4 - dev: true /vfile@6.0.1: resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} @@ -8967,21 +7889,17 @@ packages: '@types/unist': 3.0.2 unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - dev: true - /vite-node@0.28.5(@types/node@18.6.4)(sass@1.54.3): - resolution: {integrity: sha512-LmXb9saMGlrMZbXTvOveJKwMTBTNUH66c8rJnQ0ZPNX+myPEol64+szRzXtV5ORb0Hb/91yq+/D3oERoyAt6LA==} - engines: {node: '>=v14.16.0'} + /vite-node@1.4.0(@types/node@20.11.30)(sass@1.72.0): + resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} + engines: {node: ^18.0.0 || >=20.0.0} hasBin: true dependencies: cac: 6.7.14 debug: 4.3.4 - mlly: 1.1.1 - pathe: 1.1.0 + pathe: 1.1.2 picocolors: 1.0.0 - source-map: 0.6.1 - source-map-support: 0.5.21 - vite: 4.5.2(@types/node@18.6.4)(sass@1.54.3) + vite: 5.2.8(@types/node@20.11.30)(sass@1.72.0) transitivePeerDependencies: - '@types/node' - less @@ -8993,45 +7911,8 @@ packages: - terser dev: true - /vite@4.5.2(@types/node@18.6.4)(sass@1.54.3): - resolution: {integrity: sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==} - engines: {node: ^14.18.0 || >=16.0.0} - hasBin: true - peerDependencies: - '@types/node': '>= 14' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - '@types/node': 18.6.4 - esbuild: 0.18.20 - postcss: 8.4.32 - rollup: 3.29.0 - sass: 1.54.3 - optionalDependencies: - fsevents: 2.3.3 - dev: true - - /vite@5.0.8(@types/node@18.6.4)(sass@1.54.3): - resolution: {integrity: sha512-jYMALd8aeqR3yS9xlHd0OzQJndS9fH5ylVgWdB+pxTwxLKdO1pgC5Dlb398BUxpfaBxa4M9oT7j1g503Gaj5IQ==} + /vite@5.2.8(@types/node@20.11.30)(sass@1.72.0): + resolution: {integrity: sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -9058,16 +7939,16 @@ packages: terser: optional: true dependencies: - '@types/node': 18.6.4 - esbuild: 0.19.9 - postcss: 8.4.32 - rollup: 4.8.0 - sass: 1.54.3 + '@types/node': 20.11.30 + esbuild: 0.20.2 + postcss: 8.4.38 + rollup: 4.13.0 + sass: 1.72.0 optionalDependencies: fsevents: 2.3.3 dev: true - /vitefu@0.2.5(vite@5.0.8): + /vitefu@0.2.5(vite@5.2.8): resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} peerDependencies: vite: ^3.0.0 || ^4.0.0 || ^5.0.0 @@ -9075,22 +7956,25 @@ packages: vite: optional: true dependencies: - vite: 5.0.8(@types/node@18.6.4)(sass@1.54.3) + vite: 5.2.8(@types/node@20.11.30)(sass@1.72.0) dev: true - /vitest@0.28.5(sass@1.54.3): - resolution: {integrity: sha512-pyCQ+wcAOX7mKMcBNkzDwEHRGqQvHUl0XnoHR+3Pb1hytAHISgSxv9h0gUiSiYtISXUU3rMrKiKzFYDrI6ZIHA==} - engines: {node: '>=v14.16.0'} + /vitest@1.4.0(@types/node@20.11.30)(sass@1.72.0): + resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} + engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@vitest/browser': '*' - '@vitest/ui': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 1.4.0 + '@vitest/ui': 1.4.0 happy-dom: '*' jsdom: '*' peerDependenciesMeta: '@edge-runtime/vm': optional: true + '@types/node': + optional: true '@vitest/browser': optional: true '@vitest/ui': @@ -9100,29 +7984,26 @@ packages: jsdom: optional: true dependencies: - '@types/chai': 4.3.4 - '@types/chai-subset': 1.3.3 - '@types/node': 18.6.4 - '@vitest/expect': 0.28.5 - '@vitest/runner': 0.28.5 - '@vitest/spy': 0.28.5 - '@vitest/utils': 0.28.5 - acorn: 8.8.2 - acorn-walk: 8.2.0 - cac: 6.7.14 - chai: 4.3.7 + '@types/node': 20.11.30 + '@vitest/expect': 1.4.0 + '@vitest/runner': 1.4.0 + '@vitest/snapshot': 1.4.0 + '@vitest/spy': 1.4.0 + '@vitest/utils': 1.4.0 + acorn-walk: 8.3.2 + chai: 4.4.1 debug: 4.3.4 - local-pkg: 0.4.3 - pathe: 1.1.0 + execa: 8.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.8 + pathe: 1.1.2 picocolors: 1.0.0 - source-map: 0.6.1 - std-env: 3.3.2 - strip-literal: 1.0.1 - tinybench: 2.3.1 - tinypool: 0.3.1 - tinyspy: 1.1.1 - vite: 4.5.2(@types/node@18.6.4)(sass@1.54.3) - vite-node: 0.28.5(@types/node@18.6.4)(sass@1.54.3) + std-env: 3.7.0 + strip-literal: 2.0.0 + tinybench: 2.6.0 + tinypool: 0.8.2 + vite: 5.2.8(@types/node@20.11.30)(sass@1.72.0) + vite-node: 1.4.0(@types/node@20.11.30)(sass@1.72.0) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -9134,49 +8015,51 @@ packages: - terser dev: true - /volar-service-css@0.0.13(@volar/language-service@1.10.1): - resolution: {integrity: sha512-WAuo7oDYgTQ1cr45EqTGoPGtClj0f5PZDQARgQveXKu0CQgyXn8Bs7c4EjDR0fNLhiG3moBEs2uSUGxjSKghxw==} + /volar-service-css@0.0.34(@volar/language-service@2.1.2): + resolution: {integrity: sha512-C7ua0j80ZD7bsgALAz/cA1bykPehoIa5n+3+Ccr+YLpj0fypqw9iLUmGLX11CqzqNCO2XFGe/1eXB/c+SWrF/g==} peerDependencies: - '@volar/language-service': ~1.10.0 + '@volar/language-service': ~2.1.0 peerDependenciesMeta: '@volar/language-service': optional: true dependencies: - '@volar/language-service': 1.10.1 - vscode-css-languageservice: 6.2.7 - vscode-uri: 3.0.7 + '@volar/language-service': 2.1.2 + vscode-css-languageservice: 6.2.12 + vscode-languageserver-textdocument: 1.0.11 + vscode-uri: 3.0.8 dev: false - /volar-service-emmet@0.0.13(@volar/language-service@1.10.1): - resolution: {integrity: sha512-y/U3up9b3YA8DL36h6KUGnBoH/TUmr1Iv9HWuSeWJKoA6LOt57AOIgzl7+/zY8d+0+C0jGqpV4CM8V5+TjptvQ==} + /volar-service-emmet@0.0.34(@volar/language-service@2.1.2): + resolution: {integrity: sha512-ubQvMCmHPp8Ic82LMPkgrp9ot+u2p/RDd0RyT0EykRkZpWsagHUF5HWkVheLfiMyx2rFuWx/+7qZPOgypx6h6g==} peerDependencies: - '@volar/language-service': ~1.10.0 + '@volar/language-service': ~2.1.0 peerDependenciesMeta: '@volar/language-service': optional: true dependencies: - '@volar/language-service': 1.10.1 + '@volar/language-service': 2.1.2 '@vscode/emmet-helper': 2.9.2 - volar-service-html: 0.0.13(@volar/language-service@1.10.1) + vscode-html-languageservice: 5.1.2 dev: false - /volar-service-html@0.0.13(@volar/language-service@1.10.1): - resolution: {integrity: sha512-Y4pfmNsIpkDTixJVdobRMDZm5Ax90magUCdYl6HUN0/RstxHb3ogEodTT1GtNmoek/YYTrxbWZYN/Yq49+9zdg==} + /volar-service-html@0.0.34(@volar/language-service@2.1.2): + resolution: {integrity: sha512-kMEneea1tQbiRcyKavqdrSVt8zV06t+0/3pGkjO3gV6sikXTNShIDkdtB4Tq9vE2cQdM50TuS7utVV7iysUxHw==} peerDependencies: - '@volar/language-service': ~1.10.0 + '@volar/language-service': ~2.1.0 peerDependenciesMeta: '@volar/language-service': optional: true dependencies: - '@volar/language-service': 1.10.1 - vscode-html-languageservice: 5.0.7 - vscode-uri: 3.0.7 + '@volar/language-service': 2.1.2 + vscode-html-languageservice: 5.1.2 + vscode-languageserver-textdocument: 1.0.11 + vscode-uri: 3.0.8 dev: false - /volar-service-prettier@0.0.13(@volar/language-service@1.10.1)(prettier@3.0.2): - resolution: {integrity: sha512-4V/v+oNXyoC4QRNCY6JDAD4BvVatuswyH8o7flgO/XHDRIG+WwGo8Avsbmq4TLktjBGFUa4Gb9aK9+RkznEEZQ==} + /volar-service-prettier@0.0.34(@volar/language-service@2.1.2)(prettier@3.2.5): + resolution: {integrity: sha512-BNfJ8FwfPi1Wm/JkuzNjraOLdtKieGksNT/bDyquygVawv1QUzO2HB1hiMKfZGdcSFG5ZL9R0j7bBfRTfXA2gg==} peerDependencies: - '@volar/language-service': ~1.10.0 + '@volar/language-service': ~2.1.0 prettier: ^2.2 || ^3.0 peerDependenciesMeta: '@volar/language-service': @@ -9184,102 +8067,93 @@ packages: prettier: optional: true dependencies: - '@volar/language-service': 1.10.1 - prettier: 3.0.2 + '@volar/language-service': 2.1.2 + prettier: 3.2.5 + vscode-uri: 3.0.8 dev: false - /volar-service-typescript-twoslash-queries@0.0.13(@volar/language-service@1.10.1): - resolution: {integrity: sha512-KGk5ek2v7T8OSY9YdMmqGOT0KkoUQAe8RbPmoZibT9F3vgmmWVgaAoIlDZ1vwt7VfQeZvRmhvRJhqpCA80ZF8Q==} + /volar-service-typescript-twoslash-queries@0.0.34(@volar/language-service@2.1.2): + resolution: {integrity: sha512-XAY2YtWKUp6ht89gxt3L5Dr46LU45d/VlBkj1KXUwNlinpoWiGN4Nm3B6DRF3VoBThAnQgm4c7WD0S+5yTzh+w==} peerDependencies: - '@volar/language-service': ~1.10.0 + '@volar/language-service': ~2.1.0 peerDependenciesMeta: '@volar/language-service': optional: true dependencies: - '@volar/language-service': 1.10.1 + '@volar/language-service': 2.1.2 dev: false - /volar-service-typescript@0.0.13(@volar/language-service@1.10.1)(@volar/typescript@1.10.1): - resolution: {integrity: sha512-fwpoA1L/bCXz5hl9W4EYJYNyorocfdfbLQ9lTM3rPVOzjRZVknEE8XP31RMPZhEg3sOxKh18+sLEL7j3bip8ew==} + /volar-service-typescript@0.0.34(@volar/language-service@2.1.2): + resolution: {integrity: sha512-NbAry0w8ZXFgGsflvMwmPDCzgJGx3C+eYxFEbldaumkpTAJiywECWiUbPIOfmEHgpOllUKSnhwtLlWFK4YnfQg==} peerDependencies: - '@volar/language-service': ~1.10.0 - '@volar/typescript': ~1.10.0 + '@volar/language-service': ~2.1.0 peerDependenciesMeta: '@volar/language-service': optional: true dependencies: - '@volar/language-service': 1.10.1 - '@volar/typescript': 1.10.1 - semver: 7.5.4 - typescript-auto-import-cache: 0.3.0 - vscode-languageserver-textdocument: 1.0.8 + '@volar/language-service': 2.1.2 + path-browserify: 1.0.1 + semver: 7.6.0 + typescript-auto-import-cache: 0.3.2 + vscode-languageserver-textdocument: 1.0.11 vscode-nls: 5.2.0 - vscode-uri: 3.0.7 dev: false - /vscode-css-languageservice@6.2.7: - resolution: {integrity: sha512-Jd8wpIg5kJ15CfrieoEPvu3gGFc36sbM3qXCtjVq5zrnLEX5NhHxikMDtf8AgQsYklXiDqiZLKoBnzkJtRbTHQ==} + /vscode-css-languageservice@6.2.12: + resolution: {integrity: sha512-PS9r7HgNjqzRl3v91sXpCyZPc8UDotNo6gntFNtGCKPhGA9Frk7g/VjX1Mbv3F00pn56D+rxrFzR9ep4cawOgA==} dependencies: - '@vscode/l10n': 0.0.16 - vscode-languageserver-textdocument: 1.0.8 - vscode-languageserver-types: 3.17.3 - vscode-uri: 3.0.7 + '@vscode/l10n': 0.0.18 + vscode-languageserver-textdocument: 1.0.11 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.0.8 dev: false - /vscode-html-languageservice@5.0.7: - resolution: {integrity: sha512-jX+7/kUXrdOaRT8vqYR/jLxrGDib+Far8I7n/A6apuEl88k+mhIHZPwc6ezuLeiCKUCaLG4b0dqFwjVa7QL3/w==} + /vscode-html-languageservice@5.1.2: + resolution: {integrity: sha512-wkWfEx/IIR3s2P5yD4aTGHiOb8IAzFxgkSt1uSC3itJ4oDAm23yG7o0L29JljUdnXDDgLafPAvhv8A2I/8riHw==} dependencies: - '@vscode/l10n': 0.0.16 - vscode-languageserver-textdocument: 1.0.8 - vscode-languageserver-types: 3.17.3 - vscode-uri: 3.0.7 + '@vscode/l10n': 0.0.18 + vscode-languageserver-textdocument: 1.0.11 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.0.8 dev: false - /vscode-jsonrpc@8.1.0: - resolution: {integrity: sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==} + /vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} engines: {node: '>=14.0.0'} dev: false - /vscode-languageserver-protocol@3.17.3: - resolution: {integrity: sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==} + /vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} dependencies: - vscode-jsonrpc: 8.1.0 - vscode-languageserver-types: 3.17.3 + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 dev: false - /vscode-languageserver-textdocument@1.0.8: - resolution: {integrity: sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q==} + /vscode-languageserver-textdocument@1.0.11: + resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==} dev: false - /vscode-languageserver-types@3.17.3: - resolution: {integrity: sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==} + /vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} dev: false - /vscode-languageserver@8.1.0: - resolution: {integrity: sha512-eUt8f1z2N2IEUDBsKaNapkz7jl5QpskN2Y0G01T/ItMxBxw1fJwvtySGB9QMecatne8jFIWJGWI61dWjyTLQsw==} + /vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} hasBin: true dependencies: - vscode-languageserver-protocol: 3.17.3 + vscode-languageserver-protocol: 3.17.5 dev: false /vscode-nls@5.2.0: resolution: {integrity: sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==} dev: false - /vscode-oniguruma@1.7.0: - resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} - dev: true - - /vscode-textmate@8.0.0: - resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} - dev: true - /vscode-uri@2.1.2: resolution: {integrity: sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==} dev: false - /vscode-uri@3.0.7: - resolution: {integrity: sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA==} + /vscode-uri@3.0.8: + resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} dev: false /walk-back@5.1.0: @@ -9291,8 +8165,8 @@ packages: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} dev: true - /web-streams-polyfill@3.2.0: - resolution: {integrity: sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==} + /web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} dev: true @@ -9317,6 +8191,34 @@ packages: is-symbol: 1.0.4 dev: true + /which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + dependencies: + function.prototype.name: 1.1.6 + has-tostringtag: 1.0.2 + is-async-function: 2.0.0 + is-date-object: 1.0.5 + is-finalizationregistry: 1.0.2 + is-generator-function: 1.0.10 + is-regex: 1.1.4 + is-weakref: 1.0.2 + isarray: 2.0.5 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.2 + which-typed-array: 1.1.15 + dev: true + + /which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.3 + dev: true + /which-pm-runs@1.1.0: resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} engines: {node: '>=4'} @@ -9338,16 +8240,15 @@ packages: path-exists: 4.0.0 dev: true - /which-typed-array@1.1.9: - resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} + /which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.0.1 - has-tostringtag: 1.0.0 - is-typed-array: 1.1.10 + has-tostringtag: 1.0.2 dev: true /which@2.0.2: @@ -9374,11 +8275,6 @@ packages: string-width: 5.1.2 dev: true - /word-wrap@1.2.3: - resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} - engines: {node: '>=0.10.0'} - dev: true - /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -9397,6 +8293,15 @@ packages: strip-ansi: 7.1.0 dev: true + /wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + dependencies: + ansi-styles: 6.2.1 + string-width: 7.1.0 + strip-ansi: 7.1.0 + dev: true + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -9420,8 +8325,8 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - /yaml@2.3.1: - resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} + /yaml@2.3.4: + resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==} engines: {node: '>= 14'} dev: true @@ -9434,7 +8339,7 @@ packages: engines: {node: '>=12'} dependencies: cliui: 8.0.1 - escalade: 3.1.1 + escalade: 3.1.2 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 @@ -9452,17 +8357,17 @@ packages: engines: {node: '>=12.20'} dev: true - /zod@3.21.1: - resolution: {integrity: sha512-+dTu2m6gmCbO9Ahm4ZBDapx2O6ZY9QSPXst2WXjcznPMwf2YNpn3RevLx4KkZp1OPW/ouFcoBtBzFz/LeY69oA==} + /zod-to-json-schema@3.22.4(zod@3.22.4): + resolution: {integrity: sha512-2Ed5dJ+n/O3cU383xSY28cuVi0BCQhF8nYqWU5paEpl7fVdqdAmiLdqLyfblbNdfOFwFfi/mqU4O1pwc60iBhQ==} + peerDependencies: + zod: ^3.22.4 + dependencies: + zod: 3.22.4 dev: true /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: true - /zwitch@2.0.2: - resolution: {integrity: sha512-JZxotl7SxAJH0j7dN4pxsTV6ZLXoLdGME+PsjkL/DaBrVryK9kTGq06GfKrwcSOqypP+fdXGoCHE36b99fWVoA==} - /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - dev: true diff --git a/docs/scripts/add-language.mjs b/docs/scripts/add-language.mjs index a6fed7b651..4f92a327d9 100644 --- a/docs/scripts/add-language.mjs +++ b/docs/scripts/add-language.mjs @@ -74,7 +74,7 @@ class LanguageScaffolder { name ? true : kleur.reset('[Press Enter to resubmit] ') + - kleur.red().italic('Please enter a language name.'), + kleur.red().italic('Please enter a language name.'), format: (value) => value.trim(), }, { @@ -140,8 +140,8 @@ class LanguageScaffolder { const key = t.isStringLiteral(prop.key) ? prop.key.value : t.isIdentifier(prop.key) - ? prop.key.name - : undefined; + ? prop.key.name + : undefined; if (key !== this.#tag) continue; langAlreadyInList = true; diff --git a/docs/scripts/generate-integration-pages.ts b/docs/scripts/generate-integration-pages.ts index 5208d38d1b..de2a458ec7 100644 --- a/docs/scripts/generate-integration-pages.ts +++ b/docs/scripts/generate-integration-pages.ts @@ -90,8 +90,8 @@ class IntegrationPagesBuilder { const category = keywords.includes('renderer') ? 'renderer' : keywords.includes('astro-adapter') - ? 'adapter' - : 'other'; + ? 'adapter' + : 'other'; const i18nReady = (!this.#i18nNotReadyIntegrations.has(packageName)).toString(); const readme = await (await fetch(readmeURL)).text(); return { name, category, readme, srcdir: packageName, i18nReady, isPrivate }; @@ -102,9 +102,7 @@ class IntegrationPagesBuilder { */ async #getIntegrationData(): Promise { // Read all the packages in Astro’s integrations directory. - const url = `https://api.github.com/repos/${this.#sourceRepo}/contents/${ - this.#sourcePath - }?ref=${this.#sourceBranch}`; + const url = `https://api.github.com/repos/${this.#sourceRepo}/contents/${this.#sourcePath}?ref=${this.#sourceBranch}`; const packages: { name: string }[] = await githubGet({ url, githubToken: this.#githubToken }); const integrationData = await Promise.all( @@ -112,12 +110,8 @@ class IntegrationPagesBuilder { .filter((pkg) => !this.#deprecatedIntegrations.has(pkg.name)) .map(async (pkg) => { - const pkgJsonURL = `https://raw.githubusercontent.com/${this.#sourceRepo}/${ - this.#sourceBranch - }/${this.#sourcePath}/${pkg.name}/package.json`; - const readmeURL = `https://raw.githubusercontent.com/${this.#sourceRepo}/${ - this.#sourceBranch - }/${this.#sourcePath}/${pkg.name}/README.md`; + const pkgJsonURL = `https://raw.githubusercontent.com/${this.#sourceRepo}/${this.#sourceBranch}/${this.#sourcePath}/${pkg.name}/package.json`; + const readmeURL = `https://raw.githubusercontent.com/${this.#sourceRepo}/${this.#sourceBranch}/${this.#sourcePath}/${pkg.name}/README.md`; return this.#getSingleIntegrationData({ packageName: pkg.name, @@ -146,9 +140,7 @@ class IntegrationPagesBuilder { }: IntegrationData): Promise { // Remove title from body readme = readme.replace(/^# (.+)/, ''); - const githubLink = `https://github.com/${this.#sourceRepo}/tree/${this.#sourceBranch}/${ - this.#sourcePath - }/${srcdir}/`; + const githubLink = `https://github.com/${this.#sourceRepo}/tree/${this.#sourceBranch}/${this.#sourcePath}/${srcdir}/`; const createDescription = (name: string, category: string): string => { return `Learn how to use the ${name} ${prettyCategoryDescription[category]}.`; diff --git a/docs/scripts/lib/translation-status/builder.ts b/docs/scripts/lib/translation-status/builder.ts index f938422443..57af927053 100644 --- a/docs/scripts/lib/translation-status/builder.ts +++ b/docs/scripts/lib/translation-status/builder.ts @@ -190,7 +190,7 @@ export class TranslationStatusBuilder { // @ts-expect-error enTranslation.length is defined in one case const isIncomplete = enTranslation.length ? module.filter((k: { labelIsTranslated: boolean }) => k.labelIsTranslated).length !== - (enTranslation as typeof navTranslations).length + (enTranslation as typeof navTranslations).length : !this.equalKeys(module, enTranslation as NestedRecord); const data = await this.getGitHistory(subpath); @@ -212,7 +212,7 @@ export class TranslationStatusBuilder { page, type: 'commits', query: data ? `?since=${data.lastMajorCommitDate}` : '', - }), + }), }; } @@ -526,14 +526,14 @@ export class TranslationStatusBuilder { ? `(${this.renderLink( content.translations[lang].githubUrl, 'outdated translation' - )}, ${this.renderLink( + )}, ${this.renderLink( content.translations[lang].sourceHistoryUrl!, 'source change history' - )})` + )})` : `(${this.renderLink( content.translations[lang].githubUrl, 'incomplete translation' - )})`) + + )})`) + `` ) ); @@ -552,7 +552,7 @@ export class TranslationStatusBuilder { `https://github.com/${this.githubRepo}/blob/main/old-translations/${lang}/${content.subpath}`, `View\xa0old\xa0translation`, 'create-button' - )}   ` + )}   ` : '') + this.renderCreatePageButton(lang, content.subpath) + `` diff --git a/docs/src/components/Card.astro b/docs/src/components/Card.astro index edbc5d05bd..d4f931fd90 100644 --- a/docs/src/components/Card.astro +++ b/docs/src/components/Card.astro @@ -34,7 +34,8 @@ const backgroundStyle = hasScreenshot /* Neon glow effect */ border-radius: 2px; border: 1px solid var(--theme-glow-highlight); - box-shadow: 0 0 var(--theme-glow-blur) var(--theme-glow-diffuse), + box-shadow: + 0 0 var(--theme-glow-blur) var(--theme-glow-diffuse), inset 0 0 var(--theme-glow-blur) var(--theme-glow-diffuse); } .card-header { diff --git a/docs/src/components/FacePile.astro b/docs/src/components/FacePile.astro index 6e703d46b7..6eb63df2f8 100644 --- a/docs/src/components/FacePile.astro +++ b/docs/src/components/FacePile.astro @@ -68,7 +68,8 @@ const { contributors } = Astro.props as Props; height: 100%; object-fit: cover; background-color: var(--theme-bg); - box-shadow: 0 0 0 var(--avatar-outline-width) var(--theme-divider), + box-shadow: + 0 0 0 var(--avatar-outline-width) var(--theme-divider), 0 0 0 var(--avatar-outline-offset) var(--theme-bg), 0 0 0 calc(var(--avatar-outline-offset) + var(--avatar-outline-width)) var(--theme-divider), 0 0 calc(var(--theme-glow-blur) + var(--avatar-outline-offset)) var(--theme-glow-diffuse); diff --git a/docs/src/components/HeadCommon.astro b/docs/src/components/HeadCommon.astro index a5bc11c77e..d4833a60db 100644 --- a/docs/src/components/HeadCommon.astro +++ b/docs/src/components/HeadCommon.astro @@ -12,22 +12,24 @@ import '@fontsource/ibm-plex-mono/400-italic.css'; + diff --git a/docs/src/components/LeftSidebar/LeftSidebar.astro b/docs/src/components/LeftSidebar/LeftSidebar.astro index 3a4ae29e3e..8e8036653b 100644 --- a/docs/src/components/LeftSidebar/LeftSidebar.astro +++ b/docs/src/components/LeftSidebar/LeftSidebar.astro @@ -19,14 +19,17 @@ const { currentPage } = Astro.props as Props; const currentPageMatch = removeLeadingSlash(removeTrailingSlash(currentPage)); // Group nav menu by sections to properly render. -const sidebarSections = (await getNav(Astro)).reduce((collection, item) => { - if ('header' in item) { - collection.push({ ...item, children: [] }); - } else { - collection[collection.length - 1].children.push(item); - } - return collection; -}, [] as Parameters[0]['sidebarSections']); +const sidebarSections = (await getNav(Astro)).reduce( + (collection, item) => { + if ('header' in item) { + collection.push({ ...item, children: [] }); + } else { + collection[collection.length - 1].children.push(item); + } + return collection; + }, + [] as Parameters[0]['sidebarSections'] +); const learnSections = sidebarSections.filter((section) => section.type === 'tech'); const apiSections = sidebarSections.filter((section) => section.type === 'biz'); diff --git a/docs/src/components/RightSidebar/CommunityMenu.astro b/docs/src/components/RightSidebar/CommunityMenu.astro index 730b8c5917..59933699a1 100644 --- a/docs/src/components/RightSidebar/CommunityMenu.astro +++ b/docs/src/components/RightSidebar/CommunityMenu.astro @@ -97,7 +97,11 @@ const HeadingWrapper = hideOnLargerScreens ? 'summary' : 'div'; -->
  • - + diff --git a/docs/src/components/tabs/TabbedContent.astro b/docs/src/components/tabs/TabbedContent.astro index d9cf3e2bdd..0fcf3dadb0 100644 --- a/docs/src/components/tabs/TabbedContent.astro +++ b/docs/src/components/tabs/TabbedContent.astro @@ -89,10 +89,10 @@ const { tabs } = Astro.props as Props; e.key === 'ArrowLeft' ? index - 1 : e.key === 'ArrowRight' - ? index + 1 - : e.key === 'ArrowDown' - ? 'down' - : null; + ? index + 1 + : e.key === 'ArrowDown' + ? 'down' + : null; if (dir !== null) { e.preventDefault(); // If the down key is pressed, move focus to the open panel, @@ -100,8 +100,8 @@ const { tabs } = Astro.props as Props; dir === 'down' ? panels[i].focus() : tabs[dir] - ? this.switchTab(tabs[dir], dir) - : void 0; + ? this.switchTab(tabs[dir], dir) + : void 0; } }); }); diff --git a/docs/src/components/tutorial/Box.astro b/docs/src/components/tutorial/Box.astro index 065c1845f0..18d9a3a596 100644 --- a/docs/src/components/tutorial/Box.astro +++ b/docs/src/components/tutorial/Box.astro @@ -23,8 +23,11 @@ const { icon } = Astro.props; padding-inline-start: 2rem; padding-inline-end: 4rem; border: 1px solid var(--theme-shade-subtle); - box-shadow: -0.5rem 0.5rem 0 -1px var(--theme-bg), -0.5rem 0.5rem var(--theme-shade-subtle), - -1rem 1rem 0 -1px var(--theme-bg), -1rem 1rem var(--theme-shade-subtle); + box-shadow: + -0.5rem 0.5rem 0 -1px var(--theme-bg), + -0.5rem 0.5rem var(--theme-shade-subtle), + -1rem 1rem 0 -1px var(--theme-bg), + -1rem 1rem var(--theme-shade-subtle); background-color: var(--theme-bg); } diff --git a/docs/src/content/docs/en/designs/e2e-testing.mdx b/docs/src/content/docs/en/designs/e2e-testing.mdx new file mode 100644 index 0000000000..8eeffa23d0 --- /dev/null +++ b/docs/src/content/docs/en/designs/e2e-testing.mdx @@ -0,0 +1,210 @@ +--- +title: E2E Testing +description: E2E Testing +--- +## E2E Overview +We currently use Playwright as the testing framework for E2E testing. In order to ensure the credibility of the test, we no longer use stub/mock 3rd services, but directly test against 3rd services. +```plantuml +@startuml +set separator none +title Heartbeat - E2E Architecture + +left to right direction + +!include +!include +!include + +Person(E2ErunnerPlaywright, "E2E runner \n Playwright", $descr="", $tags="", $link="") +System(3rdApi, "3rdApi", $descr="", $tags="", $link="") + +System_Boundary("Heartbeat_boundary", "Heartbeat", $tags="") { + Container(Heartbeat.HBFrontend, "HB Frontend", $techn="", $descr="", $tags="", $link="") + Container(Heartbeat.HBBackend, "HB Backend", $techn="", $descr="", $tags="", $link="") +} + +Rel(E2ErunnerPlaywright, Heartbeat.HBFrontend, "Uses", $techn="", $tags="", $link="") +Rel(Heartbeat.HBFrontend, Heartbeat.HBBackend, "API call to", $techn="", $tags="", $link="") +Rel(Heartbeat.HBBackend, 3rdApi, "", $techn="", $tags="", $link="") + +SHOW_LEGEND(true) +@enduml +``` + +## What is Playwright? +Playwright is an open-source automation library for browser testing and web scraping developed by Microsoft. It provides a high-level API for automating browsers such as Chrome, Firefox, and WebKit. It's similar to other tools like Puppeteer but offers cross-browser support and additional features like being able to test on multiple browsers in parallel. + +## Why Playwright? +🔄 We decided to switch from Cypress to Playwright, mainly considering the following: + +1. **Cross-browser testing:** Playwright supports multiple browsers like Chrome, Firefox, and WebKit, making it easier to test the web application's compatibility across different browsers. Cypress primarily supports Chrome, although there are efforts to add support for other browsers as well. + +2. **Parallel testing:** Playwright allows for running tests in parallel across multiple browser instances, which can significantly reduce test execution time. This is particularly valuable when dealing with a large number of tests in a test suite. + +3. **Better automation capabilities:** Playwright provides more automation capabilities compared to Cypress, such as downloads automation, geolocation simulation, and network request interception. It offers more flexibility in how tests are written and executed. + +4. **Faster execution:** Playwright is known for its faster test execution speed due to its efficient architecture and features like the ability to interact with elements without the need for waiting timers. + +5. **Community and support:** Playwright is developed and maintained by Microsoft, which has a strong reputation in the software development industry. This can instill confidence in teams regarding the tool's long-term maintenance and support. + +In addition, playwright has some advantages for developers: + +1. **Programming language support:** Playwright supports multiple languages such as JavaScript, TypeScript, Python, and C#, providing more options for writing tests in a language preferred by the team. This allows developers to leverage their existing skills and knowledge. + +2. **Asynchronous handling:** Playwright uses a more traditional async/await syntax for handling asynchronous operations, which can be more familiar to developers who are used to working with promises and async/await in modern JavaScript. But Cypress actually returned something only pretending to be a Promise. + +3. **Page object model:** Playwright provides better support for the page object model, allowing for a more organized and maintainable test structure by separating page interactions into reusable components. + +4. **Debugging capabilities:** Playwright offers better debugging capabilities, such as the ability to pause and inspect the state of the application during test execution, making it easier to troubleshoot issues and write reliable tests. + +5. **Reporter:** Playwright's HTML reporter produces a self-contained folder that contains report for the test run that can be served as a web page. It's easy to read and retrace errors than Cypress. + +## E2E file structure +```sh +e2e +├── fixtures +├── pages +├── reports +├── specs +├── temp +├── test-results +└── utils +``` +- `fixtures`: This directory contain test fixtures or data used in the E2E tests, such as sample data, expect result, and [Playwright fixtures](https://playwright.dev/docs/test-fixtures). + +- `pages`: This directory typically includes page objects or modules that represent different pages of the application being tested. Page objects help in organizing and maintaining the code for interacting with specific pages. ref: [Playwright POM](https://playwright.dev/docs/pom) + +- `reports`: This directory store the generated test reports or logs after running the E2E tests, which provide insights into the test results, failures, and performance metrics. + +- `specs`: This directory contains the test specifications or scenarios that reflect the [E2E Test Case Summary](https://docs.google.com/spreadsheets/d/1vT5LnQb940HK12V0o0kZlWI9SJwY03SqZHmZ11B9f9Q/edit?usp=sharing) cases. + +- `temp`: This directory be used to store temporary files or data during the test execution. This folder is not defined by playwright, but specified by the programmer. + +- `test-results`: The output directory for files created during test execution. This directory is cleaned at the start. + +- `utils`: This directory hold utility functions or helper modules that provide common functionalities used across the E2E tests. + + +## How to use in the local environment +The following command working within the directory: `/frontend` + +### Initial setup +1. `pnpm install` +2. `pnpm exec playwright install --with-deps` +3. Create the `e2e/.env.local` by following the `e2e/.env.example`. For heartbeat dev, you could find the .env.example from [here](https://drive.google.com/file/d/1Q-HSJ-qvgvt8es2bWSIC2M891q5Xfcom/view?usp=drive_link). + +### Run E2E testing with local env +- Run E2E (You should start FE&BE services firstly): `pnpm run e2e:local` +- Run E2E with FE&BE services: `pnpm run e2e:with-server` + +### Serve HTML testing report +`pnpm run e2e:report PATH/TO/e2e-reports/html` + + +## Debugging tests +### Debug tests in UI mode +*Run E2E with debug UI*: `pnpm run e2e:ui` +It's highly recommend debugging your tests with UI Mode for a better developer experience where you can easily walk through each step of the test and visually see what was happening before, during and after each step. UI mode also comes with many other features such as the locator picker, watch mode and more. + +![UI mode](https://github.com/microsoft/playwright/assets/13063165/ffca2fd1-5349-41fb-ade9-ace143bb2c58) + +### Debug tests with the Playwright Inspector +*Run E2E with debug Inspector*: `pnpm run e2e:debug` +The Playwright Inspector provides a user-friendly interface where you can view the DOM, inspect elements, debug JavaScript code, interact with the page, and understand the state of the application during test execution. + +![Debug mode](https://github.com/microsoft/playwright/assets/13063165/6b3b3caa-d258-4cb8-aa05-cd407f501626) + +### Debug the pipeline E2E result with Playwright HTML report +- Download the report package from the pipeline build -> `🚀Run e2e` section -> Artifacts tab +- Unzip the report and run `pnpm run e2e:report PATH/TO/e2e-reports/html` + +## How to generate screenshots +The pipeline run in the linux environment, so the screenshots are slightly different, so we need to use the macOS and docker container to generate a set of screenshots during development. + +### Generate for macOS testing +`pnpm run e2e:updateSnapshots` + +### Generate for pipeline testing +1. Build image for the first run: `pnpm run e2e:build-docker-image` +2. `pnpm run e2e:updateSnapshots-docker` + +## How to add new testing env-var +If you need to add a new token or password to the test case, you should use the environment variable instead of plaintext to write it into the codebase. +1. Add env-var into `frontend/e2e/.env.local` +2. Add env-var sample into `frontend/e2e/.env.example` +3. Add env-var into `.github/workflows/build-and-deploy.yml` e2e section +4. Add env-var into `ops/check.sh` e2e_container_check function +5. For the pipeline, we currently use the Buildkite environment hook to inject environment variables + - SSH into the Buildkite Agent EC2 server + - Add env-var into `/etc/buildkite-agent/hooks/environment` + +## How the pipeline run E2E testing +Due to permission issues with the Buildkite Agent EC2 server installation Playwright dependencies, we use docker images to run tests instead of native environment. + +Pipeline config `.buildkite/pipeline.yml`: +```yaml + - label: ":rocket: Run e2e" + branches: main + key: "check-e2e" + depends_on: + - "deploy-e2e" + - "check-shell" + - "check-security" + - "check-frontend" + - "check-px" + - deny-css-rgba-check + - deny-css-hex-check + - "check-backend" + - "check-frontend-license" + - "check-backend-license" + command: ./ops/check.sh e2e-container + plugins: + - artifacts#v1.9.0: + upload: "./e2e-reports.tar.gz" + expire_in: "${RETENTION_DAYS} days" +``` + +E2E runner script `./ops/check.sh`: +```shell +e2e_container_check() { + docker build -t "heartbeat_e2e:latest" ./ -f ./ops/infra/Dockerfile.e2e + + set +e + local result + docker run \ + --name hb_e2e_runner \ + -e "APP_ORIGIN=${APP_HTTP_SCHEDULE:-}://${AWS_EC2_IP_E2E:-}:${AWS_EC2_IP_E2E_PORT:-}" \ + -e "E2E_TOKEN_JIRA=${E2E_TOKEN_JIRA:-}" \ + -e "E2E_TOKEN_BUILD_KITE=${E2E_TOKEN_BUILD_KITE:-}" \ + -e "E2E_TOKEN_GITHUB=${E2E_TOKEN_GITHUB:-}" \ + -e "CI=${CI:-}" \ + heartbeat_e2e:latest \ + pnpm run e2e:major-ci + result=$? + set -e + + docker cp hb_e2e_runner:/app/e2e/reports ./e2e-reports + docker rm hb_e2e_runner + tar -zcvf ./e2e-reports.tar.gz ./e2e-reports + exit $result +} +``` + +## FAQ +### `Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:xxxx/` +Start your local FE and BE services firstly + +### `Error: Failed to start E2E testing, please configure the env var APP_ORIGIN` +Please check if the `.env.local` file is configured + +### I encountered some assertion errors in the test +- Please make sure that you have rebased the latest code and restarted the local FE and BE services +- Please check whether the token in env file is invalid +- If you modify the relevant code, please check whether it has broken the test +- If your want or need to modify the assertion or screenshot, you should align with BA instead of working silently + - If the screenshot is incorrect, you need to update the screenshot of both macOS and linux + +## Know issues +- In headed mode, screenshot comparison fails +- On the pipeline, the E2E docker image needs to be built every time +- Currently, we only test the Chrome browser locally and on the pipeline diff --git a/docs/src/content/docs/en/designs/optimize-generate-report.mdx b/docs/src/content/docs/en/designs/optimize-generate-report.mdx new file mode 100644 index 0000000000..b5133f956c --- /dev/null +++ b/docs/src/content/docs/en/designs/optimize-generate-report.mdx @@ -0,0 +1,213 @@ +--- +title: Optimize generate report +description: solve issue about when one of the export button can not click but export metric button can click +--- + +# Context +Considering to fix the issue about when one of the export button can not click but export metric button can click, and change logic about polling api in backend. we have to optimize generate report flow. + +# Design +## C2 - Generate report - AS-IS + +### Sequence Diagram + +```plantuml +@startuml report +skin rose +title C2 - Heartbeat - Generate Report - Transition +participant Frontend +participant Backend +participant Jira +participant Buildkite +participant Github + +Frontend -> Backend: generate metrics for board related +activate Backend +Backend --> Frontend: response 202 with callback +deactivate Backend + +group async process board related metrics + Backend -> Jira: get Jira raw data + activate Backend + activate Jira + Jira --> Backend: Jira raw + deactivate Jira + Backend -> Backend: calculate metrics for velocity, cycle time and classification + Backend -> Backend: generate board report csv + deactivate Backend + note left + response.boardMetricsCompleted is true + board metrics are ready for display + board csv is ready for download + end note +end + +Frontend -> Backend: generate metrics for pipeline,Source control(if required) related +activate Backend +Backend --> Frontend: response 202 with callback +deactivate Backend + +group async process pipeline and sourcecontrol related metrics + Backend -> Buildkite: get Buildkite raw data + activate Backend + activate Buildkite + Buildkite --> Backend: Buildkite raw data + deactivate Buildkite + Backend -> Backend: calculate metrics for deployment frequency, \nchange failure rate and mean time to recovery + note left + pipeline metrics are ready for display + end note + opt if lead time for change required + Backend -> Github: get Github raw data + activate Github + Github --> Backend: Github raw data + deactivate Github + Backend -> Backend: calculate metrics for lead time for change + note left + LTFC is ready for display + end note + end + Backend -> Backend: generate pipeline report csv with github data if required + note left + response.doraMetricsCompleted is true + pipeline report is ready for download + end note + deactivate Backend +end + +loop until (response code is 201 and body.allMetricsReady==true) or (response code is 5xx) + Frontend -> Backend: Polling the report + activate Backend + alt report is not ready + Backend --> Frontend: response 200 + else report is ready + Backend --> Frontend: response 201 with report + else fail to process + Backend --> Frontend: response 5xx + end + deactivate Backend +end +@enduml +``` + +## C2 - Generate report - TO-BE +### Sequence Diagram + + +```plantuml +@startuml report +skin rose +title C2 - Heartbeat - Generate Report - Transition +participant Frontend +participant Backend +participant Jira +participant Buildkite +participant Github + +Frontend -> Backend: generate metrics for board or dora or both of them +activate Backend +Backend --> Frontend: response 202 with callback +deactivate Backend + + +Backend --> Backend: initialize reportStatus according to metricTypes list element +activate Backend + +group check if metricCsv can be generated +loop until (metricTypes list final element) + + alt metricsType is Board + group thread 1: async process board related metrics + Backend -> Jira: get Jira raw data,put this thread into thread list + activate Backend + activate Jira + Jira --> Backend: Jira raw + deactivate Jira + Backend -> Backend: calculate metrics for velocity, cycle time and classification + Backend -> Backend: generate board report json + note left + board metrics are ready for display + end note + group thread 2: async process to generate board data csv + Backend -> Backend: generate board data csv + note left + update response.boardMetricsCompleted as true + board csv is ready for download + end note + end + deactivate Backend + end + else metricsType is Dora + group thread 3: async process pipeline and sourcecontrol related metrics + Backend -> Buildkite: get Buildkite raw data, put this thread into thread list + activate Backend + activate Buildkite + Buildkite --> Backend: Buildkite raw data + deactivate Buildkite + Backend -> Backend: calculate metrics for deployment frequency, change failure rate and mean time to recovery + Backend -> Backend: generate pipline report json + note left + pipeline metrics are ready for display + end note + opt if lead time for change required + Backend -> Github: get Github raw data + activate Github + Github --> Backend: GitHub raw data + deactivate Github + Backend -> Backend: calculate metrics for lead time for change + Backend -> Backend: generate GitHub report json + note left + LTFC is ready for display + end note + end + group thread 4: async process to generate dora csv + Backend -> Backend: generate pipeline data csv with github data if required + note left + update response.doraMetricsCompleted as true + pipeline report is ready for download + end note + end + deactivate Backend + end + end + end + + group async process to generate metric csv + Backend --> Backend: wait for thread 1 and thread 3 are all execute completed + Backend --> Backend: generate metric csv + note left + update overallMetricsCompleted as true and all metric csv is ready for download + end note + end + deactivate Backend +end + +loop until (response code is 201 and body.allMetricsCompleted==true) or (response code is 5xx) + Frontend -> Backend: Polling the report + activate Backend + Backend --> Backend: allMetricsCompleted == (boardMetricsCompleted is null || boardMetricsCompleted) && (doraMetricsCompleted is null || doraMetricsCompleted) && overallMetricsCompleted + alt report is not ready + Backend --> Frontend: response 200 + else report is ready + Backend --> Frontend: response 201 with report + else fail to process + Backend --> Frontend: response 5xx + end + deactivate Backend +end +@enduml +``` + +### API Design +the request body of generate report api is the same as as-is, but will add one parameter in the request body. +``` +URI: POST /reports +Request payload: +{ + ... + "metricTypes": [ + "string" + ] +} +``` + diff --git a/docs/src/content/docs/en/spikes/tech-spikes-calculate-rework-of-board-card.mdx b/docs/src/content/docs/en/spikes/tech-spikes-calculate-rework-of-board-card.mdx new file mode 100644 index 0000000000..0a4e9f650d --- /dev/null +++ b/docs/src/content/docs/en/spikes/tech-spikes-calculate-rework-of-board-card.mdx @@ -0,0 +1,207 @@ +--- +title: Spike the logic of calculating card rework +description: Spike the logic of calculating card rework +--- + +# + +## Background + +According to the needs of the business, users believe that analyzing the overall situation of the flow state of the card can be a good way to find the problems of the team and optimize the team. + +## Expect + + 1. View the rework times of all cards in the current iteration. + 2. Export the rework times of each card. + +## Solutions + +### 1. Modifying the original API + +#### 1.1 Modifying to generate report API design +On the design of the original report generating API, a new rework setting is added to the request body. +- TO-BE +```json +path: /reports/{metricType} +method: POST +request body: { + ... + "reworkTimesSetting" : { + "reworkState": String + "excludedStates": List + } +} +``` + +#### 1.2 Modifying to query generation metric API design +On the design of the original query generation metric API, add rework metrics data to the response body. +- TO-BE +```json +path: /reports/{reportId} +method: GET +response: { + ... + "rework" : { + "totalReworkTimes": Integer + "reworkState": String + "fromToDo": Integer + "fromInDev": Integer + "fromBlock": Integer + "fromWaitingForTesting": Integer + "fromTesting": Integer + "fromReview": Integer + "fromDone": Integer + "totalReworkCards": Integer + "reworkCardsRatio": Double + + } +} +``` + +### 2. Calculate rework times and return to frontend + +We will calculate rework times for each `from state` on every real done card. The specific tasks are as follows: + +1. In the `JiraService.getRealDoneCards()` method, we added a new line that references the getReworkTimes method below the line getAssigneeSet(). +2. We created a new method called `getReworkTimes()`, which receives a `CardHistoryResponseDTO` and returns a list of `ReworkTimeInfo`, which is a class as follows: + +``` +ReworkTimeInfo +{ + "state": "In Dev", + "times": 1 +} +``` + +`ReworkTimeInfo` has two variables, `state` stores the rework target state. And `times` stores the rework times which is initialized to 0. + +3. We define a `reworkJudgmentMap` map, whose key is target rework state and value is a set containing source states. + + ``` + reworkJudgmentMap.put(ANALYSE, Set.of(TODO, DEVELOPMENT, BLOCK, REVIEW, WAITING, TESTING, DONE)); + reworkJudgmentMap.put(TODO, Set.of(DEVELOPMENT, BLOCK, REVIEW, WAITING, TESTING, DONE)); + reworkJudgmentMap.put(DEVELOPMENT, Set.of(BLOCK, REVIEW, WAITING, TESTING, DONE)); + reworkJudgmentMap.put(BLOCK, Set.of(REVIEW, WAITING, TESTING, DONE)); + reworkJudgmentMap.put(REVIEW, Set.of(WAITING, TESTING, DONE)); + reworkJudgmentMap.put(WAITING, Set.of(TESTING, DONE)); + reworkJudgmentMap.put(TESTING, Set.of(DONE)); + ``` + +4. Determine the rework we need, there are three condition +- Initialize rework judgment. +- Rework state selected by the user. +- User excluded state. +```plantuml +@startuml calculate rework times when not check "Consider the 'Flag' as 'Block'" +skin rose +start +:get the constant of reworkJudgmentMap; +:get the rework state and exclude state selected by the user; +:get the value of reworkJudgementMap according to rework state, and then exclude the value of reworkJudgementMap according to exclude states; +:input fromState and toState; + if(reworkJudgementMap.get(fromState).contain(toState)) then(yes) + :return true; + else(no) + endif +stop +@end +@enduml +``` + +5. In the `getReworkTimes()` method, if user not check "Consider the 'Flag' as 'Block', we will do as follows: + +```plantuml +@startuml calculate rework times when not check "Consider the 'Flag' as 'Block'" +skin rose +start +:get our need rework condition; +:filter out all items that have the field id as 'status'; + repeat : Pick up one item; + if( Is the "from" member contained by the the value mapped by "reworkJudgmentMap" when "to" member is used as the key and the "to" member is rework condition?) then (yes) + :Create a new ReworkTimeInfo object; + :Set the "state" member to "from"; + :Set the "times" member add 1; + else(no) + endif + repeat while (Is there any item in the list?) is (Yes) + ->No; +:return the list of ReworkTimeInfo objects; + +stop +@enduml +``` +6. Some users are in the block state according to the added flag, so if user check "Consider the 'Flag' as 'Block', and the calculation logic should change, the implementation pseudocode is as follows: +```plantuml +@startuml Way of Code Working +skin rose +start + +: get our need rework condition; +: get all history items of card; +: filter state and flag change history items; +: Define currentState and hasBlock; +repeat :iterate all history items; + if (history item is state change?) then (yes) + :currentState = card.to.state; + if (hasBlock?) then (no) + if (is rework condition?) then (yes) + :fromToTimes += 1; + else (no) + endif + else(yes) + endif + elseif (history item is flag change?) then (yes) + if (history item is add flag?) then (yes) + :hasBlock = true; + if (is rework condition?) then (yes) + :fromCurrentStateToBlock += 1; + else(no) + endif + elseif (history item is cancel flag?) then (yes) + :hasBlock = false; + if (is rework condition?) then (yes) + :fromBlockToCurrentState += 1; + else(no) + endif + else(no) + endif + else(no) + endif +repeat while (has history item not iteration?) is (yes) +:return the list of ReworkTimeInfo objects; +stop +@enduml +``` + +7. We set the `ReworkTimeInfo` list in `JiraCardDTO` object as `reworkTimeInfos` member variables. +8. In the `GenerateReporterService.generateBoardReporter()` method, we use `reworkTimeInfos` variable in `JiraCardDTO` object calculate rework metrics and put them to `ReportResponse`, which are the same as the API in the 1.2 section. + - We traverse all items in `reworkTimeInfos` and sum the rework times in each state. + - Then we sum the total time of reworks in each state as the `totalReworkTimes`. + - If a card's `reworkTimeInfos` is not empty, we set `totalReworkCards` add one. + - The `reworkCardsRatio` is calculated by dividing `totalReworkCards` by total real done card count on the current sprint. + + + +### 3. Modifying the logic of generating a board csv +When generating the board csv file, we need to export the rework information of each card, so we need to add a series of table header information, and assemble and filter according to the config provided by reworkSetting. The following is the full table header information about rework: + +- rework times to `state` +- from in todo to `state` +- from in dev to `state` +- from block to `state` +- from review to `state` +- from waiting for testing to `state` +- from testing to `state` +- from done to `state` + +`state` is the rework in our previous setting, and the exported table header is spliced according to it. + +According to business needs, we will only have some headers, for example, we need to calculate `rework to review`, exported headers will have `rework times to review` , `from done to waiting for testing`,`from done to testing` and `from done to review`. Because the meaning of rework is to calculate from the subsequent process to the previous process + +*Implementation method* + +In 2, we have obtained `reworkTimeInfos`, and then can use this data to write into a csv file. + +tasking: +- In `KanbanCsvService.generateCSVForBoard()` method, we need to use reworkTimeInfos to build table header. Reference CycleTimeInfo to build the header. +- In `CSVFileGenerator.getFixedFieldsData()` method, we need to build the row for rework. Reference `CSVFileGenerator.getOriginCycleTimePerRow()`. diff --git a/docs/src/content/docs/en/spikes/tech-spikes-chart-api-solutions.mdx b/docs/src/content/docs/en/spikes/tech-spikes-chart-api-solutions.mdx new file mode 100644 index 0000000000..892b448c1e --- /dev/null +++ b/docs/src/content/docs/en/spikes/tech-spikes-chart-api-solutions.mdx @@ -0,0 +1,186 @@ +--- +title: Spike Chart API +description: Seeking Suitable Chart API Solution +--- + +### Summary + +As our roadmap planed, Heartbeat application will show charts with the all metrics data. +So that, the user can compare the historical data with different sprint. + +We need to choose a charting backend and frontend API refactor solution to implement the design. + +### Context + +According to the new business requirements and the roadmap, there are some points for charting API refactor. + +* Support metrics page frontend transfer more date list and call backend API +* Support report page frontend transfer more date list and call backend API + +### Options + +#### Metrics page + +FrontEnd can get the date list and separate it in order to send the request. + +```json +dateRange: [ + { + "startDate": "2024-02-01T00:00:00.000+08:00", + "endDate": "2024-02-14T23:59:59.999+08:00" + }, + { + "startDate": "2024-02-15T00:00:00.000+08:00", + "endDate": "2024-02-29T23:59:59.999+08:00" + }, +], +``` + +```plantuml +@startuml Charting API refactor +skin rose +title Charting API - Metrics page + +:Frontend separate the date list in order to send the request to backend; + +fork + :fetch parameters as usual (dateRange[0]); + :get response as usual (dateRange[0]); +fork again + :fetch parameters as usual (dateRange[1]); + :get response as usual (dateRange[1]); +fork again + :fetch parameters as usual (dateRange[N]); + :get response as usual (dateRange[N]); +end fork +:combined all data; + +stop +@enduml +``` + +As shown in the image, we want to refactor the frontend and backend solutions for metrics page. About `board info`, `pipelines info`, +`pipelines step` API, we can transfer multiple stage date list params by multiple times call for backend. +When the frontend calls the backend API in parallel multiple times, backend invoke original service to handle logic and return response. +Then we accept all responses from the backend and combine the data for parallel processing of data. + +The APIs for the metrics page that we do not need to modify are as follows: +- POST /boards/`{boardType}`/info +- POST /pipelines/`{pipelineType}`/info +- POST /pipelines/`{pipelineType}`/`{organizationId}`/pipelines/`{buildId}`/steps + +#### Report page + +```plantuml +@startuml Charting API refactor +skin rose +title Charting API - Report page + +:Frontend separate the date list in order to send the request to backend; + +fork + :fetch parameters as usual (dateRange[0]); + :get response as usual (dateRange[0]); +fork again + :fetch parameters as usual (dateRange[1]); + :get response as usual (dateRange[1]); +fork again + :fetch parameters as usual (dateRange[N]); + :get response as usual (dateRange[N]); +end fork +:combined data for charting; + +stop +@enduml +``` + +As shown in the image, we want to refactor the frontend and backend solutions for report page. About `reports board`, `reports dora`, +`reports poll {reportId}` API, we can transfer date info time and time again. Due to the logic of the report page. +Then we accept all responses from the backend and combine the data for charting. + +The APIs for the report page that we do not need to modify are as follows: +- POST /reports +- GET /reports/`{reportId}` +- GET /reports/metric/`{reportId}` +- GET /reports/board/`{reportId}` +- GET /reports/pipeline/`{reportId}` + + +Corresponding information about chart information and response: + +- velocity: +```json +"velocity": { + "velocityForSP": 30.0, + "velocityForCards": 22 +} +``` + +- Average Cycle Time: +```json +"cycleTime": { + "averageCycleTimePerCard": 4.03, + "averageCycleTimePerSP": 2.96 +} +``` + +- Cycle Time Allocation +```json +// Total cycle time +"cycleTime": { + "totalTimeForCards": 88.67 +} + +// Total development time +{ + "optionalItemName": "In Dev", + "averageTimeForSP": 1.66, + "averageTimeForCards": 2.26, + "totalTime": 49.67 +}, + +// Block time +{ + "optionalItemName": "Block", + "averageTimeForSP": 0.28, + "averageTimeForCards": 0.38, + "totalTime": 8.3 +}, + +// Review time +{ + "optionalItemName": "Review", + "averageTimeForSP": 0.26, + "averageTimeForCards": 0.35, + "totalTime": 7.69 +}, + +// Wait for testing time +{ + "optionalItemName": "Waiting for testing", + "averageTimeForSP": 0.4, + "averageTimeForCards": 0.54, + "totalTime": 11.96 +}, + +// Testing time +{ + "optionalItemName": "Testing", + "averageTimeForSP": 0.37, + "averageTimeForCards": 0.5, + "totalTime": 11.05 +} + +``` + +- Rework +```json +"rework": { + // Total rework times + "totalReworkTimes": 2, + // Total rework cards + "totalReworkCards": 2, + // Total cards ratio + "reworkCardsRatio": 0.6667 +}, +``` diff --git a/docs/src/content/docs/en/spikes/tech-spikes-chart-tools-solutions.mdx b/docs/src/content/docs/en/spikes/tech-spikes-chart-tools-solutions.mdx new file mode 100644 index 0000000000..b154ae5dcc --- /dev/null +++ b/docs/src/content/docs/en/spikes/tech-spikes-chart-tools-solutions.mdx @@ -0,0 +1,55 @@ +--- +title: Spike Chart Tool +description: Seeking Suitable Chart Components for Heartbeat +--- + +### Summary + +As our roadmap planed, Heartbeat application will show charts with the all metrics data. +So that, the user can compare the historical data with different sprint. + +But now, there is no chart implementation in this project, and we need to choose a charting library to implement the design. + +### Context + +According to the new business requirements and the roadmap, there is a need to introduce a general-purpose chart library to support corresponding development needs. Based on business requirements, there are several requirements for charts: +* Responsive support +* Support for multiple y-axes and composite charts +* Support for customizing tooltips +* Support for filtering charts by clicking the legends + +### Scope + +Frontend + +### Current Status + +None chart tools + +### Options + +- [npm trend](https://npmtrends.com/@nivo/core-vs-Nivo-vs-apexcharts-vs-d3-vs-echarts-vs-recharts-vs-victory-core) + +| |[ECharts](https://echarts.apache.org/en/index.html) | **Recharts** | **Nivo** | **Victory** | +|-----------------------------------|-|--------------------------------------------------------------|--------------------------------------------------------------|--------------------------------------------------------------| +| **Description** |A ASF open JS visualization library | Currently the most common React lightweight chart library, supports interactive building using JSX | Heavyweight chart library supporting a wide range of chart types, allowing for custom charts | Completely custom, interactive chart library with customization options | +| **Pros** |1. Pure dependencies(just depends on `zrender`), Frame independent
    2. Customization for diff charts
    3. Controlled by the Apache rog
    4. Multiple chart types
    5. Active community | 1. Implemented based on D3.
    2. Supports responsive layout and is friendly to React, with comprehensive documentation and examples.
    3. Simple configuration, allowing mixed chart implementation through nesting, and supports custom tooltip rendering.
    4. Filtering charts can be achieved through custom legends.
    5. Basic level of extensibility, allowing for custom chart combinations to meet business needs through configuration. However
    6. Intuitive for developers, easy to get started with. | 1. Implemented based on D3.
    2. Supports responsive layout.
    3. Supports on-demand importing of charts.
    4. Offers a wide range of configurable options, supporting custom composite charts.
    5. Supports custom tooltips.
    6. Documentation is readily available, and demo code can be generated through configuration.
    7. Supports custom composite charts.
    | 1. Completely implemented based on customization and personalization for charts.
    2. Friendly to React, allowing chart construction using JSX nodes.
    3. Complete extensibility, capable of building charts entirely from scratch. | +| **Cons** | | 1. Not support more complex combinations and customizations of charts.
    2. Depends on the React framework | 1. High configuration cost.
    2. Manually implement legend filtering.
    3. Manually implement custom tooltips.
    4. Moderate difficulty getting started. | 1. High configuration cost.
    2. Difficulty in getting started.
    3. High learning cost. | +| **Developer experience Friendly** |HIGH | HIGH | MEDIUM | LOW | +| **Learning Curve** |Low | Low | MEDIUM | LOW | +| **Chart Type Supported** |Extensive | Common Charts | Common Charts | Common Charts | +| **Extensibility** |HIGH | MEDIUM | MEDIUM | HIGH | +| **Performance** |HIGH | HIGH | HIGH | HIGH | +| **Testability** |HIGH | MEDIUM | LOW | MEDIUM | +| **Implementation Effort** |Low | MEDIUM | HIGH | HIGH | +| **Maintenance Effort** |Low | MEDIUM | MEDIUM | HIGH | +| **Recommendation** |RECOMMEND | CONSIDER | CONSIDER | DECLINE | + +### Demo + +