diff --git a/.build/jsonSchema.ts b/.build/jsonSchema.ts index 6fd8ca3f5..50b9ff097 100644 --- a/.build/jsonSchema.ts +++ b/.build/jsonSchema.ts @@ -25,6 +25,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [ 'sankey', 'block', 'packet', + 'architecture', ] as const; /** diff --git a/.cspell/code-terms.txt b/.cspell/code-terms.txt index 6d6dad045..8e4c02261 100644 --- a/.cspell/code-terms.txt +++ b/.cspell/code-terms.txt @@ -55,6 +55,7 @@ GENERICTYPE getBoundarys grammr graphtype +halign iife interp introdcued @@ -66,6 +67,7 @@ Kaufmann keyify LABELPOS LABELTYPE +layoutstop lcov LEFTOF Lexa @@ -138,6 +140,7 @@ tsdoc typeof typestr unshift +urlsafe verifymethod VERIFYMTHD WARN_DOCSDIR_DOESNT_MATCH diff --git a/.cspell/libraries.txt b/.cspell/libraries.txt index 3bfec1d5f..73a2dceeb 100644 --- a/.cspell/libraries.txt +++ b/.cspell/libraries.txt @@ -24,11 +24,13 @@ Doctave DokuWiki dompurify elkjs +fcose fontawesome Foswiki Gitea graphlib Grav +icones iconify Inkdrop jiti diff --git a/.cspell/mermaid-terms.txt b/.cspell/mermaid-terms.txt index 46ad6dddb..59a3d108f 100644 --- a/.cspell/mermaid-terms.txt +++ b/.cspell/mermaid-terms.txt @@ -1,5 +1,6 @@ Adamiecki arrowend +Bendpoints bmatrix braintree catmull diff --git a/.cspell/misc-terms.txt b/.cspell/misc-terms.txt index 3fc094309..1820e3c86 100644 --- a/.cspell/misc-terms.txt +++ b/.cspell/misc-terms.txt @@ -4,3 +4,4 @@ handDrawn KOEPF neato newbranch +validify diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index f26bf4ab8..6a43791ed 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -7,17 +7,19 @@ on: permissions: contents: read +concurrency: ${{ github.workflow }}-${{ github.ref }} + jobs: autofix: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 # uses version from "packageManager" field in package.json - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: cache: pnpm node-version-file: '.node-version' @@ -40,4 +42,4 @@ jobs: working-directory: ./packages/mermaid run: pnpm run docs:build - - uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c + - uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c # main diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 0ce778957..eb0c4594a 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -8,6 +8,8 @@ on: pull_request: merge_group: +concurrency: ${{ github.workflow }}-${{ github.ref }} + permissions: contents: read @@ -16,12 +18,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: cache: pnpm node-version-file: '.node-version' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index c6e96912e..000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Build - -on: - push: {} - merge_group: - pull_request: - types: - - opened - - synchronize - - ready_for_review - -permissions: - contents: read - -jobs: - build-mermaid: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: pnpm/action-setup@v4 - # uses version from "packageManager" field in package.json - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - cache: pnpm - node-version-file: '.node-version' - - - name: Install Packages - run: | - pnpm install --frozen-lockfile - env: - CYPRESS_CACHE_FOLDER: .cache/Cypress - - - name: Run Build - run: pnpm run build - - - name: Upload Mermaid Build as Artifact - uses: actions/upload-artifact@v4 - with: - name: mermaid-build - path: packages/mermaid/dist - - - name: Upload Mermaid Mindmap Build as Artifact - uses: actions/upload-artifact@v4 - with: - name: mermaid-mindmap-build - path: packages/mermaid-mindmap/dist diff --git a/.github/workflows/check-readme-in-sync.yml b/.github/workflows/check-readme-in-sync.yml index ad6df66b5..5c940c087 100644 --- a/.github/workflows/check-readme-in-sync.yml +++ b/.github/workflows/check-readme-in-sync.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Check for difference in README.md and docs/README.md run: | diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml deleted file mode 100644 index 012fbf19d..000000000 --- a/.github/workflows/checks.yml +++ /dev/null @@ -1,26 +0,0 @@ -on: - push: - merge_group: - pull_request: - types: - - opened - - synchronize - - ready_for_review - -name: Static analysis on Test files - -jobs: - check-tests: - runs-on: ubuntu-latest - name: check tests - if: github.repository_owner == 'mermaid-js' - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: testomatio/check-tests@stable - with: - framework: cypress - tests: './cypress/e2e/**/**.spec.js' - token: ${{ secrets.GITHUB_TOKEN }} - has-tests-label: true diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 764ec598c..65962ce64 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -11,6 +11,9 @@ on: - synchronize - ready_for_review +permissions: # added using https://github.com/step-security/secure-repo + contents: read + jobs: analyze: name: Analyze @@ -29,11 +32,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 with: config-file: ./.github/codeql/codeql-config.yml languages: ${{ matrix.language }} @@ -45,7 +48,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 # ā„¹ļø Command-line programs to run using the OS shell. # šŸ“š See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -59,4 +62,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 0d4a01360..521735e6e 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -15,6 +15,6 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout Repository' - uses: actions/checkout@v4 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: 'Dependency Review' - uses: actions/dependency-review-action@v4 + uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4 diff --git a/.github/workflows/e2e-applitools.yml b/.github/workflows/e2e-applitools.yml index 5e5407a23..6da65afe5 100644 --- a/.github/workflows/e2e-applitools.yml +++ b/.github/workflows/e2e-applitools.yml @@ -11,6 +11,8 @@ on: default: master description: 'Parent branch to use for PRs' +concurrency: ${{ github.workflow }}-${{ github.ref }} + permissions: contents: read @@ -30,13 +32,13 @@ jobs: run: | echo "::error,title=Not using Applitools::APPLITOOLS_API_KEY is empty, disabling Applitools for this run." - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 # uses version from "packageManager" field in package.json - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version-file: '.node-version' @@ -52,7 +54,7 @@ jobs: APPLITOOLS_SERVER_URL: 'https://eyesapi.applitools.com' - name: Cypress run - uses: cypress-io/github-action@v4 + uses: cypress-io/github-action@d79d2d530a66e641eb4a5f227e13bc985c60b964 # v4.2.2 id: cypress with: start: pnpm run dev diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 2600b3fb8..2b91d078e 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -2,11 +2,15 @@ name: E2E on: push: - branches-ignore: - - 'gh-readonly-queue/**' + branches: + - develop + - master + - release/** pull_request: merge_group: +concurrency: ${{ github.workflow }}-${{ github.ref }} + permissions: contents: read @@ -32,15 +36,15 @@ jobs: image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1 options: --user 1001 steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version-file: '.node-version' - name: Cache snapshots id: cache-snapshot - uses: actions/cache@v4 + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: save-always: true path: ./cypress/snapshots @@ -49,13 +53,13 @@ jobs: # If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots. - name: Switch to base branch if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }} - uses: actions/checkout@v4 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: ref: ${{ env.targetHash }} - name: Install dependencies if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }} - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@df7484c5ba85def7eef30db301afa688187bc378 # v6.7.2 with: # just perform install runTests: false @@ -78,26 +82,26 @@ jobs: matrix: containers: [1, 2, 3, 4] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 # uses version from "packageManager" field in package.json - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version-file: '.node-version' # These cached snapshots are downloaded, providing the reference snapshots. - name: Cache snapshots id: cache-snapshot - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: ./cypress/snapshots key: ${{ runner.os }}-snapshots-${{ env.targetHash }} - name: Install dependencies - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@df7484c5ba85def7eef30db301afa688187bc378 # v6.7.2 with: runTests: false @@ -113,7 +117,7 @@ jobs: # Install NPM dependencies, cache them correctly # and run all Cypress tests - name: Cypress run - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@df7484c5ba85def7eef30db301afa688187bc378 # v6.7.2 id: cypress # If CYPRESS_RECORD_KEY is set, run in parallel on all containers # Otherwise (e.g. if running from fork), we run on a single container only @@ -137,7 +141,7 @@ jobs: ARGOS_PARALLEL_INDEX: ${{ matrix.containers }} - name: Upload Coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 # Run step only pushes to develop and pull_requests if: ${{ steps.cypress.conclusion == 'success' && (github.event_name == 'pull_request' || github.ref == 'refs/heads/develop')}} with: diff --git a/.github/workflows/issue-triage.yml b/.github/workflows/issue-triage.yml index 129bd62b6..87a6e958b 100644 --- a/.github/workflows/issue-triage.yml +++ b/.github/workflows/issue-triage.yml @@ -4,11 +4,17 @@ on: issues: types: [opened] +permissions: # added using https://github.com/step-security/secure-repo + contents: read + jobs: triage: + permissions: + issues: write # for andymckay/labeler to label issues + pull-requests: write # for andymckay/labeler to label PRs runs-on: ubuntu-latest steps: - - uses: andymckay/labeler@1.0.4 + - uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 # 1.0.4 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' add-labels: 'Status: Triage' diff --git a/.github/workflows/link-checker.yml b/.github/workflows/link-checker.yml index bf54d7df2..0a2b74dfe 100644 --- a/.github/workflows/link-checker.yml +++ b/.github/workflows/link-checker.yml @@ -19,6 +19,9 @@ on: # * is a special character in YAML so you have to quote this string - cron: '30 8 * * *' +permissions: # added using https://github.com/step-security/secure-repo + contents: read + jobs: link-checker: runs-on: ubuntu-latest @@ -26,17 +29,17 @@ jobs: # lychee only uses the GITHUB_TOKEN to avoid rate-limiting contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Restore lychee cache - uses: actions/cache@v4 + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: .lycheecache key: cache-lychee-${{ github.sha }} restore-keys: cache-lychee- - name: Link Checker - uses: lycheeverse/lychee-action@v1.9.3 + uses: lycheeverse/lychee-action@c053181aa0c3d17606addfe97a9075a32723548a # v1.9.3 with: args: >- --config .github/lychee.toml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 632cd6ddc..febd2f92d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,26 +4,32 @@ on: push: merge_group: pull_request: - types: - - opened - - synchronize - - ready_for_review workflow_dispatch: +concurrency: ${{ github.workflow }}-${{ github.ref }} + permissions: contents: write jobs: + docker-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - uses: hadolint/hadolint-action@54c9adbab1582c2ef04b2016b760714a4bfde3cf # v3.1.0 + with: + verbose: true lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 # uses version from "packageManager" field in package.json - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: cache: pnpm node-version-file: '.node-version' @@ -82,3 +88,10 @@ jobs: working-directory: ./packages/mermaid continue-on-error: ${{ github.event_name == 'push' }} run: pnpm run docs:verify + + - uses: testomatio/check-tests@0ea638fcec1820cf2e7b9854fdbdd04128a55bd4 # stable + with: + framework: cypress + tests: './cypress/e2e/**/**.spec.js' + token: ${{ secrets.GITHUB_TOKEN }} + has-tests-label: true diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 096590346..c9faaa062 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -22,7 +22,7 @@ jobs: pull-requests: write # write permission is required to label PRs steps: - name: Label PR - uses: release-drafter/release-drafter@v6 + uses: release-drafter/release-drafter@3f0f87098bd6b5c5b9a36d49c41d998ea58f9348 # v6.0.0 with: config-name: pr-labeler.yml disable-autolabeler: false diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 4ff5f4117..ecb411b5c 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -23,12 +23,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: cache: pnpm node-version-file: '.node-version' @@ -37,13 +37,13 @@ jobs: run: pnpm install --frozen-lockfile - name: Setup Pages - uses: actions/configure-pages@v4 + uses: actions/configure-pages@1f0c5cde4bc74cd7e1254d0cb4de8d49e9068c7d # v4.0.0 - name: Run Build run: pnpm --filter mermaid run docs:build:vitepress - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 with: path: packages/mermaid/src/vitepress/.vitepress/dist @@ -56,4 +56,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 diff --git a/.github/workflows/release-preview-publish.yml b/.github/workflows/release-preview-publish.yml index 91e3ac981..96556aa26 100644 --- a/.github/workflows/release-preview-publish.yml +++ b/.github/workflows/release-preview-publish.yml @@ -9,14 +9,14 @@ jobs: publish-preview: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: fetch-depth: 0 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: cache: pnpm node-version-file: '.node-version' @@ -28,7 +28,7 @@ jobs: CYPRESS_CACHE_FOLDER: .cache/Cypress - name: Install Json - run: npm i json --global + run: npm i json@11.0.0 --global - name: Publish working-directory: ./packages/mermaid diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml deleted file mode 100644 index 4dcf709c0..000000000 --- a/.github/workflows/release-publish.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Publish release - -on: - release: - types: [published] - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: fregante/setup-git-user@v2 - - - uses: pnpm/action-setup@v4 - # uses version from "packageManager" field in package.json - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - cache: pnpm - node-version-file: '.node-version' - - - name: Install Packages - run: | - pnpm install --frozen-lockfile - npm i json --global - env: - CYPRESS_CACHE_FOLDER: .cache/Cypress - - - name: Prepare release - run: | - VERSION=${GITHUB_REF:10} - echo "Preparing release $VERSION" - git checkout -t origin/release/$VERSION - npm version --no-git-tag-version --allow-same-version $VERSION - git add package.json - git commit -nm "Bump version $VERSION" - git checkout -t origin/master - git merge -m "Release $VERSION" --no-ff release/$VERSION - git push --no-verify - - - name: Publish - run: | - npm set //registry.npmjs.org/:_authToken $NPM_TOKEN - npm publish - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0b36a82b3..3db5f6f37 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,18 +7,26 @@ on: concurrency: ${{ github.workflow }}-${{ github.ref }} +permissions: # added using https://github.com/step-security/secure-repo + contents: read + jobs: release: + if: github.repository == 'mermaid-js/mermaid' + permissions: + contents: write # to create release (changesets/action) + id-token: write # OpenID Connect token needed for provenance + pull-requests: write # to create pull request (changesets/action) name: Release runs-on: ubuntu-latest steps: - name: Checkout Repo - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: cache: pnpm node-version-file: '.node-version' @@ -28,10 +36,11 @@ jobs: - name: Create Release Pull Request or Publish to npm id: changesets - uses: changesets/action@v1 + uses: changesets/action@aba318e9165b45b7948c60273e0b72fce0a64eb9 # v1.4.7 with: version: pnpm changeset:version publish: pnpm changeset:publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_CONFIG_PROVENANCE: true diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 000000000..0dee2e666 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,37 @@ +name: Scorecard supply-chain security +on: + branch_protection_rule: + push: + branches: + - develop + schedule: + - cron: 29 15 * * 0 +permissions: read-all +jobs: + analysis: + name: Scorecard analysis + permissions: + id-token: write + security-events: write + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false + - name: Run analysis + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + - name: Upload artifact + uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + - name: Upload to code-scanning + uses: github/codeql-action/upload-sarif@f0f3afee809481da311ca3a6ff1ff51d81dbeb24 # v3.26.4 + with: + sarif_file: results.sarif diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a0b284a68..375d5fada 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,13 +9,13 @@ jobs: unit-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 # uses version from "packageManager" field in package.json - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: cache: pnpm node-version-file: '.node-version' @@ -39,7 +39,7 @@ jobs: pnpm exec vitest run ./packages/mermaid/src/diagrams/gantt/ganttDb.spec.ts --coverage - name: Upload Coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 # Run step only pushes to develop and pull_requests if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/develop' }} with: diff --git a/.github/workflows/unlock-reopened-issues.yml b/.github/workflows/unlock-reopened-issues.yml index 4c5379729..b854eeb4b 100644 --- a/.github/workflows/unlock-reopened-issues.yml +++ b/.github/workflows/unlock-reopened-issues.yml @@ -8,6 +8,6 @@ jobs: triage: runs-on: ubuntu-latest steps: - - uses: Dunning-Kruger/unlock-issues@v1 + - uses: Dunning-Kruger/unlock-issues@b06b7f7e5c3f2eaa1c6d5d89f40930e4d6d9699e # v1 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' diff --git a/.github/workflows/update-browserlist.yml b/.github/workflows/update-browserlist.yml index f8f7696cd..1b26271f7 100644 --- a/.github/workflows/update-browserlist.yml +++ b/.github/workflows/update-browserlist.yml @@ -8,18 +8,18 @@ jobs: update-browser-list: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 - run: npx update-browserslist-db@latest - name: Commit changes - uses: EndBug/add-and-commit@v9 + uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4 with: author_name: ${{ github.actor }} author_email: ${{ github.actor }}@users.noreply.github.com message: 'chore: update browsers list' push: false - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0 with: branch: update-browserslist title: Update Browserslist diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 000000000..280ff9592 --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,2 @@ +ignored: + - DL3002 # TODO: Last USER should not be root diff --git a/.husky/pre-commit b/.husky/pre-commit index ad85fc42c..cc173f4a5 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,2 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - +#!/usr/bin/env sh NODE_OPTIONS="--max_old_space_size=8192" pnpm run pre-commit diff --git a/Dockerfile b/Dockerfile index 7bec3bd4b..fa933f999 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,2 +1,10 @@ -FROM node:20.12.2-alpine3.19 AS base -RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL="$(which sh)" sh - +FROM node:20.12.2-alpine3.19@sha256:7a91aa397f2e2dfbfcdad2e2d72599f374e0b0172be1d86eeb73f1d33f36a4b2 + +USER 0:0 + +RUN corepack enable \ + && corepack enable pnpm + +ENV NODE_OPTIONS="--max_old_space_size=8192" + +EXPOSE 9000 3333 diff --git a/README.md b/README.md index 8d5eebfeb..456747132 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,8 @@ Try Live Editor previews of future releases: @@ -82,6 +83,10 @@ You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include- For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](https://mermaid.js.org/intro/getting-started.html), [Usage](https://mermaid.js.org/config/usage.html) and [Tutorials](https://mermaid.js.org/ecosystem/tutorials.html). +Our PR Visual Regression Testing is powered by [Argos](https://argos-ci.com/?utm_source=mermaid&utm_campaign=oss) with their generous Open Source plan. It makes the process of reviewing PRs with visual changes a breeze. + +[![Covered by Argos Visual Testing](https://argos-ci.com/badge-large.svg)](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss) + In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests. diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts index 133a35032..74b17cf05 100644 --- a/cypress/helpers/util.ts +++ b/cypress/helpers/util.ts @@ -73,7 +73,7 @@ export const imgSnapshotTest = ( export const urlSnapshotTest = ( url: string, - options: CypressMermaidConfig, + options: CypressMermaidConfig = {}, _api = false, validation?: any ): void => { diff --git a/cypress/integration/rendering/architecture.spec.ts b/cypress/integration/rendering/architecture.spec.ts new file mode 100644 index 000000000..1deb1f7da --- /dev/null +++ b/cypress/integration/rendering/architecture.spec.ts @@ -0,0 +1,180 @@ +import { imgSnapshotTest, urlSnapshotTest } from '../../helpers/util.ts'; + +describe.skip('architecture diagram', () => { + it('should render a simple architecture diagram with groups', () => { + imgSnapshotTest( + `architecture-beta + group api(cloud)[API] + + service db(database)[Database] in api + service disk1(disk)[Storage] in api + service disk2(disk)[Storage] in api + service server(server)[Server] in api + service gateway(internet)[Gateway] + + db L--R server + disk1 T--B server + disk2 T--B db + server T--B gateway + ` + ); + }); + it('should render an architecture diagram with groups within groups', () => { + imgSnapshotTest( + `architecture-beta + group api[API] + group public[Public API] in api + group private[Private API] in api + + service serv1(server)[Server] in public + + service serv2(server)[Server] in private + service db(database)[Database] in private + + service gateway(internet)[Gateway] in api + + serv1 B--T serv2 + serv2 L--R db + serv1 L--R gateway + ` + ); + }); + it('should render an architecture diagram with the fallback icon', () => { + imgSnapshotTest( + `architecture-beta + service unknown(iconnamedoesntexist)[Unknown Icon] + ` + ); + }); + it('should render an architecture diagram with split directioning', () => { + imgSnapshotTest( + `architecture-beta + service db(database)[Database] + service s3(disk)[Storage] + service serv1(server)[Server 1] + service serv2(server)[Server 2] + service disk(disk)[Disk] + + db L--R s3 + serv1 L--T s3 + serv2 L--B s3 + serv1 T--B disk + ` + ); + }); + it('should render an architecture diagram with directional arrows', () => { + imgSnapshotTest( + `architecture-beta + service servC(server)[Server 1] + service servL(server)[Server 2] + service servR(server)[Server 3] + service servT(server)[Server 4] + service servB(server)[Server 5] + + servC (L--R) servL + servC (R--L) servR + servC (T--B) servT + servC (B--T) servB + + servL (T--L) servT + servL (B--L) servB + servR (T--R) servT + servR (B--R) servB + ` + ); + }); + it('should render an architecture diagram with group edges', () => { + imgSnapshotTest( + `architecture-beta + group left_group(cloud)[Left] + group right_group(cloud)[Right] + group top_group(cloud)[Top] + group bottom_group(cloud)[Bottom] + group center_group(cloud)[Center] + + service left_disk(disk)[Disk] in left_group + service right_disk(disk)[Disk] in right_group + service top_disk(disk)[Disk] in top_group + service bottom_disk(disk)[Disk] in bottom_group + service center_disk(disk)[Disk] in center_group + + left_disk{group} (R--L) center_disk{group} + right_disk{group} (L--R) center_disk{group} + top_disk{group} (B--T) center_disk{group} + bottom_disk{group} (T--B) center_disk{group} + ` + ); + }); + it('should render an architecture diagram with edge labels', () => { + imgSnapshotTest( + `architecture-beta + service servC(server)[Server 1] + service servL(server)[Server 2] + service servR(server)[Server 3] + service servT(server)[Server 4] + service servB(server)[Server 5] + + servC L-[Label]-R servL + servC R-[Label]-L servR + servC T-[Label]-B servT + servC B-[Label]-T servB + + servL T-[Label]-L servT + servL B-[Label]-L servB + servR T-[Label]-R servT + servR B-[Label]-R servB + ` + ); + }); + it('should render an architecture diagram with simple junction edges', () => { + imgSnapshotTest( + `architecture-beta + service left_disk(disk)[Disk] + service top_disk(disk)[Disk] + service bottom_disk(disk)[Disk] + service top_gateway(internet)[Gateway] + service bottom_gateway(internet)[Gateway] + junction juncC + junction juncR + + left_disk R--L juncC + top_disk B--T juncC + bottom_disk T--B juncC + juncC R--L juncR + top_gateway B--T juncR + bottom_gateway T--B juncR + ` + ); + }); + it('should render an architecture diagram with complex junction edges', () => { + imgSnapshotTest( + `architecture-beta + group left + group right + service left_disk(disk)[Disk] in left + service top_disk(disk)[Disk] in left + service bottom_disk(disk)[Disk] in left + service top_gateway(internet)[Gateway] in right + service bottom_gateway(internet)[Gateway] in right + junction juncC in left + junction juncR in right + + left_disk R--L juncC + top_disk B--T juncC + bottom_disk T--B juncC + + + top_gateway (B--T juncR + bottom_gateway (T--B juncR + + juncC{group} R--L) juncR{group} + ` + ); + }); +}); + +describe('architecture - external', () => { + it('should allow adding external icons', () => { + urlSnapshotTest('http://localhost:9000/architecture-external.html'); + }); +}); diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js index 1a2340906..aad9b1cf7 100644 --- a/cypress/integration/rendering/erDiagram.spec.js +++ b/cypress/integration/rendering/erDiagram.spec.js @@ -321,4 +321,37 @@ ORDER ||--|{ LINE-ITEM : contains { logLevel: 1 } ); }); + + it('should render relationship labels with line breaks', () => { + imgSnapshotTest( + ` + erDiagram + p[Person] { + string firstName + string lastName + } + a["Customer Account"] { + string email + } + + b["Customer Account Secondary"] { + string email + } + + c["Customer Account Tertiary"] { + string email + } + + d["Customer Account Nth"] { + string email + } + + p ||--o| a : "has
one" + p ||--o| b : "has
one
two" + p ||--o| c : "has
one
two
three" + p ||--o| d : "has
one
two
three
...
Nth" + `, + { logLevel: 1 } + ); + }); }); diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js index c2fd0b011..452cdb5a0 100644 --- a/cypress/integration/rendering/flowchart-v2.spec.js +++ b/cypress/integration/rendering/flowchart-v2.spec.js @@ -99,7 +99,7 @@ describe('Flowchart v2', () => { const style = svg.attr('style'); expect(style).to.match(/^max-width: [\d.]+px;$/); const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join('')); - expect(maxWidthValue).to.be.within(446 * 0.95 - 1, 446 * 1.05); + expect(maxWidthValue).to.be.within(417 * 0.95, 417 * 1.05); }); }); it('8: should render a flowchart when useMaxWidth is false', () => { @@ -118,7 +118,7 @@ describe('Flowchart v2', () => { const width = parseFloat(svg.attr('width')); // use within because the absolute value can be slightly different depending on the environment ±5% // expect(height).to.be.within(446 * 0.95, 446 * 1.05); - expect(width).to.be.within(446 * 0.95 - 1, 446 * 1.05); + expect(width).to.be.within(417 * 0.95, 417 * 1.05); expect(svg).to.not.have.attr('style'); }); }); diff --git a/cypress/platform/architecture-external.html b/cypress/platform/architecture-external.html new file mode 100644 index 000000000..71770425d --- /dev/null +++ b/cypress/platform/architecture-external.html @@ -0,0 +1,52 @@ + + + + + + Architecture Mermaid Test Page + + + + + +

External Icons Demo

+
+    architecture-beta
+      service s3(logos:aws-s3)[Cloud Store]
+      service ec2(logos:aws-ec2)[Server]
+      service api(logos:aws-api-gateway)[Api Gateway]
+      service fa(fa:image)[Font Awesome Icon]
+    
+ + + + diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index 8808a3c9d..a3cbf60bf 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -73,7 +73,9 @@ font-family: monospace; font-size: 72px; } - + pre { + width: 100%; + } /* tspan { font-size: 6px !important; } */ @@ -88,17 +90,202 @@ config: look: handDrawn layout: elk + elk: + + +--- +stateDiagram-v2 + direction LR + accTitle: An idealized Open Source supply-chain graph + + %% + state "🟦 Importer" as author_importer + state "🟄 Supplier, Owner" as author_owner + state "🟨🟄 Maintainer, Author\n🟨 Custodian" as author + state "🟩 Distributor" as repository_distributor + state "🟦 Importer" as language_importer + state "🟦🟨 Packager" as language_packager + state "🟦🟨 OSS Steward" as language_steward + state "🟨 Curator" as language_curator + state "🟩 Distributor" as language_distributor + state "🟦 Contributor" as contributor + state "🟦 Importer" as package_importer + state "🟨 Patcher" as package_patcher + state "🟨🟦 Builder\n🟨🟦 Packager\n🟨🟦 Containerizer" as package_packager + state "🟨 Curator" as package_curator + state "🟩 Distributor" as package_distributor + state "🟦 Importer" as integrator_importer + state "🟄 Supplier, Manufacturer, Owner" as integrator_owner + state "🟦🟨🟄 Integrator, Developer" as integrator_developer + state "🟩🟨 SBOM Redactor\n🟩 Publisher" as integrator_publisher + state "🟦🟨 Builder" as integrator_builder + state "🟨 Deployer" as deployer + state "🟦 Vuln. Checker" as integrator_checker + state "🟩🟨 SBOM Redactor" as redactor + state "🟦 Consumer\n🟦 User" as consumer + state "🟦 Auditor" as auditor_internal + state "🟦 Auditor" as auditor_external + + %% + classDef createsSBOM stroke:red,stroke-width:3px; + classDef updatesSBOM stroke:yellow,stroke-width:3px; + classDef assemblesSBOM stroke:yellow,stroke-width:3px; + classDef distributesSBOM stroke:green,stroke-width:3px; + classDef verifiesSBOM stroke:#07f,stroke-width:3px; + + %% + class author_importer verifiesSBOM + class author_owner createsSBOM + class manufacturer_owner createsSBOM + class author assemblesSBOM + class package_importer verifiesSBOM + class package_patcher updatesSBOM + class package_packager assemblesSBOM + class package_curator distributesSBOM + class package_distributor distributesSBOM + class language_importer verifiesSBOM + class language_packager assemblesSBOM + class language_steward updatesSBOM + class language_curator distributesSBOM + class language_distributor distributesSBOM + class repository_distributor distributesSBOM + class integrator_importer verifiesSBOM + class integrator_owner createsSBOM + class integrator_developer assemblesSBOM + class integrator_publisher distributesSBOM + class integrator_builder assemblesSBOM + class integrator_checker verifiesSBOM + class deployer assemblesSBOM + class redactor distributesSBOM + class auditor_internal verifiesSBOM + class auditor_external verifiesSBOM + + state "Maintainer Environment" as environment_maintainer { + [*] --> author_importer + [*] --> author + author_importer --> author + author_owner --> author + author --> language_packager + } + + [*] --> environment_maintainer + + state "Language Ecosystem" as ecosystem_lang { + [*] --> language_importer + [*] --> language_steward + [*] --> language_curator + [*] --> language_distributor + language_importer --> language_distributor + language_importer --> language_curator + language_steward --> language_curator + language_curator --> language_distributor + } + + language_packager --> ecosystem_lang + ecosystem_lang --> ecosystem_lang + + state "Public Collaboration Ecosystem" as ecosystem_repo { + [*] --> repository_distributor + } + + author --> ecosystem_repo + ecosystem_repo --> author + + repository_distributor --> contributor + contributor --> repository_distributor + + state "Package Ecosystem" as ecosystem_package { + [*] --> package_importer + [*] --> package_packager + [*] --> package_patcher + package_importer --> package_patcher + package_importer --> package_packager + package_patcher --> package_packager + package_packager --> package_curator + package_packager --> package_distributor + package_curator --> package_distributor + } + + repository_distributor --> ecosystem_package + language_distributor --> ecosystem_package + ecosystem_package --> ecosystem_package + + state "Integrator Environment" as environment_integrator { + [*] --> integrator_developer + [*] --> integrator_importer + integrator_importer --> integrator_developer + integrator_owner --> integrator_developer + integrator_builder --> integrator_publisher + integrator_developer --> integrator_checker + integrator_checker --> integrator_developer + auditor_internal --> integrator_developer + integrator_developer --> integrator_builder + integrator_developer --> auditor_internal + } + + repository_distributor --> environment_integrator + language_distributor --> environment_integrator + package_distributor --> environment_integrator + + state "Production Environment" as environment_prod { + [*] --> deployer + deployer --> redactor + } + + integrator_publisher --> [*] + integrator_developer --> environment_prod + integrator_builder --> environment_prod + integrator_publisher --> environment_prod + + deployer --> auditor_external + deployer --> consumer + redactor --> consumer + + + + +
+---
+  title: hello2
+  config:
+    look: handDrawn
+    layout: dagre
     elk:
         nodePlacementStrategy: BRANDES_KOEPF
 ---
-flowchart LR
-  A[Start] --Some text--> B(Continue)
-  B --> C{Evaluate}
-  C -- One --> D[Option 1]
-  C -- Two --> E[Option 2]
-  C -- Three --> F[fa:fa-car Option 3]
+stateDiagram-v2
+  A --> A
+  state A {
+    B --> D
+    state B {
+      C
+    }
+    state D {
+      E
+    }
+  }
 
 
+
+
+---
+  title: hello2
+  config:
+    look: handDrawn
+    layout: dagre
+    elk:
+        nodePlacementStrategy: BRANDES_KOEPF
+---
+flowchart
+  A --> A
+  subgraph A
+    B --> B
+    subgraph B
+      C
+    end
+  end
+
 
 
@@ -195,7 +382,7 @@ flowchart LR messageFontFamily: 'courier', }, fontSize: 12, - logLevel: 0, + logLevel: 3, securityLevel: 'loose', }); function callback() { diff --git a/demos/architecture.html b/demos/architecture.html new file mode 100644 index 000000000..6d978d952 --- /dev/null +++ b/demos/architecture.html @@ -0,0 +1,256 @@ + + + + + + Architecture Mermaid Quick Test Page + + + + + +

Architecture diagram demo

+

Simple diagram with groups

+
+      architecture-beta
+        group api(cloud)[API]
+
+        service db(database)[Database] in api
+        service disk1(disk)[Storage] in api
+        service disk2(disk)[Storage] in api
+        service server(server)[Server] in api
+        service gateway(internet)[Gateway] 
+
+        db:L -- R:server
+        disk1:T -- B:server
+        disk2:T -- B:db
+        server:T -- B:gateway
+    
+
+ +

Groups within groups

+
+      architecture-beta
+        group api[API]
+        group public[Public API] in api
+        group private[Private API] in api
+
+
+        service serv1(server)[Server] in public
+
+
+        service serv2(server)[Server] in private
+        service db(database)[Database] in private
+
+        service gateway(internet)[Gateway] in api
+
+        serv1:B -- T:serv2
+
+        serv2:L -- R:db
+
+        serv1:L -- R:gateway
+    
+
+ +

Default icon (?) from unknown icon name

+
+      architecture-beta
+        service unknown(iconnamedoesntexist)[Unknown Icon]
+    
+
+ +

Split Direction

+
+      architecture-beta
+        service db(database)[Database]
+        service s3(disk)[Storage]
+        service serv1(server)[Server 1]
+        service serv2(server)[Server 2]
+        service disk(disk)[Disk]
+
+        db:L -- R:s3
+        serv1:L -- T:s3
+        serv2:L -- B:s3
+        serv1:T -- B:disk
+    
+
+ +

Arrow Tests

+
+      architecture-beta
+        service servC(server)[Server 1]
+        service servL(server)[Server 2]
+        service servR(server)[Server 3]
+        service servT(server)[Server 4]
+        service servB(server)[Server 5]
+
+        servC:L <--> R:servL
+        servC:R <--> L:servR
+        servC:T <--> B:servT
+        servC:B <--> T:servB
+
+        servL:T <--> L:servT
+        servL:B <--> L:servB
+        servR:T <--> R:servT
+        servR:B <--> R:servB
+    
+
+      architecture-beta
+        service servC(server)[Server 1]
+        service servL(server)[Server 2]
+        service servR(server)[Server 3]
+        service servT(server)[Server 4]
+        service servB(server)[Server 5]
+
+        servC:L <--> R:servL
+        servC:R <--> L:servR
+        servC:T <--> B:servT
+        servC:B <--> T:servB
+
+        servT:L <--> T:servL
+        servB:L <--> B:servL
+        servT:R <--> T:servR
+        servB:R <--> B:servR
+    
+
+ +

Group Edges

+
+      architecture-beta
+        group left_group(cloud)[Left]
+        group right_group(cloud)[Right]
+        group top_group(cloud)[Top]
+        group bottom_group(cloud)[Bottom]
+        group center_group(cloud)[Center]
+
+        service left_disk(disk)[Disk] in left_group
+        service right_disk(disk)[Disk] in right_group
+        service top_disk(disk)[Disk] in top_group
+        service bottom_disk(disk)[Disk] in bottom_group
+        service center_disk(disk)[Disk] in center_group
+
+        left_disk{group}:R <--> L:center_disk{group}
+        right_disk{group}:L <--> R:center_disk{group}
+        top_disk{group}:B <--> T:center_disk{group}
+        bottom_disk{group}:T <--> B:center_disk{group}
+  
+
+ +

Edge Label Test

+
+      architecture-beta
+        service servC(server)[Server 1]
+        service servL(server)[Server 2]
+        service servR(server)[Server 3]
+        service servT(server)[Server 4]
+        service servB(server)[Server 5]
+
+        servC:L -[Label]- R:servL
+        servC:R -[Label]- L:servR
+        servC:T -[Label]- B:servT
+        servC:B -[Label]- T:servB
+
+        servL:T -[Label]- L:servT
+        servL:B -[Label]- L:servB
+        servR:T -[Label]- R:servT
+        servR:B -[Label]- R:servB
+    
+
+      architecture-beta
+        service servC(server)[Server 1]
+        service servL(server)[Server 2]
+        service servR(server)[Server 3]
+        service servT(server)[Server 4]
+        service servB(server)[Server 5]
+
+        servC:L -[Label that is Long]- R:servL
+        servC:R -[Label that is Long]- L:servR
+        servC:T -[Label that is Long]- B:servT
+        servC:B -[Label that is Long]- T:servB
+
+        servL:T -[Label that is Long]- L:servT
+        servL:B -[Label that is Long]- L:servB
+        servR:T -[Label that is Long]- R:servT
+        servR:B -[Label that is Long]- R:servB
+    
+ +
+

Junction Demo

+
+      architecture-beta
+        service left_disk(disk)[Disk]
+        service top_disk(disk)[Disk]
+        service bottom_disk(disk)[Disk]
+        service top_gateway(internet)[Gateway]
+        service bottom_gateway(internet)[Gateway]
+        junction juncC
+        junction juncR
+
+        left_disk:R -- L:juncC
+        top_disk:B -- T:juncC
+        bottom_disk:T -- B:juncC
+        juncC:R -- L:juncR
+        top_gateway:B -- T:juncR
+        bottom_gateway:T -- B:juncR
+    
+
+ +

Junction Demo Groups

+
+      architecture-beta
+        group left
+        group right
+        service left_disk(disk)[Disk] in left
+        service top_disk(disk)[Disk] in left
+        service bottom_disk(disk)[Disk] in left
+        service top_gateway(internet)[Gateway] in right
+        service bottom_gateway(internet)[Gateway] in right
+        junction juncC in left
+        junction juncR in right
+
+        left_disk:R -- L:juncC
+        top_disk:B -- T:juncC
+        bottom_disk:T -- B:juncC
+
+
+        top_gateway:B <-- T:juncR
+        bottom_gateway:T <-- B:juncR
+
+        juncC{group}:R --> L:juncR{group}
+    
+
+ +

External Icons Demo

+
+    architecture-beta
+      service s3(logos:aws-s3)[Cloud Store]
+      service ec2(logos:aws-ec2)[Server]
+      service api(logos:aws-api-gateway)[Api Gateway]
+      service fa(fa:image)[Font Awesome Icon]
+    
+ + + + diff --git a/demos/er.html b/demos/er.html index 0b4b82bac..b6c503c2e 100644 --- a/demos/er.html +++ b/demos/er.html @@ -125,6 +125,35 @@
+
+    erDiagram
+      p[Person] {
+          string firstName
+          string lastName
+      }
+      a["Customer Account"] {
+          string email
+      }
+
+      b["Customer Account Secondary"] {
+        string email
+      }
+      
+      c["Customer Account Tertiary"] {
+        string email
+      }
+      
+      d["Customer Account Nth"] {
+        string email
+      }
+
+      p ||--o| a : "has
one" + p ||--o| b : "has
one
two" + p ||--o| c : "has
one
two
three" + p ||--o| d : "has
one
two
three
...
Nth" +
+
+
     erDiagram
       _customer_order {
diff --git a/demos/index.html b/demos/index.html
index 61a86a2aa..07b51a313 100644
--- a/demos/index.html
+++ b/demos/index.html
@@ -88,6 +88,9 @@
       
  • Layered Blocks

  • +
  • +

    Architecture

    +
  • diff --git a/docker-compose.yml b/docker-compose.yml index 841f07ff9..5fa1ff04a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,9 +7,6 @@ services: tty: true working_dir: /mermaid mem_limit: '8G' - entrypoint: ./docker-entrypoint.sh - environment: - - NODE_OPTIONS=--max_old_space_size=8192 volumes: - ./:/mermaid - root_cache:/root/.cache diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh deleted file mode 100755 index c222b7fd5..000000000 --- a/docker-entrypoint.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -source /root/.shrc -exec "$@" diff --git a/docs/community/contributing.md b/docs/community/contributing.md index c78a3cb40..792c90a98 100644 --- a/docs/community/contributing.md +++ b/docs/community/contributing.md @@ -370,9 +370,9 @@ If the users have no way to know that things have changed, then you haven't real Likewise, if users don't know that there is a new feature that you've implemented, it will forever remain unknown and unused. The documentation has to be updated for users to know that things have been changed and added! -If you are adding a new feature, add `(v10.8.0+)` in the title or description. It will be replaced automatically with the current version number when the release happens. +If you are adding a new feature, add `(v+)` in the title or description. It will be replaced automatically with the current version number when the release happens. -eg: `# Feature Name (v10.8.0+)` +eg: `# Feature Name (v+)` We know it can sometimes be hard to code _and_ write user documentation. diff --git a/docs/config/setup/classes/mermaid.UnknownDiagramError.md b/docs/config/setup/classes/mermaid.UnknownDiagramError.md index abe205bb5..3e1edf597 100644 --- a/docs/config/setup/classes/mermaid.UnknownDiagramError.md +++ b/docs/config/setup/classes/mermaid.UnknownDiagramError.md @@ -127,7 +127,7 @@ Error.prepareStackTrace #### Defined in -node_modules/@types/node/globals.d.ts:98 +node_modules/@types/node/globals.d.ts:28 --- @@ -141,7 +141,7 @@ Error.stackTraceLimit #### Defined in -node_modules/@types/node/globals.d.ts:100 +node_modules/@types/node/globals.d.ts:30 ## Methods @@ -168,4 +168,4 @@ Error.captureStackTrace #### Defined in -node_modules/@types/node/globals.d.ts:91 +node_modules/@types/node/globals.d.ts:21 diff --git a/docs/config/setup/interfaces/mermaid.Mermaid.md b/docs/config/setup/interfaces/mermaid.Mermaid.md index 09fab149c..f4e9eb4ec 100644 --- a/docs/config/setup/interfaces/mermaid.Mermaid.md +++ b/docs/config/setup/interfaces/mermaid.Mermaid.md @@ -28,7 +28,7 @@ page. #### Defined in -[packages/mermaid/src/mermaid.ts:435](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L435) +[packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436) --- @@ -59,7 +59,7 @@ A graph definition key #### Defined in -[packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437) +[packages/mermaid/src/mermaid.ts:438](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L438) --- @@ -89,7 +89,7 @@ Use [initialize](mermaid.Mermaid.md#initialize) and [run](mermaid.Mermaid.md#run #### Defined in -[packages/mermaid/src/mermaid.ts:430](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L430) +[packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431) --- @@ -116,7 +116,7 @@ This function should be called before the run function. #### Defined in -[packages/mermaid/src/mermaid.ts:434](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L434) +[packages/mermaid/src/mermaid.ts:435](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L435) --- @@ -130,7 +130,7 @@ Use [parse](mermaid.Mermaid.md#parse) and [render](mermaid.Mermaid.md#render) in #### Defined in -[packages/mermaid/src/mermaid.ts:424](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L424) +[packages/mermaid/src/mermaid.ts:425](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L425) --- @@ -180,7 +180,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not #### Defined in -[packages/mermaid/src/mermaid.ts:425](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L425) +[packages/mermaid/src/mermaid.ts:426](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L426) --- @@ -190,7 +190,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not #### Defined in -[packages/mermaid/src/mermaid.ts:419](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L419) +[packages/mermaid/src/mermaid.ts:420](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L420) --- @@ -218,7 +218,31 @@ Used to register external diagram types. #### Defined in -[packages/mermaid/src/mermaid.ts:433](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L433) +[packages/mermaid/src/mermaid.ts:434](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L434) + +--- + +### registerIconPacks + +• **registerIconPacks**: (`iconLoaders`: `IconLoader`\[]) => `void` + +#### Type declaration + +ā–ø (`iconLoaders`): `void` + +##### Parameters + +| Name | Type | +| :------------ | :-------------- | +| `iconLoaders` | `IconLoader`\[] | + +##### Returns + +`void` + +#### Defined in + +[packages/mermaid/src/mermaid.ts:439](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L439) --- @@ -242,7 +266,7 @@ Used to register external diagram types. #### Defined in -[packages/mermaid/src/mermaid.ts:432](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L432) +[packages/mermaid/src/mermaid.ts:433](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L433) --- @@ -268,7 +292,7 @@ Used to register external diagram types. #### Defined in -[packages/mermaid/src/mermaid.ts:426](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L426) +[packages/mermaid/src/mermaid.ts:427](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L427) --- @@ -316,7 +340,7 @@ Renders the mermaid diagrams #### Defined in -[packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431) +[packages/mermaid/src/mermaid.ts:432](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L432) --- @@ -351,7 +375,7 @@ to it (eg. dart interop wrapper). (Initially there is no parseError member of me #### Defined in -[packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436) +[packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437) --- @@ -361,4 +385,4 @@ to it (eg. dart interop wrapper). (Initially there is no parseError member of me #### Defined in -[packages/mermaid/src/mermaid.ts:418](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L418) +[packages/mermaid/src/mermaid.ts:419](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L419) diff --git a/docs/config/setup/interfaces/mermaid.MermaidConfig.md b/docs/config/setup/interfaces/mermaid.MermaidConfig.md index ca7828f52..ad078653a 100644 --- a/docs/config/setup/interfaces/mermaid.MermaidConfig.md +++ b/docs/config/setup/interfaces/mermaid.MermaidConfig.md @@ -16,7 +16,17 @@ #### Defined in -[packages/mermaid/src/config.type.ts:112](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L112) +[packages/mermaid/src/config.type.ts:122](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L122) + +--- + +### architecture + +• `Optional` **architecture**: `ArchitectureDiagramConfig` + +#### Defined in + +[packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194) --- @@ -29,7 +39,7 @@ This matters if you are using base tag settings. #### Defined in -[packages/mermaid/src/config.type.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L131) +[packages/mermaid/src/config.type.ts:141](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L141) --- @@ -39,7 +49,7 @@ This matters if you are using base tag settings. #### Defined in -[packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189) +[packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200) --- @@ -49,7 +59,7 @@ This matters if you are using base tag settings. #### Defined in -[packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186) +[packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197) --- @@ -59,7 +69,7 @@ This matters if you are using base tag settings. #### Defined in -[packages/mermaid/src/config.type.ts:177](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L177) +[packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187) --- @@ -69,7 +79,7 @@ This matters if you are using base tag settings. #### Defined in -[packages/mermaid/src/config.type.ts:103](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L103) +[packages/mermaid/src/config.type.ts:113](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L113) --- @@ -83,7 +93,7 @@ You can set this attribute to base the seed on a static string. #### Defined in -[packages/mermaid/src/config.type.ts:171](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L171) +[packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181) --- @@ -101,7 +111,7 @@ should not change unless content is changed. #### Defined in -[packages/mermaid/src/config.type.ts:164](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L164) +[packages/mermaid/src/config.type.ts:174](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L174) --- @@ -111,7 +121,7 @@ should not change unless content is changed. #### Defined in -[packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190) +[packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201) --- @@ -121,10 +131,11 @@ should not change unless content is changed. #### Type declaration -| Name | Type | Description | -| :----------------------- | :---------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `mergeEdges?` | `boolean` | Elk specific option that allows edges to share path where it convenient. It can make for pretty diagrams but can also make it harder to read the diagram. | -| `nodePlacementStrategy?` | `"SIMPLE"` \| `"NETWORK_SIMPLEX"` \| `"LINEAR_SEGMENTS"` \| `"BRANDES_KOEPF"` | Elk specific option affecting how nodes are placed. | +| Name | Type | Description | +| :----------------------- | :-------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `cycleBreakingStrategy?` | `"GREEDY"` \| `"DEPTH_FIRST"` \| `"INTERACTIVE"` \| `"MODEL_ORDER"` \| `"GREEDY_MODEL_ORDER"` | This strategy decides how to find cycles in the graph and deciding which edges need adjustment to break loops. | +| `mergeEdges?` | `boolean` | Elk specific option that allows edges to share path where it convenient. It can make for pretty diagrams but can also make it harder to read the diagram. | +| `nodePlacementStrategy?` | `"SIMPLE"` \| `"NETWORK_SIMPLEX"` \| `"LINEAR_SEGMENTS"` \| `"BRANDES_KOEPF"` | Elk specific option affecting how nodes are placed. | #### Defined in @@ -138,7 +149,7 @@ should not change unless content is changed. #### Defined in -[packages/mermaid/src/config.type.ts:179](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L179) +[packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189) --- @@ -148,7 +159,7 @@ should not change unless content is changed. #### Defined in -[packages/mermaid/src/config.type.ts:172](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L172) +[packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182) --- @@ -162,7 +173,7 @@ See #### Defined in -[packages/mermaid/src/config.type.ts:111](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L111) +[packages/mermaid/src/config.type.ts:121](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L121) --- @@ -172,7 +183,7 @@ See #### Defined in -[packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192) +[packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203) --- @@ -186,7 +197,7 @@ If set to true, ignores legacyMathML. #### Defined in -[packages/mermaid/src/config.type.ts:153](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L153) +[packages/mermaid/src/config.type.ts:163](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L163) --- @@ -196,7 +207,7 @@ If set to true, ignores legacyMathML. #### Defined in -[packages/mermaid/src/config.type.ts:174](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L174) +[packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184) --- @@ -206,7 +217,7 @@ If set to true, ignores legacyMathML. #### Defined in -[packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185) +[packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196) --- @@ -228,7 +239,7 @@ Defines the seed to be used when using handDrawn look. This is important for the #### Defined in -[packages/mermaid/src/config.type.ts:104](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L104) +[packages/mermaid/src/config.type.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L114) --- @@ -238,7 +249,7 @@ Defines the seed to be used when using handDrawn look. This is important for the #### Defined in -[packages/mermaid/src/config.type.ts:175](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L175) +[packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185) --- @@ -265,7 +276,7 @@ fall back to legacy rendering for KaTeX. #### Defined in -[packages/mermaid/src/config.type.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L146) +[packages/mermaid/src/config.type.ts:156](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L156) --- @@ -277,7 +288,7 @@ This option decides the amount of logging to be used by mermaid. #### Defined in -[packages/mermaid/src/config.type.ts:117](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L117) +[packages/mermaid/src/config.type.ts:127](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L127) --- @@ -299,7 +310,7 @@ Defines which main look to use for the diagram. #### Defined in -[packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193) +[packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204) --- @@ -333,7 +344,7 @@ The maximum allowed size of the users text diagram #### Defined in -[packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184) +[packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195) --- @@ -343,7 +354,7 @@ The maximum allowed size of the users text diagram #### Defined in -[packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188) +[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199) --- @@ -353,7 +364,7 @@ The maximum allowed size of the users text diagram #### Defined in -[packages/mermaid/src/config.type.ts:180](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L180) +[packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190) --- @@ -363,7 +374,7 @@ The maximum allowed size of the users text diagram #### Defined in -[packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181) +[packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191) --- @@ -373,7 +384,7 @@ The maximum allowed size of the users text diagram #### Defined in -[packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183) +[packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193) --- @@ -383,7 +394,7 @@ The maximum allowed size of the users text diagram #### Defined in -[packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187) +[packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198) --- @@ -397,7 +408,7 @@ This prevents malicious graph directives from overriding a site's default securi #### Defined in -[packages/mermaid/src/config.type.ts:138](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L138) +[packages/mermaid/src/config.type.ts:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L148) --- @@ -409,7 +420,7 @@ Level of trust for parsed diagram #### Defined in -[packages/mermaid/src/config.type.ts:121](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L121) +[packages/mermaid/src/config.type.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L131) --- @@ -419,7 +430,7 @@ Level of trust for parsed diagram #### Defined in -[packages/mermaid/src/config.type.ts:173](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L173) +[packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183) --- @@ -431,7 +442,7 @@ Dictates whether mermaid starts on Page load #### Defined in -[packages/mermaid/src/config.type.ts:125](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L125) +[packages/mermaid/src/config.type.ts:135](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L135) --- @@ -441,7 +452,7 @@ Dictates whether mermaid starts on Page load #### Defined in -[packages/mermaid/src/config.type.ts:178](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L178) +[packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188) --- @@ -454,7 +465,7 @@ This is useful when you want to control how to handle syntax errors in your appl #### Defined in -[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199) +[packages/mermaid/src/config.type.ts:210](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L210) --- @@ -497,7 +508,7 @@ You may also use `themeCSS` to override this value. #### Defined in -[packages/mermaid/src/config.type.ts:176](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L176) +[packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186) --- @@ -507,7 +518,7 @@ You may also use `themeCSS` to override this value. #### Defined in -[packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191) +[packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202) --- @@ -517,4 +528,4 @@ You may also use `themeCSS` to override this value. #### Defined in -[packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182) +[packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192) diff --git a/docs/config/setup/interfaces/mermaid.RunOptions.md b/docs/config/setup/interfaces/mermaid.RunOptions.md index aae004d6d..0bcfc2356 100644 --- a/docs/config/setup/interfaces/mermaid.RunOptions.md +++ b/docs/config/setup/interfaces/mermaid.RunOptions.md @@ -18,7 +18,7 @@ The nodes to render. If this is set, `querySelector` will be ignored. #### Defined in -[packages/mermaid/src/mermaid.ts:48](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L48) +[packages/mermaid/src/mermaid.ts:49](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L49) --- @@ -44,7 +44,7 @@ A callback to call after each diagram is rendered. #### Defined in -[packages/mermaid/src/mermaid.ts:52](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L52) +[packages/mermaid/src/mermaid.ts:53](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L53) --- @@ -56,7 +56,7 @@ The query selector to use when finding elements to render. Default: `".mermaid"` #### Defined in -[packages/mermaid/src/mermaid.ts:44](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L44) +[packages/mermaid/src/mermaid.ts:45](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L45) --- @@ -68,4 +68,4 @@ If `true`, errors will be logged to the console, but not thrown. Default: `false #### Defined in -[packages/mermaid/src/mermaid.ts:56](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L56) +[packages/mermaid/src/mermaid.ts:57](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L57) diff --git a/docs/config/setup/modules/mermaid.md b/docs/config/setup/modules/mermaid.md index bdaeb05e1..60c219f2d 100644 --- a/docs/config/setup/modules/mermaid.md +++ b/docs/config/setup/modules/mermaid.md @@ -87,4 +87,4 @@ #### Defined in -[packages/mermaid/src/mermaid.ts:440](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L440) +[packages/mermaid/src/mermaid.ts:442](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L442) diff --git a/docs/ecosystem/integrations-community.md b/docs/ecosystem/integrations-community.md index 75f20dd1a..a9687359b 100644 --- a/docs/ecosystem/integrations-community.md +++ b/docs/ecosystem/integrations-community.md @@ -74,6 +74,7 @@ To add an integration to this list, see the [Integrations - create page](./integ - [Markdown for mermaid plugin](https://github.com/jamieh-mongolian/markdown-for-mermaid-plugin) - [redmine-mermaid](https://github.com/styz/redmine_mermaid) - Visual Studio Code [Polyglot Interactive Notebooks](https://github.com/dotnet/interactive#net-interactive) +- [Microsoft Loop](https://loop.cloud.microsoft) āœ… ### LLM integrations diff --git a/docs/ecosystem/tutorials.md b/docs/ecosystem/tutorials.md index 26498f090..6d7966c31 100644 --- a/docs/ecosystem/tutorials.md +++ b/docs/ecosystem/tutorials.md @@ -63,7 +63,7 @@ import matplotlib.pyplot as plt def mm(graph): graphbytes = graph.encode("utf8") - base64_bytes = base64.b64encode(graphbytes) + base64_bytes = base64.urlsafe_b64encode(graphbytes) base64_string = base64_bytes.decode("ascii") display(Image(url="https://mermaid.ink/img/" + base64_string)) diff --git a/docs/intro/index.md b/docs/intro/index.md index fda36f1da..5a71b45a4 100644 --- a/docs/intro/index.md +++ b/docs/intro/index.md @@ -55,6 +55,10 @@ For a more detailed introduction to Mermaid and some of its more basic uses, loo **Thanks to all involved, people committing pull requests, people answering questions and special thanks to Tyler Long who is helping me maintain the project šŸ™** +Our PR Visual Regression Testing is powered by [Argos](https://argos-ci.com/?utm_source=mermaid&utm_campaign=oss) with their generous Open Source plan. It makes the process of reviewing PRs with visual changes a breeze. + +[![Covered by Argos Visual Testing](https://argos-ci.com/badge-large.svg)](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss) + In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests. diff --git a/docs/intro/syntax-reference.md b/docs/intro/syntax-reference.md index 00330f21d..f736840e6 100644 --- a/docs/intro/syntax-reference.md +++ b/docs/intro/syntax-reference.md @@ -83,3 +83,139 @@ Allows for the limited reconfiguration of a diagram just before it is rendered. ### [Theme Manipulation](../config/theming.md) An application of using Directives to change [Themes](../config/theming.md). `Theme` is a value within Mermaid's configuration that dictates the color scheme for diagrams. + +### Layout and look + +We've restructured how Mermaid renders diagrams, enabling new features like selecting layout and look. **Currently, this is supported for flowcharts and state diagrams**, with plans to extend support to all diagram types. + +### Selecting Diagram Looks + +Mermaid offers a variety of styles or ā€œlooksā€ for your diagrams, allowing you to tailor the visual appearance to match your specific needs or preferences. Whether you prefer a hand-drawn or classic style, you can easily customize your diagrams. + +**Available Looks:** + +``` +• Hand-Drawn Look: For a more personal, creative touch, the hand-drawn look brings a sketch-like quality to your diagrams. This style is perfect for informal settings or when you want to add a bit of personality to your diagrams. +• Classic Look: If you prefer the traditional Mermaid style, the classic look maintains the original appearance that many users are familiar with. It’s great for consistency across projects or when you want to keep the familiar aesthetic. +``` + +**How to Select a Look:** + +You can select a look by adding the look parameter in the metadata section of your Mermaid diagram code. Here’s an example: + +```mermaid-example +--- +config: + look: handDrawn + theme: neutral +--- +flowchart LR + A[Start] --> B{Decision} + B -->|Yes| C[Continue] + B -->|No| D[Stop] +``` + +```mermaid +--- +config: + look: handDrawn + theme: neutral +--- +flowchart LR + A[Start] --> B{Decision} + B -->|Yes| C[Continue] + B -->|No| D[Stop] +``` + +#### Selecting Layout Algorithms + +In addition to customizing the look of your diagrams, Mermaid Chart now allows you to choose different layout algorithms to better organize and present your diagrams, especially when dealing with more complex structures. The layout algorithm dictates how nodes and edges are arranged on the page. + +#### Supported Layout Algorithms: + +``` +• Dagre (default): This is the classic layout algorithm that has been used in Mermaid for a long time. It provides a good balance of simplicity and visual clarity, making it ideal for most diagrams. +• ELK: For those who need more sophisticated layout capabilities, especially when working with large or intricate diagrams, the ELK (Eclipse Layout Kernel) layout offers advanced options. It provides a more optimized arrangement, potentially reducing overlapping and improving readability. This is not included out the box but needs to be added when integrating mermaid for sites/applications that want to have elk support. +``` + +#### How to Select a Layout Algorithm: + +You can specify the layout algorithm directly in the metadata section of your Mermaid diagram code. Here’s an example: + +```mermaid-example +--- +config: + layout: elk + look: handDrawn + theme: dark +--- +flowchart TB + A[Start] --> B{Decision} + B -->|Yes| C[Continue] + B -->|No| D[Stop] +``` + +```mermaid +--- +config: + layout: elk + look: handDrawn + theme: dark +--- +flowchart TB + A[Start] --> B{Decision} + B -->|Yes| C[Continue] + B -->|No| D[Stop] +``` + +In this example, the `layout: elk` line configures the diagram to use the ELK layout algorithm, along with the hand drawn look and forest theme. + +#### Customizing ELK Layout: + +When using the ELK layout, you can further refine the diagram’s configuration, such as how nodes are placed and whether parallel edges should be combined: + +- To combine parallel edges, use mergeEdges: true | false. +- To configure node placement, use nodePlacementStrategy with the following options: + - SIMPLE + - NETWORK_SIMPLEX + - LINEAR_SEGMENTS + - BRANDES_KOEPF (default) + +**Example configuration:** + +``` +--- +config: + layout: elk + elk: + mergeEdges: true + nodePlacementStrategy: LINEAR_SEGMENTS +--- +flowchart LR + A[Start] --> B{Choose Path} + B -->|Option 1| C[Path 1] + B -->|Option 2| D[Path 2] + +#### Using Dagre Layout with Classic Look: +``` + +Another example: + +``` +--- +config: + layout: dagre + look: classic + theme: default +--- + +flowchart LR +A[Start] --> B{Choose Path} +B -->|Option 1| C[Path 1] +B -->|Option 2| D[Path 2] + +``` + +These options give you the flexibility to create diagrams that not only look great but are also arranged to best suit your data’s structure and flow. + +When integrating Mermaid, you can include look and layout configuration with the initialize call. This is also where you add the loading of elk. diff --git a/docs/news/blog.md b/docs/news/blog.md index eede934e8..4d33f67f5 100644 --- a/docs/news/blog.md +++ b/docs/news/blog.md @@ -12,6 +12,24 @@ Discover the fresh new and unique Neo and Hand-Drawn looks for Mermaid Diagrams, while still offering the classic look you love. +## [Mermaid v11 is out!](https://www.mermaidchart.com/blog/posts/mermaid-v11/) + +23 August 2024 Ā· 2 mins + +Mermaid v11 introduces advanced layout options, new diagram types, and enhanced customization features, thanks to the incredible contributions from our community. + +## [Mermaid Innovation - Introducing New Looks for Mermaid Diagrams](https://www.mermaidchart.com/blog/posts/mermaid-innovation-introducing-new-looks-for-mermaid-diagrams/) + +6 August 2024 Ā·3 mins + +Discover the fresh new and unique Neo and Hand-Drawn looks for Mermaid Diagrams, while still offering the classic look you love. + +## [The Mermaid Chart Plugin for Jira: A How-To User Guide](https://www.mermaidchart.com/blog/posts/the-mermaid-chart-plugin-for-jira-a-how-to-user-guide/) + +31 July 2024 Ā· 5 mins + +The Mermaid Chart plugin for Jira has arrived! + ## [Mermaid AI Is Here to Change the Game For Diagram Creation](https://www.mermaidchart.com/blog/posts/mermaid-ai-is-here-to-change-the-game-for-diagram-creation/) 22 July 2024 Ā· 5 mins diff --git a/docs/syntax/architecture.md b/docs/syntax/architecture.md new file mode 100644 index 000000000..12a7cf409 --- /dev/null +++ b/docs/syntax/architecture.md @@ -0,0 +1,275 @@ +> **Warning** +> +> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. +> +> ## Please edit the corresponding file in [/packages/mermaid/src/docs/syntax/architecture.md](../../packages/mermaid/src/docs/syntax/architecture.md). + +# Architecture Diagrams Documentation (v11.1.0+) + +> In the context of mermaid-js, the architecture diagram is used to show the relationship between services and resources commonly found within the Cloud or CI/CD deployments. In an architecture diagram, services (nodes) are connected by edges. Related services can be placed within groups to better illustrate how they are organized. + +## Example + +```mermaid-example +architecture-beta + group api(cloud)[API] + + service db(database)[Database] in api + service disk1(disk)[Storage] in api + service disk2(disk)[Storage] in api + service server(server)[Server] in api + + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db +``` + +```mermaid +architecture-beta + group api(cloud)[API] + + service db(database)[Database] in api + service disk1(disk)[Storage] in api + service disk2(disk)[Storage] in api + service server(server)[Server] in api + + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db +``` + +## Syntax + +The building blocks of an architecture are `groups`, `services`, `edges`, and `junctions`. + +For supporting components, icons are declared by surrounding the icon name with `()`, while labels are declared by surrounding the text with `[]`. + +To begin an architecture diagram, use the keyword `architecture-beta`, followed by your groups, services, edges, and junctions. While each of the 3 building blocks can be declared in any order, care must be taken to ensure the identifier was previously declared by another component. + +### Groups + +The syntax for declaring a group is: + +``` +group {group id}({icon name})[{title}] (in {parent id})? +``` + +Put together: + +``` +group public_api(cloud)[Public API] +``` + +creates a group identified as `public_api`, uses the icon `cloud`, and has the label `Public API`. + +Additionally, groups can be placed within a group using the optional `in` keyword + +``` +group private_api(cloud)[Private API] in public_api +``` + +### Services + +The syntax for declaring a service is: + +``` +service {service id}({icon name})[{title}] (in {parent id})? +``` + +Put together: + +``` +service database(db)[Database] +``` + +creates the service identified as `database`, using the icon `db`, with the label `Database`. + +If the service belongs to a group, it can be placed inside it through the optional `in` keyword + +``` +service database(db)[Database] in private_api +``` + +### Edges + +The syntax for declaring an edge is: + +``` +{serviceId}{{group}}?:{T|B|L|R} {<}?--{>}? {T|B|L|R}:{serviceId}{{group}}? +``` + +#### Edge Direction + +The side of the service the edge comes out of is specified by adding a colon (`:`) to the side of the service connecting to the arrow and adding `L|R|T|B` + +For example: + +``` +db:R -- L:server +``` + +creates an edge between the services `db` and `server`, with the edge coming out of the right of `db` and the left of `server`. + +``` +db:T -- L:server +``` + +creates a 90 degree edge between the services `db` and `server`, with the edge coming out of the top of `db` and the left of `server`. + +#### Arrows + +Arrows can be added to each side of an edge by adding `<` before the direction on the left, and/or `>` after the direction on the right. + +For example: + +``` +subnet:R --> L:gateway +``` + +creates an edge with the arrow going into the `gateway` service + +#### Edges out of Groups + +To have an edge go from a group to another group or service within another group, the `{group}` modifier can be added after the `serviceId`. + +For example: + +``` +service server[Server] in groupOne +service subnet[Subnet] in groupTwo + +server{group}:B --> T:subnet{group} +``` + +creates an edge going out of `groupOne`, adjacent to `server`, and into `groupTwo`, adjacent to `subnet`. + +It's important to note that `groupId`s cannot be used for specifying edges and the `{group}` modifier can only be used for services within a group. + +### Junctions + +Junctions are a special type of node which acts as a potential 4-way split between edges. + +The syntax for declaring a junction is: + +``` +junction {junction id} (in {parent id})? +``` + +```mermaid-example +architecture-beta + service left_disk(disk)[Disk] + service top_disk(disk)[Disk] + service bottom_disk(disk)[Disk] + service top_gateway(internet)[Gateway] + service bottom_gateway(internet)[Gateway] + junction junctionCenter + junction junctionRight + + left_disk:R -- L:junctionCenter + top_disk:B -- T:junctionCenter + bottom_disk:T -- B:junctionCenter + junctionCenter:R -- L:junctionRight + top_gateway:B -- T:junctionRight + bottom_gateway:T -- B:junctionRight +``` + +```mermaid +architecture-beta + service left_disk(disk)[Disk] + service top_disk(disk)[Disk] + service bottom_disk(disk)[Disk] + service top_gateway(internet)[Gateway] + service bottom_gateway(internet)[Gateway] + junction junctionCenter + junction junctionRight + + left_disk:R -- L:junctionCenter + top_disk:B -- T:junctionCenter + bottom_disk:T -- B:junctionCenter + junctionCenter:R -- L:junctionRight + top_gateway:B -- T:junctionRight + bottom_gateway:T -- B:junctionRight +``` + +## Icons + +By default, architecture diagram supports the following icons: `cloud`, `database`, `disk`, `internet`, `server`. +Users can use any of the 200,000+ icons available in iconify.design, or add their own custom icons, by following the steps below. + +The icon packs available can be found at [icones.js.org](https://icones.js.org/). +We use the name defined when registering the icon pack, to override the prefix field of the iconify pack. This allows the user to use shorter names for the icons. It also allows us to load a particular pack only when it is used in a diagram. + +Using JSON file directly from CDN: + +```js +import mermaid from 'CDN/mermaid.esm.mjs'; +mermaid.registerIconPacks([ + { + name: 'logos', + loader: () => + fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()), + }, +]); +``` + +Using packages and a bundler: + +```bash +npm install @iconify-json/logos +``` + +With lazy loading + +```js +import mermaid from 'mermaid'; + +mermaid.registerIconPacks([ + { + name: 'logos', + loader: () => import('@iconify-json/logos').then((module) => module.icons), + }, +]); +``` + +Without lazy loading + +```js +import mermaid from 'mermaid'; +import { icons } from '@iconify-json/logos'; +mermaid.registerIconPacks([ + { + name: icons.prefix, // To use the prefix defined in the icon pack + icons, + }, +]); +``` + +After the icons are installed, they can be used in the architecture diagram by using the format "name:icon-name", where name is the value used when registering the icon pack. + +```mermaid-example +architecture-beta + group api(logos:aws-lambda)[API] + + service db(logos:aws-aurora)[Database] in api + service disk1(logos:aws-glacier)[Storage] in api + service disk2(logos:aws-s3)[Storage] in api + service server(logos:aws-ec2)[Server] in api + + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db +``` + +```mermaid +architecture-beta + group api(logos:aws-lambda)[API] + + service db(logos:aws-aurora)[Database] in api + service disk1(logos:aws-glacier)[Storage] in api + service disk2(logos:aws-s3)[Storage] in api + service server(logos:aws-ec2)[Server] in api + + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db +``` diff --git a/docs/syntax/entityRelationshipDiagram.md b/docs/syntax/entityRelationshipDiagram.md index ae84d1e9e..693cb53c7 100644 --- a/docs/syntax/entityRelationshipDiagram.md +++ b/docs/syntax/entityRelationshipDiagram.md @@ -286,6 +286,7 @@ erDiagram - If you want the relationship label to be more than one word, you must use double quotes around the phrase - If you don't want a label at all on a relationship, you must use an empty double-quoted string +- (v11.1.0+) If you want a multi-line label on a relationship, use `
    ` between the two lines (`"first line
    second line"`) ## Styling diff --git a/package.json b/package.json index f0045f7ae..5683b6ed9 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "10.2.4", "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "type": "module", - "packageManager": "pnpm@9.7.1+sha512.faf344af2d6ca65c4c5c8c2224ea77a81a5e8859cbc4e06b1511ddce2f0151512431dd19e6aff31f2c6a8f5f2aced9bd2273e1fed7dd4de1868984059d2c4247", + "packageManager": "pnpm@9.9.0+sha512.60c18acd138bff695d339be6ad13f7e936eea6745660d4cc4a776d5247c540d0edee1a563695c183a66eb917ef88f2b4feb1fc25f32a7adcadc7aaf3438e99c1", "keywords": [ "diagram", "markdown", @@ -42,7 +42,7 @@ "test": "pnpm lint && vitest run", "test:watch": "vitest --watch", "test:coverage": "vitest --coverage", - "prepare": "husky install && pnpm build", + "prepare": "husky && pnpm build", "pre-commit": "lint-staged" }, "repository": { @@ -88,7 +88,7 @@ "cpy-cli": "^5.0.0", "cross-env": "^7.0.3", "cspell": "^8.6.0", - "cypress": "^13.11.0", + "cypress": "^13.14.1", "cypress-image-snapshot": "^4.0.1", "esbuild": "^0.21.5", "eslint": "^9.4.0", @@ -116,7 +116,6 @@ "markdown-table": "^3.0.3", "nyc": "^15.1.0", "path-browserify": "^1.0.1", - "pnpm": "^8.15.5", "prettier": "^3.2.5", "prettier-plugin-jsdoc": "^1.3.0", "rimraf": "^5.0.5", diff --git a/packages/mermaid-layout-elk/CHANGELOG.md b/packages/mermaid-layout-elk/CHANGELOG.md new file mode 100644 index 000000000..9588e7885 --- /dev/null +++ b/packages/mermaid-layout-elk/CHANGELOG.md @@ -0,0 +1,25 @@ +# @mermaid-js/layout-elk + +## 0.1.3 + +### Patch Changes + +- [#5810](https://github.com/mermaid-js/mermaid/pull/5810) [`33a809f`](https://github.com/mermaid-js/mermaid/commit/33a809f09a9aa1f84ba06201ab550bad81c3ff65) Thanks [@knsv](https://github.com/knsv)! - fix: Updates to the default elk configuration + feat: exposing cycleBreakingStrategy to the configuration so that it can be modified suing the configuration. +- Updated dependencies [[`6ecdf7b`](https://github.com/mermaid-js/mermaid/commit/6ecdf7be688efdc53c52fea3ba891327242bc890), [`28bd07f`](https://github.com/mermaid-js/mermaid/commit/28bd07fdeb4fc981107d21317ec6160b31f80116), [`8e640da`](https://github.com/mermaid-js/mermaid/commit/8e640da5436e8ae013b11b1c1821a9afcc15d0d3), [`256a148`](https://github.com/mermaid-js/mermaid/commit/256a148bbf484fc7db6c19f94dd69d5d268ee048), [`16faef4`](https://github.com/mermaid-js/mermaid/commit/16faef4613b91a7d3a98a1563c25b57f9238acc7)]: + - mermaid@11.1.0 + +## 0.1.2 + +### Patch Changes + +- [#5761](https://github.com/mermaid-js/mermaid/pull/5761) [`b34dfe8`](https://github.com/mermaid-js/mermaid/commit/b34dfe8f45eded31da10965ced7ea40fde1ca76c) Thanks [@sidharthv96](https://github.com/sidharthv96)! - Fix type file path + +## 0.1.1 + +### Patch Changes + +- [#5758](https://github.com/mermaid-js/mermaid/pull/5758) [`501a55d`](https://github.com/mermaid-js/mermaid/commit/501a55d8f225901ba345c498dec4298490a0196e) Thanks [@sidharthv96](https://github.com/sidharthv96)! - fix: Types path + +- Updated dependencies [[`5deaef4`](https://github.com/mermaid-js/mermaid/commit/5deaef456e74d796866431c26f69360e4e74dbff)]: + - mermaid@11.0.2 diff --git a/packages/mermaid-layout-elk/package.json b/packages/mermaid-layout-elk/package.json index 78ad309b8..673702be7 100644 --- a/packages/mermaid-layout-elk/package.json +++ b/packages/mermaid-layout-elk/package.json @@ -1,14 +1,14 @@ { "name": "@mermaid-js/layout-elk", - "version": "0.1.0", + "version": "0.1.3", "description": "ELK layout engine for mermaid", "module": "dist/mermaid-layout-elk.core.mjs", - "types": "dist/packages/mermaid-layout-elk/src/index.d.ts", + "types": "dist/layouts.d.ts", "type": "module", "exports": { ".": { "import": "./dist/mermaid-layout-elk.core.mjs", - "types": "./dist/packages/mermaid-layout-elk/src/index.d.ts" + "types": "./dist/layouts.d.ts" }, "./*": "./*" }, diff --git a/packages/mermaid-layout-elk/src/render.ts b/packages/mermaid-layout-elk/src/render.ts index 117ca6276..7ac43bb7f 100644 --- a/packages/mermaid-layout-elk/src/render.ts +++ b/packages/mermaid-layout-elk/src/render.ts @@ -752,14 +752,34 @@ export const render = async ( 'nodePlacement.strategy': data4Layout.config.elk.nodePlacementStrategy, 'elk.layered.mergeEdges': data4Layout.config.elk.mergeEdges, 'elk.direction': 'DOWN', - 'spacing.baseValue': 30, - // 'spacing.nodeNode': 40, - // 'spacing.nodeNodeBetweenLayers': 45, - // 'spacing.edgeNode': 40, - // 'spacing.edgeNodeBetweenLayers': 30, - // 'spacing.edgeEdge': 30, - // 'spacing.edgeEdgeBetweenLayers': 40, - // 'spacing.nodeSelfLoop': 50, + 'spacing.baseValue': 35, + 'elk.layered.unnecessaryBendpoints': true, + 'elk.layered.cycleBreaking.strategy': data4Layout.config.elk.cycleBreakingStrategy, + // 'spacing.nodeNode': 20, + // 'spacing.nodeNodeBetweenLayers': 25, + // 'spacing.edgeNode': 20, + // 'spacing.edgeNodeBetweenLayers': 10, + // 'spacing.edgeEdge': 10, + // 'spacing.edgeEdgeBetweenLayers': 20, + // 'spacing.nodeSelfLoop': 20, + + // Tweaking options + // 'elk.layered.nodePlacement.favorStraightEdges': true, + // 'nodePlacement.feedbackEdges': true, + // 'elk.layered.wrapping.multiEdge.improveCuts': true, + // 'elk.layered.wrapping.multiEdge.improveWrappedEdges': true, + // 'elk.layered.wrapping.strategy': 'MULTI_EDGE', + // 'elk.layered.edgeRouting.selfLoopDistribution': 'EQUALLY', + // 'elk.layered.mergeHierarchyEdges': true, + // 'elk.layered.feedbackEdges': true, + // 'elk.layered.crossingMinimization.semiInteractive': true, + // 'elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor': 1, + // 'elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth': 4.0, + // 'elk.layered.wrapping.validify.strategy': 'LOOK_BACK', + // 'elk.insideSelfLoops.activate': true, + // 'elk.alg.layered.options.EdgeStraighteningStrategy': 'NONE', + // 'elk.layered.considerModelOrder.strategy': 'NODES_AND_EDGES', // NODES_AND_EDGES + // 'elk.layered.wrapping.cutting.strategy': 'ARD', // NODES_AND_EDGES }, children: [], edges: [], diff --git a/packages/mermaid/CHANGELOG.md b/packages/mermaid/CHANGELOG.md index fe7360f81..d7221d232 100644 --- a/packages/mermaid/CHANGELOG.md +++ b/packages/mermaid/CHANGELOG.md @@ -1,5 +1,42 @@ # mermaid +## 11.1.0 + +### Minor Changes + +- [#5793](https://github.com/mermaid-js/mermaid/pull/5793) [`6ecdf7b`](https://github.com/mermaid-js/mermaid/commit/6ecdf7be688efdc53c52fea3ba891327242bc890) Thanks [@sidharthv96](https://github.com/sidharthv96)! - feat: Add support for iconify icons + +- [#5711](https://github.com/mermaid-js/mermaid/pull/5711) [`8e640da`](https://github.com/mermaid-js/mermaid/commit/8e640da5436e8ae013b11b1c1821a9afcc15d0d3) Thanks [@NicolasNewman](https://github.com/NicolasNewman)! - feat(er): allow multi-line relationship labels + +- [#5452](https://github.com/mermaid-js/mermaid/pull/5452) [`256a148`](https://github.com/mermaid-js/mermaid/commit/256a148bbf484fc7db6c19f94dd69d5d268ee048) Thanks [@NicolasNewman](https://github.com/NicolasNewman)! - New Diagram: Architecture + + Adds architecture diagrams which allows users to show relations between services. + +### Patch Changes + +- [#5810](https://github.com/mermaid-js/mermaid/pull/5810) [`28bd07f`](https://github.com/mermaid-js/mermaid/commit/28bd07fdeb4fc981107d21317ec6160b31f80116) Thanks [@knsv](https://github.com/knsv)! - Fix for self loops in cluster + Supporting legacy defaultRenderer directive + +- [#5789](https://github.com/mermaid-js/mermaid/pull/5789) [`16faef4`](https://github.com/mermaid-js/mermaid/commit/16faef4613b91a7d3a98a1563c25b57f9238acc7) Thanks [@sidharthv96](https://github.com/sidharthv96)! - chore: Move icons to architecture, remove full icon sets to reduce bundle size + +- Updated dependencies [[`256a148`](https://github.com/mermaid-js/mermaid/commit/256a148bbf484fc7db6c19f94dd69d5d268ee048), [`7d8143b`](https://github.com/mermaid-js/mermaid/commit/7d8143b917ee3562149a0e0a821ed2d6f29cc05d)]: + - @mermaid-js/parser@0.3.0 + +## 11.0.2 + +### Patch Changes + +- [#5664](https://github.com/mermaid-js/mermaid/pull/5664) [`5deaef4`](https://github.com/mermaid-js/mermaid/commit/5deaef456e74d796866431c26f69360e4e74dbff) Thanks [@Austin-Fulbright](https://github.com/Austin-Fulbright)! - chore: Migrate git graph to langium, use typescript for internals + +- Updated dependencies [[`5deaef4`](https://github.com/mermaid-js/mermaid/commit/5deaef456e74d796866431c26f69360e4e74dbff)]: + - @mermaid-js/parser@0.2.0 + +## 11.0.1 + +### Patch Changes + +- [#2](https://github.com/calvinvette/mermaid/pull/2) [`bf05d87`](https://github.com/mermaid-js/mermaid/commit/bf05d8781edacb580fdb053da167e968b7570117) Thanks [@calvinvette](https://github.com/calvinvette)! - test changeset + ## 11.0.2 ### Patch Changes diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json index 8956eb1e5..317150788 100644 --- a/packages/mermaid/package.json +++ b/packages/mermaid/package.json @@ -1,6 +1,6 @@ { "name": "mermaid", - "version": "11.0.2", + "version": "11.1.0", "description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.", "type": "module", "module": "./dist/mermaid.core.mjs", @@ -68,9 +68,11 @@ }, "dependencies": { "@braintree/sanitize-url": "^7.0.1", + "@iconify/utils": "^2.1.32", "@mermaid-js/parser": "workspace:^", "cytoscape": "^3.29.2", "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.10", @@ -87,7 +89,9 @@ }, "devDependencies": { "@adobe/jsonschema2md": "^8.0.0", + "@iconify/types": "^2.0.0", "@types/cytoscape": "^3.21.4", + "@types/cytoscape-fcose": "^2.2.4", "@types/d3": "^7.4.3", "@types/d3-sankey": "^0.12.4", "@types/d3-scale": "^4.0.8", diff --git a/packages/mermaid/scripts/update-release-version.mts b/packages/mermaid/scripts/update-release-version.mts index a5943b37b..0459d3444 100644 --- a/packages/mermaid/scripts/update-release-version.mts +++ b/packages/mermaid/scripts/update-release-version.mts @@ -5,23 +5,34 @@ * So contributors adding new features will only have to add the placeholder and not worry about updating the version number. * */ +import { readFile, writeFile } from 'fs/promises'; import { posix } from 'path'; import { - getGlobs, getFilesFromGlobs, - SOURCE_DOCS_DIR, - readSyncedUTF8file, + getGlobs, MERMAID_RELEASE_VERSION, + readSyncedUTF8file, + SOURCE_DOCS_DIR, } from './docs.mjs'; -import { writeFile } from 'fs/promises'; const verifyOnly: boolean = process.argv.includes('--verify'); const versionPlaceholder = ''; +const verifyDocumentation = async () => { + const fileContent = await readFile('./src/docs/community/contributing.md', 'utf-8'); + if (!fileContent.includes(versionPlaceholder)) { + console.error( + `The placeholder ${versionPlaceholder} is not present in the contributing.md file.` + ); + process.exit(1); + } +}; + const main = async () => { + await verifyDocumentation(); const sourceDirGlob = posix.join('.', SOURCE_DOCS_DIR, '**'); const mdFileGlobs = getGlobs([posix.join(sourceDirGlob, '*.md')]); - mdFileGlobs.push('!**/community/development.md', '!**/community/code.md'); + mdFileGlobs.push('!**/community/contributing.md'); const mdFiles = await getFilesFromGlobs(mdFileGlobs); mdFiles.sort(); const mdFilesWithPlaceholder: string[] = []; diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index 972f85bc4..035a158e0 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -99,6 +99,16 @@ export interface MermaidConfig { * */ nodePlacementStrategy?: 'SIMPLE' | 'NETWORK_SIMPLEX' | 'LINEAR_SEGMENTS' | 'BRANDES_KOEPF'; + /** + * This strategy decides how to find cycles in the graph and deciding which edges need adjustment to break loops. + * + */ + cycleBreakingStrategy?: + | 'GREEDY' + | 'DEPTH_FIRST' + | 'INTERACTIVE' + | 'MODEL_ORDER' + | 'GREEDY_MODEL_ORDER'; }; darkMode?: boolean; htmlLabels?: boolean; @@ -181,6 +191,7 @@ export interface MermaidConfig { quadrantChart?: QuadrantChartConfig; xyChart?: XYChartConfig; requirement?: RequirementDiagramConfig; + architecture?: ArchitectureDiagramConfig; mindmap?: MindmapDiagramConfig; gitGraph?: GitGraphDiagramConfig; c4?: C4DiagramConfig; @@ -991,6 +1002,17 @@ export interface RequirementDiagramConfig extends BaseDiagramConfig { rect_padding?: number; line_height?: number; } +/** + * The object containing configurations specific for architecture diagrams + * + * This interface was referenced by `MermaidConfig`'s JSON-Schema + * via the `definition` "ArchitectureDiagramConfig". + */ +export interface ArchitectureDiagramConfig extends BaseDiagramConfig { + padding?: number; + iconSize?: number; + fontSize?: number; +} /** * The object containing configurations specific for mindmap diagrams * diff --git a/packages/mermaid/src/diagram-api/diagram-orchestration.ts b/packages/mermaid/src/diagram-api/diagram-orchestration.ts index 55d05c9aa..d68a1c498 100644 --- a/packages/mermaid/src/diagram-api/diagram-orchestration.ts +++ b/packages/mermaid/src/diagram-api/diagram-orchestration.ts @@ -22,6 +22,7 @@ import mindmap from '../diagrams/mindmap/detector.js'; import sankey from '../diagrams/sankey/sankeyDetector.js'; import { packet } from '../diagrams/packet/detector.js'; import block from '../diagrams/block/blockDetector.js'; +import architecture from '../diagrams/architecture/architectureDetector.js'; import { registerLazyLoadedDiagrams } from './detectType.js'; import { registerDiagram } from './diagramAPI.js'; @@ -90,6 +91,7 @@ export const addDiagrams = () => { sankey, packet, xychart, - block + block, + architecture ); }; diff --git a/packages/mermaid/src/diagrams/architecture/architectureDb.ts b/packages/mermaid/src/diagrams/architecture/architectureDb.ts new file mode 100644 index 000000000..93fa71ca3 --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureDb.ts @@ -0,0 +1,333 @@ +import type { ArchitectureDiagramConfig } from '../../config.type.js'; +import DEFAULT_CONFIG from '../../defaultConfig.js'; +import { getConfig } from '../../diagram-api/diagramAPI.js'; +import type { D3Element } from '../../types.js'; +import { ImperativeState } from '../../utils/imperativeState.js'; +import { + clear as commonClear, + getAccDescription, + getAccTitle, + getDiagramTitle, + setAccDescription, + setAccTitle, + setDiagramTitle, +} from '../common/commonDb.js'; +import type { + ArchitectureDB, + ArchitectureDirectionPair, + ArchitectureDirectionPairMap, + ArchitectureEdge, + ArchitectureGroup, + ArchitectureJunction, + ArchitectureNode, + ArchitectureService, + ArchitectureSpatialMap, + ArchitectureState, +} from './architectureTypes.js'; +import { + getArchitectureDirectionPair, + isArchitectureDirection, + isArchitectureJunction, + isArchitectureService, + shiftPositionByArchitectureDirectionPair, +} from './architectureTypes.js'; + +const DEFAULT_ARCHITECTURE_CONFIG: Required = + DEFAULT_CONFIG.architecture; + +const state = new ImperativeState(() => ({ + nodes: {}, + groups: {}, + edges: [], + registeredIds: {}, + config: DEFAULT_ARCHITECTURE_CONFIG, + dataStructures: undefined, + elements: {}, +})); + +const clear = (): void => { + state.reset(); + commonClear(); +}; + +const addService = function ({ + id, + icon, + in: parent, + title, + iconText, +}: Omit) { + if (state.records.registeredIds[id] !== undefined) { + throw new Error( + `The service id [${id}] is already in use by another ${state.records.registeredIds[id]}` + ); + } + if (parent !== undefined) { + if (id === parent) { + throw new Error(`The service [${id}] cannot be placed within itself`); + } + if (state.records.registeredIds[parent] === undefined) { + throw new Error( + `The service [${id}]'s parent does not exist. Please make sure the parent is created before this service` + ); + } + if (state.records.registeredIds[parent] === 'node') { + throw new Error(`The service [${id}]'s parent is not a group`); + } + } + + state.records.registeredIds[id] = 'node'; + + state.records.nodes[id] = { + id, + type: 'service', + icon, + iconText, + title, + edges: [], + in: parent, + }; +}; + +const getServices = (): ArchitectureService[] => + Object.values(state.records.nodes).filter(isArchitectureService); + +const addJunction = function ({ id, in: parent }: Omit) { + state.records.registeredIds[id] = 'node'; + + state.records.nodes[id] = { + id, + type: 'junction', + edges: [], + in: parent, + }; +}; + +const getJunctions = (): ArchitectureJunction[] => + Object.values(state.records.nodes).filter(isArchitectureJunction); + +const getNodes = (): ArchitectureNode[] => Object.values(state.records.nodes); + +const getNode = (id: string): ArchitectureNode | null => state.records.nodes[id]; + +const addGroup = function ({ id, icon, in: parent, title }: ArchitectureGroup) { + if (state.records.registeredIds[id] !== undefined) { + throw new Error( + `The group id [${id}] is already in use by another ${state.records.registeredIds[id]}` + ); + } + if (parent !== undefined) { + if (id === parent) { + throw new Error(`The group [${id}] cannot be placed within itself`); + } + if (state.records.registeredIds[parent] === undefined) { + throw new Error( + `The group [${id}]'s parent does not exist. Please make sure the parent is created before this group` + ); + } + if (state.records.registeredIds[parent] === 'node') { + throw new Error(`The group [${id}]'s parent is not a group`); + } + } + + state.records.registeredIds[id] = 'group'; + + state.records.groups[id] = { + id, + icon, + title, + in: parent, + }; +}; +const getGroups = (): ArchitectureGroup[] => { + return Object.values(state.records.groups); +}; + +const addEdge = function ({ + lhsId, + rhsId, + lhsDir, + rhsDir, + lhsInto, + rhsInto, + lhsGroup, + rhsGroup, + title, +}: ArchitectureEdge) { + if (!isArchitectureDirection(lhsDir)) { + throw new Error( + `Invalid direction given for left hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${lhsDir}` + ); + } + if (!isArchitectureDirection(rhsDir)) { + throw new Error( + `Invalid direction given for right hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${rhsDir}` + ); + } + + if (state.records.nodes[lhsId] === undefined && state.records.groups[lhsId] === undefined) { + throw new Error( + `The left-hand id [${lhsId}] does not yet exist. Please create the service/group before declaring an edge to it.` + ); + } + if (state.records.nodes[rhsId] === undefined && state.records.groups[lhsId] === undefined) { + throw new Error( + `The right-hand id [${rhsId}] does not yet exist. Please create the service/group before declaring an edge to it.` + ); + } + + const lhsGroupId = state.records.nodes[lhsId].in; + const rhsGroupId = state.records.nodes[rhsId].in; + if (lhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) { + throw new Error( + `The left-hand id [${lhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.` + ); + } + if (rhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) { + throw new Error( + `The right-hand id [${rhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.` + ); + } + + const edge = { + lhsId, + lhsDir, + lhsInto, + lhsGroup, + rhsId, + rhsDir, + rhsInto, + rhsGroup, + title, + }; + + state.records.edges.push(edge); + if (state.records.nodes[lhsId] && state.records.nodes[rhsId]) { + state.records.nodes[lhsId].edges.push(state.records.edges[state.records.edges.length - 1]); + state.records.nodes[rhsId].edges.push(state.records.edges[state.records.edges.length - 1]); + } +}; + +const getEdges = (): ArchitectureEdge[] => state.records.edges; + +/** + * Returns the current diagram's adjacency list & spatial map. + * If they have not been created, run the algorithms to generate them. + * @returns + */ +const getDataStructures = () => { + if (state.records.dataStructures === undefined) { + // Create an adjacency list of the diagram to perform BFS on + // Outer reduce applied on all services + // Inner reduce applied on the edges for a service + const adjList = Object.entries(state.records.nodes).reduce< + Record + >((prevOuter, [id, service]) => { + prevOuter[id] = service.edges.reduce((prevInner, edge) => { + if (edge.lhsId === id) { + // source is LHS + const pair = getArchitectureDirectionPair(edge.lhsDir, edge.rhsDir); + if (pair) { + prevInner[pair] = edge.rhsId; + } + } else { + // source is RHS + const pair = getArchitectureDirectionPair(edge.rhsDir, edge.lhsDir); + if (pair) { + prevInner[pair] = edge.lhsId; + } + } + return prevInner; + }, {}); + return prevOuter; + }, {}); + + // Configuration for the initial pass of BFS + const firstId = Object.keys(adjList)[0]; + const visited = { [firstId]: 1 }; + const notVisited = Object.keys(adjList).reduce( + (prev, id) => (id === firstId ? prev : { ...prev, [id]: 1 }), + {} as Record + ); + + // Perform BFS on the adjacency list + const BFS = (startingId: string): ArchitectureSpatialMap => { + const spatialMap = { [startingId]: [0, 0] }; + const queue = [startingId]; + while (queue.length > 0) { + const id = queue.shift(); + if (id) { + visited[id] = 1; + delete notVisited[id]; + const adj = adjList[id]; + const [posX, posY] = spatialMap[id]; + Object.entries(adj).forEach(([dir, rhsId]) => { + if (!visited[rhsId]) { + spatialMap[rhsId] = shiftPositionByArchitectureDirectionPair( + [posX, posY], + dir as ArchitectureDirectionPair + ); + queue.push(rhsId); + } + }); + } + } + return spatialMap; + }; + const spatialMaps = [BFS(firstId)]; + + // If our diagram is disconnected, keep adding additional spatial maps until all disconnected graphs have been found + while (Object.keys(notVisited).length > 0) { + spatialMaps.push(BFS(Object.keys(notVisited)[0])); + } + state.records.dataStructures = { + adjList, + spatialMaps, + }; + } + return state.records.dataStructures; +}; + +const setElementForId = (id: string, element: D3Element) => { + state.records.elements[id] = element; +}; +const getElementById = (id: string) => state.records.elements[id]; + +export const db: ArchitectureDB = { + clear, + setDiagramTitle, + getDiagramTitle, + setAccTitle, + getAccTitle, + setAccDescription, + getAccDescription, + + addService, + getServices, + addJunction, + getJunctions, + getNodes, + getNode, + addGroup, + getGroups, + addEdge, + getEdges, + setElementForId, + getElementById, + getDataStructures, +}; + +/** + * Typed wrapper for resolving an architecture diagram's config fields. Returns the default value if undefined + * @param field - the config field to access + * @returns + */ +export function getConfigField( + field: T +): Required[T] { + const arch = getConfig().architecture; + if (arch?.[field]) { + return arch[field] as Required[T]; + } + return DEFAULT_ARCHITECTURE_CONFIG[field]; +} diff --git a/packages/mermaid/src/diagrams/architecture/architectureDetector.ts b/packages/mermaid/src/diagrams/architecture/architectureDetector.ts new file mode 100644 index 000000000..c15b474ab --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureDetector.ts @@ -0,0 +1,24 @@ +import type { + DiagramDetector, + DiagramLoader, + ExternalDiagramDefinition, +} from '../../diagram-api/types.js'; + +const id = 'architecture'; + +const detector: DiagramDetector = (txt) => { + return /^\s*architecture/.test(txt); +}; + +const loader: DiagramLoader = async () => { + const { diagram } = await import('./architectureDiagram.js'); + return { id, diagram }; +}; + +const architecture: ExternalDiagramDefinition = { + id, + detector, + loader, +}; + +export default architecture; diff --git a/packages/mermaid/src/diagrams/architecture/architectureDiagram.ts b/packages/mermaid/src/diagrams/architecture/architectureDiagram.ts new file mode 100644 index 000000000..82dacd3e1 --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureDiagram.ts @@ -0,0 +1,12 @@ +import type { DiagramDefinition } from '../../diagram-api/types.js'; +import { parser } from './architectureParser.js'; +import { db } from './architectureDb.js'; +import styles from './architectureStyles.js'; +import { renderer } from './architectureRenderer.js'; + +export const diagram: DiagramDefinition = { + parser, + db, + renderer, + styles, +}; diff --git a/packages/mermaid/src/diagrams/architecture/architectureIcons.ts b/packages/mermaid/src/diagrams/architecture/architectureIcons.ts new file mode 100644 index 000000000..dd6c99f9c --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureIcons.ts @@ -0,0 +1,43 @@ +import { unknownIcon } from '$root/rendering-util/icons.js'; +import type { IconifyJSON } from '@iconify/types'; + +const wrapIcon = (icon: string) => { + return `${icon}`; +}; + +export const architectureIcons: IconifyJSON = { + prefix: 'mermaid-architecture', + height: 80, + width: 80, + icons: { + database: { + body: wrapIcon( + '' + ), + }, + server: { + body: wrapIcon( + '' + ), + }, + disk: { + body: wrapIcon( + '' + ), + }, + internet: { + body: wrapIcon( + '' + ), + }, + cloud: { + body: wrapIcon( + '' + ), + }, + unknown: unknownIcon, + blank: { + body: wrapIcon(''), + }, + }, +}; diff --git a/packages/mermaid/src/diagrams/architecture/architectureParser.ts b/packages/mermaid/src/diagrams/architecture/architectureParser.ts new file mode 100644 index 000000000..a7159d907 --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureParser.ts @@ -0,0 +1,24 @@ +import type { Architecture } from '@mermaid-js/parser'; +import { parse } from '@mermaid-js/parser'; +import { log } from '../../logger.js'; +import type { ParserDefinition } from '../../diagram-api/types.js'; +import { populateCommonDb } from '../common/populateCommonDb.js'; +import type { ArchitectureDB } from './architectureTypes.js'; +import { db } from './architectureDb.js'; + +const populateDb = (ast: Architecture, db: ArchitectureDB) => { + populateCommonDb(ast, db); + ast.groups.map(db.addGroup); + ast.services.map((service) => db.addService({ ...service, type: 'service' })); + ast.junctions.map((service) => db.addJunction({ ...service, type: 'junction' })); + // @ts-ignore TODO our parser guarantees the type is L/R/T/B and not string. How to change to union type? + ast.edges.map(db.addEdge); +}; + +export const parser: ParserDefinition = { + parse: async (input: string): Promise => { + const ast: Architecture = await parse('architecture', input); + log.debug(ast); + populateDb(ast, db); + }, +}; diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts new file mode 100644 index 000000000..3abb69b9f --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -0,0 +1,466 @@ +import { registerIconPacks } from '$root/rendering-util/icons.js'; +import type { Position } from 'cytoscape'; +import cytoscape from 'cytoscape'; +import type { FcoseLayoutOptions } from 'cytoscape-fcose'; +import fcose from 'cytoscape-fcose'; +import { select } from 'd3'; +import type { DrawDefinition, SVG } from '../../diagram-api/types.js'; +import type { Diagram } from '../../Diagram.js'; +import { log } from '../../logger.js'; +import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; +import { setupGraphViewbox } from '../../setupGraphViewbox.js'; +import { getConfigField } from './architectureDb.js'; +import { architectureIcons } from './architectureIcons.js'; +import type { + ArchitectureDataStructures, + ArchitectureJunction, + ArchitectureSpatialMap, + EdgeSingular, + EdgeSingularData, + NodeSingularData, +} from './architectureTypes.js'; +import { + type ArchitectureDB, + type ArchitectureDirection, + type ArchitectureEdge, + type ArchitectureGroup, + type ArchitectureService, + ArchitectureDirectionName, + edgeData, + getOppositeArchitectureDirection, + isArchitectureDirectionXY, + isArchitectureDirectionY, + nodeData, +} from './architectureTypes.js'; +import { drawEdges, drawGroups, drawJunctions, drawServices } from './svgDraw.js'; + +registerIconPacks([ + { + name: architectureIcons.prefix, + icons: architectureIcons, + }, +]); +cytoscape.use(fcose); + +function addServices(services: ArchitectureService[], cy: cytoscape.Core) { + services.forEach((service) => { + cy.add({ + group: 'nodes', + data: { + type: 'service', + id: service.id, + icon: service.icon, + label: service.title, + parent: service.in, + width: getConfigField('iconSize'), + height: getConfigField('iconSize'), + } as NodeSingularData, + classes: 'node-service', + }); + }); +} + +function addJunctions(junctions: ArchitectureJunction[], cy: cytoscape.Core) { + junctions.forEach((junction) => { + cy.add({ + group: 'nodes', + data: { + type: 'junction', + id: junction.id, + parent: junction.in, + width: getConfigField('iconSize'), + height: getConfigField('iconSize'), + } as NodeSingularData, + classes: 'node-junction', + }); + }); +} + +function positionNodes(db: ArchitectureDB, cy: cytoscape.Core) { + cy.nodes().map((node) => { + const data = nodeData(node); + if (data.type === 'group') { + return; + } + data.x = node.position().x; + data.y = node.position().y; + + const nodeElem = db.getElementById(data.id); + nodeElem.attr('transform', 'translate(' + (data.x || 0) + ',' + (data.y || 0) + ')'); + }); +} + +function addGroups(groups: ArchitectureGroup[], cy: cytoscape.Core) { + groups.forEach((group) => { + cy.add({ + group: 'nodes', + data: { + type: 'group', + id: group.id, + icon: group.icon, + label: group.title, + parent: group.in, + } as NodeSingularData, + classes: 'node-group', + }); + }); +} + +function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) { + edges.forEach((parsedEdge) => { + const { lhsId, rhsId, lhsInto, lhsGroup, rhsInto, lhsDir, rhsDir, rhsGroup, title } = + parsedEdge; + const edgeType = isArchitectureDirectionXY(parsedEdge.lhsDir, parsedEdge.rhsDir) + ? 'segments' + : 'straight'; + const edge: EdgeSingularData = { + id: `${lhsId}-${rhsId}`, + label: title, + source: lhsId, + sourceDir: lhsDir, + sourceArrow: lhsInto, + sourceGroup: lhsGroup, + sourceEndpoint: + lhsDir === 'L' + ? '0 50%' + : lhsDir === 'R' + ? '100% 50%' + : lhsDir === 'T' + ? '50% 0' + : '50% 100%', + target: rhsId, + targetDir: rhsDir, + targetArrow: rhsInto, + targetGroup: rhsGroup, + targetEndpoint: + rhsDir === 'L' + ? '0 50%' + : rhsDir === 'R' + ? '100% 50%' + : rhsDir === 'T' + ? '50% 0' + : '50% 100%', + }; + cy.add({ + group: 'edges', + data: edge, + classes: edgeType, + }); + }); +} + +function getAlignments(spatialMaps: ArchitectureSpatialMap[]): fcose.FcoseAlignmentConstraint { + const alignments = spatialMaps.map((spatialMap) => { + const horizontalAlignments: Record = {}; + const verticalAlignments: Record = {}; + // Group service ids in an object with their x and y coordinate as the key + Object.entries(spatialMap).forEach(([id, [x, y]]) => { + if (!horizontalAlignments[y]) { + horizontalAlignments[y] = []; + } + if (!verticalAlignments[x]) { + verticalAlignments[x] = []; + } + horizontalAlignments[y].push(id); + verticalAlignments[x].push(id); + }); + // Merge the values of each object into a list if the inner list has at least 2 elements + return { + horiz: Object.values(horizontalAlignments).filter((arr) => arr.length > 1), + vert: Object.values(verticalAlignments).filter((arr) => arr.length > 1), + }; + }); + + // Merge the alignment lists for each spatial map into one 2d array per axis + const [horizontal, vertical] = alignments.reduce( + ([prevHoriz, prevVert], { horiz, vert }) => { + return [ + [...prevHoriz, ...horiz], + [...prevVert, ...vert], + ]; + }, + [[] as string[][], [] as string[][]] + ); + + return { + horizontal, + vertical, + }; +} + +function getRelativeConstraints( + spatialMaps: ArchitectureSpatialMap[] +): fcose.FcoseRelativePlacementConstraint[] { + const relativeConstraints: fcose.FcoseRelativePlacementConstraint[] = []; + const posToStr = (pos: number[]) => `${pos[0]},${pos[1]}`; + const strToPos = (pos: string) => pos.split(',').map((p) => parseInt(p)); + + spatialMaps.forEach((spatialMap) => { + const invSpatialMap = Object.fromEntries( + Object.entries(spatialMap).map(([id, pos]) => [posToStr(pos), id]) + ); + + // perform BFS + const queue = [posToStr([0, 0])]; + const visited: Record = {}; + const directions: Record = { + L: [-1, 0], + R: [1, 0], + T: [0, 1], + B: [0, -1], + }; + while (queue.length > 0) { + const curr = queue.shift(); + if (curr) { + visited[curr] = 1; + const currId = invSpatialMap[curr]; + if (currId) { + const currPos = strToPos(curr); + Object.entries(directions).forEach(([dir, shift]) => { + const newPos = posToStr([currPos[0] + shift[0], currPos[1] + shift[1]]); + const newId = invSpatialMap[newPos]; + // If there is an adjacent service to the current one and it has not yet been visited + if (newId && !visited[newPos]) { + queue.push(newPos); + // @ts-ignore cannot determine if left/right or top/bottom are paired together + relativeConstraints.push({ + [ArchitectureDirectionName[dir as ArchitectureDirection]]: newId, + [ArchitectureDirectionName[ + getOppositeArchitectureDirection(dir as ArchitectureDirection) + ]]: currId, + gap: 1.5 * getConfigField('iconSize'), + }); + } + }); + } + } + } + }); + return relativeConstraints; +} + +function layoutArchitecture( + services: ArchitectureService[], + junctions: ArchitectureJunction[], + groups: ArchitectureGroup[], + edges: ArchitectureEdge[], + { spatialMaps }: ArchitectureDataStructures +): Promise { + return new Promise((resolve) => { + const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none'); + const cy = cytoscape({ + container: document.getElementById('cy'), + style: [ + { + selector: 'edge', + style: { + 'curve-style': 'straight', + label: 'data(label)', + 'source-endpoint': 'data(sourceEndpoint)', + 'target-endpoint': 'data(targetEndpoint)', + }, + }, + { + selector: 'edge.segments', + style: { + 'curve-style': 'segments', + 'segment-weights': '0', + 'segment-distances': [0.5], + // @ts-ignore Incorrect library types + 'edge-distances': 'endpoints', + 'source-endpoint': 'data(sourceEndpoint)', + 'target-endpoint': 'data(targetEndpoint)', + }, + }, + { + selector: 'node', + style: { + // @ts-ignore Incorrect library types + 'compound-sizing-wrt-labels': 'include', + }, + }, + { + selector: 'node[label]', + style: { + 'text-valign': 'bottom', + 'text-halign': 'center', + 'font-size': `${getConfigField('fontSize')}px`, + }, + }, + { + selector: '.node-service', + style: { + label: 'data(label)', + width: 'data(width)', + height: 'data(height)', + }, + }, + { + selector: '.node-junction', + style: { + width: 'data(width)', + height: 'data(height)', + }, + }, + { + selector: '.node-group', + style: { + // @ts-ignore Incorrect library types + padding: `${getConfigField('padding')}px`, + }, + }, + ], + }); + // Remove element after layout + renderEl.remove(); + + addGroups(groups, cy); + addServices(services, cy); + addJunctions(junctions, cy); + addEdges(edges, cy); + + // Use the spatial map to create alignment arrays for fcose + const alignmentConstraint = getAlignments(spatialMaps); + + // Create the relative constraints for fcose by using an inverse of the spatial map and performing BFS on it + const relativePlacementConstraint = getRelativeConstraints(spatialMaps); + + const layout = cy.layout({ + name: 'fcose', + quality: 'proof', + styleEnabled: false, + animate: false, + nodeDimensionsIncludeLabels: false, + // Adjust the edge parameters if it passes through the border of a group + // Hacky fix for: https://github.com/iVis-at-Bilkent/cytoscape.js-fcose/issues/67 + idealEdgeLength(edge: EdgeSingular) { + const [nodeA, nodeB] = edge.connectedNodes(); + const { parent: parentA } = nodeData(nodeA); + const { parent: parentB } = nodeData(nodeB); + const elasticity = + parentA === parentB ? 1.5 * getConfigField('iconSize') : 0.5 * getConfigField('iconSize'); + return elasticity; + }, + edgeElasticity(edge: EdgeSingular) { + const [nodeA, nodeB] = edge.connectedNodes(); + const { parent: parentA } = nodeData(nodeA); + const { parent: parentB } = nodeData(nodeB); + const elasticity = parentA === parentB ? 0.45 : 0.001; + return elasticity; + }, + alignmentConstraint, + relativePlacementConstraint, + } as FcoseLayoutOptions); + + // Once the diagram has been generated and the service's position cords are set, adjust the XY edges to have a 90deg bend + layout.one('layoutstop', () => { + function getSegmentWeights( + source: Position, + target: Position, + pointX: number, + pointY: number + ) { + let W, D; + const { x: sX, y: sY } = source; + const { x: tX, y: tY } = target; + + D = + (pointY - sY + ((sX - pointX) * (sY - tY)) / (sX - tX)) / + Math.sqrt(1 + Math.pow((sY - tY) / (sX - tX), 2)); + W = Math.sqrt(Math.pow(pointY - sY, 2) + Math.pow(pointX - sX, 2) - Math.pow(D, 2)); + + const distAB = Math.sqrt(Math.pow(tX - sX, 2) + Math.pow(tY - sY, 2)); + W = W / distAB; + + //check whether the point (pointX, pointY) is on right or left of the line src to tgt. for instance : a point C(X, Y) and line (AB). d=(xB-xA)(yC-yA)-(yB-yA)(xC-xA). if d>0, then C is on left of the line. if d<0, it is on right. if d=0, it is on the line. + let delta1 = (tX - sX) * (pointY - sY) - (tY - sY) * (pointX - sX); + switch (true) { + case delta1 >= 0: + delta1 = 1; + break; + case delta1 < 0: + delta1 = -1; + break; + } + //check whether the point (pointX, pointY) is "behind" the line src to tgt + let delta2 = (tX - sX) * (pointX - sX) + (tY - sY) * (pointY - sY); + switch (true) { + case delta2 >= 0: + delta2 = 1; + break; + case delta2 < 0: + delta2 = -1; + break; + } + + D = Math.abs(D) * delta1; //ensure that sign of D is same as sign of delta1. Hence we need to take absolute value of D and multiply by delta1 + W = W * delta2; + + return { + distances: D, + weights: W, + }; + } + cy.startBatch(); + for (const edge of Object.values(cy.edges())) { + if (edge.data?.()) { + const { x: sX, y: sY } = edge.source().position(); + const { x: tX, y: tY } = edge.target().position(); + if (sX !== tX && sY !== tY) { + const sEP = edge.sourceEndpoint(); + const tEP = edge.targetEndpoint(); + const { sourceDir } = edgeData(edge); + const [pointX, pointY] = isArchitectureDirectionY(sourceDir) + ? [sEP.x, tEP.y] + : [tEP.x, sEP.y]; + const { weights, distances } = getSegmentWeights(sEP, tEP, pointX, pointY); + edge.style('segment-distances', distances); + edge.style('segment-weights', weights); + } + } + } + cy.endBatch(); + layout.run(); + }); + layout.run(); + + cy.ready((e) => { + log.info('Ready', e); + resolve(cy); + }); + }); +} + +export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) => { + const db = diagObj.db as ArchitectureDB; + + const services = db.getServices(); + const junctions = db.getJunctions(); + const groups = db.getGroups(); + const edges = db.getEdges(); + const ds = db.getDataStructures(); + + const svg: SVG = selectSvgElement(id); + + const edgesElem = svg.append('g'); + edgesElem.attr('class', 'architecture-edges'); + + const servicesElem = svg.append('g'); + servicesElem.attr('class', 'architecture-services'); + + const groupElem = svg.append('g'); + groupElem.attr('class', 'architecture-groups'); + + await drawServices(db, servicesElem, services); + drawJunctions(db, servicesElem, junctions); + + const cy = await layoutArchitecture(services, junctions, groups, edges, ds); + + await drawEdges(edgesElem, cy); + await drawGroups(groupElem, cy); + positionNodes(db, cy); + + setupGraphViewbox(undefined, svg, getConfigField('padding'), getConfigField('useMaxWidth')); +}; + +export const renderer = { draw }; diff --git a/packages/mermaid/src/diagrams/architecture/architectureStyles.ts b/packages/mermaid/src/diagrams/architecture/architectureStyles.ts new file mode 100644 index 000000000..7f494ecd1 --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureStyles.ts @@ -0,0 +1,38 @@ +import type { DiagramStylesProvider } from '../../diagram-api/types.js'; +import type { ArchitectureStyleOptions } from './architectureTypes.js'; + +const getStyles: DiagramStylesProvider = (options: ArchitectureStyleOptions) => + ` + .edge { + stroke-width: ${options.archEdgeWidth}; + stroke: ${options.archEdgeColor}; + fill: none; + } + + .arrow { + fill: ${options.archEdgeArrowColor}; + } + + .node-bkg { + fill: none; + stroke: ${options.archGroupBorderColor}; + stroke-width: ${options.archGroupBorderWidth}; + stroke-dasharray: 8; + } + .node-icon-text { + display: flex; + align-items: center; + } + + .node-icon-text > div { + color: #fff; + margin: 1px; + height: fit-content; + text-align: center; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + } +`; + +export default getStyles; diff --git a/packages/mermaid/src/diagrams/architecture/architectureTypes.ts b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts new file mode 100644 index 000000000..b3ef55ec6 --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts @@ -0,0 +1,351 @@ +import type { DiagramDB } from '../../diagram-api/types.js'; +import type { ArchitectureDiagramConfig } from '../../config.type.js'; +import type { D3Element } from '../../types.js'; +import type cytoscape from 'cytoscape'; + +/*=======================================*\ +| Architecture Diagram Types | +\*=======================================*/ + +export type ArchitectureDirection = 'L' | 'R' | 'T' | 'B'; +export type ArchitectureDirectionX = Extract; +export type ArchitectureDirectionY = Extract; + +/** + * Contains LL, RR, TT, BB which are impossible connections + */ +export type InvalidArchitectureDirectionPair = `${ArchitectureDirection}${ArchitectureDirection}`; +export type ArchitectureDirectionPair = Exclude< + InvalidArchitectureDirectionPair, + 'LL' | 'RR' | 'TT' | 'BB' +>; +export type ArchitectureDirectionPairXY = Exclude< + InvalidArchitectureDirectionPair, + 'LL' | 'RR' | 'TT' | 'BB' | 'LR' | 'RL' | 'TB' | 'BT' +>; + +export const ArchitectureDirectionName = { + L: 'left', + R: 'right', + T: 'top', + B: 'bottom', +} as const; + +export const ArchitectureDirectionArrow = { + L: (scale: number) => `${scale},${scale / 2} 0,${scale} 0,0`, + R: (scale: number) => `0,${scale / 2} ${scale},0 ${scale},${scale}`, + T: (scale: number) => `0,0 ${scale},0 ${scale / 2},${scale}`, + B: (scale: number) => `${scale / 2},0 ${scale},${scale} 0,${scale}`, +} as const; + +export const ArchitectureDirectionArrowShift = { + L: (orig: number, arrowSize: number) => orig - arrowSize + 2, + R: (orig: number, _arrowSize: number) => orig - 2, + T: (orig: number, arrowSize: number) => orig - arrowSize + 2, + B: (orig: number, _arrowSize: number) => orig - 2, +} as const; + +export const getOppositeArchitectureDirection = function ( + x: ArchitectureDirection +): ArchitectureDirection { + if (isArchitectureDirectionX(x)) { + return x === 'L' ? 'R' : 'L'; + } else { + return x === 'T' ? 'B' : 'T'; + } +}; + +export const isArchitectureDirection = function (x: unknown): x is ArchitectureDirection { + const temp = x as ArchitectureDirection; + return temp === 'L' || temp === 'R' || temp === 'T' || temp === 'B'; +}; + +export const isArchitectureDirectionX = function ( + x: ArchitectureDirection +): x is ArchitectureDirectionX { + const temp = x as ArchitectureDirectionX; + return temp === 'L' || temp === 'R'; +}; + +export const isArchitectureDirectionY = function ( + x: ArchitectureDirection +): x is ArchitectureDirectionY { + const temp = x as ArchitectureDirectionY; + return temp === 'T' || temp === 'B'; +}; + +export const isArchitectureDirectionXY = function ( + a: ArchitectureDirection, + b: ArchitectureDirection +) { + const aX_bY = isArchitectureDirectionX(a) && isArchitectureDirectionY(b); + const aY_bX = isArchitectureDirectionY(a) && isArchitectureDirectionX(b); + return aX_bY || aY_bX; +}; + +export const isArchitecturePairXY = function ( + pair: ArchitectureDirectionPair +): pair is ArchitectureDirectionPairXY { + const lhs = pair[0] as ArchitectureDirection; + const rhs = pair[1] as ArchitectureDirection; + const aX_bY = isArchitectureDirectionX(lhs) && isArchitectureDirectionY(rhs); + const aY_bX = isArchitectureDirectionY(lhs) && isArchitectureDirectionX(rhs); + return aX_bY || aY_bX; +}; + +/** + * Verifies that the architecture direction pair does not contain an invalid match (LL, RR, TT, BB) + * @param x - architecture direction pair which could potentially be invalid + * @returns true if the pair is not LL, RR, TT, or BB + */ +export const isValidArchitectureDirectionPair = function ( + x: InvalidArchitectureDirectionPair +): x is ArchitectureDirectionPair { + return x !== 'LL' && x !== 'RR' && x !== 'TT' && x !== 'BB'; +}; + +export type ArchitectureDirectionPairMap = { + [key in ArchitectureDirectionPair]?: string; +}; + +/** + * Creates a pair of the directions of each side of an edge. This function should be used instead of manually creating it to ensure that the source is always the first character. + * + * Note: Undefined is returned when sourceDir and targetDir are the same. In theory this should never happen since the diagram parser throws an error if a user defines it as such. + * @param sourceDir - source direction + * @param targetDir - target direction + * @returns + */ +export const getArchitectureDirectionPair = function ( + sourceDir: ArchitectureDirection, + targetDir: ArchitectureDirection +): ArchitectureDirectionPair | undefined { + const pair: `${ArchitectureDirection}${ArchitectureDirection}` = `${sourceDir}${targetDir}`; + return isValidArchitectureDirectionPair(pair) ? pair : undefined; +}; + +/** + * Given an x,y position for an arrow and the direction of the edge it belongs to, return a factor for slightly shifting the edge + * @param param0 - [x, y] coordinate pair + * @param pair - architecture direction pair + * @returns a new [x, y] coordinate pair + */ +export const shiftPositionByArchitectureDirectionPair = function ( + [x, y]: number[], + pair: ArchitectureDirectionPair +): number[] { + const lhs = pair[0] as ArchitectureDirection; + const rhs = pair[1] as ArchitectureDirection; + if (isArchitectureDirectionX(lhs)) { + if (isArchitectureDirectionY(rhs)) { + return [x + (lhs === 'L' ? -1 : 1), y + (rhs === 'T' ? 1 : -1)]; + } else { + return [x + (lhs === 'L' ? -1 : 1), y]; + } + } else { + if (isArchitectureDirectionX(rhs)) { + return [x + (rhs === 'L' ? 1 : -1), y + (lhs === 'T' ? 1 : -1)]; + } else { + return [x, y + (lhs === 'T' ? 1 : -1)]; + } + } +}; + +/** + * Given the directional pair of an XY edge, get the scale factors necessary to shift the coordinates inwards towards the edge + * @param pair - XY pair of an edge + * @returns - number[] containing [+/- 1, +/- 1] + */ +export const getArchitectureDirectionXYFactors = function ( + pair: ArchitectureDirectionPairXY +): number[] { + if (pair === 'LT' || pair === 'TL') { + return [1, 1]; + } else if (pair === 'BL' || pair === 'LB') { + return [1, -1]; + } else if (pair === 'BR' || pair === 'RB') { + return [-1, -1]; + } else { + return [-1, 1]; + } +}; + +export interface ArchitectureStyleOptions { + archEdgeColor: string; + archEdgeArrowColor: string; + archEdgeWidth: string; + archGroupBorderColor: string; + archGroupBorderWidth: string; +} + +export interface ArchitectureService { + id: string; + type: 'service'; + edges: ArchitectureEdge[]; + icon?: string; + iconText?: string; + title?: string; + in?: string; + width?: number; + height?: number; +} + +export interface ArchitectureJunction { + id: string; + type: 'junction'; + edges: ArchitectureEdge[]; + in?: string; + width?: number; + height?: number; +} + +export type ArchitectureNode = ArchitectureService | ArchitectureJunction; + +export const isArchitectureService = function (x: ArchitectureNode): x is ArchitectureService { + const temp = x as ArchitectureService; + return temp.type === 'service'; +}; + +export const isArchitectureJunction = function (x: ArchitectureNode): x is ArchitectureJunction { + const temp = x as ArchitectureJunction; + return temp.type === 'junction'; +}; + +export interface ArchitectureGroup { + id: string; + icon?: string; + title?: string; + in?: string; +} + +export interface ArchitectureEdge
    { + lhsId: string; + lhsDir: DT; + lhsInto?: boolean; + lhsGroup?: boolean; + rhsId: string; + rhsDir: DT; + rhsInto?: boolean; + rhsGroup?: boolean; + title?: string; +} + +export interface ArchitectureDB extends DiagramDB { + clear: () => void; + addService: (service: Omit) => void; + getServices: () => ArchitectureService[]; + addJunction: (service: Omit) => void; + getJunctions: () => ArchitectureJunction[]; + getNodes: () => ArchitectureNode[]; + getNode: (id: string) => ArchitectureNode | null; + addGroup: (group: ArchitectureGroup) => void; + getGroups: () => ArchitectureGroup[]; + addEdge: (edge: ArchitectureEdge) => void; + getEdges: () => ArchitectureEdge[]; + setElementForId: (id: string, element: D3Element) => void; + getElementById: (id: string) => D3Element; + getDataStructures: () => ArchitectureDataStructures; +} + +export type ArchitectureAdjacencyList = Record; +export type ArchitectureSpatialMap = Record; +export interface ArchitectureDataStructures { + adjList: ArchitectureAdjacencyList; + spatialMaps: ArchitectureSpatialMap[]; +} + +export interface ArchitectureState extends Record { + nodes: Record; + groups: Record; + edges: ArchitectureEdge[]; + registeredIds: Record; + dataStructures?: ArchitectureDataStructures; + elements: Record; + config: ArchitectureDiagramConfig; +} + +/*=======================================*\ +| Cytoscape Override Types | +\*=======================================*/ + +export interface EdgeSingularData { + id: string; + label?: string; + source: string; + sourceDir: ArchitectureDirection; + sourceArrow?: boolean; + sourceGroup?: boolean; + target: string; + targetDir: ArchitectureDirection; + targetArrow?: boolean; + targetGroup?: boolean; + [key: string]: any; +} + +export const edgeData = (edge: cytoscape.EdgeSingular) => { + return edge.data() as EdgeSingularData; +}; + +export interface EdgeSingular extends cytoscape.EdgeSingular { + _private: { + bodyBounds: unknown; + rscratch: { + startX: number; + startY: number; + midX: number; + midY: number; + endX: number; + endY: number; + }; + }; + data(): EdgeSingularData; + data(key: T): EdgeSingularData[T]; +} + +export type NodeSingularData = + | { + type: 'service'; + id: string; + icon?: string; + label?: string; + parent?: string; + width: number; + height: number; + [key: string]: any; + } + | { + type: 'junction'; + id: string; + parent?: string; + width: number; + height: number; + [key: string]: any; + } + | { + type: 'group'; + id: string; + icon?: string; + label?: string; + parent?: string; + [key: string]: any; + }; + +export const nodeData = (node: cytoscape.NodeSingular) => { + return node.data() as NodeSingularData; +}; + +export interface NodeSingular extends cytoscape.NodeSingular { + _private: { + bodyBounds: { + h: number; + w: number; + x1: number; + x2: number; + y1: number; + y2: number; + }; + children: cytoscape.NodeSingular[]; + }; + data(): NodeSingularData; + data(key: T): NodeSingularData[T]; +} diff --git a/packages/mermaid/src/diagrams/architecture/svgDraw.ts b/packages/mermaid/src/diagrams/architecture/svgDraw.ts new file mode 100644 index 000000000..357839394 --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/svgDraw.ts @@ -0,0 +1,370 @@ +import { getIconSVG } from '$root/rendering-util/icons.js'; +import type cytoscape from 'cytoscape'; +import { getConfig } from '../../diagram-api/diagramAPI.js'; +import { createText } from '../../rendering-util/createText.js'; +import type { D3Element } from '../../types.js'; +import { db, getConfigField } from './architectureDb.js'; +import { architectureIcons } from './architectureIcons.js'; +import { + ArchitectureDirectionArrow, + ArchitectureDirectionArrowShift, + edgeData, + getArchitectureDirectionPair, + getArchitectureDirectionXYFactors, + isArchitectureDirectionX, + isArchitectureDirectionXY, + isArchitectureDirectionY, + isArchitecturePairXY, + nodeData, + type ArchitectureDB, + type ArchitectureJunction, + type ArchitectureService, +} from './architectureTypes.js'; + +export const drawEdges = async function (edgesEl: D3Element, cy: cytoscape.Core) { + const padding = getConfigField('padding'); + const iconSize = getConfigField('iconSize'); + const halfIconSize = iconSize / 2; + const arrowSize = iconSize / 6; + const halfArrowSize = arrowSize / 2; + + await Promise.all( + cy.edges().map(async (edge) => { + const { + source, + sourceDir, + sourceArrow, + sourceGroup, + target, + targetDir, + targetArrow, + targetGroup, + label, + } = edgeData(edge); + let { x: startX, y: startY } = edge[0].sourceEndpoint(); + const { x: midX, y: midY } = edge[0].midpoint(); + let { x: endX, y: endY } = edge[0].targetEndpoint(); + + // Adjust the edge distance if it has the {group} modifier + const groupEdgeShift = padding + 4; + // +18 comes from the service label height that extends the padding on the bottom side of each group + if (sourceGroup) { + if (isArchitectureDirectionX(sourceDir)) { + startX += sourceDir === 'L' ? -groupEdgeShift : groupEdgeShift; + } else { + startY += sourceDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18; + } + } + + if (targetGroup) { + if (isArchitectureDirectionX(targetDir)) { + endX += targetDir === 'L' ? -groupEdgeShift : groupEdgeShift; + } else { + endY += targetDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18; + } + } + + // Adjust the edge distance if it doesn't have the {group} modifier and the endpoint is a junction node + if (!sourceGroup && db.getNode(source)?.type === 'junction') { + if (isArchitectureDirectionX(sourceDir)) { + startX += sourceDir === 'L' ? halfIconSize : -halfIconSize; + } else { + startY += sourceDir === 'T' ? halfIconSize : -halfIconSize; + } + } + if (!targetGroup && db.getNode(target)?.type === 'junction') { + if (isArchitectureDirectionX(targetDir)) { + endX += targetDir === 'L' ? halfIconSize : -halfIconSize; + } else { + endY += targetDir === 'T' ? halfIconSize : -halfIconSize; + } + } + + if (edge[0]._private.rscratch) { + // const bounds = edge[0]._private.rscratch; + + const g = edgesEl.insert('g'); + + g.insert('path') + .attr('d', `M ${startX},${startY} L ${midX},${midY} L${endX},${endY} `) + .attr('class', 'edge'); + + if (sourceArrow) { + const xShift = isArchitectureDirectionX(sourceDir) + ? ArchitectureDirectionArrowShift[sourceDir](startX, arrowSize) + : startX - halfArrowSize; + const yShift = isArchitectureDirectionY(sourceDir) + ? ArchitectureDirectionArrowShift[sourceDir](startY, arrowSize) + : startY - halfArrowSize; + + g.insert('polygon') + .attr('points', ArchitectureDirectionArrow[sourceDir](arrowSize)) + .attr('transform', `translate(${xShift},${yShift})`) + .attr('class', 'arrow'); + } + if (targetArrow) { + const xShift = isArchitectureDirectionX(targetDir) + ? ArchitectureDirectionArrowShift[targetDir](endX, arrowSize) + : endX - halfArrowSize; + const yShift = isArchitectureDirectionY(targetDir) + ? ArchitectureDirectionArrowShift[targetDir](endY, arrowSize) + : endY - halfArrowSize; + + g.insert('polygon') + .attr('points', ArchitectureDirectionArrow[targetDir](arrowSize)) + .attr('transform', `translate(${xShift},${yShift})`) + .attr('class', 'arrow'); + } + + if (label) { + const axis = !isArchitectureDirectionXY(sourceDir, targetDir) + ? isArchitectureDirectionX(sourceDir) + ? 'X' + : 'Y' + : 'XY'; + + let width = 0; + if (axis === 'X') { + width = Math.abs(startX - endX); + } else if (axis === 'Y') { + // Reduce width by a factor of 1.5 to avoid overlapping service labels + width = Math.abs(startY - endY) / 1.5; + } else { + width = Math.abs(startX - endX) / 2; + } + + const textElem = g.append('g'); + await createText( + textElem, + label, + { + useHtmlLabels: false, + width, + classes: 'architecture-service-label', + }, + getConfig() + ); + + textElem + .attr('dy', '1em') + .attr('alignment-baseline', 'middle') + .attr('dominant-baseline', 'middle') + .attr('text-anchor', 'middle'); + + if (axis === 'X') { + textElem.attr('transform', 'translate(' + midX + ', ' + midY + ')'); + } else if (axis === 'Y') { + textElem.attr('transform', 'translate(' + midX + ', ' + midY + ') rotate(-90)'); + } else if (axis === 'XY') { + const pair = getArchitectureDirectionPair(sourceDir, targetDir); + if (pair && isArchitecturePairXY(pair)) { + const bboxOrig = textElem.node().getBoundingClientRect(); + const [x, y] = getArchitectureDirectionXYFactors(pair); + + textElem + .attr('dominant-baseline', 'auto') + .attr('transform', `rotate(${-1 * x * y * 45})`); + + // Calculate the new width/height with the rotation applied, and transform to the proper position + const bboxNew = textElem.node().getBoundingClientRect(); + textElem.attr( + 'transform', + ` + translate(${midX}, ${midY - bboxOrig.height / 2}) + translate(${(x * bboxNew.width) / 2}, ${(y * bboxNew.height) / 2}) + rotate(${-1 * x * y * 45}, 0, ${bboxOrig.height / 2}) + ` + ); + } + } + } + } + }) + ); +}; + +export const drawGroups = async function (groupsEl: D3Element, cy: cytoscape.Core) { + const padding = getConfigField('padding'); + const groupIconSize = padding * 0.75; + + const fontSize = getConfigField('fontSize'); + + const iconSize = getConfigField('iconSize'); + const halfIconSize = iconSize / 2; + + await Promise.all( + cy.nodes().map(async (node) => { + const data = nodeData(node); + if (data.type === 'group') { + const { h, w, x1, y1 } = node.boundingBox(); + + groupsEl + .append('rect') + .attr('x', x1 + halfIconSize) + .attr('y', y1 + halfIconSize) + .attr('width', w) + .attr('height', h) + .attr('class', 'node-bkg'); + + const groupLabelContainer = groupsEl.append('g'); + let shiftedX1 = x1; + let shiftedY1 = y1; + if (data.icon) { + const bkgElem = groupLabelContainer.append('g'); + bkgElem.html( + `${await getIconSVG(data.icon, { height: groupIconSize, width: groupIconSize, fallbackPrefix: architectureIcons.prefix })}` + ); + bkgElem.attr( + 'transform', + 'translate(' + + (shiftedX1 + halfIconSize + 1) + + ', ' + + (shiftedY1 + halfIconSize + 1) + + ')' + ); + shiftedX1 += groupIconSize; + // TODO: test with more values + // - 1 - 2 comes from the Y axis transform of the icon and label + shiftedY1 += fontSize / 2 - 1 - 2; + } + if (data.label) { + const textElem = groupLabelContainer.append('g'); + await createText( + textElem, + data.label, + { + useHtmlLabels: false, + width: w, + classes: 'architecture-service-label', + }, + getConfig() + ); + textElem + .attr('dy', '1em') + .attr('alignment-baseline', 'middle') + .attr('dominant-baseline', 'start') + .attr('text-anchor', 'start'); + + textElem.attr( + 'transform', + 'translate(' + + (shiftedX1 + halfIconSize + 4) + + ', ' + + (shiftedY1 + halfIconSize + 2) + + ')' + ); + } + } + }) + ); +}; + +export const drawServices = async function ( + db: ArchitectureDB, + elem: D3Element, + services: ArchitectureService[] +): Promise { + for (const service of services) { + const serviceElem = elem.append('g'); + const iconSize = getConfigField('iconSize'); + + if (service.title) { + const textElem = serviceElem.append('g'); + await createText( + textElem, + service.title, + { + useHtmlLabels: false, + width: iconSize * 1.5, + classes: 'architecture-service-label', + }, + getConfig() + ); + + textElem + .attr('dy', '1em') + .attr('alignment-baseline', 'middle') + .attr('dominant-baseline', 'middle') + .attr('text-anchor', 'middle'); + + textElem.attr('transform', 'translate(' + iconSize / 2 + ', ' + iconSize + ')'); + } + + const bkgElem = serviceElem.append('g'); + if (service.icon) { + // TODO: should a warning be given to end-users saying which icon names are available? + // if (!isIconNameInUse(service.icon)) { + // throw new Error(`Invalid SVG Icon name: "${service.icon}"`); + // } + bkgElem.html( + `${await getIconSVG(service.icon, { height: iconSize, width: iconSize, fallbackPrefix: architectureIcons.prefix })}` + ); + } else if (service.iconText) { + bkgElem.html( + `${await getIconSVG('blank', { height: iconSize, width: iconSize, fallbackPrefix: architectureIcons.prefix })}` + ); + const textElemContainer = bkgElem.append('g'); + const fo = textElemContainer + .append('foreignObject') + .attr('width', iconSize) + .attr('height', iconSize); + const divElem = fo + .append('div') + .attr('class', 'node-icon-text') + .attr('style', `height: ${iconSize}px;`) + .append('div') + .html(service.iconText); + const fontSize = + parseInt( + window + .getComputedStyle(divElem.node(), null) + .getPropertyValue('font-size') + .replace(/\D/g, '') + ) ?? 16; + divElem.attr('style', `-webkit-line-clamp: ${Math.floor((iconSize - 2) / fontSize)};`); + } else { + bkgElem + .append('path') + .attr('class', 'node-bkg') + .attr('id', 'node-' + service.id) + .attr( + 'd', + `M0 ${iconSize} v${-iconSize} q0,-5 5,-5 h${iconSize} q5,0 5,5 v${iconSize} H0 Z` + ); + } + + serviceElem.attr('class', 'architecture-service'); + + const { width, height } = serviceElem._groups[0][0].getBBox(); + service.width = width; + service.height = height; + db.setElementForId(service.id, serviceElem); + } + return 0; +}; + +export const drawJunctions = function ( + db: ArchitectureDB, + elem: D3Element, + junctions: ArchitectureJunction[] +) { + junctions.forEach((junction) => { + const junctionElem = elem.append('g'); + const iconSize = getConfigField('iconSize'); + + const bkgElem = junctionElem.append('g'); + bkgElem + .append('rect') + .attr('id', 'node-' + junction.id) + .attr('fill-opacity', '0') + .attr('width', iconSize) + .attr('height', iconSize); + + junctionElem.attr('class', 'architecture-junction'); + + const { width, height } = junctionElem._groups[0][0].getBBox(); + junctionElem.width = width; + junctionElem.height = height; + db.setElementForId(junction.id, junctionElem); + }); +}; diff --git a/packages/mermaid/src/diagrams/er/erRenderer.js b/packages/mermaid/src/diagrams/er/erRenderer.js index 33fb5bd4a..0327bfc9d 100644 --- a/packages/mermaid/src/diagrams/er/erRenderer.js +++ b/packages/mermaid/src/diagrams/er/erRenderer.js @@ -519,6 +519,8 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) { // Append a text node containing the label const labelId = 'rel' + relCnt; + const labelText = rel.roleA.split(/
    /g); + const labelNode = svg .append('text') .classed('er relationshipLabel', true) @@ -528,8 +530,20 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) { .style('text-anchor', 'middle') .style('dominant-baseline', 'middle') .style('font-family', getConfig().fontFamily) - .style('font-size', conf.fontSize + 'px') - .text(rel.roleA); + .style('font-size', conf.fontSize + 'px'); + + if (labelText.length == 1) { + labelNode.text(rel.roleA); + } else { + const firstShift = -(labelText.length - 1) * 0.5; + labelText.forEach((txt, i) => { + labelNode + .append('tspan') + .attr('x', labelPoint.x) + .attr('dy', `${i === 0 ? firstShift : 1}em`) + .text(txt); + }); + } // Figure out how big the opaque 'container' rectangle needs to be const labelBBox = labelNode.node().getBBox(); diff --git a/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts b/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts index b66afe4bf..df3f57e47 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts @@ -7,13 +7,14 @@ import type { const id = 'flowchart-v2'; const detector: DiagramDetector = (txt, config) => { - if ( - config?.flowchart?.defaultRenderer === 'dagre-d3' || - config?.flowchart?.defaultRenderer === 'elk' - ) { + if (config?.flowchart?.defaultRenderer === 'dagre-d3') { return false; } + if (config?.flowchart?.defaultRenderer === 'elk') { + config.layout = 'elk'; + } + // If we have configured to use dagre-wrapper then we should return true in this function for graph code thus making it use the new flowchart diagram if (/^\s*graph/.test(txt) && config?.flowchart?.defaultRenderer === 'dagre-wrapper') { return true; diff --git a/packages/mermaid/src/diagrams/git/gitGraph.spec.ts b/packages/mermaid/src/diagrams/git/gitGraph.spec.ts new file mode 100644 index 000000000..9b3236f90 --- /dev/null +++ b/packages/mermaid/src/diagrams/git/gitGraph.spec.ts @@ -0,0 +1,1322 @@ +import { rejects } from 'assert'; +import { db } from './gitGraphAst.js'; +import { parser } from './gitGraphParser.js'; + +describe('when parsing a gitGraph', function () { + beforeEach(function () { + db.clear(); + }); + describe('when parsing basic gitGraph', function () { + it('should handle a gitGraph definition', async () => { + const str = `gitGraph:\n commit\n`; + + await parser.parse(str); + const commits = db.getCommits(); + + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + }); + + it('should handle set direction top to bottom', async () => { + const str = 'gitGraph TB:\n' + 'commit\n'; + + await parser.parse(str); + const commits = db.getCommits(); + + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('TB'); + expect(db.getBranches().size).toBe(1); + }); + + it('should handle set direction bottom to top', async () => { + const str = 'gitGraph BT:\n' + 'commit\n'; + + await parser.parse(str); + const commits = db.getCommits(); + + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('BT'); + expect(db.getBranches().size).toBe(1); + }); + + it('should checkout a branch', async () => { + const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n'; + + await parser.parse(str); + const commits = db.getCommits(); + + expect(commits.size).toBe(0); + expect(db.getCurrentBranch()).toBe('new'); + }); + + it('should switch a branch', async () => { + const str = 'gitGraph:\n' + 'branch new\n' + 'switch new\n'; + + await parser.parse(str); + const commits = db.getCommits(); + + expect(commits.size).toBe(0); + expect(db.getCurrentBranch()).toBe('new'); + }); + + it('should add commits to checked out branch', async () => { + const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n' + 'commit\n' + 'commit\n'; + + await parser.parse(str); + const commits = db.getCommits(); + + expect(commits.size).toBe(2); + expect(db.getCurrentBranch()).toBe('new'); + const branchCommit = db.getBranches().get('new'); + expect(branchCommit).not.toBeNull(); + if (branchCommit) { + expect(commits.get(branchCommit)?.parents).not.toBeNull(); + } + }); + it('should handle commit with args', async () => { + const str = 'gitGraph:\n' + 'commit "a commit"\n'; + + await parser.parse(str); + const commits = db.getCommits(); + + expect(commits.size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe('a commit'); + expect(db.getCurrentBranch()).toBe('main'); + }); + + it.skip('should reset a branch', async () => { + const str = + 'gitGraph:\n' + + 'commit\n' + + 'commit\n' + + 'branch newbranch\n' + + 'checkout newbranch\n' + + 'commit\n' + + 'reset main\n'; + + await parser.parse(str); + + const commits = db.getCommits(); + expect(commits.size).toBe(3); + expect(db.getCurrentBranch()).toBe('newbranch'); + expect(db.getBranches().get('newbranch')).toEqual(db.getBranches().get('main')); + expect(db.getHead()?.id).toEqual(db.getBranches().get('newbranch')); + }); + + it.skip('reset can take an argument', async () => { + const str = + 'gitGraph:\n' + + 'commit\n' + + 'commit\n' + + 'branch newbranch\n' + + 'checkout newbranch\n' + + 'commit\n' + + 'reset main^\n'; + + await parser.parse(str); + + const commits = db.getCommits(); + expect(commits.size).toBe(3); + expect(db.getCurrentBranch()).toBe('newbranch'); + const branch = db.getBranches().get('main'); + const main = commits.get(branch ?? ''); + expect(db.getHead()?.id).toEqual(main?.parents); + }); + + it.skip('should handle fast forwardable merges', async () => { + const str = + 'gitGraph:\n' + + 'commit\n' + + 'branch newbranch\n' + + 'checkout newbranch\n' + + 'commit\n' + + 'commit\n' + + 'checkout main\n' + + 'merge newbranch\n'; + + await parser.parse(str); + + const commits = db.getCommits(); + expect(commits.size).toBe(4); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getBranches().get('newbranch')).toEqual(db.getBranches().get('main')); + expect(db.getHead()?.id).toEqual(db.getBranches().get('newbranch')); + }); + + it('should handle cases when merge is a noop', async () => { + const str = + 'gitGraph:\n' + + 'commit\n' + + 'branch newbranch\n' + + 'checkout newbranch\n' + + 'commit\n' + + 'commit\n' + + 'merge main\n'; + + await parser.parse(str); + + const commits = db.getCommits(); + expect(commits.size).toBe(4); + expect(db.getCurrentBranch()).toBe('newbranch'); + expect(db.getBranches().get('newbranch')).not.toEqual(db.getBranches().get('main')); + expect(db.getHead()?.id).toEqual(db.getBranches().get('newbranch')); + }); + + it('should handle merge with 2 parents', async () => { + const str = + 'gitGraph:\n' + + 'commit\n' + + 'branch newbranch\n' + + 'checkout newbranch\n' + + 'commit\n' + + 'commit\n' + + 'checkout main\n' + + 'commit\n' + + 'merge newbranch\n'; + + await parser.parse(str); + + const commits = db.getCommits(); + expect(commits.size).toBe(5); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getBranches().get('newbranch')).not.toEqual(db.getBranches().get('main')); + expect(db.getHead()?.id).toEqual(db.getBranches().get('main')); + }); + + it.skip('should handle ff merge when history walk has two parents (merge commit)', async () => { + const str = + 'gitGraph:\n' + + 'commit\n' + + 'branch newbranch\n' + + 'checkout newbranch\n' + + 'commit\n' + + 'commit\n' + + 'checkout main\n' + + 'commit\n' + + 'merge newbranch\n' + + 'commit\n' + + 'checkout newbranch\n' + + 'merge main\n'; + + await parser.parse(str); + + const commits = db.getCommits(); + expect(commits.size).toBe(7); + expect(db.getCurrentBranch()).toBe('newbranch'); + expect(db.getBranches().get('newbranch')).toEqual(db.getBranches().get('main')); + expect(db.getHead()?.id).toEqual(db.getBranches().get('main')); + + db.prettyPrint(); + }); + + it('should generate an array of known branches', async () => { + const str = + 'gitGraph:\n' + + 'commit\n' + + 'branch b1\n' + + 'checkout b1\n' + + 'commit\n' + + 'commit\n' + + 'branch b2\n'; + + await parser.parse(str); + const branches = db.getBranchesAsObjArray(); + + expect(branches).toHaveLength(3); + expect(branches[0]).toHaveProperty('name', 'main'); + expect(branches[1]).toHaveProperty('name', 'b1'); + expect(branches[2]).toHaveProperty('name', 'b2'); + }); + }); + + describe('when parsing more advanced gitGraphs', () => { + it('should handle a gitGraph commit with NO params, get auto-generated read-only ID', async () => { + const str = `gitGraph: + commit + `; + await parser.parse(str); + const commits = db.getCommits(); + //console.info(commits); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe(''); + expect(commits.get(key)?.id).not.toBeNull(); + expect(commits.get(key)?.tags).toStrictEqual([]); + expect(commits.get(key)?.type).toBe(0); + }); + + it('should handle a gitGraph commit with custom commit id only', async () => { + const str = `gitGraph: + commit id:"1111" + `; + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe(''); + expect(commits.get(key)?.id).toBe('1111'); + expect(commits.get(key)?.tags).toStrictEqual([]); + expect(commits.get(key)?.type).toBe(0); + }); + + it('should handle a gitGraph commit with custom commit tag only', async () => { + const str = `gitGraph: + commit tag:"test" + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe(''); + expect(commits.get(key)?.id).not.toBeNull(); + expect(commits.get(key)?.tags).toStrictEqual(['test']); + expect(commits.get(key)?.type).toBe(0); + }); + + it('should handle a gitGraph commit with custom commit type HIGHLIGHT only', async () => { + const str = `gitGraph: + commit type: HIGHLIGHT + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe(''); + expect(commits.get(key)?.id).not.toBeNull(); + expect(commits.get(key)?.tags).toStrictEqual([]); + expect(commits.get(key)?.type).toBe(2); + }); + + it('should handle a gitGraph commit with custom commit type REVERSE only', async () => { + const str = `gitGraph: + commit type: REVERSE + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe(''); + expect(commits.get(key)?.id).not.toBeNull(); + expect(commits.get(key)?.tags).toStrictEqual([]); + expect(commits.get(key)?.type).toBe(1); + }); + + it('should handle a gitGraph commit with custom commit type NORMAL only', async () => { + const str = `gitGraph: + commit type: NORMAL + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe(''); + expect(commits.get(key)?.id).not.toBeNull(); + expect(commits.get(key)?.tags).toStrictEqual([]); + expect(commits.get(key)?.type).toBe(0); + }); + + it('should handle a gitGraph commit with custom commit msg only', async () => { + const str = `gitGraph: + commit "test commit" + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe('test commit'); + expect(commits.get(key)?.id).not.toBeNull(); + expect(commits.get(key)?.tags).toStrictEqual([]); + expect(commits.get(key)?.type).toBe(0); + }); + + it('should handle a gitGraph commit with custom commit "msg:" key only', async () => { + const str = `gitGraph: + commit msg: "test commit" + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe('test commit'); + expect(commits.get(key)?.id).not.toBeNull(); + expect(commits.get(key)?.tags).toStrictEqual([]); + expect(commits.get(key)?.type).toBe(0); + }); + + it('should handle a gitGraph commit with custom commit id, tag only', async () => { + const str = `gitGraph: + commit id:"1111" tag: "test tag" + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe(''); + expect(commits.get(key)?.id).toBe('1111'); + expect(commits.get(key)?.tags).toStrictEqual(['test tag']); + expect(commits.get(key)?.type).toBe(0); + }); + + it('should handle a gitGraph commit with custom commit type, tag only', async () => { + const str = `gitGraph: + commit type:HIGHLIGHT tag: "test tag" + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe(''); + expect(commits.get(key)?.id).not.toBeNull(); + expect(commits.get(key)?.tags).toStrictEqual(['test tag']); + expect(commits.get(key)?.type).toBe(2); + }); + + it('should handle a gitGraph commit with custom commit tag and type only', async () => { + const str = `gitGraph: + commit tag: "test tag" type:HIGHLIGHT + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe(''); + expect(commits.get(key)?.id).not.toBeNull(); + expect(commits.get(key)?.tags).toStrictEqual(['test tag']); + expect(commits.get(key)?.type).toBe(2); + }); + + it('should handle a gitGraph commit with custom commit id, type and tag only', async () => { + const str = `gitGraph: + commit id:"1111" type:REVERSE tag: "test tag" + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe(''); + expect(commits.get(key)?.id).toBe('1111'); + expect(commits.get(key)?.tags).toStrictEqual(['test tag']); + expect(commits.get(key)?.type).toBe(1); + }); + + it('should handle a gitGraph commit with custom commit id, type, tag and msg', async () => { + const str = `gitGraph: + commit id:"1111" type:REVERSE tag: "test tag" msg:"test msg" + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe('test msg'); + expect(commits.get(key)?.id).toBe('1111'); + expect(commits.get(key)?.tags).toStrictEqual(['test tag']); + expect(commits.get(key)?.type).toBe(1); + }); + + it('should handle a gitGraph commit with custom type,tag, msg, commit id,', async () => { + const str = `gitGraph: + commit type:REVERSE tag: "test tag" msg: "test msg" id: "1111" + + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe('test msg'); + expect(commits.get(key)?.id).toBe('1111'); + expect(commits.get(key)?.tags).toStrictEqual(['test tag']); + expect(commits.get(key)?.type).toBe(1); + }); + + it('should handle a gitGraph commit with custom tag, msg, commit id, type,', async () => { + const str = `gitGraph: + commit tag: "test tag" msg:"test msg" id:"1111" type:REVERSE + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe('test msg'); + expect(commits.get(key)?.id).toBe('1111'); + expect(commits.get(key)?.tags).toStrictEqual(['test tag']); + expect(commits.get(key)?.type).toBe(1); + }); + + it('should handle a gitGraph commit with custom msg, commit id, type,tag', async () => { + const str = `gitGraph: + commit msg:"test msg" id:"1111" type:REVERSE tag: "test tag" + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + const key = commits.keys().next().value; + expect(commits.get(key)?.message).toBe('test msg'); + expect(commits.get(key)?.id).toBe('1111'); + expect(commits.get(key)?.tags).toStrictEqual(['test tag']); + expect(commits.get(key)?.type).toBe(1); + }); + + it('should handle 3 straight commits', async () => { + const str = `gitGraph: + commit + commit + commit + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(3); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(1); + }); + + it('should handle new branch creation', async () => { + const str = `gitGraph: + commit + branch testBranch + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('testBranch'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(2); + }); + + it('should allow quoted branch names', async () => { + const str = `gitGraph: + commit + branch "branch" + checkout "branch" + commit + checkout main + merge "branch" + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(3); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(2); + const [commit1, commit2, commit3] = commits.keys(); + expect(commits.get(commit1)?.branch).toBe('main'); + expect(commits.get(commit2)?.branch).toBe('branch'); + expect(commits.get(commit3)?.branch).toBe('main'); + expect(db.getBranchesAsObjArray()).toStrictEqual([{ name: 'main' }, { name: 'branch' }]); + }); + + it('should allow _-./ characters in branch names', async () => { + const str = `gitGraph: + commit + branch azAZ_-./test + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('azAZ_-./test'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(2); + }); + + it('should allow branch names starting with numbers', async () => { + const str = `gitGraph: + commit + %% branch names starting with numbers are not recommended, but are supported by git + branch 1.0.1 + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('1.0.1'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(2); + }); + + it('should allow branch names starting with unusual prefixes', async () => { + const str = `gitGraph: + commit + %% branch names starting with numbers are not recommended, but are supported by git + branch branch01 + branch checkout02 + branch cherry-pick03 + branch branch/example-branch + branch merge/test_merge + %% single character branch name + branch A + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('A'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(7); + expect([...db.getBranches().keys()]).toEqual( + expect.arrayContaining([ + 'branch01', + 'checkout02', + 'cherry-pick03', + 'branch/example-branch', + 'merge/test_merge', + 'A', + ]) + ); + }); + + it('should handle new branch checkout', async () => { + const str = `gitGraph: + commit + branch testBranch + checkout testBranch + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('testBranch'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(2); + }); + it('should handle new branch checkout with order', async () => { + const str = `gitGraph: + commit + branch test1 order: 3 + branch test2 order: 2 + branch test3 order: 1 + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('test3'); + expect(db.getBranches().size).toBe(4); + expect(db.getBranchesAsObjArray()).toStrictEqual([ + { name: 'main' }, + { name: 'test3' }, + { name: 'test2' }, + { name: 'test1' }, + ]); + }); + it('should handle new branch checkout with and without order', async () => { + const str = `gitGraph: + commit + branch test1 order: 1 + branch test2 + branch test3 + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('test3'); + expect(db.getBranches().size).toBe(4); + expect(db.getBranchesAsObjArray()).toStrictEqual([ + { name: 'main' }, + { name: 'test2' }, + { name: 'test3' }, + { name: 'test1' }, + ]); + }); + + it('should handle new branch checkout & commit', async () => { + const str = `gitGraph: + commit + branch testBranch + checkout testBranch + commit + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(2); + expect(db.getCurrentBranch()).toBe('testBranch'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(2); + const [commit1, commit2] = commits.keys(); + expect(commits.get(commit1)?.branch).toBe('main'); + expect(commits.get(commit1)?.parents).toStrictEqual([]); + expect(commits.get(commit2)?.branch).toBe('testBranch'); + expect(commits.get(commit2)?.parents).toStrictEqual([commit1]); + }); + + it('should handle new branch checkout & commit and merge', async () => { + const str = `gitGraph: + commit + branch testBranch + checkout testBranch + commit + commit + checkout main + merge testBranch + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(4); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(2); + const [commit1, commit2, commit3, commit4] = commits.keys(); + expect(commits.get(commit1)?.branch).toBe('main'); + expect(commits.get(commit1)?.parents).toStrictEqual([]); + expect(commits.get(commit2)?.branch).toBe('testBranch'); + expect(commits.get(commit2)?.parents).toStrictEqual([commits.get(commit1)?.id]); + expect(commits.get(commit3)?.branch).toBe('testBranch'); + expect(commits.get(commit3)?.parents).toStrictEqual([commits.get(commit2)?.id]); + expect(commits.get(commit4)?.branch).toBe('main'); + expect(commits.get(commit4)?.parents).toStrictEqual([ + commits.get(commit1)?.id, + commits.get(commit3)?.id, + ]); + expect(db.getBranchesAsObjArray()).toStrictEqual([{ name: 'main' }, { name: 'testBranch' }]); + }); + + it('should handle new branch switch', async () => { + const str = `gitGraph: + commit + branch testBranch + switch testBranch + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(1); + expect(db.getCurrentBranch()).toBe('testBranch'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(2); + }); + + it('should handle new branch switch & commit', async () => { + const str = `gitGraph: + commit + branch testBranch + switch testBranch + commit + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(2); + expect(db.getCurrentBranch()).toBe('testBranch'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(2); + const [commit1, commit2] = commits.keys(); + expect(commits.get(commit1)?.branch).toBe('main'); + expect(commits.get(commit1)?.parents).toStrictEqual([]); + expect(commits.get(commit2)?.branch).toBe('testBranch'); + expect(commits.get(commit2)?.parents).toStrictEqual([commit1]); + }); + + it('should handle new branch switch & commit and merge', async () => { + const str = `gitGraph: + commit + branch testBranch + switch testBranch + commit + commit + switch main + merge testBranch + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(4); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(2); + const [commit1, commit2, commit3, commit4] = commits.keys(); + expect(commits.get(commit1)?.branch).toBe('main'); + expect(commits.get(commit1)?.parents).toStrictEqual([]); + expect(commits.get(commit2)?.branch).toBe('testBranch'); + expect(commits.get(commit2)?.parents).toStrictEqual([commits.get(commit1)?.id]); + expect(commits.get(commit3)?.branch).toBe('testBranch'); + expect(commits.get(commit3)?.parents).toStrictEqual([commits.get(commit2)?.id]); + expect(commits.get(commit4)?.branch).toBe('main'); + expect(commits.get(commit4)?.parents).toStrictEqual([ + commits.get(commit1)?.id, + commits.get(commit3)?.id, + ]); + expect(db.getBranchesAsObjArray()).toStrictEqual([{ name: 'main' }, { name: 'testBranch' }]); + }); + + it('should handle merge tags', async () => { + const str = `gitGraph: + commit + branch testBranch + checkout testBranch + commit + checkout main + merge testBranch tag: "merge-tag" + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(3); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + expect(db.getBranches().size).toBe(2); + const [commit1, commit2, commit3] = commits.keys(); + expect(commits.get(commit1)?.branch).toBe('main'); + expect(commits.get(commit1)?.parents).toStrictEqual([]); + + expect(commits.get(commit2)?.branch).toBe('testBranch'); + expect(commits.get(commit2)?.parents).toStrictEqual([commits.get(commit1)?.id]); + + expect(commits.get(commit3)?.branch).toBe('main'); + expect(commits.get(commit3)?.parents).toStrictEqual([ + commits.get(commit1)?.id, + commits.get(commit2)?.id, + ]); + expect(commits.get(commit3)?.tags).toStrictEqual(['merge-tag']); + expect(db.getBranchesAsObjArray()).toStrictEqual([{ name: 'main' }, { name: 'testBranch' }]); + }); + + it('should handle merge with custom ids, tags and type', async () => { + const str = `gitGraph: + commit + branch testBranch + checkout testBranch + commit + checkout main + %% Merge Tag and ID + merge testBranch tag: "merge-tag" id: "2-222" + branch testBranch2 + checkout testBranch2 + commit + checkout main + %% Merge ID and Tag (reverse order) + merge testBranch2 id: "4-444" tag: "merge-tag2" type:HIGHLIGHT + branch testBranch3 + checkout testBranch3 + commit + checkout main + %% just Merge ID + merge testBranch3 id: "6-666" + `; + + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(7); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getDirection()).toBe('LR'); + + // The order of these commits is in alphabetical order of IDs + const [ + mainCommit, + testBranchCommit, + testBranchMerge, + testBranch2Commit, + testBranch2Merge, + testBranch3Commit, + testBranch3Merge, + ] = [...commits.values()]; + + expect(mainCommit.branch).toBe('main'); + expect(mainCommit.parents).toStrictEqual([]); + + expect(testBranchCommit.branch).toBe('testBranch'); + expect(testBranchCommit.parents).toStrictEqual([mainCommit.id]); + + expect(testBranchMerge.branch).toBe('main'); + expect(testBranchMerge.parents).toStrictEqual([mainCommit.id, testBranchCommit.id]); + expect(testBranchMerge.tags).toStrictEqual(['merge-tag']); + expect(testBranchMerge.id).toBe('2-222'); + + expect(testBranch2Merge.branch).toBe('main'); + expect(testBranch2Merge.parents).toStrictEqual([testBranchMerge.id, testBranch2Commit.id]); + expect(testBranch2Merge.tags).toStrictEqual(['merge-tag2']); + expect(testBranch2Merge.id).toBe('4-444'); + expect(testBranch2Merge.customType).toBe(2); + expect(testBranch2Merge.customId).toBe(true); + + expect(testBranch3Merge.branch).toBe('main'); + expect(testBranch3Merge.parents).toStrictEqual([testBranch2Merge.id, testBranch3Commit.id]); + expect(testBranch3Merge.id).toBe('6-666'); + + expect(db.getBranchesAsObjArray()).toStrictEqual([ + { name: 'main' }, + { name: 'testBranch' }, + { name: 'testBranch2' }, + { name: 'testBranch3' }, + ]); + }); + + it('should support cherry-picking commits', async () => { + const str = `gitGraph + commit id: "ZERO" + branch develop + commit id:"A" + checkout main + cherry-pick id:"A" + `; + + await parser.parse(str); + const commits = db.getCommits(); + const cherryPickCommitID = [...commits.keys()][2]; + expect(commits.get(cherryPickCommitID)?.tags).toStrictEqual(['cherry-pick:A']); + expect(commits.get(cherryPickCommitID)?.branch).toBe('main'); + }); + + it('should support cherry-picking commits with custom tag', async () => { + const str = `gitGraph + commit id: "ZERO" + branch develop + commit id:"A" + checkout main + cherry-pick id:"A" tag:"MyTag" + `; + + await parser.parse(str); + const commits = db.getCommits(); + const cherryPickCommitID = [...commits.keys()][2]; + expect(commits.get(cherryPickCommitID)?.tags).toStrictEqual(['MyTag']); + expect(commits.get(cherryPickCommitID)?.branch).toBe('main'); + }); + + it('should support cherry-picking commits with no tag', async () => { + const str = `gitGraph + commit id: "ZERO" + branch develop + commit id:"A" + checkout main + cherry-pick id:"A" tag:"" + `; + + await parser.parse(str); + const commits = db.getCommits(); + const cherryPickCommitID = [...commits.keys()][2]; + expect(commits.get(cherryPickCommitID)?.tags).toStrictEqual([]); + expect(commits.get(cherryPickCommitID)?.branch).toBe('main'); + }); + + it('should support cherry-picking of merge commits', async () => { + const str = `gitGraph + commit id: "ZERO" + branch feature + branch release + checkout feature + commit id: "A" + commit id: "B" + checkout main + merge feature id: "M" + checkout release + cherry-pick id: "M" parent:"B" + `; + + await parser.parse(str); + const commits = db.getCommits(); + const cherryPickCommitID = [...commits.keys()][4]; + expect(commits.get(cherryPickCommitID)?.tags).toStrictEqual(['cherry-pick:M|parent:B']); + expect(commits.get(cherryPickCommitID)?.branch).toBe('release'); + }); + + it('should support cherry-picking of merge commits with tag', async () => { + const str = `gitGraph + commit id: "ZERO" + branch feature + branch release + checkout feature + commit id: "A" + commit id: "B" + checkout main + merge feature id: "M" + checkout release + cherry-pick id: "M" parent:"ZERO" tag: "v1.0" + `; + + await parser.parse(str); + const commits = db.getCommits(); + const cherryPickCommitID = [...commits.keys()][4]; + expect(commits.get(cherryPickCommitID)?.tags).toStrictEqual(['v1.0']); + expect(commits.get(cherryPickCommitID)?.branch).toBe('release'); + }); + + it('should support cherry-picking of merge commits with additional commit', async () => { + const str = `gitGraph + commit id: "ZERO" + branch feature + branch release + checkout feature + commit id: "A" + commit id: "B" + checkout main + merge feature id: "M" + checkout release + commit id: "C" + cherry-pick id: "M" tag: "v2.1:ZERO" parent:"ZERO" + commit id: "D" + `; + + await parser.parse(str); + const commits = db.getCommits(); + const cherryPickCommitID = [...commits.keys()][5]; + expect(commits.get(cherryPickCommitID)?.tags).toStrictEqual(['v2.1:ZERO']); + expect(commits.get(cherryPickCommitID)?.branch).toBe('release'); + }); + + it('should support cherry-picking of merge commits with empty tag', async () => { + const str = `gitGraph + commit id: "ZERO" + branch feature + branch release + checkout feature + commit id: "A" + commit id: "B" + checkout main + merge feature id: "M" + checkout release + commit id: "C" + cherry-pick id:"M" parent: "ZERO" tag:"" + commit id: "D" + cherry-pick id:"M" tag:"" parent: "B" + `; + + await parser.parse(str); + const commits = db.getCommits(); + const cherryPickCommitID = [...commits.keys()][5]; + const cherryPickCommitID2 = [...commits.keys()][7]; + expect(commits.get(cherryPickCommitID)?.tags).toStrictEqual([]); + expect(commits.get(cherryPickCommitID2)?.tags).toStrictEqual([]); + expect(commits.get(cherryPickCommitID)?.branch).toBe('release'); + }); + + it('should fail cherry-picking of merge commits if the parent of merge commits is not specified', async () => { + await expect( + parser.parse( + `gitGraph + commit id: "ZERO" + branch feature + branch release + checkout feature + commit id: "A" + commit id: "B" + checkout main + merge feature id: "M" + checkout release + commit id: "C" + cherry-pick id:"M" + ` + ) + ).rejects.toThrow( + 'Incorrect usage of cherry-pick: If the source commit is a merge commit, an immediate parent commit must be specified.' + ); + }); + + it('should fail cherry-picking of merge commits when the parent provided is not an immediate parent of cherry picked commit', async () => { + await expect( + parser.parse( + `gitGraph + commit id: "ZERO" + branch feature + branch release + checkout feature + commit id: "A" + commit id: "B" + checkout main + merge feature id: "M" + checkout release + commit id: "C" + cherry-pick id:"M" parent: "A" + ` + ) + ).rejects.toThrow( + 'Invalid operation: The specified parent commit is not an immediate parent of the cherry-picked commit.' + ); + }); + + it('should throw error when try to branch existing branch: main', async () => { + const str = `gitGraph + commit + branch testBranch + commit + branch main + commit + checkout main + merge testBranch + `; + + try { + await parser.parse(str); + expect(true).toBe(false); + } catch (e: any) { + expect(e.message).toBe( + 'Trying to create an existing branch. (Help: Either use a new name if you want create a new branch or try using "checkout main")' + ); + } + }); + it('should throw error when try to branch existing branch: testBranch', async () => { + const str = `gitGraph + commit + branch testBranch + commit + branch testBranch + commit + checkout main + merge testBranch + `; + + try { + await parser.parse(str); + // Fail test if above expression doesn't throw anything. + expect(true).toBe(false); + } catch (e: any) { + expect(e.message).toBe( + 'Trying to create an existing branch. (Help: Either use a new name if you want create a new branch or try using "checkout testBranch")' + ); + } + }); + it('should throw error when try to checkout unknown branch: testBranch', async () => { + const str = `gitGraph + commit + checkout testBranch + commit + branch testBranch + commit + checkout main + merge testBranch + `; + + try { + await parser.parse(str); + // Fail test if above expression doesn't throw anything. + expect(true).toBe(false); + } catch (e: any) { + expect(e.message).toBe( + 'Trying to checkout branch which is not yet created. (Help try using "branch testBranch")' + ); + } + }); + it('should throw error when trying to merge, when current branch has no commits', async () => { + const str = `gitGraph + merge testBranch + commit + checkout testBranch + commit + branch testBranch + commit + checkout main + merge testBranch + `; + + try { + await parser.parse(str); + // Fail test if above expression doesn't throw anything. + expect(true).toBe(false); + } catch (e: any) { + expect(e.message).toBe('Incorrect usage of "merge". Current branch (main)has no commits'); + } + }); + it('should throw error when trying to merge unknown branch', async () => { + const str = `gitGraph + commit + merge testBranch + commit + checkout testBranch + commit + branch testBranch + commit + checkout main + merge testBranch + `; + + try { + await parser.parse(str); + expect(true).toBe(false); + } catch (e: any) { + expect(e.message).toBe( + 'Incorrect usage of "merge". Branch to be merged (testBranch) does not exist' + ); + } + }); + it('should throw error when trying to merge branch to itself', async () => { + const str = `gitGraph + commit + branch testBranch + merge testBranch + `; + + try { + await parser.parse(str); + // Fail test if above expression doesn't throw anything. + expect(true).toBe(false); + } catch (e: any) { + expect(e.message).toBe('Incorrect usage of "merge". Cannot merge a branch to itself'); + } + }); + + it('should throw error when using existing id as merge ID', async () => { + const str = `gitGraph + commit id: "1-111" + branch testBranch + commit id: "2-222" + commit id: "3-333" + checkout main + merge testBranch id: "1-111" + `; + + try { + await parser.parse(str); + // Fail test if above expression doesn't throw anything. + expect(true).toBe(false); + } catch (e: any) { + expect(e.message).toBe( + 'Incorrect usage of "merge". Commit with id:1-111 already exists, use different custom Id' + ); + } + }); + it('should throw error when trying to merge branches having same heads', async () => { + const str = `gitGraph + commit + branch testBranch + checkout main + merge testBranch + `; + + try { + await parser.parse(str); + // Fail test if above expression doesn't throw anything. + expect(true).toBe(false); + } catch (e: any) { + expect(e.message).toBe('Incorrect usage of "merge". Both branches have same head'); + } + }); + it('should throw error when trying to merge branch which has no commits', async () => { + const str = `gitGraph + branch test1 + + checkout main + commit + merge test1 + `; + + try { + await parser.parse(str); + // Fail test if above expression doesn't throw anything. + expect(true).toBe(false); + } catch (e: any) { + expect(e.message).toBe( + 'Incorrect usage of "merge". Branch to be merged (test1) has no commits' + ); + } + }); + describe('accessibility', () => { + it('should handle a title and a description (accDescr)', async () => { + const str = `gitGraph: + accTitle: This is a title + accDescr: This is a description + commit + `; + await parser.parse(str); + expect(db.getAccTitle()).toBe('This is a title'); + expect(db.getAccDescription()).toBe('This is a description'); + }); + it('should handle a title and a multiline description (accDescr)', async () => { + const str = `gitGraph: + accTitle: This is a title + accDescr { + This is a description + using multiple lines + } + commit + `; + await parser.parse(str); + expect(db.getAccTitle()).toBe('This is a title'); + expect(db.getAccDescription()).toBe('This is a description\nusing multiple lines'); + }); + }); + + describe('unsafe properties', () => { + for (const prop of ['__proto__', 'constructor']) { + it(`should work with custom commit id or branch name ${prop}`, async () => { + const str = `gitGraph + commit id:"${prop}" + branch ${prop} + checkout ${prop} + commit + checkout main + merge ${prop} + `; + await parser.parse(str); + const commits = db.getCommits(); + expect(commits.size).toBe(3); + expect(commits.keys().next().value).toBe(prop); + expect(db.getCurrentBranch()).toBe('main'); + expect(db.getBranches().size).toBe(2); + expect(db.getBranchesAsObjArray()[1].name).toBe(prop); + }); + } + }); + }); +}); diff --git a/packages/mermaid/src/diagrams/git/gitGraphAst.js b/packages/mermaid/src/diagrams/git/gitGraphAst.js deleted file mode 100644 index cebc4fc3e..000000000 --- a/packages/mermaid/src/diagrams/git/gitGraphAst.js +++ /dev/null @@ -1,535 +0,0 @@ -import { log } from '../../logger.js'; -import { random } from '../../utils.js'; -import { getConfig } from '../../diagram-api/diagramAPI.js'; -import common from '../common/common.js'; -import { - setAccTitle, - getAccTitle, - getAccDescription, - setAccDescription, - clear as commonClear, - setDiagramTitle, - getDiagramTitle, -} from '../common/commonDb.js'; - -let { mainBranchName, mainBranchOrder } = getConfig().gitGraph; -let commits = new Map(); -let head = null; -let branchesConfig = new Map(); -branchesConfig.set(mainBranchName, { name: mainBranchName, order: mainBranchOrder }); -let branches = new Map(); -branches.set(mainBranchName, head); -let curBranch = mainBranchName; -let direction = 'LR'; -let seq = 0; - -/** - * - */ -function getId() { - return random({ length: 7 }); -} - -// /** -// * @param currentCommit -// * @param otherCommit -// */ - -// function isFastForwardable(currentCommit, otherCommit) { -// log.debug('Entering isFastForwardable:', currentCommit.id, otherCommit.id); -// let cnt = 0; -// while (currentCommit.seq <= otherCommit.seq && currentCommit !== otherCommit && cnt < 1000) { -// cnt++; -// // only if other branch has more commits -// if (otherCommit.parent == null) break; -// if (Array.isArray(otherCommit.parent)) { -// log.debug('In merge commit:', otherCommit.parent); -// return ( -// isFastForwardable(currentCommit, commits.get(otherCommit.parent[0])) || -// isFastForwardable(currentCommit, commits.get(otherCommit.parent[1])) -// ); -// } else { -// otherCommit = commits.get(otherCommit.parent); -// } -// } -// log.debug(currentCommit.id, otherCommit.id); -// return currentCommit.id === otherCommit.id; -// } - -/** - * @param currentCommit - * @param otherCommit - */ -// function isReachableFrom(currentCommit, otherCommit) { -// const currentSeq = currentCommit.seq; -// const otherSeq = otherCommit.seq; -// if (currentSeq > otherSeq) return isFastForwardable(otherCommit, currentCommit); -// return false; -// } - -/** - * @param list - * @param fn - */ -function uniqBy(list, fn) { - const recordMap = Object.create(null); - return list.reduce((out, item) => { - const key = fn(item); - if (!recordMap[key]) { - recordMap[key] = true; - out.push(item); - } - return out; - }, []); -} - -export const setDirection = function (dir) { - direction = dir; -}; -let options = {}; -export const setOptions = function (rawOptString) { - log.debug('options str', rawOptString); - rawOptString = rawOptString?.trim(); - rawOptString = rawOptString || '{}'; - try { - options = JSON.parse(rawOptString); - } catch (e) { - log.error('error while parsing gitGraph options', e.message); - } -}; - -export const getOptions = function () { - return options; -}; - -export const commit = function (msg, id, type, tags) { - log.debug('Entering commit:', msg, id, type, tags); - const config = getConfig(); - id = common.sanitizeText(id, config); - msg = common.sanitizeText(msg, config); - tags = tags?.map((tag) => common.sanitizeText(tag, config)); - const commit = { - id: id ? id : seq + '-' + getId(), - message: msg, - seq: seq++, - type: type ? type : commitType.NORMAL, - tags: tags ?? [], - parents: head == null ? [] : [head.id], - branch: curBranch, - }; - head = commit; - commits.set(commit.id, commit); - branches.set(curBranch, commit.id); - log.debug('in pushCommit ' + commit.id); -}; - -export const branch = function (name, order) { - name = common.sanitizeText(name, getConfig()); - if (!branches.has(name)) { - branches.set(name, head != null ? head.id : null); - branchesConfig.set(name, { name, order: order ? parseInt(order, 10) : null }); - checkout(name); - log.debug('in createBranch'); - } else { - let error = new Error( - 'Trying to create an existing branch. (Help: Either use a new name if you want create a new branch or try using "checkout ' + - name + - '")' - ); - error.hash = { - text: 'branch ' + name, - token: 'branch ' + name, - line: '1', - loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, - expected: ['"checkout ' + name + '"'], - }; - throw error; - } -}; - -export const merge = function (otherBranch, custom_id, override_type, custom_tags) { - const config = getConfig(); - otherBranch = common.sanitizeText(otherBranch, config); - custom_id = common.sanitizeText(custom_id, config); - - const currentCommit = commits.get(branches.get(curBranch)); - const otherCommit = commits.get(branches.get(otherBranch)); - if (curBranch === otherBranch) { - let error = new Error('Incorrect usage of "merge". Cannot merge a branch to itself'); - error.hash = { - text: 'merge ' + otherBranch, - token: 'merge ' + otherBranch, - line: '1', - loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, - expected: ['branch abc'], - }; - throw error; - } else if (currentCommit === undefined || !currentCommit) { - let error = new Error( - 'Incorrect usage of "merge". Current branch (' + curBranch + ')has no commits' - ); - error.hash = { - text: 'merge ' + otherBranch, - token: 'merge ' + otherBranch, - line: '1', - loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, - expected: ['commit'], - }; - throw error; - } else if (!branches.has(otherBranch)) { - let error = new Error( - 'Incorrect usage of "merge". Branch to be merged (' + otherBranch + ') does not exist' - ); - error.hash = { - text: 'merge ' + otherBranch, - token: 'merge ' + otherBranch, - line: '1', - loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, - expected: ['branch ' + otherBranch], - }; - throw error; - } else if (otherCommit === undefined || !otherCommit) { - let error = new Error( - 'Incorrect usage of "merge". Branch to be merged (' + otherBranch + ') has no commits' - ); - error.hash = { - text: 'merge ' + otherBranch, - token: 'merge ' + otherBranch, - line: '1', - loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, - expected: ['"commit"'], - }; - throw error; - } else if (currentCommit === otherCommit) { - let error = new Error('Incorrect usage of "merge". Both branches have same head'); - error.hash = { - text: 'merge ' + otherBranch, - token: 'merge ' + otherBranch, - line: '1', - loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, - expected: ['branch abc'], - }; - throw error; - } else if (custom_id && commits.has(custom_id)) { - let error = new Error( - 'Incorrect usage of "merge". Commit with id:' + - custom_id + - ' already exists, use different custom Id' - ); - error.hash = { - text: 'merge ' + otherBranch + custom_id + override_type + custom_tags?.join(','), - token: 'merge ' + otherBranch + custom_id + override_type + custom_tags?.join(','), - line: '1', - loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, - expected: [ - `merge ${otherBranch} ${custom_id}_UNIQUE ${override_type} ${custom_tags?.join(',')}`, - ], - }; - - throw error; - } - // if (isReachableFrom(currentCommit, otherCommit)) { - // log.debug('Already merged'); - // return; - // } - // if (isFastForwardable(currentCommit, otherCommit)) { - // branches.set(curBranch, branches.get(otherBranch)); - // head = commits.get(branches.get(curBranch)); - // } else { - // create merge commit - const commit = { - id: custom_id ? custom_id : seq + '-' + getId(), - message: 'merged branch ' + otherBranch + ' into ' + curBranch, - seq: seq++, - parents: [head == null ? null : head.id, branches.get(otherBranch)], - branch: curBranch, - type: commitType.MERGE, - customType: override_type, - customId: custom_id ? true : false, - tags: custom_tags ? custom_tags : [], - }; - head = commit; - commits.set(commit.id, commit); - branches.set(curBranch, commit.id); - // } - log.debug(branches); - log.debug('in mergeBranch'); -}; - -export const cherryPick = function (sourceId, targetId, tags, parentCommitId) { - log.debug('Entering cherryPick:', sourceId, targetId, tags); - const config = getConfig(); - sourceId = common.sanitizeText(sourceId, config); - targetId = common.sanitizeText(targetId, config); - tags = tags?.map((tag) => common.sanitizeText(tag, config)); - parentCommitId = common.sanitizeText(parentCommitId, config); - - if (!sourceId || !commits.has(sourceId)) { - let error = new Error( - 'Incorrect usage of "cherryPick". Source commit id should exist and provided' - ); - error.hash = { - text: 'cherryPick ' + sourceId + ' ' + targetId, - token: 'cherryPick ' + sourceId + ' ' + targetId, - line: '1', - loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, - expected: ['cherry-pick abc'], - }; - throw error; - } - let sourceCommit = commits.get(sourceId); - let sourceCommitBranch = sourceCommit.branch; - if ( - parentCommitId && - !(Array.isArray(sourceCommit.parents) && sourceCommit.parents.includes(parentCommitId)) - ) { - let error = new Error( - 'Invalid operation: The specified parent commit is not an immediate parent of the cherry-picked commit.' - ); - throw error; - } - if (sourceCommit.type === commitType.MERGE && !parentCommitId) { - let error = new Error( - 'Incorrect usage of cherry-pick: If the source commit is a merge commit, an immediate parent commit must be specified.' - ); - throw error; - } - if (!targetId || !commits.has(targetId)) { - // cherry-pick source commit to current branch - - if (sourceCommitBranch === curBranch) { - let error = new Error( - 'Incorrect usage of "cherryPick". Source commit is already on current branch' - ); - error.hash = { - text: 'cherryPick ' + sourceId + ' ' + targetId, - token: 'cherryPick ' + sourceId + ' ' + targetId, - line: '1', - loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, - expected: ['cherry-pick abc'], - }; - throw error; - } - const currentCommit = commits.get(branches.get(curBranch)); - if (currentCommit === undefined || !currentCommit) { - let error = new Error( - 'Incorrect usage of "cherry-pick". Current branch (' + curBranch + ')has no commits' - ); - error.hash = { - text: 'cherryPick ' + sourceId + ' ' + targetId, - token: 'cherryPick ' + sourceId + ' ' + targetId, - line: '1', - loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, - expected: ['cherry-pick abc'], - }; - throw error; - } - const commit = { - id: seq + '-' + getId(), - message: 'cherry-picked ' + sourceCommit + ' into ' + curBranch, - seq: seq++, - parents: [head == null ? null : head.id, sourceCommit.id], - branch: curBranch, - type: commitType.CHERRY_PICK, - tags: tags - ? tags.filter(Boolean) - : [ - `cherry-pick:${sourceCommit.id}${ - sourceCommit.type === commitType.MERGE ? `|parent:${parentCommitId}` : '' - }`, - ], - }; - head = commit; - commits.set(commit.id, commit); - branches.set(curBranch, commit.id); - log.debug(branches); - log.debug('in cherryPick'); - } -}; -export const checkout = function (branch) { - branch = common.sanitizeText(branch, getConfig()); - if (!branches.has(branch)) { - let error = new Error( - 'Trying to checkout branch which is not yet created. (Help try using "branch ' + branch + '")' - ); - error.hash = { - text: 'checkout ' + branch, - token: 'checkout ' + branch, - line: '1', - loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, - expected: ['"branch ' + branch + '"'], - }; - throw error; - } else { - curBranch = branch; - const id = branches.get(curBranch); - head = commits.get(id); - } -}; - -// export const reset = function (commitRef) { -// log.debug('in reset', commitRef); -// const ref = commitRef.split(':')[0]; -// let parentCount = parseInt(commitRef.split(':')[1]); -// let commit = ref === 'HEAD' ? head : commits.get(branches.get(ref)); -// log.debug(commit, parentCount); -// while (parentCount > 0) { -// commit = commits.get(commit.parent); -// parentCount--; -// if (!commit) { -// const err = 'Critical error - unique parent commit not found during reset'; -// log.error(err); -// throw err; -// } -// } -// head = commit; -// branches[curBranch] = commit.id; -// }; - -/** - * @param arr - * @param key - * @param newVal - */ -function upsert(arr, key, newVal) { - const index = arr.indexOf(key); - if (index === -1) { - arr.push(newVal); - } else { - arr.splice(index, 1, newVal); - } -} - -/** @param commitArr */ -function prettyPrintCommitHistory(commitArr) { - const commit = commitArr.reduce((out, commit) => { - if (out.seq > commit.seq) { - return out; - } - return commit; - }, commitArr[0]); - let line = ''; - commitArr.forEach(function (c) { - if (c === commit) { - line += '\t*'; - } else { - line += '\t|'; - } - }); - const label = [line, commit.id, commit.seq]; - for (let branch in branches) { - if (branches.get(branch) === commit.id) { - label.push(branch); - } - } - log.debug(label.join(' ')); - if (commit.parents && commit.parents.length == 2) { - const newCommit = commits.get(commit.parents[0]); - upsert(commitArr, commit, newCommit); - commitArr.push(commits.get(commit.parents[1])); - } else if (commit.parents.length == 0) { - return; - } else { - const nextCommit = commits.get(commit.parents); - upsert(commitArr, commit, nextCommit); - } - commitArr = uniqBy(commitArr, (c) => c.id); - prettyPrintCommitHistory(commitArr); -} - -export const prettyPrint = function () { - log.debug(commits); - const node = getCommitsArray()[0]; - prettyPrintCommitHistory([node]); -}; - -export const clear = function () { - commits = new Map(); - head = null; - const { mainBranchName, mainBranchOrder } = getConfig().gitGraph; - branches = new Map(); - branches.set(mainBranchName, null); - branchesConfig = new Map(); - branchesConfig.set(mainBranchName, { name: mainBranchName, order: mainBranchOrder }); - curBranch = mainBranchName; - seq = 0; - commonClear(); -}; - -export const getBranchesAsObjArray = function () { - const branchesArray = [...branchesConfig.values()] - .map((branchConfig, i) => { - if (branchConfig.order !== null) { - return branchConfig; - } - return { - ...branchConfig, - order: parseFloat(`0.${i}`, 10), - }; - }) - .sort((a, b) => a.order - b.order) - .map(({ name }) => ({ name })); - - return branchesArray; -}; - -export const getBranches = function () { - return branches; -}; -export const getCommits = function () { - return commits; -}; -export const getCommitsArray = function () { - const commitArr = [...commits.values()]; - commitArr.forEach(function (o) { - log.debug(o.id); - }); - commitArr.sort((a, b) => a.seq - b.seq); - return commitArr; -}; -export const getCurrentBranch = function () { - return curBranch; -}; -export const getDirection = function () { - return direction; -}; -export const getHead = function () { - return head; -}; - -export const commitType = { - NORMAL: 0, - REVERSE: 1, - HIGHLIGHT: 2, - MERGE: 3, - CHERRY_PICK: 4, -}; - -export default { - getConfig: () => getConfig().gitGraph, - setDirection, - setOptions, - getOptions, - commit, - branch, - merge, - cherryPick, - checkout, - //reset, - prettyPrint, - clear, - getBranchesAsObjArray, - getBranches, - getCommits, - getCommitsArray, - getCurrentBranch, - getDirection, - getHead, - setAccTitle, - getAccTitle, - getAccDescription, - setAccDescription, - setDiagramTitle, - getDiagramTitle, - commitType, -}; diff --git a/packages/mermaid/src/diagrams/git/gitGraphAst.ts b/packages/mermaid/src/diagrams/git/gitGraphAst.ts new file mode 100644 index 000000000..44597e9d7 --- /dev/null +++ b/packages/mermaid/src/diagrams/git/gitGraphAst.ts @@ -0,0 +1,522 @@ +import { log } from '../../logger.js'; +import { cleanAndMerge, random } from '../../utils.js'; +import { getConfig as commonGetConfig } from '../../config.js'; +import common from '../common/common.js'; +import { + setAccTitle, + getAccTitle, + getAccDescription, + setAccDescription, + clear as commonClear, + setDiagramTitle, + getDiagramTitle, +} from '../common/commonDb.js'; +import type { + DiagramOrientation, + Commit, + GitGraphDB, + CommitDB, + MergeDB, + BranchDB, + CherryPickDB, +} from './gitGraphTypes.js'; +import { commitType } from './gitGraphTypes.js'; +import { ImperativeState } from '../../utils/imperativeState.js'; + +import DEFAULT_CONFIG from '../../defaultConfig.js'; + +import type { GitGraphDiagramConfig } from '../../config.type.js'; +interface GitGraphState { + commits: Map; + head: Commit | null; + branchConfig: Map; + branches: Map; + currBranch: string; + direction: DiagramOrientation; + seq: number; + options: any; +} + +const DEFAULT_GITGRAPH_CONFIG: Required = DEFAULT_CONFIG.gitGraph; +const getConfig = (): Required => { + const config = cleanAndMerge({ + ...DEFAULT_GITGRAPH_CONFIG, + ...commonGetConfig().gitGraph, + }); + return config; +}; + +const state = new ImperativeState(() => { + const config = getConfig(); + const mainBranchName = config.mainBranchName; + const mainBranchOrder = config.mainBranchOrder; + return { + mainBranchName, + commits: new Map(), + head: null, + branchConfig: new Map([[mainBranchName, { name: mainBranchName, order: mainBranchOrder }]]), + branches: new Map([[mainBranchName, null]]), + currBranch: mainBranchName, + direction: 'LR', + seq: 0, + options: {}, + }; +}); + +function getID() { + return random({ length: 7 }); +} + +/** + * @param list - list of items + * @param fn - function to get the key + */ +function uniqBy(list: any[], fn: (item: any) => any) { + const recordMap = Object.create(null); + return list.reduce((out, item) => { + const key = fn(item); + if (!recordMap[key]) { + recordMap[key] = true; + out.push(item); + } + return out; + }, []); +} + +export const setDirection = function (dir: DiagramOrientation) { + state.records.direction = dir; +}; + +export const setOptions = function (rawOptString: string) { + log.debug('options str', rawOptString); + rawOptString = rawOptString?.trim(); + rawOptString = rawOptString || '{}'; + try { + state.records.options = JSON.parse(rawOptString); + } catch (e: any) { + log.error('error while parsing gitGraph options', e.message); + } +}; + +export const getOptions = function () { + return state.records.options; +}; + +export const commit = function (commitDB: CommitDB) { + let msg = commitDB.msg; + let id = commitDB.id; + const type = commitDB.type; + let tags = commitDB.tags; + + log.info('commit', msg, id, type, tags); + log.debug('Entering commit:', msg, id, type, tags); + const config = getConfig(); + id = common.sanitizeText(id, config); + msg = common.sanitizeText(msg, config); + tags = tags?.map((tag) => common.sanitizeText(tag, config)); + const newCommit: Commit = { + id: id ? id : state.records.seq + '-' + getID(), + message: msg, + seq: state.records.seq++, + type: type ?? commitType.NORMAL, + tags: tags ?? [], + parents: state.records.head == null ? [] : [state.records.head.id], + branch: state.records.currBranch, + }; + state.records.head = newCommit; + log.info('main branch', config.mainBranchName); + state.records.commits.set(newCommit.id, newCommit); + state.records.branches.set(state.records.currBranch, newCommit.id); + log.debug('in pushCommit ' + newCommit.id); +}; + +export const branch = function (branchDB: BranchDB) { + let name = branchDB.name; + const order = branchDB.order; + name = common.sanitizeText(name, getConfig()); + if (state.records.branches.has(name)) { + throw new Error( + `Trying to create an existing branch. (Help: Either use a new name if you want create a new branch or try using "checkout ${name}")` + ); + } + + state.records.branches.set(name, state.records.head != null ? state.records.head.id : null); + state.records.branchConfig.set(name, { name, order }); + checkout(name); + log.debug('in createBranch'); +}; + +export const merge = (mergeDB: MergeDB): void => { + let otherBranch = mergeDB.branch; + let customId = mergeDB.id; + const overrideType = mergeDB.type; + const customTags = mergeDB.tags; + const config = getConfig(); + otherBranch = common.sanitizeText(otherBranch, config); + if (customId) { + customId = common.sanitizeText(customId, config); + } + const currentBranchCheck = state.records.branches.get(state.records.currBranch); + const otherBranchCheck = state.records.branches.get(otherBranch); + const currentCommit = currentBranchCheck + ? state.records.commits.get(currentBranchCheck) + : undefined; + const otherCommit: Commit | undefined = otherBranchCheck + ? state.records.commits.get(otherBranchCheck) + : undefined; + if (currentCommit && otherCommit && currentCommit.branch === otherBranch) { + throw new Error(`Cannot merge branch '${otherBranch}' into itself.`); + } + if (state.records.currBranch === otherBranch) { + const error: any = new Error('Incorrect usage of "merge". Cannot merge a branch to itself'); + error.hash = { + text: `merge ${otherBranch}`, + token: `merge ${otherBranch}`, + expected: ['branch abc'], + }; + throw error; + } + if (currentCommit === undefined || !currentCommit) { + const error: any = new Error( + `Incorrect usage of "merge". Current branch (${state.records.currBranch})has no commits` + ); + error.hash = { + text: `merge ${otherBranch}`, + token: `merge ${otherBranch}`, + expected: ['commit'], + }; + throw error; + } + if (!state.records.branches.has(otherBranch)) { + const error: any = new Error( + 'Incorrect usage of "merge". Branch to be merged (' + otherBranch + ') does not exist' + ); + error.hash = { + text: `merge ${otherBranch}`, + token: `merge ${otherBranch}`, + expected: [`branch ${otherBranch}`], + }; + throw error; + } + if (otherCommit === undefined || !otherCommit) { + const error: any = new Error( + 'Incorrect usage of "merge". Branch to be merged (' + otherBranch + ') has no commits' + ); + error.hash = { + text: `merge ${otherBranch}`, + token: `merge ${otherBranch}`, + expected: ['"commit"'], + }; + throw error; + } + if (currentCommit === otherCommit) { + const error: any = new Error('Incorrect usage of "merge". Both branches have same head'); + error.hash = { + text: `merge ${otherBranch}`, + token: `merge ${otherBranch}`, + expected: ['branch abc'], + }; + throw error; + } + if (customId && state.records.commits.has(customId)) { + const error: any = new Error( + 'Incorrect usage of "merge". Commit with id:' + + customId + + ' already exists, use different custom Id' + ); + error.hash = { + text: `merge ${otherBranch} ${customId} ${overrideType} ${customTags?.join(' ')}`, + token: `merge ${otherBranch} ${customId} ${overrideType} ${customTags?.join(' ')}`, + expected: [ + `merge ${otherBranch} ${customId}_UNIQUE ${overrideType} ${customTags?.join(' ')}`, + ], + }; + + throw error; + } + + const verifiedBranch: string = otherBranchCheck ? otherBranchCheck : ''; //figure out a cleaner way to do this + + const commit = { + id: customId || `${state.records.seq}-${getID()}`, + message: `merged branch ${otherBranch} into ${state.records.currBranch}`, + seq: state.records.seq++, + parents: state.records.head == null ? [] : [state.records.head.id, verifiedBranch], + branch: state.records.currBranch, + type: commitType.MERGE, + customType: overrideType, + customId: customId ? true : false, + tags: customTags ?? [], + } satisfies Commit; + state.records.head = commit; + state.records.commits.set(commit.id, commit); + state.records.branches.set(state.records.currBranch, commit.id); + log.debug(state.records.branches); + log.debug('in mergeBranch'); +}; + +export const cherryPick = function (cherryPickDB: CherryPickDB) { + let sourceId = cherryPickDB.id; + let targetId = cherryPickDB.targetId; + let tags = cherryPickDB.tags; + let parentCommitId = cherryPickDB.parent; + log.debug('Entering cherryPick:', sourceId, targetId, tags); + const config = getConfig(); + sourceId = common.sanitizeText(sourceId, config); + targetId = common.sanitizeText(targetId, config); + + tags = tags?.map((tag) => common.sanitizeText(tag, config)); + + parentCommitId = common.sanitizeText(parentCommitId, config); + + if (!sourceId || !state.records.commits.has(sourceId)) { + const error: any = new Error( + 'Incorrect usage of "cherryPick". Source commit id should exist and provided' + ); + error.hash = { + text: `cherryPick ${sourceId} ${targetId}`, + token: `cherryPick ${sourceId} ${targetId}`, + expected: ['cherry-pick abc'], + }; + throw error; + } + + const sourceCommit = state.records.commits.get(sourceId); + if (sourceCommit === undefined || !sourceCommit) { + throw new Error('Incorrect usage of "cherryPick". Source commit id should exist and provided'); + } + if ( + parentCommitId && + !(Array.isArray(sourceCommit.parents) && sourceCommit.parents.includes(parentCommitId)) + ) { + const error = new Error( + 'Invalid operation: The specified parent commit is not an immediate parent of the cherry-picked commit.' + ); + throw error; + } + const sourceCommitBranch = sourceCommit.branch; + if (sourceCommit.type === commitType.MERGE && !parentCommitId) { + const error = new Error( + 'Incorrect usage of cherry-pick: If the source commit is a merge commit, an immediate parent commit must be specified.' + ); + throw error; + } + if (!targetId || !state.records.commits.has(targetId)) { + // cherry-pick source commit to current branch + + if (sourceCommitBranch === state.records.currBranch) { + const error: any = new Error( + 'Incorrect usage of "cherryPick". Source commit is already on current branch' + ); + error.hash = { + text: `cherryPick ${sourceId} ${targetId}`, + token: `cherryPick ${sourceId} ${targetId}`, + expected: ['cherry-pick abc'], + }; + throw error; + } + const currentCommitId = state.records.branches.get(state.records.currBranch); + if (currentCommitId === undefined || !currentCommitId) { + const error: any = new Error( + `Incorrect usage of "cherry-pick". Current branch (${state.records.currBranch})has no commits` + ); + error.hash = { + text: `cherryPick ${sourceId} ${targetId}`, + token: `cherryPick ${sourceId} ${targetId}`, + expected: ['cherry-pick abc'], + }; + throw error; + } + + const currentCommit = state.records.commits.get(currentCommitId); + if (currentCommit === undefined || !currentCommit) { + const error: any = new Error( + `Incorrect usage of "cherry-pick". Current branch (${state.records.currBranch})has no commits` + ); + error.hash = { + text: `cherryPick ${sourceId} ${targetId}`, + token: `cherryPick ${sourceId} ${targetId}`, + expected: ['cherry-pick abc'], + }; + throw error; + } + const commit = { + id: state.records.seq + '-' + getID(), + message: `cherry-picked ${sourceCommit?.message} into ${state.records.currBranch}`, + seq: state.records.seq++, + parents: state.records.head == null ? [] : [state.records.head.id, sourceCommit.id], + branch: state.records.currBranch, + type: commitType.CHERRY_PICK, + tags: tags + ? tags.filter(Boolean) + : [ + `cherry-pick:${sourceCommit.id}${ + sourceCommit.type === commitType.MERGE ? `|parent:${parentCommitId}` : '' + }`, + ], + }; + + state.records.head = commit; + state.records.commits.set(commit.id, commit); + state.records.branches.set(state.records.currBranch, commit.id); + log.debug(state.records.branches); + log.debug('in cherryPick'); + } +}; +export const checkout = function (branch: string) { + branch = common.sanitizeText(branch, getConfig()); + if (!state.records.branches.has(branch)) { + const error: any = new Error( + `Trying to checkout branch which is not yet created. (Help try using "branch ${branch}")` + ); + error.hash = { + text: `checkout ${branch}`, + token: `checkout ${branch}`, + expected: [`branch ${branch}`], + }; + throw error; + } else { + state.records.currBranch = branch; + const id = state.records.branches.get(state.records.currBranch); + if (id === undefined || !id) { + state.records.head = null; + } else { + state.records.head = state.records.commits.get(id) ?? null; + } + } +}; + +/** + * @param arr - array + * @param key - key + * @param newVal - new value + */ +function upsert(arr: any[], key: any, newVal: any) { + const index = arr.indexOf(key); + if (index === -1) { + arr.push(newVal); + } else { + arr.splice(index, 1, newVal); + } +} + +function prettyPrintCommitHistory(commitArr: Commit[]) { + const commit = commitArr.reduce((out, commit) => { + if (out.seq > commit.seq) { + return out; + } + return commit; + }, commitArr[0]); + let line = ''; + commitArr.forEach(function (c) { + if (c === commit) { + line += '\t*'; + } else { + line += '\t|'; + } + }); + const label = [line, commit.id, commit.seq]; + for (const branch in state.records.branches) { + if (state.records.branches.get(branch) === commit.id) { + label.push(branch); + } + } + log.debug(label.join(' ')); + if (commit.parents && commit.parents.length == 2 && commit.parents[0] && commit.parents[1]) { + const newCommit = state.records.commits.get(commit.parents[0]); + upsert(commitArr, commit, newCommit); + if (commit.parents[1]) { + commitArr.push(state.records.commits.get(commit.parents[1])!); + } + } else if (commit.parents.length == 0) { + return; + } else { + if (commit.parents[0]) { + const newCommit = state.records.commits.get(commit.parents[0]); + upsert(commitArr, commit, newCommit); + } + } + commitArr = uniqBy(commitArr, (c) => c.id); + prettyPrintCommitHistory(commitArr); +} + +export const prettyPrint = function () { + log.debug(state.records.commits); + const node = getCommitsArray()[0]; + prettyPrintCommitHistory([node]); +}; + +export const clear = function () { + state.reset(); + commonClear(); +}; + +export const getBranchesAsObjArray = function () { + const branchesArray = [...state.records.branchConfig.values()] + .map((branchConfig, i) => { + if (branchConfig.order !== null && branchConfig.order !== undefined) { + return branchConfig; + } + return { + ...branchConfig, + order: parseFloat(`0.${i}`), + }; + }) + .sort((a, b) => (a.order ?? 0) - (b.order ?? 0)) + .map(({ name }) => ({ name })); + + return branchesArray; +}; + +export const getBranches = function () { + return state.records.branches; +}; +export const getCommits = function () { + return state.records.commits; +}; +export const getCommitsArray = function () { + const commitArr = [...state.records.commits.values()]; + commitArr.forEach(function (o) { + log.debug(o.id); + }); + commitArr.sort((a, b) => a.seq - b.seq); + return commitArr; +}; +export const getCurrentBranch = function () { + return state.records.currBranch; +}; +export const getDirection = function () { + return state.records.direction; +}; +export const getHead = function () { + return state.records.head; +}; + +export const db: GitGraphDB = { + commitType, + getConfig, + setDirection, + setOptions, + getOptions, + commit, + branch, + merge, + cherryPick, + checkout, + //reset, + prettyPrint, + clear, + getBranchesAsObjArray, + getBranches, + getCommits, + getCommitsArray, + getCurrentBranch, + getDirection, + getHead, + setAccTitle, + getAccTitle, + getAccDescription, + setAccDescription, + setDiagramTitle, + getDiagramTitle, +}; diff --git a/packages/mermaid/src/diagrams/git/gitGraphDiagram.ts b/packages/mermaid/src/diagrams/git/gitGraphDiagram.ts index 2a9efdb59..d6e8a0613 100644 --- a/packages/mermaid/src/diagrams/git/gitGraphDiagram.ts +++ b/packages/mermaid/src/diagrams/git/gitGraphDiagram.ts @@ -1,13 +1,13 @@ // @ts-ignore: JISON doesn't support types -import gitGraphParser from './parser/gitGraph.jison'; -import gitGraphDb from './gitGraphAst.js'; +import { parser } from './gitGraphParser.js'; +import { db } from './gitGraphAst.js'; import gitGraphRenderer from './gitGraphRenderer.js'; import gitGraphStyles from './styles.js'; import type { DiagramDefinition } from '../../diagram-api/types.js'; export const diagram: DiagramDefinition = { - parser: gitGraphParser, - db: gitGraphDb, + parser, + db, renderer: gitGraphRenderer, styles: gitGraphStyles, }; diff --git a/packages/mermaid/src/diagrams/git/gitGraphParser.spec.js b/packages/mermaid/src/diagrams/git/gitGraphParser.spec.js deleted file mode 100644 index d498577fe..000000000 --- a/packages/mermaid/src/diagrams/git/gitGraphParser.spec.js +++ /dev/null @@ -1,272 +0,0 @@ -import gitGraphAst from './gitGraphAst.js'; -import { parser } from './parser/gitGraph.jison'; - -describe('when parsing a gitGraph', function () { - beforeEach(function () { - parser.yy = gitGraphAst; - parser.yy.clear(); - }); - it('should handle a gitGraph definition', function () { - const str = 'gitGraph:\n' + 'commit\n'; - - parser.parse(str); - const commits = parser.yy.getCommits(); - - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - }); - - it('should handle a gitGraph definition with empty options', function () { - const str = 'gitGraph:\n' + 'options\n' + ' end\n' + 'commit\n'; - - parser.parse(str); - const commits = parser.yy.getCommits(); - - expect(parser.yy.getOptions()).toEqual({}); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - }); - - it('should handle a gitGraph definition with valid options', function () { - const str = 'gitGraph:\n' + 'options\n' + '{"key": "value"}\n' + 'end\n' + 'commit\n'; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(parser.yy.getOptions().key).toBe('value'); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - }); - - it('should not fail on a gitGraph with malformed json', function () { - const str = 'gitGraph:\n' + 'options\n' + '{"key": "value"\n' + 'end\n' + 'commit\n'; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - }); - - it('should handle set direction top to bottom', function () { - const str = 'gitGraph TB:\n' + 'commit\n'; - - parser.parse(str); - const commits = parser.yy.getCommits(); - - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('TB'); - expect(parser.yy.getBranches().size).toBe(1); - }); - - it('should handle set direction bottom to top', function () { - const str = 'gitGraph BT:\n' + 'commit\n'; - - parser.parse(str); - const commits = parser.yy.getCommits(); - - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('BT'); - expect(parser.yy.getBranches().size).toBe(1); - }); - - it('should checkout a branch', function () { - const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n'; - - parser.parse(str); - const commits = parser.yy.getCommits(); - - expect(commits.size).toBe(0); - expect(parser.yy.getCurrentBranch()).toBe('new'); - }); - - it('should switch a branch', function () { - const str = 'gitGraph:\n' + 'branch new\n' + 'switch new\n'; - - parser.parse(str); - const commits = parser.yy.getCommits(); - - expect(commits.size).toBe(0); - expect(parser.yy.getCurrentBranch()).toBe('new'); - }); - - it('should add commits to checked out branch', function () { - const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n' + 'commit\n' + 'commit\n'; - - parser.parse(str); - const commits = parser.yy.getCommits(); - - expect(commits.size).toBe(2); - expect(parser.yy.getCurrentBranch()).toBe('new'); - const branchCommit = parser.yy.getBranches().get('new'); - expect(branchCommit).not.toBeNull(); - expect(commits.get(branchCommit).parent).not.toBeNull(); - }); - it('should handle commit with args', function () { - const str = 'gitGraph:\n' + 'commit "a commit"\n'; - - parser.parse(str); - const commits = parser.yy.getCommits(); - - expect(commits.size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe('a commit'); - expect(parser.yy.getCurrentBranch()).toBe('main'); - }); - - // Reset has been commented out in JISON - it.skip('should reset a branch', function () { - const str = - 'gitGraph:\n' + - 'commit\n' + - 'commit\n' + - 'branch newbranch\n' + - 'checkout newbranch\n' + - 'commit\n' + - 'reset main\n'; - - parser.parse(str); - - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(3); - expect(parser.yy.getCurrentBranch()).toBe('newbranch'); - expect(parser.yy.getBranches().get('newbranch')).toEqual(parser.yy.getBranches().get('main')); - expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches().get('newbranch')); - }); - - it.skip('reset can take an argument', function () { - const str = - 'gitGraph:\n' + - 'commit\n' + - 'commit\n' + - 'branch newbranch\n' + - 'checkout newbranch\n' + - 'commit\n' + - 'reset main^\n'; - - parser.parse(str); - - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(3); - expect(parser.yy.getCurrentBranch()).toBe('newbranch'); - const main = commits.get(parser.yy.getBranches().get('main')); - expect(parser.yy.getHead().id).toEqual(main.parent); - }); - - it.skip('should handle fast forwardable merges', function () { - const str = - 'gitGraph:\n' + - 'commit\n' + - 'branch newbranch\n' + - 'checkout newbranch\n' + - 'commit\n' + - 'commit\n' + - 'checkout main\n' + - 'merge newbranch\n'; - - parser.parse(str); - - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(4); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getBranches().get('newbranch')).toEqual(parser.yy.getBranches().get('main')); - expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches().get('newbranch')); - }); - - it('should handle cases when merge is a noop', function () { - const str = - 'gitGraph:\n' + - 'commit\n' + - 'branch newbranch\n' + - 'checkout newbranch\n' + - 'commit\n' + - 'commit\n' + - 'merge main\n'; - - parser.parse(str); - - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(4); - expect(parser.yy.getCurrentBranch()).toBe('newbranch'); - expect(parser.yy.getBranches().get('newbranch')).not.toEqual( - parser.yy.getBranches().get('main') - ); - expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches().get('newbranch')); - }); - - it('should handle merge with 2 parents', function () { - const str = - 'gitGraph:\n' + - 'commit\n' + - 'branch newbranch\n' + - 'checkout newbranch\n' + - 'commit\n' + - 'commit\n' + - 'checkout main\n' + - 'commit\n' + - 'merge newbranch\n'; - - parser.parse(str); - - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(5); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getBranches().get('newbranch')).not.toEqual( - parser.yy.getBranches().get('main') - ); - expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches().get('main')); - }); - - it.skip('should handle ff merge when history walk has two parents (merge commit)', function () { - const str = - 'gitGraph:\n' + - 'commit\n' + - 'branch newbranch\n' + - 'checkout newbranch\n' + - 'commit\n' + - 'commit\n' + - 'checkout main\n' + - 'commit\n' + - 'merge newbranch\n' + - 'commit\n' + - 'checkout newbranch\n' + - 'merge main\n'; - - parser.parse(str); - - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(7); - expect(parser.yy.getCurrentBranch()).toBe('newbranch'); - expect(parser.yy.getBranches().get('newbranch')).toEqual(parser.yy.getBranches().get('main')); - expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches().get('main')); - - parser.yy.prettyPrint(); - }); - - it('should generate an array of known branches', function () { - const str = - 'gitGraph:\n' + - 'commit\n' + - 'branch b1\n' + - 'checkout b1\n' + - 'commit\n' + - 'commit\n' + - 'branch b2\n'; - - parser.parse(str); - const branches = gitGraphAst.getBranchesAsObjArray(); - - expect(branches).toHaveLength(3); - expect(branches[0]).toHaveProperty('name', 'main'); - expect(branches[1]).toHaveProperty('name', 'b1'); - expect(branches[2]).toHaveProperty('name', 'b2'); - }); -}); diff --git a/packages/mermaid/src/diagrams/git/gitGraphParser.ts b/packages/mermaid/src/diagrams/git/gitGraphParser.ts new file mode 100644 index 000000000..c56bc6f44 --- /dev/null +++ b/packages/mermaid/src/diagrams/git/gitGraphParser.ts @@ -0,0 +1,243 @@ +import type { GitGraph } from '@mermaid-js/parser'; +import { parse } from '@mermaid-js/parser'; +import type { ParserDefinition } from '../../diagram-api/types.js'; +import { log } from '../../logger.js'; +import { populateCommonDb } from '../common/populateCommonDb.js'; +import { db } from './gitGraphAst.js'; +import { commitType } from './gitGraphTypes.js'; +import type { + CheckoutAst, + CherryPickingAst, + MergeAst, + CommitAst, + BranchAst, + GitGraphDBParseProvider, + CommitDB, + BranchDB, + MergeDB, + CherryPickDB, +} from './gitGraphTypes.js'; + +const populate = (ast: GitGraph, db: GitGraphDBParseProvider) => { + populateCommonDb(ast, db); + // @ts-ignore: this wont exist if the direction is not specified + if (ast.dir) { + // @ts-ignore: this wont exist if the direction is not specified + db.setDirection(ast.dir); + } + for (const statement of ast.statements) { + parseStatement(statement, db); + } +}; + +const parseStatement = (statement: any, db: GitGraphDBParseProvider) => { + const parsers: Record void> = { + Commit: (stmt) => db.commit(parseCommit(stmt)), + Branch: (stmt) => db.branch(parseBranch(stmt)), + Merge: (stmt) => db.merge(parseMerge(stmt)), + Checkout: (stmt) => db.checkout(parseCheckout(stmt)), + CherryPicking: (stmt) => db.cherryPick(parseCherryPicking(stmt)), + }; + + const parser = parsers[statement.$type]; + if (parser) { + parser(statement); + } else { + log.error(`Unknown statement type: ${statement.$type}`); + } +}; + +const parseCommit = (commit: CommitAst): CommitDB => { + const commitDB: CommitDB = { + id: commit.id, + msg: commit.message ?? '', + type: commit.type !== undefined ? commitType[commit.type] : commitType.NORMAL, + tags: commit.tags ?? undefined, + }; + return commitDB; +}; + +const parseBranch = (branch: BranchAst): BranchDB => { + const branchDB: BranchDB = { + name: branch.name, + order: branch.order ?? 0, + }; + return branchDB; +}; + +const parseMerge = (merge: MergeAst): MergeDB => { + const mergeDB: MergeDB = { + branch: merge.branch, + id: merge.id ?? '', + type: merge.type !== undefined ? commitType[merge.type] : undefined, + tags: merge.tags ?? undefined, + }; + return mergeDB; +}; + +const parseCheckout = (checkout: CheckoutAst): string => { + const branch = checkout.branch; + return branch; +}; + +const parseCherryPicking = (cherryPicking: CherryPickingAst): CherryPickDB => { + const cherryPickDB: CherryPickDB = { + id: cherryPicking.id, + targetId: '', + tags: cherryPicking.tags?.length === 0 ? undefined : cherryPicking.tags, + parent: cherryPicking.parent, + }; + return cherryPickDB; +}; + +export const parser: ParserDefinition = { + parse: async (input: string): Promise => { + const ast: GitGraph = await parse('gitGraph', input); + log.debug(ast); + populate(ast, db); + }, +}; + +if (import.meta.vitest) { + const { it, expect, describe } = import.meta.vitest; + + const mockDB: GitGraphDBParseProvider = { + commitType: commitType, + setDirection: vi.fn(), + commit: vi.fn(), + branch: vi.fn(), + merge: vi.fn(), + cherryPick: vi.fn(), + checkout: vi.fn(), + }; + + describe('GitGraph Parser', () => { + it('should parse a commit statement', () => { + const commit = { + $type: 'Commit', + id: '1', + message: 'test', + tags: ['tag1', 'tag2'], + type: 'NORMAL', + }; + parseStatement(commit, mockDB); + expect(mockDB.commit).toHaveBeenCalledWith({ + id: '1', + msg: 'test', + tags: ['tag1', 'tag2'], + type: 0, + }); + }); + it('should parse a branch statement', () => { + const branch = { + $type: 'Branch', + name: 'newBranch', + order: 1, + }; + parseStatement(branch, mockDB); + expect(mockDB.branch).toHaveBeenCalledWith({ name: 'newBranch', order: 1 }); + }); + it('should parse a checkout statement', () => { + const checkout = { + $type: 'Checkout', + branch: 'newBranch', + }; + parseStatement(checkout, mockDB); + expect(mockDB.checkout).toHaveBeenCalledWith('newBranch'); + }); + it('should parse a merge statement', () => { + const merge = { + $type: 'Merge', + branch: 'newBranch', + id: '1', + tags: ['tag1', 'tag2'], + type: 'NORMAL', + }; + parseStatement(merge, mockDB); + expect(mockDB.merge).toHaveBeenCalledWith({ + branch: 'newBranch', + id: '1', + tags: ['tag1', 'tag2'], + type: 0, + }); + }); + it('should parse a cherry picking statement', () => { + const cherryPick = { + $type: 'CherryPicking', + id: '1', + tags: ['tag1', 'tag2'], + parent: '2', + }; + parseStatement(cherryPick, mockDB); + expect(mockDB.cherryPick).toHaveBeenCalledWith({ + id: '1', + targetId: '', + parent: '2', + tags: ['tag1', 'tag2'], + }); + }); + + it('should parse a langium generated gitGraph ast', () => { + const dummy: GitGraph = { + $type: 'GitGraph', + statements: [], + }; + const gitGraphAst: GitGraph = { + $type: 'GitGraph', + statements: [ + { + $container: dummy, + $type: 'Commit', + id: '1', + message: 'test', + tags: ['tag1', 'tag2'], + type: 'NORMAL', + }, + { + $container: dummy, + $type: 'Branch', + name: 'newBranch', + order: 1, + }, + { + $container: dummy, + $type: 'Merge', + branch: 'newBranch', + id: '1', + tags: ['tag1', 'tag2'], + type: 'NORMAL', + }, + { + $container: dummy, + $type: 'Checkout', + branch: 'newBranch', + }, + { + $container: dummy, + $type: 'CherryPicking', + id: '1', + tags: ['tag1', 'tag2'], + parent: '2', + }, + ], + }; + + populate(gitGraphAst, mockDB); + + expect(mockDB.commit).toHaveBeenCalledWith({ + id: '1', + msg: 'test', + tags: ['tag1', 'tag2'], + type: 0, + }); + expect(mockDB.branch).toHaveBeenCalledWith({ name: 'newBranch', order: 1 }); + expect(mockDB.merge).toHaveBeenCalledWith({ + branch: 'newBranch', + id: '1', + tags: ['tag1', 'tag2'], + type: 0, + }); + expect(mockDB.checkout).toHaveBeenCalledWith('newBranch'); + }); + }); +} diff --git a/packages/mermaid/src/diagrams/git/gitGraphParserV2.spec.js b/packages/mermaid/src/diagrams/git/gitGraphParserV2.spec.js deleted file mode 100644 index 1fb64a5c4..000000000 --- a/packages/mermaid/src/diagrams/git/gitGraphParserV2.spec.js +++ /dev/null @@ -1,1107 +0,0 @@ -import gitGraphAst from './gitGraphAst.js'; -import { parser } from './parser/gitGraph.jison'; - -describe('when parsing a gitGraph', function () { - beforeEach(function () { - parser.yy = gitGraphAst; - parser.yy.clear(); - }); - it('should handle a gitGraph commit with NO pararms, get auto-generated reandom ID', function () { - const str = `gitGraph: - commit - `; - parser.parse(str); - const commits = parser.yy.getCommits(); - //console.info(commits); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe(''); - expect(commits.get(key).id).not.toBeNull(); - expect(commits.get(key).tags).toStrictEqual([]); - expect(commits.get(key).type).toBe(0); - }); - - it('should handle a gitGraph commit with custom commit id only', function () { - const str = `gitGraph: - commit id:"1111" - `; - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe(''); - expect(commits.get(key).id).toBe('1111'); - expect(commits.get(key).tags).toStrictEqual([]); - expect(commits.get(key).type).toBe(0); - }); - - it('should handle a gitGraph commit with custom commit tag only', function () { - const str = `gitGraph: - commit tag:"test" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe(''); - expect(commits.get(key).id).not.toBeNull(); - expect(commits.get(key).tags).toStrictEqual(['test']); - expect(commits.get(key).type).toBe(0); - }); - - it('should handle a gitGraph commit with custom commit type HIGHLIGHT only', function () { - const str = `gitGraph: - commit type: HIGHLIGHT - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe(''); - expect(commits.get(key).id).not.toBeNull(); - expect(commits.get(key).tags).toStrictEqual([]); - expect(commits.get(key).type).toBe(2); - }); - - it('should handle a gitGraph commit with custom commit type REVERSE only', function () { - const str = `gitGraph: - commit type: REVERSE - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe(''); - expect(commits.get(key).id).not.toBeNull(); - expect(commits.get(key).tags).toStrictEqual([]); - expect(commits.get(key).type).toBe(1); - }); - - it('should handle a gitGraph commit with custom commit type NORMAL only', function () { - const str = `gitGraph: - commit type: NORMAL - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe(''); - expect(commits.get(key).id).not.toBeNull(); - expect(commits.get(key).tags).toStrictEqual([]); - expect(commits.get(key).type).toBe(0); - }); - - it('should handle a gitGraph commit with custom commit msg only', function () { - const str = `gitGraph: - commit "test commit" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe('test commit'); - expect(commits.get(key).id).not.toBeNull(); - expect(commits.get(key).tags).toStrictEqual([]); - expect(commits.get(key).type).toBe(0); - }); - - it('should handle a gitGraph commit with custom commit "msg:" key only', function () { - const str = `gitGraph: - commit msg: "test commit" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe('test commit'); - expect(commits.get(key).id).not.toBeNull(); - expect(commits.get(key).tags).toStrictEqual([]); - expect(commits.get(key).type).toBe(0); - }); - - it('should handle a gitGraph commit with custom commit id, tag only', function () { - const str = `gitGraph: - commit id:"1111" tag: "test tag" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe(''); - expect(commits.get(key).id).toBe('1111'); - expect(commits.get(key).tags).toStrictEqual(['test tag']); - expect(commits.get(key).type).toBe(0); - }); - - it('should handle a gitGraph commit with custom commit type, tag only', function () { - const str = `gitGraph: - commit type:HIGHLIGHT tag: "test tag" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe(''); - expect(commits.get(key).id).not.toBeNull(); - expect(commits.get(key).tags).toStrictEqual(['test tag']); - expect(commits.get(key).type).toBe(2); - }); - - it('should handle a gitGraph commit with custom commit tag and type only', function () { - const str = `gitGraph: - commit tag: "test tag" type:HIGHLIGHT - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe(''); - expect(commits.get(key).id).not.toBeNull(); - expect(commits.get(key).tags).toStrictEqual(['test tag']); - expect(commits.get(key).type).toBe(2); - }); - - it('should handle a gitGraph commit with custom commit id, type and tag only', function () { - const str = `gitGraph: - commit id:"1111" type:REVERSE tag: "test tag" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe(''); - expect(commits.get(key).id).toBe('1111'); - expect(commits.get(key).tags).toStrictEqual(['test tag']); - expect(commits.get(key).type).toBe(1); - }); - - it('should handle a gitGraph commit with custom commit id, type, tag and msg', function () { - const str = `gitGraph: - commit id:"1111" type:REVERSE tag: "test tag" msg:"test msg" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe('test msg'); - expect(commits.get(key).id).toBe('1111'); - expect(commits.get(key).tags).toStrictEqual(['test tag']); - expect(commits.get(key).type).toBe(1); - }); - - it('should handle a gitGraph commit with custom type,tag, msg, commit id,', function () { - const str = `gitGraph: - commit type:REVERSE tag: "test tag" msg: "test msg" id: "1111" - - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe('test msg'); - expect(commits.get(key).id).toBe('1111'); - expect(commits.get(key).tags).toStrictEqual(['test tag']); - expect(commits.get(key).type).toBe(1); - }); - - it('should handle a gitGraph commit with custom tag, msg, commit id, type,', function () { - const str = `gitGraph: - commit tag: "test tag" msg:"test msg" id:"1111" type:REVERSE - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe('test msg'); - expect(commits.get(key).id).toBe('1111'); - expect(commits.get(key).tags).toStrictEqual(['test tag']); - expect(commits.get(key).type).toBe(1); - }); - - it('should handle a gitGraph commit with custom msg, commit id, type,tag', function () { - const str = `gitGraph: - commit msg:"test msg" id:"1111" type:REVERSE tag: "test tag" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - const key = commits.keys().next().value; - expect(commits.get(key).message).toBe('test msg'); - expect(commits.get(key).id).toBe('1111'); - expect(commits.get(key).tags).toStrictEqual(['test tag']); - expect(commits.get(key).type).toBe(1); - }); - - it('should handle 3 straight commits', function () { - const str = `gitGraph: - commit - commit - commit - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(3); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(1); - }); - - it('should handle new branch creation', function () { - const str = `gitGraph: - commit - branch testBranch - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('testBranch'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(2); - }); - - it('should allow quoted branch names', function () { - const str = `gitGraph: - commit - branch "branch" - checkout "branch" - commit - checkout main - merge "branch" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(3); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(2); - const [commit1, commit2, commit3] = commits.keys(); - expect(commits.get(commit1).branch).toBe('main'); - expect(commits.get(commit2).branch).toBe('branch'); - expect(commits.get(commit3).branch).toBe('main'); - expect(parser.yy.getBranchesAsObjArray()).toStrictEqual([{ name: 'main' }, { name: 'branch' }]); - }); - - it('should allow _-./ characters in branch names', function () { - const str = `gitGraph: - commit - branch azAZ_-./test - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('azAZ_-./test'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(2); - }); - - it('should allow branch names starting with numbers', function () { - const str = `gitGraph: - commit - %% branch names starting with numbers are not recommended, but are supported by git - branch 1.0.1 - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('1.0.1'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(2); - }); - - it('should allow branch names starting with unusual prefixes', function () { - const str = `gitGraph: - commit - %% branch names starting with numbers are not recommended, but are supported by git - branch branch01 - branch checkout02 - branch cherry-pick03 - branch branch/example-branch - branch merge/test_merge - %% single character branch name - branch A - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('A'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(7); - expect([...parser.yy.getBranches().keys()]).toEqual( - expect.arrayContaining([ - 'branch01', - 'checkout02', - 'cherry-pick03', - 'branch/example-branch', - 'merge/test_merge', - 'A', - ]) - ); - }); - - it('should handle new branch checkout', function () { - const str = `gitGraph: - commit - branch testBranch - checkout testBranch - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('testBranch'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(2); - }); - it('should handle new branch checkout with order', function () { - const str = `gitGraph: - commit - branch test1 order: 3 - branch test2 order: 2 - branch test3 order: 1 - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('test3'); - expect(parser.yy.getBranches().size).toBe(4); - expect(parser.yy.getBranchesAsObjArray()).toStrictEqual([ - { name: 'main' }, - { name: 'test3' }, - { name: 'test2' }, - { name: 'test1' }, - ]); - }); - it('should handle new branch checkout with and without order', function () { - const str = `gitGraph: - commit - branch test1 order: 1 - branch test2 - branch test3 - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('test3'); - expect(parser.yy.getBranches().size).toBe(4); - expect(parser.yy.getBranchesAsObjArray()).toStrictEqual([ - { name: 'main' }, - { name: 'test2' }, - { name: 'test3' }, - { name: 'test1' }, - ]); - }); - - it('should handle new branch checkout & commit', function () { - const str = `gitGraph: - commit - branch testBranch - checkout testBranch - commit - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(2); - expect(parser.yy.getCurrentBranch()).toBe('testBranch'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(2); - const [commit1, commit2] = commits.keys(); - expect(commits.get(commit1).branch).toBe('main'); - expect(commits.get(commit1).parents).toStrictEqual([]); - expect(commits.get(commit2).branch).toBe('testBranch'); - expect(commits.get(commit2).parents).toStrictEqual([commit1]); - }); - - it('should handle new branch checkout & commit and merge', function () { - const str = `gitGraph: - commit - branch testBranch - checkout testBranch - commit - commit - checkout main - merge testBranch - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(4); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(2); - const [commit1, commit2, commit3, commit4] = commits.keys(); - expect(commits.get(commit1).branch).toBe('main'); - expect(commits.get(commit1).parents).toStrictEqual([]); - expect(commits.get(commit2).branch).toBe('testBranch'); - expect(commits.get(commit2).parents).toStrictEqual([commits.get(commit1).id]); - expect(commits.get(commit3).branch).toBe('testBranch'); - expect(commits.get(commit3).parents).toStrictEqual([commits.get(commit2).id]); - expect(commits.get(commit4).branch).toBe('main'); - expect(commits.get(commit4).parents).toStrictEqual([ - commits.get(commit1).id, - commits.get(commit3).id, - ]); - expect(parser.yy.getBranchesAsObjArray()).toStrictEqual([ - { name: 'main' }, - { name: 'testBranch' }, - ]); - }); - - it('should handle new branch switch', function () { - const str = `gitGraph: - commit - branch testBranch - switch testBranch - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(1); - expect(parser.yy.getCurrentBranch()).toBe('testBranch'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(2); - }); - - it('should handle new branch switch & commit', function () { - const str = `gitGraph: - commit - branch testBranch - switch testBranch - commit - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(2); - expect(parser.yy.getCurrentBranch()).toBe('testBranch'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(2); - const [commit1, commit2] = commits.keys(); - expect(commits.get(commit1).branch).toBe('main'); - expect(commits.get(commit1).parents).toStrictEqual([]); - expect(commits.get(commit2).branch).toBe('testBranch'); - expect(commits.get(commit2).parents).toStrictEqual([commit1]); - }); - - it('should handle new branch switch & commit and merge', function () { - const str = `gitGraph: - commit - branch testBranch - switch testBranch - commit - commit - switch main - merge testBranch - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(4); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(2); - const [commit1, commit2, commit3, commit4] = commits.keys(); - expect(commits.get(commit1).branch).toBe('main'); - expect(commits.get(commit1).parents).toStrictEqual([]); - expect(commits.get(commit2).branch).toBe('testBranch'); - expect(commits.get(commit2).parents).toStrictEqual([commits.get(commit1).id]); - expect(commits.get(commit3).branch).toBe('testBranch'); - expect(commits.get(commit3).parents).toStrictEqual([commits.get(commit2).id]); - expect(commits.get(commit4).branch).toBe('main'); - expect(commits.get(commit4).parents).toStrictEqual([ - commits.get(commit1).id, - commits.get(commit3).id, - ]); - expect(parser.yy.getBranchesAsObjArray()).toStrictEqual([ - { name: 'main' }, - { name: 'testBranch' }, - ]); - }); - - it('should handle merge tags', function () { - const str = `gitGraph: - commit - branch testBranch - checkout testBranch - commit - checkout main - merge testBranch tag: "merge-tag" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(3); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - expect(parser.yy.getBranches().size).toBe(2); - const [commit1, commit2, commit3] = commits.keys(); - expect(commits.get(commit1).branch).toBe('main'); - expect(commits.get(commit1).parents).toStrictEqual([]); - - expect(commits.get(commit2).branch).toBe('testBranch'); - expect(commits.get(commit2).parents).toStrictEqual([commits.get(commit1).id]); - - expect(commits.get(commit3).branch).toBe('main'); - expect(commits.get(commit3).parents).toStrictEqual([ - commits.get(commit1).id, - commits.get(commit2).id, - ]); - expect(commits.get(commit3).tags).toStrictEqual(['merge-tag']); - expect(parser.yy.getBranchesAsObjArray()).toStrictEqual([ - { name: 'main' }, - { name: 'testBranch' }, - ]); - }); - - it('should handle merge with custom ids, tags and typr', function () { - const str = `gitGraph: - commit - branch testBranch - checkout testBranch - commit - checkout main - %% Merge Tag and ID - merge testBranch tag: "merge-tag" id: "2-222" - branch testBranch2 - checkout testBranch2 - commit - checkout main - %% Merge ID and Tag (reverse order) - merge testBranch2 id: "4-444" tag: "merge-tag2" type:HIGHLIGHT - branch testBranch3 - checkout testBranch3 - commit - checkout main - %% just Merge ID - merge testBranch3 id: "6-666" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(7); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getDirection()).toBe('LR'); - - // The order of these commits is in alphabetical order of IDs - const [ - mainCommit, - testBranchCommit, - testBranchMerge, - testBranch2Commit, - testBranch2Merge, - testBranch3Commit, - testBranch3Merge, - ] = [...commits.values()]; - - expect(mainCommit.branch).toBe('main'); - expect(mainCommit.parents).toStrictEqual([]); - - expect(testBranchCommit.branch).toBe('testBranch'); - expect(testBranchCommit.parents).toStrictEqual([mainCommit.id]); - - expect(testBranchMerge.branch).toBe('main'); - expect(testBranchMerge.parents).toStrictEqual([mainCommit.id, testBranchCommit.id]); - expect(testBranchMerge.tags).toStrictEqual(['merge-tag']); - expect(testBranchMerge.id).toBe('2-222'); - - expect(testBranch2Merge.branch).toBe('main'); - expect(testBranch2Merge.parents).toStrictEqual([testBranchMerge.id, testBranch2Commit.id]); - expect(testBranch2Merge.tags).toStrictEqual(['merge-tag2']); - expect(testBranch2Merge.id).toBe('4-444'); - expect(testBranch2Merge.customType).toBe(2); - expect(testBranch2Merge.customId).toBe(true); - - expect(testBranch3Merge.branch).toBe('main'); - expect(testBranch3Merge.parents).toStrictEqual([testBranch2Merge.id, testBranch3Commit.id]); - expect(testBranch3Merge.id).toBe('6-666'); - - expect(parser.yy.getBranchesAsObjArray()).toStrictEqual([ - { name: 'main' }, - { name: 'testBranch' }, - { name: 'testBranch2' }, - { name: 'testBranch3' }, - ]); - }); - - it('should support cherry-picking commits', function () { - const str = `gitGraph - commit id: "ZERO" - branch develop - commit id:"A" - checkout main - cherry-pick id:"A" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - const cherryPickCommitID = [...commits.keys()][2]; - expect(commits.get(cherryPickCommitID).tags).toStrictEqual(['cherry-pick:A']); - expect(commits.get(cherryPickCommitID).branch).toBe('main'); - }); - - it('should support cherry-picking commits with custom tag', function () { - const str = `gitGraph - commit id: "ZERO" - branch develop - commit id:"A" - checkout main - cherry-pick id:"A" tag:"MyTag" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - const cherryPickCommitID = [...commits.keys()][2]; - expect(commits.get(cherryPickCommitID).tags).toStrictEqual(['MyTag']); - expect(commits.get(cherryPickCommitID).branch).toBe('main'); - }); - - it('should support cherry-picking commits with no tag', function () { - const str = `gitGraph - commit id: "ZERO" - branch develop - commit id:"A" - checkout main - cherry-pick id:"A" tag:"" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - const cherryPickCommitID = [...commits.keys()][2]; - expect(commits.get(cherryPickCommitID).tags).toStrictEqual([]); - expect(commits.get(cherryPickCommitID).branch).toBe('main'); - }); - - it('should support cherry-picking of merge commits', function () { - const str = `gitGraph - commit id: "ZERO" - branch feature - branch release - checkout feature - commit id: "A" - commit id: "B" - checkout main - merge feature id: "M" - checkout release - cherry-pick id: "M" parent:"B" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - const cherryPickCommitID = [...commits.keys()][4]; - expect(commits.get(cherryPickCommitID).tags).toStrictEqual(['cherry-pick:M|parent:B']); - expect(commits.get(cherryPickCommitID).branch).toBe('release'); - }); - - it('should support cherry-picking of merge commits with tag', function () { - const str = `gitGraph - commit id: "ZERO" - branch feature - branch release - checkout feature - commit id: "A" - commit id: "B" - checkout main - merge feature id: "M" - checkout release - cherry-pick id: "M" parent:"ZERO" tag: "v1.0" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - const cherryPickCommitID = [...commits.keys()][4]; - expect(commits.get(cherryPickCommitID).tags).toStrictEqual(['v1.0']); - expect(commits.get(cherryPickCommitID).branch).toBe('release'); - }); - - it('should support cherry-picking of merge commits with additional commit', function () { - const str = `gitGraph - commit id: "ZERO" - branch feature - branch release - checkout feature - commit id: "A" - commit id: "B" - checkout main - merge feature id: "M" - checkout release - commit id: "C" - cherry-pick id: "M" tag: "v2.1:ZERO" parent:"ZERO" - commit id: "D" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - const cherryPickCommitID = [...commits.keys()][5]; - expect(commits.get(cherryPickCommitID).tags).toStrictEqual(['v2.1:ZERO']); - expect(commits.get(cherryPickCommitID).branch).toBe('release'); - }); - - it('should support cherry-picking of merge commits with empty tag', function () { - const str = `gitGraph - commit id: "ZERO" - branch feature - branch release - checkout feature - commit id: "A" - commit id: "B" - checkout main - merge feature id: "M" - checkout release - commit id: "C" - cherry-pick id:"M" parent: "ZERO" tag:"" - commit id: "D" - cherry-pick id:"M" tag:"" parent: "B" - `; - - parser.parse(str); - const commits = parser.yy.getCommits(); - const cherryPickCommitID = [...commits.keys()][5]; - const cherryPickCommitID2 = [...commits.keys()][7]; - expect(commits.get(cherryPickCommitID).tags).toStrictEqual([]); - expect(commits.get(cherryPickCommitID2).tags).toStrictEqual([]); - expect(commits.get(cherryPickCommitID).branch).toBe('release'); - }); - - it('should fail cherry-picking of merge commits if the parent of merge commits is not specified', function () { - expect(() => - parser - .parse( - `gitGraph - commit id: "ZERO" - branch feature - branch release - checkout feature - commit id: "A" - commit id: "B" - checkout main - merge feature id: "M" - checkout release - commit id: "C" - cherry-pick id:"M" - ` - ) - .toThrow( - 'Incorrect usage of cherry-pick: If the source commit is a merge commit, an immediate parent commit must be specified.' - ) - ); - }); - - it('should fail cherry-picking of merge commits when the parent provided is not an immediate parent of cherry picked commit', function () { - expect(() => - parser - .parse( - `gitGraph - commit id: "ZERO" - branch feature - branch release - checkout feature - commit id: "A" - commit id: "B" - checkout main - merge feature id: "M" - checkout release - commit id: "C" - cherry-pick id:"M" parent: "A" - ` - ) - .toThrow( - 'Invalid operation: The specified parent commit is not an immediate parent of the cherry-picked commit.' - ) - ); - }); - - it('should throw error when try to branch existing branch: main', function () { - const str = `gitGraph - commit - branch testBranch - commit - branch main - commit - checkout main - merge testBranch - `; - - try { - parser.parse(str); - // Fail test if above expression doesn't throw anything. - expect(true).toBe(false); - } catch (e) { - expect(e.message).toBe( - 'Trying to create an existing branch. (Help: Either use a new name if you want create a new branch or try using "checkout main")' - ); - } - }); - it('should throw error when try to branch existing branch: testBranch', function () { - const str = `gitGraph - commit - branch testBranch - commit - branch testBranch - commit - checkout main - merge testBranch - `; - - try { - parser.parse(str); - // Fail test if above expression doesn't throw anything. - expect(true).toBe(false); - } catch (e) { - expect(e.message).toBe( - 'Trying to create an existing branch. (Help: Either use a new name if you want create a new branch or try using "checkout testBranch")' - ); - } - }); - it('should throw error when try to checkout unknown branch: testBranch', function () { - const str = `gitGraph - commit - checkout testBranch - commit - branch testBranch - commit - checkout main - merge testBranch - `; - - try { - parser.parse(str); - // Fail test if above expression doesn't throw anything. - expect(true).toBe(false); - } catch (e) { - expect(e.message).toBe( - 'Trying to checkout branch which is not yet created. (Help try using "branch testBranch")' - ); - } - }); - it('should throw error when trying to merge, when current branch has no commits', function () { - const str = `gitGraph - merge testBranch - commit - checkout testBranch - commit - branch testBranch - commit - checkout main - merge testBranch - `; - - try { - parser.parse(str); - // Fail test if above expression doesn't throw anything. - expect(true).toBe(false); - } catch (e) { - expect(e.message).toBe('Incorrect usage of "merge". Current branch (main)has no commits'); - } - }); - it('should throw error when trying to merge unknown branch', function () { - const str = `gitGraph - commit - merge testBranch - commit - checkout testBranch - commit - branch testBranch - commit - checkout main - merge testBranch - `; - - try { - parser.parse(str); - // Fail test if above expression doesn't throw anything. - expect(true).toBe(false); - } catch (e) { - expect(e.message).toBe( - 'Incorrect usage of "merge". Branch to be merged (testBranch) does not exist' - ); - } - }); - it('should throw error when trying to merge branch to itself', function () { - const str = `gitGraph - commit - branch testBranch - merge testBranch - `; - - try { - parser.parse(str); - // Fail test if above expression doesn't throw anything. - expect(true).toBe(false); - } catch (e) { - expect(e.message).toBe('Incorrect usage of "merge". Cannot merge a branch to itself'); - } - }); - - it('should throw error when using existing id as merge ID', function () { - const str = `gitGraph - commit id: "1-111" - branch testBranch - commit id: "2-222" - commit id: "3-333" - checkout main - merge testBranch id: "1-111" - `; - - try { - parser.parse(str); - // Fail test if above expression doesn't throw anything. - expect(true).toBe(false); - } catch (e) { - expect(e.message).toBe( - 'Incorrect usage of "merge". Commit with id:1-111 already exists, use different custom Id' - ); - } - }); - it('should throw error when trying to merge branches having same heads', function () { - const str = `gitGraph - commit - branch testBranch - checkout main - merge testBranch - `; - - try { - parser.parse(str); - // Fail test if above expression doesn't throw anything. - expect(true).toBe(false); - } catch (e) { - expect(e.message).toBe('Incorrect usage of "merge". Both branches have same head'); - } - }); - it('should throw error when trying to merge branch which has no commits', function () { - const str = `gitGraph - branch test1 - - checkout main - commit - merge test1 - `; - - try { - parser.parse(str); - // Fail test if above expression doesn't throw anything. - expect(true).toBe(false); - } catch (e) { - expect(e.message).toBe( - 'Incorrect usage of "merge". Branch to be merged (test1) has no commits' - ); - } - }); - describe('accessibility', () => { - it('should handle a title and a description (accDescr)', () => { - const str = `gitGraph: - accTitle: This is a title - accDescr: This is a description - commit - `; - parser.parse(str); - expect(parser.yy.getAccTitle()).toBe('This is a title'); - expect(parser.yy.getAccDescription()).toBe('This is a description'); - }); - it('should handle a title and a multiline description (accDescr)', () => { - const str = `gitGraph: - accTitle: This is a title - accDescr { - This is a description - using multiple lines - } - commit - `; - parser.parse(str); - expect(parser.yy.getAccTitle()).toBe('This is a title'); - expect(parser.yy.getAccDescription()).toBe('This is a description\nusing multiple lines'); - }); - }); - - describe('unsafe properties', () => { - for (const prop of ['__proto__', 'constructor']) { - it(`should work with custom commit id or branch name ${prop}`, () => { - const str = `gitGraph - commit id:"${prop}" - branch ${prop} - checkout ${prop} - commit - checkout main - merge ${prop} - `; - parser.parse(str); - const commits = parser.yy.getCommits(); - expect(commits.size).toBe(3); - expect(commits.keys().next().value).toBe(prop); - expect(parser.yy.getCurrentBranch()).toBe('main'); - expect(parser.yy.getBranches().size).toBe(2); - expect(parser.yy.getBranchesAsObjArray()[1].name).toBe(prop); - }); - } - }); -}); diff --git a/packages/mermaid/src/diagrams/git/gitGraphRenderer.js b/packages/mermaid/src/diagrams/git/gitGraphRenderer.js deleted file mode 100644 index b8b13e089..000000000 --- a/packages/mermaid/src/diagrams/git/gitGraphRenderer.js +++ /dev/null @@ -1,893 +0,0 @@ -import { select } from 'd3'; -import { getConfig, setupGraphViewbox } from '../../diagram-api/diagramAPI.js'; -import { log } from '../../logger.js'; -import utils from '../../utils.js'; - -/** - * @typedef {Map} CommitMap - */ - -/** @type {CommitMap} */ -let allCommitsDict = new Map(); - -const commitType = { - NORMAL: 0, - REVERSE: 1, - HIGHLIGHT: 2, - MERGE: 3, - CHERRY_PICK: 4, -}; - -const THEME_COLOR_LIMIT = 8; - -let branchPos = {}; -let commitPos = {}; -let lanes = []; -let maxPos = 0; -let dir = 'LR'; -let defaultPos = 30; -const clear = () => { - branchPos = new Map(); - commitPos = new Map(); - allCommitsDict = new Map(); - maxPos = 0; - lanes = []; - dir = 'LR'; -}; - -/** - * Draws a text, used for labels of the branches - * - * @param {string} txt The text - * @returns {SVGElement} - */ -const drawText = (txt) => { - const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text'); - let rows = []; - - // Handling of new lines in the label - if (typeof txt === 'string') { - rows = txt.split(/\\n|\n|/gi); - } else if (Array.isArray(txt)) { - rows = txt; - } else { - rows = []; - } - - for (const row of rows) { - const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); - tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve'); - tspan.setAttribute('dy', '1em'); - tspan.setAttribute('x', '0'); - tspan.setAttribute('class', 'row'); - tspan.textContent = row.trim(); - svgLabel.appendChild(tspan); - } - /** - * @param svg - * @param selector - */ - return svgLabel; -}; - -/** - * Searches for the closest parent from the parents list passed as argument. - * The parents list comes from an individual commit. The closest parent is actually - * the one farther down the graph, since that means it is closer to its child. - * - * @param {string[]} parents - * @returns {string | undefined} - */ -const findClosestParent = (parents) => { - let closestParent = ''; - let maxPosition = 0; - - parents.forEach((parent) => { - const parentPosition = - dir === 'TB' || dir === 'BT' ? commitPos.get(parent).y : commitPos.get(parent).x; - if (parentPosition >= maxPosition) { - closestParent = parent; - maxPosition = parentPosition; - } - }); - - return closestParent || undefined; -}; - -/** - * Searches for the closest parent from the parents list passed as argument for Bottom-to-Top orientation. - * The parents list comes from an individual commit. The closest parent is actually - * the one farther down the graph, since that means it is closer to its child. - * - * @param {string[]} parents - * @returns {string | undefined} - */ -const findClosestParentBT = (parents) => { - let closestParent = ''; - let maxPosition = Infinity; - - parents.forEach((parent) => { - const parentPosition = commitPos.get(parent).y; - if (parentPosition <= maxPosition) { - closestParent = parent; - maxPosition = parentPosition; - } - }); - - return closestParent || undefined; -}; - -/** - * Sets the position of the commit elements when the orientation is set to BT-Parallel. - * This is needed to render the chart in Bottom-to-Top mode while keeping the parallel - * commits in the correct position. First, it finds the correct position of the root commit - * using the findClosestParent method. Then, it uses the findClosestParentBT to set the position - * of the remaining commits. - * - * @param {any} sortedKeys - * @param {CommitMap} commits - * @param {any} defaultPos - * @param {any} commitStep - * @param {any} layoutOffset - */ -const setParallelBTPos = (sortedKeys, commits, defaultPos, commitStep, layoutOffset) => { - let curPos = defaultPos; - let maxPosition = defaultPos; - let roots = []; - sortedKeys.forEach((key) => { - const commit = commits.get(key); - if (commit.parents.length) { - const closestParent = findClosestParent(commit.parents); - curPos = commitPos.get(closestParent).y + commitStep; - if (curPos >= maxPosition) { - maxPosition = curPos; - } - } else { - roots.push(commit); - } - const x = branchPos.get(commit.branch).pos; - const y = curPos + layoutOffset; - commitPos.set(commit.id, { x: x, y: y }); - }); - curPos = maxPosition; - roots.forEach((commit) => { - const posWithOffset = curPos + defaultPos; - const y = posWithOffset; - const x = branchPos.get(commit.branch).pos; - commitPos.set(commit.id, { x: x, y: y }); - }); - sortedKeys.forEach((key) => { - const commit = commits.get(key); - if (commit.parents.length) { - const closestParent = findClosestParentBT(commit.parents); - curPos = commitPos.get(closestParent).y - commitStep; - if (curPos <= maxPosition) { - maxPosition = curPos; - } - const x = branchPos.get(commit.branch).pos; - const y = curPos - layoutOffset; - commitPos.set(commit.id, { x: x, y: y }); - } - }); -}; - -/** - * Draws the commits with its symbol and labels. The function has two modes, one which only - * calculates the positions and one that does the actual drawing. This for a simple way getting the - * vertical layering correct in the graph. - * - * @param {any} svg - * @param {CommitMap} commits - * @param {any} modifyGraph - */ -const drawCommits = (svg, commits, modifyGraph) => { - const gitGraphConfig = getConfig().gitGraph; - const gBullets = svg.append('g').attr('class', 'commit-bullets'); - const gLabels = svg.append('g').attr('class', 'commit-labels'); - let pos = 0; - - if (dir === 'TB' || dir === 'BT') { - pos = defaultPos; - } - const keys = [...commits.keys()]; - const isParallelCommits = gitGraphConfig.parallelCommits; - const layoutOffset = 10; - const commitStep = 40; - let sortedKeys = - dir !== 'BT' || (dir === 'BT' && isParallelCommits) - ? keys.sort((a, b) => { - return commits.get(a).seq - commits.get(b).seq; - }) - : keys - .sort((a, b) => { - return commits.get(a).seq - commits.get(b).seq; - }) - .reverse(); - - if (dir === 'BT' && isParallelCommits) { - setParallelBTPos(sortedKeys, commits, pos, commitStep, layoutOffset); - sortedKeys = sortedKeys.reverse(); - } - sortedKeys.forEach((key) => { - const commit = commits.get(key); - if (isParallelCommits) { - if (commit.parents.length) { - const closestParent = - dir === 'BT' ? findClosestParentBT(commit.parents) : findClosestParent(commit.parents); - if (dir === 'TB') { - pos = commitPos.get(closestParent).y + commitStep; - } else if (dir === 'BT') { - pos = commitPos.get(key).y - commitStep; - } else { - pos = commitPos.get(closestParent).x + commitStep; - } - } else { - if (dir === 'TB') { - pos = defaultPos; - } else if (dir === 'BT') { - pos = commitPos.get(key).y - commitStep; - } else { - pos = 0; - } - } - } - const posWithOffset = dir === 'BT' && isParallelCommits ? pos : pos + layoutOffset; - const y = dir === 'TB' || dir === 'BT' ? posWithOffset : branchPos.get(commit.branch).pos; - const x = dir === 'TB' || dir === 'BT' ? branchPos.get(commit.branch).pos : posWithOffset; - - // Don't draw the commits now but calculate the positioning which is used by the branch lines etc. - if (modifyGraph) { - let typeClass; - let commitSymbolType = - commit.customType !== undefined && commit.customType !== '' - ? commit.customType - : commit.type; - switch (commitSymbolType) { - case commitType.NORMAL: - typeClass = 'commit-normal'; - break; - case commitType.REVERSE: - typeClass = 'commit-reverse'; - break; - case commitType.HIGHLIGHT: - typeClass = 'commit-highlight'; - break; - case commitType.MERGE: - typeClass = 'commit-merge'; - break; - case commitType.CHERRY_PICK: - typeClass = 'commit-cherry-pick'; - break; - default: - typeClass = 'commit-normal'; - } - - if (commitSymbolType === commitType.HIGHLIGHT) { - const circle = gBullets.append('rect'); - circle.attr('x', x - 10); - circle.attr('y', y - 10); - circle.attr('height', 20); - circle.attr('width', 20); - circle.attr( - 'class', - `commit ${commit.id} commit-highlight${ - branchPos.get(commit.branch).index % THEME_COLOR_LIMIT - } ${typeClass}-outer` - ); - gBullets - .append('rect') - .attr('x', x - 6) - .attr('y', y - 6) - .attr('height', 12) - .attr('width', 12) - .attr( - 'class', - `commit ${commit.id} commit${ - branchPos.get(commit.branch).index % THEME_COLOR_LIMIT - } ${typeClass}-inner` - ); - } else if (commitSymbolType === commitType.CHERRY_PICK) { - gBullets - .append('circle') - .attr('cx', x) - .attr('cy', y) - .attr('r', 10) - .attr('class', `commit ${commit.id} ${typeClass}`); - gBullets - .append('circle') - .attr('cx', x - 3) - .attr('cy', y + 2) - .attr('r', 2.75) - .attr('fill', '#fff') - .attr('class', `commit ${commit.id} ${typeClass}`); - gBullets - .append('circle') - .attr('cx', x + 3) - .attr('cy', y + 2) - .attr('r', 2.75) - .attr('fill', '#fff') - .attr('class', `commit ${commit.id} ${typeClass}`); - gBullets - .append('line') - .attr('x1', x + 3) - .attr('y1', y + 1) - .attr('x2', x) - .attr('y2', y - 5) - .attr('stroke', '#fff') - .attr('class', `commit ${commit.id} ${typeClass}`); - gBullets - .append('line') - .attr('x1', x - 3) - .attr('y1', y + 1) - .attr('x2', x) - .attr('y2', y - 5) - .attr('stroke', '#fff') - .attr('class', `commit ${commit.id} ${typeClass}`); - } else { - const circle = gBullets.append('circle'); - circle.attr('cx', x); - circle.attr('cy', y); - circle.attr('r', commit.type === commitType.MERGE ? 9 : 10); - circle.attr( - 'class', - `commit ${commit.id} commit${branchPos.get(commit.branch).index % THEME_COLOR_LIMIT}` - ); - if (commitSymbolType === commitType.MERGE) { - const circle2 = gBullets.append('circle'); - circle2.attr('cx', x); - circle2.attr('cy', y); - circle2.attr('r', 6); - circle2.attr( - 'class', - `commit ${typeClass} ${commit.id} commit${ - branchPos.get(commit.branch).index % THEME_COLOR_LIMIT - }` - ); - } - if (commitSymbolType === commitType.REVERSE) { - const cross = gBullets.append('path'); - cross - .attr('d', `M ${x - 5},${y - 5}L${x + 5},${y + 5}M${x - 5},${y + 5}L${x + 5},${y - 5}`) - .attr( - 'class', - `commit ${typeClass} ${commit.id} commit${ - branchPos.get(commit.branch).index % THEME_COLOR_LIMIT - }` - ); - } - } - } - if (dir === 'TB' || dir === 'BT') { - commitPos.set(commit.id, { x: x, y: posWithOffset }); - } else { - commitPos.set(commit.id, { x: posWithOffset, y: y }); - } - - // The first iteration over the commits are for positioning purposes, this - // is required for drawing the lines. The circles and labels is drawn after the labels - // placing them on top of the lines. - if (modifyGraph) { - const px = 4; - const py = 2; - // Draw the commit label - if ( - commit.type !== commitType.CHERRY_PICK && - ((commit.customId && commit.type === commitType.MERGE) || - commit.type !== commitType.MERGE) && - gitGraphConfig.showCommitLabel - ) { - const wrapper = gLabels.append('g'); - const labelBkg = wrapper.insert('rect').attr('class', 'commit-label-bkg'); - - const text = wrapper - .append('text') - .attr('x', pos) - .attr('y', y + 25) - .attr('class', 'commit-label') - .text(commit.id); - let bbox = text.node().getBBox(); - - // Now we have the label, lets position the background - labelBkg - .attr('x', posWithOffset - bbox.width / 2 - py) - .attr('y', y + 13.5) - .attr('width', bbox.width + 2 * py) - .attr('height', bbox.height + 2 * py); - - if (dir === 'TB' || dir === 'BT') { - labelBkg.attr('x', x - (bbox.width + 4 * px + 5)).attr('y', y - 12); - text.attr('x', x - (bbox.width + 4 * px)).attr('y', y + bbox.height - 12); - } else { - text.attr('x', posWithOffset - bbox.width / 2); - } - if (gitGraphConfig.rotateCommitLabel) { - if (dir === 'TB' || dir === 'BT') { - text.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')'); - labelBkg.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')'); - } else { - let r_x = -7.5 - ((bbox.width + 10) / 25) * 9.5; - let r_y = 10 + (bbox.width / 25) * 8.5; - wrapper.attr( - 'transform', - 'translate(' + r_x + ', ' + r_y + ') rotate(' + -45 + ', ' + pos + ', ' + y + ')' - ); - } - } - } - if (commit.tags.length > 0) { - let yOffset = 0; - let maxTagBboxWidth = 0; - let maxTagBboxHeight = 0; - const tagElements = []; - - for (const tagValue of commit.tags.reverse()) { - const rect = gLabels.insert('polygon'); - const hole = gLabels.append('circle'); - const tag = gLabels - .append('text') - // Note that we are delaying setting the x position until we know the width of the text - .attr('y', y - 16 - yOffset) - .attr('class', 'tag-label') - .text(tagValue); - let tagBbox = tag.node().getBBox(); - maxTagBboxWidth = Math.max(maxTagBboxWidth, tagBbox.width); - maxTagBboxHeight = Math.max(maxTagBboxHeight, tagBbox.height); - - // We don't use the max over here to center the text within the tags - tag.attr('x', posWithOffset - tagBbox.width / 2); - - tagElements.push({ - tag, - hole, - rect, - yOffset, - }); - - yOffset += 20; - } - - for (const { tag, hole, rect, yOffset } of tagElements) { - const h2 = maxTagBboxHeight / 2; - const ly = y - 19.2 - yOffset; - rect.attr('class', 'tag-label-bkg').attr( - 'points', - ` - ${pos - maxTagBboxWidth / 2 - px / 2},${ly + py} - ${pos - maxTagBboxWidth / 2 - px / 2},${ly - py} - ${posWithOffset - maxTagBboxWidth / 2 - px},${ly - h2 - py} - ${posWithOffset + maxTagBboxWidth / 2 + px},${ly - h2 - py} - ${posWithOffset + maxTagBboxWidth / 2 + px},${ly + h2 + py} - ${posWithOffset - maxTagBboxWidth / 2 - px},${ly + h2 + py}` - ); - - hole - .attr('cy', ly) - .attr('cx', pos - maxTagBboxWidth / 2 + px / 2) - .attr('r', 1.5) - .attr('class', 'tag-hole'); - - if (dir === 'TB' || dir === 'BT') { - const yOrigin = pos + yOffset; - - rect - .attr('class', 'tag-label-bkg') - .attr( - 'points', - ` - ${x},${yOrigin + py} - ${x},${yOrigin - py} - ${x + layoutOffset},${yOrigin - h2 - py} - ${x + layoutOffset + maxTagBboxWidth + px},${yOrigin - h2 - py} - ${x + layoutOffset + maxTagBboxWidth + px},${yOrigin + h2 + py} - ${x + layoutOffset},${yOrigin + h2 + py}` - ) - .attr('transform', 'translate(12,12) rotate(45, ' + x + ',' + pos + ')'); - hole - .attr('cx', x + px / 2) - .attr('cy', yOrigin) - .attr('transform', 'translate(12,12) rotate(45, ' + x + ',' + pos + ')'); - tag - .attr('x', x + 5) - .attr('y', yOrigin + 3) - .attr('transform', 'translate(14,14) rotate(45, ' + x + ',' + pos + ')'); - } - } - } - } - pos = dir === 'BT' && isParallelCommits ? pos + commitStep : pos + commitStep + layoutOffset; - if (pos > maxPos) { - maxPos = pos; - } - }); -}; - -/** - * Detect if there are commits - * between commitA's x-position - * and commitB's x-position on the - * same branch as commitA, where - * commitA isn't main - * - * @param {any} commitA - * @param {any} commitB - * @param p1 - * @param p2 - * @param {CommitMap} allCommits - * @returns {boolean} - * If there are commits between - * commitA's x-position - * and commitB's x-position - * on the source branch, where - * source branch is not main - * return true - */ -const shouldRerouteArrow = (commitA, commitB, p1, p2, allCommits) => { - const commitBIsFurthest = dir === 'TB' || dir === 'BT' ? p1.x < p2.x : p1.y < p2.y; - const branchToGetCurve = commitBIsFurthest ? commitB.branch : commitA.branch; - const isOnBranchToGetCurve = (x) => x.branch === branchToGetCurve; - const isBetweenCommits = (x) => x.seq > commitA.seq && x.seq < commitB.seq; - return [...allCommits.values()].some((commitX) => { - return isBetweenCommits(commitX) && isOnBranchToGetCurve(commitX); - }); -}; - -/** - * This function find a lane in the y-axis that is not overlapping with any other lanes. This is - * used for drawing the lines between commits. - * - * @param {any} y1 - * @param {any} y2 - * @param {any} depth - * @returns {number} Y value between y1 and y2 - */ -const findLane = (y1, y2, depth = 0) => { - const candidate = y1 + Math.abs(y1 - y2) / 2; - if (depth > 5) { - return candidate; - } - - let ok = lanes.every((lane) => Math.abs(lane - candidate) >= 10); - if (ok) { - lanes.push(candidate); - return candidate; - } - const diff = Math.abs(y1 - y2); - return findLane(y1, y2 - diff / 5, depth + 1); -}; - -/** - * Draw the lines between the commits. They were arrows initially. - * - * @param {any} svg - * @param {any} commitA - * @param {any} commitB - * @param {CommitMap} allCommits - */ -const drawArrow = (svg, commitA, commitB, allCommits) => { - const p1 = commitPos.get(commitA.id); // arrowStart - const p2 = commitPos.get(commitB.id); // arrowEnd - const arrowNeedsRerouting = shouldRerouteArrow(commitA, commitB, p1, p2, allCommits); - // log.debug('drawArrow', p1, p2, arrowNeedsRerouting, commitA.id, commitB.id); - - // Lower-right quadrant logic; top-left is 0,0 - - let arc = ''; - let arc2 = ''; - let radius = 0; - let offset = 0; - let colorClassNum = branchPos.get(commitB.branch).index; - if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { - colorClassNum = branchPos.get(commitA.branch).index; - } - - let lineDef; - if (arrowNeedsRerouting) { - arc = 'A 10 10, 0, 0, 0,'; - arc2 = 'A 10 10, 0, 0, 1,'; - radius = 10; - offset = 10; - - const lineY = p1.y < p2.y ? findLane(p1.y, p2.y) : findLane(p2.y, p1.y); - const lineX = p1.x < p2.x ? findLane(p1.x, p2.x) : findLane(p2.x, p1.x); - - if (dir === 'TB') { - if (p1.x < p2.x) { - // Source commit is on branch position left of destination commit - // so render arrow rightward with colour of destination branch - lineDef = `M ${p1.x} ${p1.y} L ${lineX - radius} ${p1.y} ${arc2} ${lineX} ${ - p1.y + offset - } L ${lineX} ${p2.y - radius} ${arc} ${lineX + offset} ${p2.y} L ${p2.x} ${p2.y}`; - } else { - // Source commit is on branch position right of destination commit - // so render arrow leftward with colour of source branch - colorClassNum = branchPos.get(commitA.branch).index; - lineDef = `M ${p1.x} ${p1.y} L ${lineX + radius} ${p1.y} ${arc} ${lineX} ${ - p1.y + offset - } L ${lineX} ${p2.y - radius} ${arc2} ${lineX - offset} ${p2.y} L ${p2.x} ${p2.y}`; - } - } else if (dir === 'BT') { - if (p1.x < p2.x) { - // Source commit is on branch position left of destination commit - // so render arrow rightward with colour of destination branch - lineDef = `M ${p1.x} ${p1.y} L ${lineX - radius} ${p1.y} ${arc} ${lineX} ${ - p1.y - offset - } L ${lineX} ${p2.y + radius} ${arc2} ${lineX + offset} ${p2.y} L ${p2.x} ${p2.y}`; - } else { - // Source commit is on branch position right of destination commit - // so render arrow leftward with colour of source branch - colorClassNum = branchPos.get(commitA.branch).index; - lineDef = `M ${p1.x} ${p1.y} L ${lineX + radius} ${p1.y} ${arc2} ${lineX} ${ - p1.y - offset - } L ${lineX} ${p2.y + radius} ${arc} ${lineX - offset} ${p2.y} L ${p2.x} ${p2.y}`; - } - } else { - if (p1.y < p2.y) { - // Source commit is on branch positioned above destination commit - // so render arrow downward with colour of destination branch - lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${lineY - radius} ${arc} ${ - p1.x + offset - } ${lineY} L ${p2.x - radius} ${lineY} ${arc2} ${p2.x} ${lineY + offset} L ${p2.x} ${p2.y}`; - } else { - // Source commit is on branch positioned below destination commit - // so render arrow upward with colour of source branch - colorClassNum = branchPos.get(commitA.branch).index; - lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${lineY + radius} ${arc2} ${ - p1.x + offset - } ${lineY} L ${p2.x - radius} ${lineY} ${arc} ${p2.x} ${lineY - offset} L ${p2.x} ${p2.y}`; - } - } - } else { - arc = 'A 20 20, 0, 0, 0,'; - arc2 = 'A 20 20, 0, 0, 1,'; - radius = 20; - offset = 20; - - if (dir === 'TB') { - if (p1.x < p2.x) { - if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { - lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc} ${p1.x + offset} ${ - p2.y - } L ${p2.x} ${p2.y}`; - } else { - lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc2} ${p2.x} ${ - p1.y + offset - } L ${p2.x} ${p2.y}`; - } - } - if (p1.x > p2.x) { - arc = 'A 20 20, 0, 0, 0,'; - arc2 = 'A 20 20, 0, 0, 1,'; - radius = 20; - offset = 20; - if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { - lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc2} ${p1.x - offset} ${ - p2.y - } L ${p2.x} ${p2.y}`; - } else { - lineDef = `M ${p1.x} ${p1.y} L ${p2.x + radius} ${p1.y} ${arc} ${p2.x} ${ - p1.y + offset - } L ${p2.x} ${p2.y}`; - } - } - - if (p1.x === p2.x) { - lineDef = `M ${p1.x} ${p1.y} L ${p2.x} ${p2.y}`; - } - } else if (dir === 'BT') { - if (p1.x < p2.x) { - if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { - lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y + radius} ${arc2} ${p1.x + offset} ${ - p2.y - } L ${p2.x} ${p2.y}`; - } else { - lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc} ${p2.x} ${ - p1.y - offset - } L ${p2.x} ${p2.y}`; - } - } - if (p1.x > p2.x) { - arc = 'A 20 20, 0, 0, 0,'; - arc2 = 'A 20 20, 0, 0, 1,'; - radius = 20; - offset = 20; - - if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { - lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y + radius} ${arc} ${p1.x - offset} ${ - p2.y - } L ${p2.x} ${p2.y}`; - } else { - lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc} ${p2.x} ${ - p1.y - offset - } L ${p2.x} ${p2.y}`; - } - } - - if (p1.x === p2.x) { - lineDef = `M ${p1.x} ${p1.y} L ${p2.x} ${p2.y}`; - } - } else { - if (p1.y < p2.y) { - if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { - lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc2} ${p2.x} ${ - p1.y + offset - } L ${p2.x} ${p2.y}`; - } else { - lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc} ${p1.x + offset} ${ - p2.y - } L ${p2.x} ${p2.y}`; - } - } - if (p1.y > p2.y) { - if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { - lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc} ${p2.x} ${ - p1.y - offset - } L ${p2.x} ${p2.y}`; - } else { - lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y + radius} ${arc2} ${p1.x + offset} ${ - p2.y - } L ${p2.x} ${p2.y}`; - } - } - - if (p1.y === p2.y) { - lineDef = `M ${p1.x} ${p1.y} L ${p2.x} ${p2.y}`; - } - } - } - svg - .append('path') - .attr('d', lineDef) - .attr('class', 'arrow arrow' + (colorClassNum % THEME_COLOR_LIMIT)); -}; - -/** - * @param {*} svg - * @param {CommitMap} commits - */ -const drawArrows = (svg, commits) => { - const gArrows = svg.append('g').attr('class', 'commit-arrows'); - [...commits.keys()].forEach((key) => { - const commit = commits.get(key); - if (commit.parents && commit.parents.length > 0) { - commit.parents.forEach((parent) => { - drawArrow(gArrows, commits.get(parent), commit, commits); - }); - } - }); -}; - -/** - * Adds the branches and the branches' labels to the svg. - * - * @param svg - * @param branches - */ -const drawBranches = (svg, branches) => { - const gitGraphConfig = getConfig().gitGraph; - const g = svg.append('g'); - branches.forEach((branch, index) => { - const adjustIndexForTheme = index % THEME_COLOR_LIMIT; - - const pos = branchPos.get(branch.name).pos; - const line = g.append('line'); - line.attr('x1', 0); - line.attr('y1', pos); - line.attr('x2', maxPos); - line.attr('y2', pos); - line.attr('class', 'branch branch' + adjustIndexForTheme); - - if (dir === 'TB') { - line.attr('y1', defaultPos); - line.attr('x1', pos); - line.attr('y2', maxPos); - line.attr('x2', pos); - } else if (dir === 'BT') { - line.attr('y1', maxPos); - line.attr('x1', pos); - line.attr('y2', defaultPos); - line.attr('x2', pos); - } - lanes.push(pos); - - let name = branch.name; - - // Create the actual text element - const labelElement = drawText(name); - // Create outer g, edgeLabel, this will be positioned after graph layout - const bkg = g.insert('rect'); - const branchLabel = g.insert('g').attr('class', 'branchLabel'); - - // Create inner g, label, this will be positioned now for centering the text - const label = branchLabel.insert('g').attr('class', 'label branch-label' + adjustIndexForTheme); - label.node().appendChild(labelElement); - let bbox = labelElement.getBBox(); - bkg - .attr('class', 'branchLabelBkg label' + adjustIndexForTheme) - .attr('rx', 4) - .attr('ry', 4) - .attr('x', -bbox.width - 4 - (gitGraphConfig.rotateCommitLabel === true ? 30 : 0)) - .attr('y', -bbox.height / 2 + 8) - .attr('width', bbox.width + 18) - .attr('height', bbox.height + 4); - label.attr( - 'transform', - 'translate(' + - (-bbox.width - 14 - (gitGraphConfig.rotateCommitLabel === true ? 30 : 0)) + - ', ' + - (pos - bbox.height / 2 - 1) + - ')' - ); - if (dir === 'TB') { - bkg.attr('x', pos - bbox.width / 2 - 10).attr('y', 0); - label.attr('transform', 'translate(' + (pos - bbox.width / 2 - 5) + ', ' + 0 + ')'); - } else if (dir === 'BT') { - bkg.attr('x', pos - bbox.width / 2 - 10).attr('y', maxPos); - label.attr('transform', 'translate(' + (pos - bbox.width / 2 - 5) + ', ' + maxPos + ')'); - } else { - bkg.attr('transform', 'translate(' + -19 + ', ' + (pos - bbox.height / 2) + ')'); - } - }); -}; - -/** - * @param txt - * @param id - * @param ver - * @param diagObj - */ -export const draw = function (txt, id, ver, diagObj) { - clear(); - const conf = getConfig(); - const gitGraphConfig = conf.gitGraph; - // try { - log.debug('in gitgraph renderer', txt + '\n', 'id:', id, ver); - - allCommitsDict = diagObj.db.getCommits(); - const branches = diagObj.db.getBranchesAsObjArray(); - dir = diagObj.db.getDirection(); - const diagram = select(`[id="${id}"]`); - // Position branches - let pos = 0; - branches.forEach((branch, index) => { - const labelElement = drawText(branch.name); - const g = diagram.append('g'); - const branchLabel = g.insert('g').attr('class', 'branchLabel'); - const label = branchLabel.insert('g').attr('class', 'label branch-label'); - label.node().appendChild(labelElement); - let bbox = labelElement.getBBox(); - - branchPos.set(branch.name, { pos, index }); - pos += - 50 + - (gitGraphConfig.rotateCommitLabel ? 40 : 0) + - (dir === 'TB' || dir === 'BT' ? bbox.width / 2 : 0); - label.remove(); - branchLabel.remove(); - g.remove(); - }); - - drawCommits(diagram, allCommitsDict, false); - if (gitGraphConfig.showBranches) { - drawBranches(diagram, branches); - } - drawArrows(diagram, allCommitsDict); - drawCommits(diagram, allCommitsDict, true); - utils.insertTitle( - diagram, - 'gitTitleText', - gitGraphConfig.titleTopMargin, - diagObj.db.getDiagramTitle() - ); - - // Setup the view box and size of the svg element - setupGraphViewbox( - undefined, - diagram, - gitGraphConfig.diagramPadding, - gitGraphConfig.useMaxWidth ?? conf.useMaxWidth - ); -}; - -export default { - draw, -}; diff --git a/packages/mermaid/src/diagrams/git/gitGraphRenderer.ts b/packages/mermaid/src/diagrams/git/gitGraphRenderer.ts new file mode 100644 index 000000000..39a64a623 --- /dev/null +++ b/packages/mermaid/src/diagrams/git/gitGraphRenderer.ts @@ -0,0 +1,1350 @@ +import { select } from 'd3'; +import { getConfig, setupGraphViewbox } from '../../diagram-api/diagramAPI.js'; +import { log } from '../../logger.js'; +import utils from '../../utils.js'; +import type { DrawDefinition } from '../../diagram-api/types.js'; +import type d3 from 'd3'; +import type { Commit, GitGraphDBRenderProvider, DiagramOrientation } from './gitGraphTypes.js'; +import { commitType } from './gitGraphTypes.js'; + +interface BranchPosition { + pos: number; + index: number; +} + +interface CommitPosition { + x: number; + y: number; +} + +interface CommitPositionOffset extends CommitPosition { + posWithOffset: number; +} + +const DEFAULT_CONFIG = getConfig(); +const DEFAULT_GITGRAPH_CONFIG = DEFAULT_CONFIG?.gitGraph; +const LAYOUT_OFFSET = 10; +const COMMIT_STEP = 40; +const PX = 4; +const PY = 2; + +const THEME_COLOR_LIMIT = 8; +const branchPos = new Map(); +const commitPos = new Map(); +const defaultPos = 30; + +let allCommitsDict = new Map(); +let lanes: number[] = []; +let maxPos = 0; +let dir: DiagramOrientation = 'LR'; + +const clear = () => { + branchPos.clear(); + commitPos.clear(); + allCommitsDict.clear(); + maxPos = 0; + lanes = []; + dir = 'LR'; +}; + +const drawText = (txt: string | string[]) => { + const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + const rows = typeof txt === 'string' ? txt.split(/\\n|\n|/gi) : txt; + + rows.forEach((row) => { + const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); + tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve'); + tspan.setAttribute('dy', '1em'); + tspan.setAttribute('x', '0'); + tspan.setAttribute('class', 'row'); + tspan.textContent = row.trim(); + svgLabel.appendChild(tspan); + }); + + return svgLabel; +}; + +const findClosestParent = (parents: string[]): string | undefined => { + let closestParent: string | undefined; + let comparisonFunc; + let targetPosition: number; + if (dir === 'BT') { + comparisonFunc = (a: number, b: number) => a <= b; + targetPosition = Infinity; + } else { + comparisonFunc = (a: number, b: number) => a >= b; + targetPosition = 0; + } + + parents.forEach((parent) => { + const parentPosition = + dir === 'TB' || dir == 'BT' ? commitPos.get(parent)?.y : commitPos.get(parent)?.x; + + if (parentPosition !== undefined && comparisonFunc(parentPosition, targetPosition)) { + closestParent = parent; + targetPosition = parentPosition; + } + }); + + return closestParent; +}; + +const findClosestParentBT = (parents: string[]) => { + let closestParent = ''; + let maxPosition = Infinity; + + parents.forEach((parent) => { + const parentPosition = commitPos.get(parent)!.y; + if (parentPosition <= maxPosition) { + closestParent = parent; + maxPosition = parentPosition; + } + }); + return closestParent || undefined; +}; + +const setParallelBTPos = ( + sortedKeys: string[], + commits: Map, + defaultPos: number +) => { + let curPos = defaultPos; + let maxPosition = defaultPos; + const roots: Commit[] = []; + + sortedKeys.forEach((key) => { + const commit = commits.get(key); + if (!commit) { + throw new Error(`Commit not found for key ${key}`); + } + + if (commit.parents.length) { + curPos = calculateCommitPosition(commit); + maxPosition = Math.max(curPos, maxPosition); + } else { + roots.push(commit); + } + setCommitPosition(commit, curPos); + }); + + curPos = maxPosition; + roots.forEach((commit) => { + setRootPosition(commit, curPos, defaultPos); + }); + sortedKeys.forEach((key) => { + const commit = commits.get(key); + + if (commit?.parents.length) { + const closestParent = findClosestParentBT(commit.parents)!; + curPos = commitPos.get(closestParent)!.y - COMMIT_STEP; + if (curPos <= maxPosition) { + maxPosition = curPos; + } + const x = branchPos.get(commit.branch)!.pos; + const y = curPos - LAYOUT_OFFSET; + commitPos.set(commit.id, { x: x, y: y }); + } + }); +}; + +const findClosestParentPos = (commit: Commit): number => { + const closestParent = findClosestParent(commit.parents.filter((p) => p !== null)); + if (!closestParent) { + throw new Error(`Closest parent not found for commit ${commit.id}`); + } + + const closestParentPos = commitPos.get(closestParent)?.y; + if (closestParentPos === undefined) { + throw new Error(`Closest parent position not found for commit ${commit.id}`); + } + return closestParentPos; +}; + +const calculateCommitPosition = (commit: Commit): number => { + const closestParentPos = findClosestParentPos(commit); + return closestParentPos + COMMIT_STEP; +}; + +const setCommitPosition = (commit: Commit, curPos: number): CommitPosition => { + const branch = branchPos.get(commit.branch); + + if (!branch) { + throw new Error(`Branch not found for commit ${commit.id}`); + } + + const x = branch.pos; + const y = curPos + LAYOUT_OFFSET; + commitPos.set(commit.id, { x, y }); + return { x, y }; +}; + +const setRootPosition = (commit: Commit, curPos: number, defaultPos: number) => { + const branch = branchPos.get(commit.branch); + if (!branch) { + throw new Error(`Branch not found for commit ${commit.id}`); + } + + const y = curPos + defaultPos; + const x = branch.pos; + commitPos.set(commit.id, { x, y }); +}; + +const drawCommitBullet = ( + gBullets: d3.Selection, + commit: Commit, + commitPosition: CommitPositionOffset, + typeClass: string, + branchIndex: number, + commitSymbolType: number +) => { + if (commitSymbolType === commitType.HIGHLIGHT) { + gBullets + .append('rect') + .attr('x', commitPosition.x - 10) + .attr('y', commitPosition.y - 10) + .attr('width', 20) + .attr('height', 20) + .attr( + 'class', + `commit ${commit.id} commit-highlight${branchIndex % THEME_COLOR_LIMIT} ${typeClass}-outer` + ); + gBullets + .append('rect') + .attr('x', commitPosition.x - 6) + .attr('y', commitPosition.y - 6) + .attr('width', 12) + .attr('height', 12) + .attr( + 'class', + `commit ${commit.id} commit${branchIndex % THEME_COLOR_LIMIT} ${typeClass}-inner` + ); + } else if (commitSymbolType === commitType.CHERRY_PICK) { + gBullets + .append('circle') + .attr('cx', commitPosition.x) + .attr('cy', commitPosition.y) + .attr('r', 10) + .attr('class', `commit ${commit.id} ${typeClass}`); + gBullets + .append('circle') + .attr('cx', commitPosition.x - 3) + .attr('cy', commitPosition.y + 2) + .attr('r', 2.75) + .attr('fill', '#fff') + .attr('class', `commit ${commit.id} ${typeClass}`); + gBullets + .append('circle') + .attr('cx', commitPosition.x + 3) + .attr('cy', commitPosition.y + 2) + .attr('r', 2.75) + .attr('fill', '#fff') + .attr('class', `commit ${commit.id} ${typeClass}`); + gBullets + .append('line') + .attr('x1', commitPosition.x + 3) + .attr('y1', commitPosition.y + 1) + .attr('x2', commitPosition.x) + .attr('y2', commitPosition.y - 5) + .attr('stroke', '#fff') + .attr('class', `commit ${commit.id} ${typeClass}`); + gBullets + .append('line') + .attr('x1', commitPosition.x - 3) + .attr('y1', commitPosition.y + 1) + .attr('x2', commitPosition.x) + .attr('y2', commitPosition.y - 5) + .attr('stroke', '#fff') + .attr('class', `commit ${commit.id} ${typeClass}`); + } else { + const circle = gBullets.append('circle'); + circle.attr('cx', commitPosition.x); + circle.attr('cy', commitPosition.y); + circle.attr('r', commit.type === commitType.MERGE ? 9 : 10); + circle.attr('class', `commit ${commit.id} commit${branchIndex % THEME_COLOR_LIMIT}`); + if (commitSymbolType === commitType.MERGE) { + const circle2 = gBullets.append('circle'); + circle2.attr('cx', commitPosition.x); + circle2.attr('cy', commitPosition.y); + circle2.attr('r', 6); + circle2.attr( + 'class', + `commit ${typeClass} ${commit.id} commit${branchIndex % THEME_COLOR_LIMIT}` + ); + } + if (commitSymbolType === commitType.REVERSE) { + const cross = gBullets.append('path'); + cross + .attr( + 'd', + `M ${commitPosition.x - 5},${commitPosition.y - 5}L${commitPosition.x + 5},${commitPosition.y + 5}M${commitPosition.x - 5},${commitPosition.y + 5}L${commitPosition.x + 5},${commitPosition.y - 5}` + ) + .attr('class', `commit ${typeClass} ${commit.id} commit${branchIndex % THEME_COLOR_LIMIT}`); + } + } +}; + +const drawCommitLabel = ( + gLabels: d3.Selection, + commit: Commit, + commitPosition: CommitPositionOffset, + pos: number +) => { + if ( + commit.type !== commitType.CHERRY_PICK && + ((commit.customId && commit.type === commitType.MERGE) || commit.type !== commitType.MERGE) && + DEFAULT_GITGRAPH_CONFIG?.showCommitLabel + ) { + const wrapper = gLabels.append('g'); + const labelBkg = wrapper.insert('rect').attr('class', 'commit-label-bkg'); + const text = wrapper + .append('text') + .attr('x', pos) + .attr('y', commitPosition.y + 25) + .attr('class', 'commit-label') + .text(commit.id); + const bbox = text.node()?.getBBox(); + + if (bbox) { + labelBkg + .attr('x', commitPosition.posWithOffset - bbox.width / 2 - PY) + .attr('y', commitPosition.y + 13.5) + .attr('width', bbox.width + 2 * PY) + .attr('height', bbox.height + 2 * PY); + + if (dir === 'TB' || dir === 'BT') { + labelBkg + .attr('x', commitPosition.x - (bbox.width + 4 * PX + 5)) + .attr('y', commitPosition.y - 12); + text + .attr('x', commitPosition.x - (bbox.width + 4 * PX)) + .attr('y', commitPosition.y + bbox.height - 12); + } else { + text.attr('x', commitPosition.posWithOffset - bbox.width / 2); + } + + if (DEFAULT_GITGRAPH_CONFIG.rotateCommitLabel) { + if (dir === 'TB' || dir === 'BT') { + text.attr( + 'transform', + 'rotate(' + -45 + ', ' + commitPosition.x + ', ' + commitPosition.y + ')' + ); + labelBkg.attr( + 'transform', + 'rotate(' + -45 + ', ' + commitPosition.x + ', ' + commitPosition.y + ')' + ); + } else { + const r_x = -7.5 - ((bbox.width + 10) / 25) * 9.5; + const r_y = 10 + (bbox.width / 25) * 8.5; + wrapper.attr( + 'transform', + 'translate(' + + r_x + + ', ' + + r_y + + ') rotate(' + + -45 + + ', ' + + pos + + ', ' + + commitPosition.y + + ')' + ); + } + } + } + } +}; + +const drawCommitTags = ( + gLabels: d3.Selection, + commit: Commit, + commitPosition: CommitPositionOffset, + pos: number +) => { + if (commit.tags.length > 0) { + let yOffset = 0; + let maxTagBboxWidth = 0; + let maxTagBboxHeight = 0; + const tagElements = []; + + for (const tagValue of commit.tags.reverse()) { + const rect = gLabels.insert('polygon'); + const hole = gLabels.append('circle'); + const tag = gLabels + .append('text') + .attr('y', commitPosition.y - 16 - yOffset) + .attr('class', 'tag-label') + .text(tagValue); + const tagBbox = tag.node()?.getBBox(); + if (!tagBbox) { + throw new Error('Tag bbox not found'); + } + + maxTagBboxWidth = Math.max(maxTagBboxWidth, tagBbox.width); + maxTagBboxHeight = Math.max(maxTagBboxHeight, tagBbox.height); + + tag.attr('x', commitPosition.posWithOffset - tagBbox.width / 2); + + tagElements.push({ + tag, + hole, + rect, + yOffset, + }); + + yOffset += 20; + } + + for (const { tag, hole, rect, yOffset } of tagElements) { + const h2 = maxTagBboxHeight / 2; + const ly = commitPosition.y - 19.2 - yOffset; + rect.attr('class', 'tag-label-bkg').attr( + 'points', + ` + ${pos - maxTagBboxWidth / 2 - PX / 2},${ly + PY} + ${pos - maxTagBboxWidth / 2 - PX / 2},${ly - PY} + ${commitPosition.posWithOffset - maxTagBboxWidth / 2 - PX},${ly - h2 - PY} + ${commitPosition.posWithOffset + maxTagBboxWidth / 2 + PX},${ly - h2 - PY} + ${commitPosition.posWithOffset + maxTagBboxWidth / 2 + PX},${ly + h2 + PY} + ${commitPosition.posWithOffset - maxTagBboxWidth / 2 - PX},${ly + h2 + PY}` + ); + + hole + .attr('cy', ly) + .attr('cx', pos - maxTagBboxWidth / 2 + PX / 2) + .attr('r', 1.5) + .attr('class', 'tag-hole'); + + if (dir === 'TB' || dir === 'BT') { + const yOrigin = pos + yOffset; + + rect + .attr('class', 'tag-label-bkg') + .attr( + 'points', + ` + ${commitPosition.x},${yOrigin + 2} + ${commitPosition.x},${yOrigin - 2} + ${commitPosition.x + LAYOUT_OFFSET},${yOrigin - h2 - 2} + ${commitPosition.x + LAYOUT_OFFSET + maxTagBboxWidth + 4},${yOrigin - h2 - 2} + ${commitPosition.x + LAYOUT_OFFSET + maxTagBboxWidth + 4},${yOrigin + h2 + 2} + ${commitPosition.x + LAYOUT_OFFSET},${yOrigin + h2 + 2}` + ) + .attr('transform', 'translate(12,12) rotate(45, ' + commitPosition.x + ',' + pos + ')'); + hole + .attr('cx', commitPosition.x + PX / 2) + .attr('cy', yOrigin) + .attr('transform', 'translate(12,12) rotate(45, ' + commitPosition.x + ',' + pos + ')'); + tag + .attr('x', commitPosition.x + 5) + .attr('y', yOrigin + 3) + .attr('transform', 'translate(14,14) rotate(45, ' + commitPosition.x + ',' + pos + ')'); + } + } + } +}; + +const getCommitClassType = (commit: Commit): string => { + const commitSymbolType = commit.customType ?? commit.type; + switch (commitSymbolType) { + case commitType.NORMAL: + return 'commit-normal'; + case commitType.REVERSE: + return 'commit-reverse'; + case commitType.HIGHLIGHT: + return 'commit-highlight'; + case commitType.MERGE: + return 'commit-merge'; + case commitType.CHERRY_PICK: + return 'commit-cherry-pick'; + default: + return 'commit-normal'; + } +}; + +const calculatePosition = ( + commit: Commit, + dir: string, + pos: number, + commitPos: Map +): number => { + const defaultCommitPosition = { x: 0, y: 0 }; // Default position if commit is not found + + if (commit.parents.length > 0) { + const closestParent = findClosestParent(commit.parents); + if (closestParent) { + const parentPosition = commitPos.get(closestParent) ?? defaultCommitPosition; + + if (dir === 'TB') { + return parentPosition.y + COMMIT_STEP; + } else if (dir === 'BT') { + const currentPosition = commitPos.get(commit.id) ?? defaultCommitPosition; + return currentPosition.y - COMMIT_STEP; + } else { + return parentPosition.x + COMMIT_STEP; + } + } + } else { + if (dir === 'TB') { + return defaultPos; + } else if (dir === 'BT') { + const currentPosition = commitPos.get(commit.id) ?? defaultCommitPosition; + return currentPosition.y - COMMIT_STEP; + } else { + return 0; + } + } + return 0; +}; + +const getCommitPosition = ( + commit: Commit, + pos: number, + isParallelCommits: boolean +): CommitPositionOffset => { + const posWithOffset = dir === 'BT' && isParallelCommits ? pos : pos + LAYOUT_OFFSET; + const y = dir === 'TB' || dir === 'BT' ? posWithOffset : branchPos.get(commit.branch)?.pos; + const x = dir === 'TB' || dir === 'BT' ? branchPos.get(commit.branch)?.pos : posWithOffset; + if (x === undefined || y === undefined) { + throw new Error(`Position were undefined for commit ${commit.id}`); + } + return { x, y, posWithOffset }; +}; + +const drawCommits = ( + svg: d3.Selection, + commits: Map, + modifyGraph: boolean +) => { + if (!DEFAULT_GITGRAPH_CONFIG) { + throw new Error('GitGraph config not found'); + } + const gBullets = svg.append('g').attr('class', 'commit-bullets'); + const gLabels = svg.append('g').attr('class', 'commit-labels'); + let pos = dir === 'TB' || dir === 'BT' ? defaultPos : 0; + const keys = [...commits.keys()]; + const isParallelCommits = DEFAULT_GITGRAPH_CONFIG?.parallelCommits ?? false; + + const sortKeys = (a: string, b: string) => { + const seqA = commits.get(a)?.seq; + const seqB = commits.get(b)?.seq; + return seqA !== undefined && seqB !== undefined ? seqA - seqB : 0; + }; + + let sortedKeys = keys.sort(sortKeys); + if (dir === 'BT') { + if (isParallelCommits) { + setParallelBTPos(sortedKeys, commits, pos); + } + sortedKeys = sortedKeys.reverse(); + } + + sortedKeys.forEach((key) => { + const commit = commits.get(key); + if (!commit) { + throw new Error(`Commit not found for key ${key}`); + } + if (isParallelCommits) { + pos = calculatePosition(commit, dir, pos, commitPos); + } + + const commitPosition = getCommitPosition(commit, pos, isParallelCommits); + // Don't draw the commits now but calculate the positioning which is used by the branch lines etc. + if (modifyGraph) { + const typeClass = getCommitClassType(commit); + const commitSymbolType = commit.customType ?? commit.type; + const branchIndex = branchPos.get(commit.branch)?.index ?? 0; + drawCommitBullet(gBullets, commit, commitPosition, typeClass, branchIndex, commitSymbolType); + drawCommitLabel(gLabels, commit, commitPosition, pos); + drawCommitTags(gLabels, commit, commitPosition, pos); + } + if (dir === 'TB' || dir === 'BT') { + commitPos.set(commit.id, { x: commitPosition.x, y: commitPosition.posWithOffset }); + } else { + commitPos.set(commit.id, { x: commitPosition.posWithOffset, y: commitPosition.y }); + } + pos = dir === 'BT' && isParallelCommits ? pos + COMMIT_STEP : pos + COMMIT_STEP + LAYOUT_OFFSET; + if (pos > maxPos) { + maxPos = pos; + } + }); +}; + +const shouldRerouteArrow = ( + commitA: Commit, + commitB: Commit, + p1: CommitPosition, + p2: CommitPosition, + allCommits: Map +) => { + const commitBIsFurthest = dir === 'TB' || dir === 'BT' ? p1.x < p2.x : p1.y < p2.y; + const branchToGetCurve = commitBIsFurthest ? commitB.branch : commitA.branch; + const isOnBranchToGetCurve = (x: Commit) => x.branch === branchToGetCurve; + const isBetweenCommits = (x: Commit) => x.seq > commitA.seq && x.seq < commitB.seq; + return [...allCommits.values()].some((commitX) => { + return isBetweenCommits(commitX) && isOnBranchToGetCurve(commitX); + }); +}; + +const findLane = (y1: number, y2: number, depth = 0): number => { + const candidate = y1 + Math.abs(y1 - y2) / 2; + if (depth > 5) { + return candidate; + } + + const ok = lanes.every((lane) => Math.abs(lane - candidate) >= 10); + if (ok) { + lanes.push(candidate); + return candidate; + } + const diff = Math.abs(y1 - y2); + return findLane(y1, y2 - diff / 5, depth + 1); +}; + +const drawArrow = ( + svg: d3.Selection, + commitA: Commit, + commitB: Commit, + allCommits: Map +) => { + const p1 = commitPos.get(commitA.id); // arrowStart + const p2 = commitPos.get(commitB.id); // arrowEnd + if (p1 === undefined || p2 === undefined) { + throw new Error(`Commit positions not found for commits ${commitA.id} and ${commitB.id}`); + } + const arrowNeedsRerouting = shouldRerouteArrow(commitA, commitB, p1, p2, allCommits); + // log.debug('drawArrow', p1, p2, arrowNeedsRerouting, commitA.id, commitB.id); + + // Lower-right quadrant logic; top-left is 0,0 + + let arc = ''; + let arc2 = ''; + let radius = 0; + let offset = 0; + + let colorClassNum = branchPos.get(commitB.branch)?.index; + if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { + colorClassNum = branchPos.get(commitA.branch)?.index; + } + + let lineDef; + if (arrowNeedsRerouting) { + arc = 'A 10 10, 0, 0, 0,'; + arc2 = 'A 10 10, 0, 0, 1,'; + radius = 10; + offset = 10; + + const lineY = p1.y < p2.y ? findLane(p1.y, p2.y) : findLane(p2.y, p1.y); + + const lineX = p1.x < p2.x ? findLane(p1.x, p2.x) : findLane(p2.x, p1.x); + + if (dir === 'TB') { + if (p1.x < p2.x) { + // Source commit is on branch position left of destination commit + // so render arrow rightward with colour of destination branch + + lineDef = `M ${p1.x} ${p1.y} L ${lineX - radius} ${p1.y} ${arc2} ${lineX} ${ + p1.y + offset + } L ${lineX} ${p2.y - radius} ${arc} ${lineX + offset} ${p2.y} L ${p2.x} ${p2.y}`; + } else { + // Source commit is on branch position right of destination commit + // so render arrow leftward with colour of source branch + + colorClassNum = branchPos.get(commitA.branch)?.index; + + lineDef = `M ${p1.x} ${p1.y} L ${lineX + radius} ${p1.y} ${arc} ${lineX} ${p1.y + offset} L ${lineX} ${p2.y - radius} ${arc2} ${lineX - offset} ${p2.y} L ${p2.x} ${p2.y}`; + } + } else if (dir === 'BT') { + if (p1.x < p2.x) { + // Source commit is on branch position left of destination commit + // so render arrow rightward with colour of destination branch + + lineDef = `M ${p1.x} ${p1.y} L ${lineX - radius} ${p1.y} ${arc} ${lineX} ${p1.y - offset} L ${lineX} ${p2.y + radius} ${arc2} ${lineX + offset} ${p2.y} L ${p2.x} ${p2.y}`; + } else { + // Source commit is on branch position right of destination commit + // so render arrow leftward with colour of source branch + + colorClassNum = branchPos.get(commitA.branch)?.index; + + lineDef = `M ${p1.x} ${p1.y} L ${lineX + radius} ${p1.y} ${arc2} ${lineX} ${p1.y - offset} L ${lineX} ${p2.y + radius} ${arc} ${lineX - offset} ${p2.y} L ${p2.x} ${p2.y}`; + } + } else { + if (p1.y < p2.y) { + // Source commit is on branch positioned above destination commit + // so render arrow downward with colour of destination branch + + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${lineY - radius} ${arc} ${ + p1.x + offset + } ${lineY} L ${p2.x - radius} ${lineY} ${arc2} ${p2.x} ${lineY + offset} L ${p2.x} ${p2.y}`; + } else { + // Source commit is on branch positioned below destination commit + // so render arrow upward with colour of source branch + + colorClassNum = branchPos.get(commitA.branch)?.index; + + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${lineY + radius} ${arc2} ${ + p1.x + offset + } ${lineY} L ${p2.x - radius} ${lineY} ${arc} ${p2.x} ${lineY - offset} L ${p2.x} ${p2.y}`; + } + } + } else { + arc = 'A 20 20, 0, 0, 0,'; + arc2 = 'A 20 20, 0, 0, 1,'; + radius = 20; + offset = 20; + + if (dir === 'TB') { + if (p1.x < p2.x) { + if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc} ${p1.x + offset} ${ + p2.y + } L ${p2.x} ${p2.y}`; + } else { + lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc2} ${p2.x} ${ + p1.y + offset + } L ${p2.x} ${p2.y}`; + } + } + + if (p1.x > p2.x) { + arc = 'A 20 20, 0, 0, 0,'; + arc2 = 'A 20 20, 0, 0, 1,'; + radius = 20; + offset = 20; + if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc2} ${p1.x - offset} ${ + p2.y + } L ${p2.x} ${p2.y}`; + } else { + lineDef = `M ${p1.x} ${p1.y} L ${p2.x + radius} ${p1.y} ${arc} ${p2.x} ${ + p1.y + offset + } L ${p2.x} ${p2.y}`; + } + } + if (p1.x === p2.x) { + lineDef = `M ${p1.x} ${p1.y} L ${p2.x} ${p2.y}`; + } + } else if (dir === 'BT') { + if (p1.x < p2.x) { + if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y + radius} ${arc2} ${p1.x + offset} ${ + p2.y + } L ${p2.x} ${p2.y}`; + } else { + lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc} ${p2.x} ${ + p1.y - offset + } L ${p2.x} ${p2.y}`; + } + } + if (p1.x > p2.x) { + arc = 'A 20 20, 0, 0, 0,'; + arc2 = 'A 20 20, 0, 0, 1,'; + radius = 20; + offset = 20; + + if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y + radius} ${arc} ${p1.x - offset} ${ + p2.y + } L ${p2.x} ${p2.y}`; + } else { + lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc} ${p2.x} ${ + p1.y - offset + } L ${p2.x} ${p2.y}`; + } + } + + if (p1.x === p2.x) { + lineDef = `M ${p1.x} ${p1.y} L ${p2.x} ${p2.y}`; + } + } else { + if (p1.y < p2.y) { + if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { + lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc2} ${p2.x} ${ + p1.y + offset + } L ${p2.x} ${p2.y}`; + } else { + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc} ${p1.x + offset} ${ + p2.y + } L ${p2.x} ${p2.y}`; + } + } + if (p1.y > p2.y) { + if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) { + lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc} ${p2.x} ${ + p1.y - offset + } L ${p2.x} ${p2.y}`; + } else { + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y + radius} ${arc2} ${p1.x + offset} ${ + p2.y + } L ${p2.x} ${p2.y}`; + } + } + + if (p1.y === p2.y) { + lineDef = `M ${p1.x} ${p1.y} L ${p2.x} ${p2.y}`; + } + } + } + if (lineDef === undefined) { + throw new Error('Line definition not found'); + } + svg + .append('path') + .attr('d', lineDef) + .attr('class', 'arrow arrow' + (colorClassNum! % THEME_COLOR_LIMIT)); +}; + +const drawArrows = ( + svg: d3.Selection, + commits: Map +) => { + const gArrows = svg.append('g').attr('class', 'commit-arrows'); + [...commits.keys()].forEach((key) => { + const commit = commits.get(key); + + if (commit!.parents && commit!.parents.length > 0) { + commit!.parents.forEach((parent) => { + drawArrow(gArrows, commits.get(parent)!, commit!, commits); + }); + } + }); +}; + +const drawBranches = ( + svg: d3.Selection, + branches: { name: string }[] +) => { + const g = svg.append('g'); + branches.forEach((branch, index) => { + const adjustIndexForTheme = index % THEME_COLOR_LIMIT; + + const pos = branchPos.get(branch.name)?.pos; + if (pos === undefined) { + throw new Error(`Position not found for branch ${branch.name}`); + } + const line = g.append('line'); + line.attr('x1', 0); + line.attr('y1', pos); + line.attr('x2', maxPos); + line.attr('y2', pos); + line.attr('class', 'branch branch' + adjustIndexForTheme); + + if (dir === 'TB') { + line.attr('y1', defaultPos); + line.attr('x1', pos); + line.attr('y2', maxPos); + line.attr('x2', pos); + } else if (dir === 'BT') { + line.attr('y1', maxPos); + line.attr('x1', pos); + line.attr('y2', defaultPos); + line.attr('x2', pos); + } + lanes.push(pos); + + const name = branch.name; + + // Create the actual text element + const labelElement = drawText(name); + // Create outer g, edgeLabel, this will be positioned after graph layout + const bkg = g.insert('rect'); + const branchLabel = g.insert('g').attr('class', 'branchLabel'); + + // Create inner g, label, this will be positioned now for centering the text + const label = branchLabel.insert('g').attr('class', 'label branch-label' + adjustIndexForTheme); + + label.node()!.appendChild(labelElement); + const bbox = labelElement.getBBox(); + bkg + .attr('class', 'branchLabelBkg label' + adjustIndexForTheme) + .attr('rx', 4) + .attr('ry', 4) + .attr('x', -bbox.width - 4 - (DEFAULT_GITGRAPH_CONFIG?.rotateCommitLabel === true ? 30 : 0)) + .attr('y', -bbox.height / 2 + 8) + .attr('width', bbox.width + 18) + .attr('height', bbox.height + 4); + label.attr( + 'transform', + 'translate(' + + (-bbox.width - 14 - (DEFAULT_GITGRAPH_CONFIG?.rotateCommitLabel === true ? 30 : 0)) + + ', ' + + (pos - bbox.height / 2 - 1) + + ')' + ); + if (dir === 'TB') { + bkg.attr('x', pos - bbox.width / 2 - 10).attr('y', 0); + label.attr('transform', 'translate(' + (pos - bbox.width / 2 - 5) + ', ' + 0 + ')'); + } else if (dir === 'BT') { + bkg.attr('x', pos - bbox.width / 2 - 10).attr('y', maxPos); + label.attr('transform', 'translate(' + (pos - bbox.width / 2 - 5) + ', ' + maxPos + ')'); + } else { + bkg.attr('transform', 'translate(' + -19 + ', ' + (pos - bbox.height / 2) + ')'); + } + }); +}; + +const setBranchPosition = function ( + name: string, + pos: number, + index: number, + bbox: DOMRect, + rotateCommitLabel: boolean +): number { + branchPos.set(name, { pos, index }); + pos += 50 + (rotateCommitLabel ? 40 : 0) + (dir === 'TB' || dir === 'BT' ? bbox.width / 2 : 0); + return pos; +}; + +export const draw: DrawDefinition = function (txt, id, ver, diagObj) { + clear(); + + log.debug('in gitgraph renderer', txt + '\n', 'id:', id, ver); + if (!DEFAULT_GITGRAPH_CONFIG) { + throw new Error('GitGraph config not found'); + } + const rotateCommitLabel = DEFAULT_GITGRAPH_CONFIG.rotateCommitLabel ?? false; + const db = diagObj.db as GitGraphDBRenderProvider; + allCommitsDict = db.getCommits(); + const branches = db.getBranchesAsObjArray(); + dir = db.getDirection(); + const diagram = select(`[id="${id}"]`); + let pos = 0; + + branches.forEach((branch, index) => { + const labelElement = drawText(branch.name); + const g = diagram.append('g'); + const branchLabel = g.insert('g').attr('class', 'branchLabel'); + const label = branchLabel.insert('g').attr('class', 'label branch-label'); + label.node()?.appendChild(labelElement); + const bbox = labelElement.getBBox(); + + pos = setBranchPosition(branch.name, pos, index, bbox, rotateCommitLabel); + label.remove(); + branchLabel.remove(); + g.remove(); + }); + + drawCommits(diagram, allCommitsDict, false); + if (DEFAULT_GITGRAPH_CONFIG.showBranches) { + drawBranches(diagram, branches); + } + drawArrows(diagram, allCommitsDict); + drawCommits(diagram, allCommitsDict, true); + + utils.insertTitle( + diagram, + 'gitTitleText', + DEFAULT_GITGRAPH_CONFIG.titleTopMargin ?? 0, + db.getDiagramTitle() + ); + + // Setup the view box and size of the svg element + setupGraphViewbox( + undefined, + diagram, + DEFAULT_GITGRAPH_CONFIG.diagramPadding, + DEFAULT_GITGRAPH_CONFIG.useMaxWidth + ); +}; + +export default { + draw, +}; + +if (import.meta.vitest) { + const { it, expect, describe } = import.meta.vitest; + + describe('drawText', () => { + it('should drawText', () => { + const svgLabel = drawText('main'); + expect(svgLabel).toBeDefined(); + expect(svgLabel.children[0].innerHTML).toBe('main'); + }); + }); + + describe('branchPosition', () => { + const bbox: DOMRect = { + x: 0, + y: 0, + width: 10, + height: 10, + top: 0, + right: 0, + bottom: 0, + left: 0, + toJSON: () => '', + }; + + it('should setBranchPositions LR with two branches', () => { + dir = 'LR'; + + const pos = setBranchPosition('main', 0, 0, bbox, true); + expect(pos).toBe(90); + expect(branchPos.get('main')).toEqual({ pos: 0, index: 0 }); + const posNext = setBranchPosition('develop', pos, 1, bbox, true); + expect(posNext).toBe(180); + expect(branchPos.get('develop')).toEqual({ pos: pos, index: 1 }); + }); + + it('should setBranchPositions TB with two branches', () => { + dir = 'TB'; + bbox.width = 34.9921875; + + const pos = setBranchPosition('main', 0, 0, bbox, true); + expect(pos).toBe(107.49609375); + expect(branchPos.get('main')).toEqual({ pos: 0, index: 0 }); + + bbox.width = 56.421875; + const posNext = setBranchPosition('develop', pos, 1, bbox, true); + expect(posNext).toBe(225.70703125); + expect(branchPos.get('develop')).toEqual({ pos: pos, index: 1 }); + }); + }); + + describe('commitPosition', () => { + const commits = new Map([ + [ + 'commitZero', + { + id: 'ZERO', + message: '', + seq: 0, + type: commitType.NORMAL, + tags: [], + parents: [], + branch: 'main', + }, + ], + [ + 'commitA', + { + id: 'A', + message: '', + seq: 1, + type: commitType.NORMAL, + tags: [], + parents: ['ZERO'], + branch: 'feature', + }, + ], + [ + 'commitB', + { + id: 'B', + message: '', + seq: 2, + type: commitType.NORMAL, + tags: [], + parents: ['A'], + branch: 'feature', + }, + ], + [ + 'commitM', + { + id: 'M', + message: 'merged branch feature into main', + seq: 3, + type: commitType.MERGE, + tags: [], + parents: ['ZERO', 'B'], + branch: 'main', + customId: true, + }, + ], + [ + 'commitC', + { + id: 'C', + message: '', + seq: 4, + type: commitType.NORMAL, + tags: [], + parents: ['ZERO'], + branch: 'release', + }, + ], + [ + 'commit5_8928ea0', + { + id: '5-8928ea0', + message: 'cherry-picked [object Object] into release', + seq: 5, + type: commitType.CHERRY_PICK, + tags: [], + parents: ['C', 'M'], + branch: 'release', + }, + ], + [ + 'commitD', + { + id: 'D', + message: '', + seq: 6, + type: commitType.NORMAL, + tags: [], + parents: ['5-8928ea0'], + branch: 'release', + }, + ], + [ + 'commit7_ed848ba', + { + id: '7-ed848ba', + message: 'cherry-picked [object Object] into release', + seq: 7, + type: commitType.CHERRY_PICK, + tags: [], + parents: ['D', 'M'], + branch: 'release', + }, + ], + ]); + let pos = 0; + branchPos.set('main', { pos: 0, index: 0 }); + branchPos.set('feature', { pos: 107.49609375, index: 1 }); + branchPos.set('release', { pos: 224.03515625, index: 2 }); + + describe('TB', () => { + pos = 30; + dir = 'TB'; + const expectedCommitPositionTB = new Map([ + ['commitZero', { x: 0, y: 40, posWithOffset: 40 }], + ['commitA', { x: 107.49609375, y: 90, posWithOffset: 90 }], + ['commitB', { x: 107.49609375, y: 140, posWithOffset: 140 }], + ['commitM', { x: 0, y: 190, posWithOffset: 190 }], + ['commitC', { x: 224.03515625, y: 240, posWithOffset: 240 }], + ['commit5_8928ea0', { x: 224.03515625, y: 290, posWithOffset: 290 }], + ['commitD', { x: 224.03515625, y: 340, posWithOffset: 340 }], + ['commit7_ed848ba', { x: 224.03515625, y: 390, posWithOffset: 390 }], + ]); + commits.forEach((commit, key) => { + it(`should give the correct position for commit ${key}`, () => { + const position = getCommitPosition(commit, pos, false); + expect(position).toEqual(expectedCommitPositionTB.get(key)); + pos += 50; + }); + }); + }); + describe('LR', () => { + let pos = 30; + dir = 'LR'; + const expectedCommitPositionLR = new Map([ + ['commitZero', { x: 0, y: 40, posWithOffset: 40 }], + ['commitA', { x: 107.49609375, y: 90, posWithOffset: 90 }], + ['commitB', { x: 107.49609375, y: 140, posWithOffset: 140 }], + ['commitM', { x: 0, y: 190, posWithOffset: 190 }], + ['commitC', { x: 224.03515625, y: 240, posWithOffset: 240 }], + ['commit5_8928ea0', { x: 224.03515625, y: 290, posWithOffset: 290 }], + ['commitD', { x: 224.03515625, y: 340, posWithOffset: 340 }], + ['commit7_ed848ba', { x: 224.03515625, y: 390, posWithOffset: 390 }], + ]); + commits.forEach((commit, key) => { + it(`should give the correct position for commit ${key}`, () => { + const position = getCommitPosition(commit, pos, false); + expect(position).toEqual(expectedCommitPositionLR.get(key)); + pos += 50; + }); + }); + }); + describe('getCommitClassType', () => { + const expectedCommitClassType = new Map([ + ['commitZero', 'commit-normal'], + ['commitA', 'commit-normal'], + ['commitB', 'commit-normal'], + ['commitM', 'commit-merge'], + ['commitC', 'commit-normal'], + ['commit5_8928ea0', 'commit-cherry-pick'], + ['commitD', 'commit-normal'], + ['commit7_ed848ba', 'commit-cherry-pick'], + ]); + commits.forEach((commit, key) => { + it(`should give the correct class type for commit ${key}`, () => { + const classType = getCommitClassType(commit); + expect(classType).toBe(expectedCommitClassType.get(key)); + }); + }); + }); + }); + describe('building BT parallel commit diagram', () => { + const commits = new Map([ + [ + '1-abcdefg', + { + id: '1-abcdefg', + message: '', + seq: 0, + type: 0, + tags: [], + parents: [], + branch: 'main', + }, + ], + [ + '2-abcdefg', + { + id: '2-abcdefg', + message: '', + seq: 1, + type: 0, + tags: [], + parents: ['1-abcdefg'], + branch: 'main', + }, + ], + [ + '3-abcdefg', + { + id: '3-abcdefg', + message: '', + seq: 2, + type: 0, + tags: [], + parents: ['2-abcdefg'], + branch: 'develop', + }, + ], + [ + '4-abcdefg', + { + id: '4-abcdefg', + message: '', + seq: 3, + type: 0, + tags: [], + parents: ['3-abcdefg'], + branch: 'develop', + }, + ], + [ + '5-abcdefg', + { + id: '5-abcdefg', + message: '', + seq: 4, + type: 0, + tags: [], + parents: ['2-abcdefg'], + branch: 'feature', + }, + ], + [ + '6-abcdefg', + { + id: '6-abcdefg', + message: '', + seq: 5, + type: 0, + tags: [], + parents: ['5-abcdefg'], + branch: 'feature', + }, + ], + [ + '7-abcdefg', + { + id: '7-abcdefg', + message: '', + seq: 6, + type: 0, + tags: [], + parents: ['2-abcdefg'], + branch: 'main', + }, + ], + [ + '8-abcdefg', + { + id: '8-abcdefg', + message: '', + seq: 7, + type: 0, + tags: [], + parents: ['7-abcdefg'], + branch: 'main', + }, + ], + ]); + const expectedCommitPosition = new Map([ + ['1-abcdefg', { x: 0, y: 40 }], + ['2-abcdefg', { x: 0, y: 90 }], + ['3-abcdefg', { x: 107.49609375, y: 140 }], + ['4-abcdefg', { x: 107.49609375, y: 190 }], + ['5-abcdefg', { x: 225.70703125, y: 140 }], + ['6-abcdefg', { x: 225.70703125, y: 190 }], + ['7-abcdefg', { x: 0, y: 140 }], + ['8-abcdefg', { x: 0, y: 190 }], + ]); + + const expectedCommitPositionAfterParallel = new Map([ + ['1-abcdefg', { x: 0, y: 210 }], + ['2-abcdefg', { x: 0, y: 160 }], + ['3-abcdefg', { x: 107.49609375, y: 110 }], + ['4-abcdefg', { x: 107.49609375, y: 60 }], + ['5-abcdefg', { x: 225.70703125, y: 110 }], + ['6-abcdefg', { x: 225.70703125, y: 60 }], + ['7-abcdefg', { x: 0, y: 110 }], + ['8-abcdefg', { x: 0, y: 60 }], + ]); + + const expectedCommitCurrentPosition = new Map([ + ['1-abcdefg', 30], + ['2-abcdefg', 80], + ['3-abcdefg', 130], + ['4-abcdefg', 180], + ['5-abcdefg', 130], + ['6-abcdefg', 180], + ['7-abcdefg', 130], + ['8-abcdefg', 180], + ]); + const sortedKeys = [...expectedCommitPosition.keys()]; + it('should get the correct commit position and current position', () => { + dir = 'BT'; + let curPos = 30; + commitPos.clear(); + branchPos.clear(); + branchPos.set('main', { pos: 0, index: 0 }); + branchPos.set('develop', { pos: 107.49609375, index: 1 }); + branchPos.set('feature', { pos: 225.70703125, index: 2 }); + DEFAULT_GITGRAPH_CONFIG!.parallelCommits = true; + commits.forEach((commit, key) => { + if (commit.parents.length > 0) { + curPos = calculateCommitPosition(commit); + } + const position = setCommitPosition(commit, curPos); + expect(position).toEqual(expectedCommitPosition.get(key)); + expect(curPos).toEqual(expectedCommitCurrentPosition.get(key)); + }); + }); + + it('should get the correct commit position after parallel commits', () => { + commitPos.clear(); + branchPos.clear(); + dir = 'BT'; + const curPos = 30; + commitPos.clear(); + branchPos.clear(); + branchPos.set('main', { pos: 0, index: 0 }); + branchPos.set('develop', { pos: 107.49609375, index: 1 }); + branchPos.set('feature', { pos: 225.70703125, index: 2 }); + setParallelBTPos(sortedKeys, commits, curPos); + sortedKeys.forEach((commit) => { + const position = commitPos.get(commit); + expect(position).toEqual(expectedCommitPositionAfterParallel.get(commit)); + }); + }); + }); + DEFAULT_GITGRAPH_CONFIG!.parallelCommits = false; + it('add', () => { + commitPos.set('parent1', { x: 1, y: 1 }); + commitPos.set('parent2', { x: 2, y: 2 }); + commitPos.set('parent3', { x: 3, y: 3 }); + dir = 'LR'; + const parents = ['parent1', 'parent2', 'parent3']; + const closestParent = findClosestParent(parents); + + expect(closestParent).toBe('parent3'); + commitPos.clear(); + }); +} diff --git a/packages/mermaid/src/diagrams/git/gitGraphTypes.ts b/packages/mermaid/src/diagrams/git/gitGraphTypes.ts new file mode 100644 index 000000000..32b951bcc --- /dev/null +++ b/packages/mermaid/src/diagrams/git/gitGraphTypes.ts @@ -0,0 +1,134 @@ +import type { GitGraphDiagramConfig } from '../../config.type.js'; +import type { DiagramDBBase } from '../../diagram-api/types.js'; + +export const commitType = { + NORMAL: 0, + REVERSE: 1, + HIGHLIGHT: 2, + MERGE: 3, + CHERRY_PICK: 4, +} as const; + +export interface CommitDB { + msg: string; + id: string; + type: number; + tags?: string[]; +} + +export interface BranchDB { + name: string; + order: number; +} + +export interface MergeDB { + branch: string; + id: string; + type?: number; + tags?: string[]; +} + +export interface CherryPickDB { + id: string; + targetId: string; + parent: string; + tags?: string[]; +} + +export interface Commit { + id: string; + message: string; + seq: number; + type: number; + tags: string[]; + parents: string[]; + branch: string; + customType?: number; + customId?: boolean; +} + +export interface GitGraph { + statements: Statement[]; +} + +export type Statement = CommitAst | BranchAst | MergeAst | CheckoutAst | CherryPickingAst; + +export interface CommitAst { + $type: 'Commit'; + id: string; + message?: string; + tags?: string[]; + type?: 'NORMAL' | 'REVERSE' | 'HIGHLIGHT'; +} + +export interface BranchAst { + $type: 'Branch'; + name: string; + order?: number; +} + +export interface MergeAst { + $type: 'Merge'; + branch: string; + id?: string; + tags?: string[]; + type?: 'NORMAL' | 'REVERSE' | 'HIGHLIGHT'; +} + +export interface CheckoutAst { + $type: 'Checkout'; + branch: string; +} + +export interface CherryPickingAst { + $type: 'CherryPicking'; + id: string; + parent: string; + tags?: string[]; +} + +export interface GitGraphDB extends DiagramDBBase { + commitType: typeof commitType; + setDirection: (dir: DiagramOrientation) => void; + setOptions: (rawOptString: string) => void; + getOptions: () => any; + commit: (commitDB: CommitDB) => void; + branch: (branchDB: BranchDB) => void; + merge: (mergeDB: MergeDB) => void; + cherryPick: (cherryPickDB: CherryPickDB) => void; + checkout: (branch: string) => void; + prettyPrint: () => void; + clear: () => void; + getBranchesAsObjArray: () => { name: string }[]; + getBranches: () => Map; + getCommits: () => Map; + getCommitsArray: () => Commit[]; + getCurrentBranch: () => string; + getDirection: () => DiagramOrientation; + getHead: () => Commit | null; +} + +export interface GitGraphDBParseProvider extends Partial { + commitType: typeof commitType; + setDirection: (dir: DiagramOrientation) => void; + commit: (commitDB: CommitDB) => void; + branch: (branchDB: BranchDB) => void; + merge: (mergeDB: MergeDB) => void; + cherryPick: (cherryPickDB: CherryPickDB) => void; + checkout: (branch: string) => void; +} + +export interface GitGraphDBRenderProvider extends Partial { + prettyPrint: () => void; + clear: () => void; + getBranchesAsObjArray: () => { name: string }[]; + getBranches: () => Map; + getCommits: () => Map; + getCommitsArray: () => Commit[]; + getCurrentBranch: () => string; + getDirection: () => DiagramOrientation; + getHead: () => Commit | null; + getDiagramTitle: () => string; +} + +export type DiagramOrientation = 'LR' | 'TB' | 'BT'; diff --git a/packages/mermaid/src/diagrams/git/parser/gitGraph.jison b/packages/mermaid/src/diagrams/git/parser/gitGraph.jison deleted file mode 100644 index fa2c70586..000000000 --- a/packages/mermaid/src/diagrams/git/parser/gitGraph.jison +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Parse following - * gitGraph: - * commit - * commit - * branch - */ -%lex - -%x string -%x options -%x acc_title -%x acc_descr -%x acc_descr_multiline -%options case-insensitive - - -%% -accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; } -(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; } -accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; } -(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; } -accDescr\s*"{"\s* { this.begin("acc_descr_multiline");} -[\}] { this.popState(); } -[^\}]* return "acc_descr_multiline_value"; -(\r?\n)+ /*{console.log('New line');return 'NL';}*/ return 'NL'; -\#[^\n]* /* skip comments */ -\%%[^\n]* /* skip comments */ -"gitGraph" return 'GG'; -commit(?=\s|$) return 'COMMIT'; -"id:" return 'COMMIT_ID'; -"type:" return 'COMMIT_TYPE'; -"msg:" return 'COMMIT_MSG'; -"NORMAL" return 'NORMAL'; -"REVERSE" return 'REVERSE'; -"HIGHLIGHT" return 'HIGHLIGHT'; -"tag:" return 'COMMIT_TAG'; -branch(?=\s|$) return 'BRANCH'; -"order:" return 'ORDER'; -merge(?=\s|$) return 'MERGE'; -cherry\-pick(?=\s|$) return 'CHERRY_PICK'; -"parent:" return 'PARENT_COMMIT' -// "reset" return 'RESET'; -\b(checkout|switch)(?=\s|$) return 'CHECKOUT'; -"LR" return 'DIR'; -"TB" return 'DIR'; -"BT" return 'DIR'; -":" return ':'; -"^" return 'CARET' -"options"\r?\n this.begin("options"); // -[ \r\n\t]+"end" this.popState(); // not used anymore in the renderer, fixed for backward compatibility -[\s\S]+(?=[ \r\n\t]+"end") return 'OPT'; // -["]["] return 'EMPTYSTR'; -["] this.begin("string"); -["] this.popState(); -[^"]* return 'STR'; -[0-9]+(?=\s|$) return 'NUM'; -\w([-\./\w]*[-\w])? return 'ID'; // only a subset of https://git-scm.com/docs/git-check-ref-format -<> return 'EOF'; -\s+ /* skip all whitespace */ // lowest priority so we can use lookaheads in earlier regex - -/lex - -%left '^' - -%start start - -%% /* language grammar */ - -start - : eol start - | GG document EOF{ return $3; } - | GG ':' document EOF{ return $3; } - | GG DIR ':' document EOF {yy.setDirection($2); return $4;} - ; - - -document - : /*empty*/ - | options body { yy.setOptions($1); $$ = $2} - ; - -options - : options OPT {$1 +=$2; $$=$1} - | NL - ; -body - : /*empty*/ {$$ = []} - | body line {$1.push($2); $$=$1;} - ; -line - : statement eol {$$ =$1} - | NL - ; - -statement - : commitStatement - | mergeStatement - | cherryPickStatement - | acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); } - | acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); } - | acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);} - | branchStatement - | CHECKOUT ref {yy.checkout($2)} - // | RESET reset_arg {yy.reset($2)} - ; -branchStatement - : BRANCH ref {yy.branch($2)} - | BRANCH ref ORDER NUM {yy.branch($2, $4)} - ; - -cherryPickStatement - : CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '', undefined)} - | CHERRY_PICK COMMIT_ID STR PARENT_COMMIT STR {yy.cherryPick($3, '', undefined,$5)} - | CHERRY_PICK COMMIT_ID STR commitTags {yy.cherryPick($3, '', $4)} - | CHERRY_PICK COMMIT_ID STR PARENT_COMMIT STR commitTags {yy.cherryPick($3, '', $6,$5)} - | CHERRY_PICK COMMIT_ID STR commitTags PARENT_COMMIT STR {yy.cherryPick($3, '', $4,$6)} - | CHERRY_PICK commitTags COMMIT_ID STR {yy.cherryPick($4, '', $2)} - | CHERRY_PICK commitTags COMMIT_ID STR PARENT_COMMIT STR {yy.cherryPick($4, '', $2,$6)} - ; - -mergeStatement - : MERGE ref {yy.merge($2,'','', undefined)} - | MERGE ref COMMIT_ID STR {yy.merge($2, $4,'', undefined)} - | MERGE ref COMMIT_TYPE commitType {yy.merge($2,'', $4, undefined)} - | MERGE ref commitTags {yy.merge($2, '','',$3)} - | MERGE ref commitTags COMMIT_ID STR {yy.merge($2, $5,'', $3)} - | MERGE ref commitTags COMMIT_TYPE commitType {yy.merge($2, '',$5, $3)} - | MERGE ref COMMIT_TYPE commitType commitTags {yy.merge($2, '',$4, $5)} - | MERGE ref COMMIT_ID STR COMMIT_TYPE commitType {yy.merge($2, $4, $6, undefined)} - | MERGE ref COMMIT_ID STR commitTags {yy.merge($2, $4, '', $5)} - | MERGE ref COMMIT_TYPE commitType COMMIT_ID STR {yy.merge($2, $6,$4, undefined)} - | MERGE ref COMMIT_ID STR COMMIT_TYPE commitType commitTags {yy.merge($2, $4, $6, $7)} - | MERGE ref COMMIT_TYPE commitType commitTags COMMIT_ID STR {yy.merge($2, $7, $4, $5)} - | MERGE ref COMMIT_ID STR commitTags COMMIT_TYPE commitType {yy.merge($2, $4, $7, $5)} - | MERGE ref COMMIT_TYPE commitType COMMIT_ID STR commitTags {yy.merge($2, $6, $4, $7)} - | MERGE ref commitTags COMMIT_TYPE commitType COMMIT_ID STR {yy.merge($2, $7, $5, $3)} - | MERGE ref commitTags COMMIT_ID STR COMMIT_TYPE commitType {yy.merge($2, $5, $7, $3)} - ; - -commitStatement - : COMMIT commit_arg {yy.commit($2)} - | COMMIT commitTags {yy.commit('','',yy.commitType.NORMAL,$2)} - | COMMIT COMMIT_TYPE commitType {yy.commit('','',$3, undefined)} - | COMMIT commitTags COMMIT_TYPE commitType {yy.commit('','',$4,$2)} - | COMMIT COMMIT_TYPE commitType commitTags {yy.commit('','',$3,$4)} - | COMMIT COMMIT_ID STR {yy.commit('',$3,yy.commitType.NORMAL, undefined)} - | COMMIT COMMIT_ID STR commitTags {yy.commit('',$3,yy.commitType.NORMAL,$4)} - | COMMIT commitTags COMMIT_ID STR {yy.commit('',$4,yy.commitType.NORMAL,$2)} - | COMMIT COMMIT_ID STR COMMIT_TYPE commitType {yy.commit('',$3,$5, undefined)} - | COMMIT COMMIT_TYPE commitType COMMIT_ID STR {yy.commit('',$5,$3, undefined)} - | COMMIT COMMIT_ID STR COMMIT_TYPE commitType commitTags {yy.commit('',$3,$5,$6)} - | COMMIT COMMIT_ID STR commitTags COMMIT_TYPE commitType {yy.commit('',$3,$6,$4)} - | COMMIT COMMIT_TYPE commitType COMMIT_ID STR commitTags {yy.commit('',$5,$3,$6)} - | COMMIT COMMIT_TYPE commitType commitTags COMMIT_ID STR {yy.commit('',$6,$3,$4)} - | COMMIT commitTags COMMIT_TYPE commitType COMMIT_ID STR {yy.commit('',$6,$4,$2)} - | COMMIT commitTags COMMIT_ID STR COMMIT_TYPE commitType {yy.commit('',$4,$6,$2)} - | COMMIT COMMIT_MSG STR {yy.commit($3,'',yy.commitType.NORMAL, undefined)} - | COMMIT commitTags COMMIT_MSG STR {yy.commit($4,'',yy.commitType.NORMAL,$2)} - | COMMIT COMMIT_MSG STR commitTags {yy.commit($3,'',yy.commitType.NORMAL,$4)} - | COMMIT COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($3,'',$5, undefined)} - | COMMIT COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($5,'',$3, undefined)} - | COMMIT COMMIT_ID STR COMMIT_MSG STR {yy.commit($5,$3,yy.commitType.NORMAL, undefined)} - | COMMIT COMMIT_MSG STR COMMIT_ID STR {yy.commit($3,$5,yy.commitType.NORMAL, undefined)} - - | COMMIT COMMIT_MSG STR COMMIT_TYPE commitType commitTags {yy.commit($3,'',$5,$6)} - | COMMIT COMMIT_MSG STR commitTags COMMIT_TYPE commitType {yy.commit($3,'',$6,$4)} - | COMMIT COMMIT_TYPE commitType COMMIT_MSG STR commitTags {yy.commit($5,'',$3,$6)} - | COMMIT COMMIT_TYPE commitType commitTags COMMIT_MSG STR {yy.commit($6,'',$3,$4)} - | COMMIT commitTags COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($6,'',$4,$2)} - | COMMIT commitTags COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($4,'',$6,$2)} - - | COMMIT COMMIT_MSG STR COMMIT_TYPE commitType COMMIT_ID STR {yy.commit($3,$7,$5, undefined)} - | COMMIT COMMIT_MSG STR COMMIT_ID STR COMMIT_TYPE commitType {yy.commit($3,$5,$7, undefined)} - | COMMIT COMMIT_TYPE commitType COMMIT_MSG STR COMMIT_ID STR {yy.commit($5,$7,$3, undefined)} - | COMMIT COMMIT_TYPE commitType COMMIT_ID STR COMMIT_MSG STR {yy.commit($7,$5,$3, undefined)} - | COMMIT COMMIT_ID STR COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($7,$3,$5, undefined)} - | COMMIT COMMIT_ID STR COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($5,$3,$7, undefined)} - - | COMMIT COMMIT_MSG STR commitTags COMMIT_ID STR {yy.commit($3,$6,yy.commitType.NORMAL,$4)} - | COMMIT COMMIT_MSG STR COMMIT_ID STR commitTags {yy.commit($3,$5,yy.commitType.NORMAL,$6)} - | COMMIT commitTags COMMIT_MSG STR COMMIT_ID STR {yy.commit($4,$6,yy.commitType.NORMAL,$2)} - | COMMIT commitTags COMMIT_ID STR COMMIT_MSG STR {yy.commit($6,$4,yy.commitType.NORMAL,$2)} - | COMMIT COMMIT_ID STR commitTags COMMIT_MSG STR {yy.commit($6,$3,yy.commitType.NORMAL,$4)} - | COMMIT COMMIT_ID STR COMMIT_MSG STR commitTags {yy.commit($5,$3,yy.commitType.NORMAL,$6)} - - | COMMIT COMMIT_MSG STR COMMIT_ID STR COMMIT_TYPE commitType commitTags {yy.commit($3,$5,$7,$8)} - | COMMIT COMMIT_MSG STR COMMIT_ID STR commitTags COMMIT_TYPE commitType {yy.commit($3,$5,$8,$6)} - | COMMIT COMMIT_MSG STR COMMIT_TYPE commitType COMMIT_ID STR commitTags {yy.commit($3,$7,$5,$8)} - | COMMIT COMMIT_MSG STR COMMIT_TYPE commitType commitTags COMMIT_ID STR {yy.commit($3,$8,$5,$6)} - | COMMIT COMMIT_MSG STR commitTags COMMIT_ID STR COMMIT_TYPE commitType {yy.commit($3,$6,$8,$4)} - | COMMIT COMMIT_MSG STR commitTags COMMIT_TYPE commitType COMMIT_ID STR {yy.commit($3,$8,$6,$4)} - - | COMMIT COMMIT_ID STR COMMIT_MSG STR COMMIT_TYPE commitType commitTags {yy.commit($5,$3,$7,$8)} - | COMMIT COMMIT_ID STR COMMIT_MSG STR commitTags COMMIT_TYPE commitType {yy.commit($5,$3,$8,$6)} - | COMMIT COMMIT_ID STR COMMIT_TYPE commitType COMMIT_MSG STR commitTags {yy.commit($7,$3,$5,$8)} - | COMMIT COMMIT_ID STR COMMIT_TYPE commitType commitTags COMMIT_MSG STR {yy.commit($8,$3,$5,$6)} - | COMMIT COMMIT_ID STR commitTags COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($6,$3,$8,$4)} - | COMMIT COMMIT_ID STR commitTags COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($8,$3,$6,$4)} - - | COMMIT commitTags COMMIT_ID STR COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($8,$4,$6,$2)} - | COMMIT commitTags COMMIT_ID STR COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($6,$4,$8,$2)} - | COMMIT commitTags COMMIT_TYPE commitType COMMIT_ID STR COMMIT_MSG STR {yy.commit($8,$6,$4,$2)} - | COMMIT commitTags COMMIT_TYPE commitType COMMIT_MSG STR COMMIT_ID STR {yy.commit($6,$8,$4,$2)} - | COMMIT commitTags COMMIT_MSG STR COMMIT_ID STR COMMIT_TYPE commitType {yy.commit($4,$6,$8,$2)} - | COMMIT commitTags COMMIT_MSG STR COMMIT_TYPE commitType COMMIT_ID STR {yy.commit($4,$8,$6,$2)} - - | COMMIT COMMIT_TYPE commitType COMMIT_ID STR COMMIT_MSG STR commitTags {yy.commit($7,$5,$3,$8)} - | COMMIT COMMIT_TYPE commitType COMMIT_ID STR commitTags COMMIT_MSG STR {yy.commit($8,$5,$3,$6)} - | COMMIT COMMIT_TYPE commitType commitTags COMMIT_MSG STR COMMIT_ID STR {yy.commit($6,$8,$3,$4)} - | COMMIT COMMIT_TYPE commitType commitTags COMMIT_ID STR COMMIT_MSG STR {yy.commit($8,$6,$3,$4)} - | COMMIT COMMIT_TYPE commitType COMMIT_MSG STR COMMIT_ID STR commitTags {yy.commit($5,$7,$3,$8)} - | COMMIT COMMIT_TYPE commitType COMMIT_MSG STR commitTags COMMIT_ID STR {yy.commit($5,$8,$3,$6)} - ; -commit_arg - : /* empty */ {$$ = ""} - | STR {$$=$1} - ; -commitType - : NORMAL { $$=yy.commitType.NORMAL;} - | REVERSE { $$=yy.commitType.REVERSE;} - | HIGHLIGHT { $$=yy.commitType.HIGHLIGHT;} - ; -commitTags - : COMMIT_TAG STR {$$=[$2]} - | COMMIT_TAG EMPTYSTR {$$=['']} - | commitTags COMMIT_TAG STR {$commitTags.push($3); $$=$commitTags;} - | commitTags COMMIT_TAG EMPTYSTR {$commitTags.push(''); $$=$commitTags;} - ; - -ref - : ID - | STR - ; - -eol - : NL - | ';' - | EOF - ; -// reset_arg -// : 'HEAD' reset_parents{$$ = $1+ ":" + $2 } -// | ID reset_parents{$$ = $1+ ":" + yy.count; yy.count = 0} -// ; -// reset_parents -// : /* empty */ {yy.count = 0} -// | CARET reset_parents { yy.count += 1 } -// ; diff --git a/packages/mermaid/src/docs/.vitepress/config.ts b/packages/mermaid/src/docs/.vitepress/config.ts index 940fc6940..619de961d 100644 --- a/packages/mermaid/src/docs/.vitepress/config.ts +++ b/packages/mermaid/src/docs/.vitepress/config.ts @@ -157,6 +157,7 @@ function sidebarSyntax() { { text: 'XY Chart šŸ”„', link: '/syntax/xyChart' }, { text: 'Block Diagram šŸ”„', link: '/syntax/block' }, { text: 'Packet šŸ”„', link: '/syntax/packet' }, + { text: 'Architecture šŸ”„', link: '/syntax/architecture' }, { text: 'Other Examples', link: '/syntax/examples' }, ], }, diff --git a/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts b/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts index 47e238692..2357fe384 100644 --- a/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts +++ b/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts @@ -2,6 +2,13 @@ import mermaid, { type MermaidConfig } from 'mermaid'; import zenuml from '../../../../../mermaid-zenuml/dist/mermaid-zenuml.core.mjs'; const init = mermaid.registerExternalDiagrams([zenuml]); +mermaid.registerIconPacks([ + { + name: 'logos', + loader: () => + fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()), + }, +]); export const render = async (id: string, code: string, config: MermaidConfig): Promise => { await init; diff --git a/packages/mermaid/src/docs/community/contributing.md b/packages/mermaid/src/docs/community/contributing.md index 71048d095..4cd649563 100644 --- a/packages/mermaid/src/docs/community/contributing.md +++ b/packages/mermaid/src/docs/community/contributing.md @@ -371,9 +371,9 @@ If the users have no way to know that things have changed, then you haven't real Likewise, if users don't know that there is a new feature that you've implemented, it will forever remain unknown and unused. The documentation has to be updated for users to know that things have been changed and added! -If you are adding a new feature, add `(v10.8.0+)` in the title or description. It will be replaced automatically with the current version number when the release happens. +If you are adding a new feature, add `(v+)` in the title or description. It will be replaced automatically with the current version number when the release happens. -eg: `# Feature Name (v10.8.0+)` +eg: `# Feature Name (v+)` We know it can sometimes be hard to code _and_ write user documentation. diff --git a/packages/mermaid/src/docs/ecosystem/integrations-community.md b/packages/mermaid/src/docs/ecosystem/integrations-community.md index 81b0386b1..15f802ed5 100644 --- a/packages/mermaid/src/docs/ecosystem/integrations-community.md +++ b/packages/mermaid/src/docs/ecosystem/integrations-community.md @@ -69,6 +69,7 @@ To add an integration to this list, see the [Integrations - create page](./integ - [Markdown for mermaid plugin](https://github.com/jamieh-mongolian/markdown-for-mermaid-plugin) - [redmine-mermaid](https://github.com/styz/redmine_mermaid) - Visual Studio Code [Polyglot Interactive Notebooks](https://github.com/dotnet/interactive#net-interactive) +- [Microsoft Loop](https://loop.cloud.microsoft) āœ… ### LLM integrations diff --git a/packages/mermaid/src/docs/ecosystem/tutorials.md b/packages/mermaid/src/docs/ecosystem/tutorials.md index d5bf9330d..7258361bf 100644 --- a/packages/mermaid/src/docs/ecosystem/tutorials.md +++ b/packages/mermaid/src/docs/ecosystem/tutorials.md @@ -57,7 +57,7 @@ import matplotlib.pyplot as plt def mm(graph): graphbytes = graph.encode("utf8") - base64_bytes = base64.b64encode(graphbytes) + base64_bytes = base64.urlsafe_b64encode(graphbytes) base64_string = base64_bytes.decode("ascii") display(Image(url="https://mermaid.ink/img/" + base64_string)) diff --git a/packages/mermaid/src/docs/intro/index.md b/packages/mermaid/src/docs/intro/index.md index ed2df32dc..627efdaa1 100644 --- a/packages/mermaid/src/docs/intro/index.md +++ b/packages/mermaid/src/docs/intro/index.md @@ -50,6 +50,10 @@ For a more detailed introduction to Mermaid and some of its more basic uses, loo **Thanks to all involved, people committing pull requests, people answering questions and special thanks to Tyler Long who is helping me maintain the project šŸ™** +Our PR Visual Regression Testing is powered by [Argos](https://argos-ci.com/?utm_source=mermaid&utm_campaign=oss) with their generous Open Source plan. It makes the process of reviewing PRs with visual changes a breeze. + +[![Covered by Argos Visual Testing](https://argos-ci.com/badge-large.svg)](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss) + In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests.
    diff --git a/packages/mermaid/src/docs/intro/syntax-reference.md b/packages/mermaid/src/docs/intro/syntax-reference.md index d4ee1067f..7d7fd5994 100644 --- a/packages/mermaid/src/docs/intro/syntax-reference.md +++ b/packages/mermaid/src/docs/intro/syntax-reference.md @@ -65,3 +65,110 @@ Allows for the limited reconfiguration of a diagram just before it is rendered. ### [Theme Manipulation](../config/theming.md) An application of using Directives to change [Themes](../config/theming.md). `Theme` is a value within Mermaid's configuration that dictates the color scheme for diagrams. + +### Layout and look + +We've restructured how Mermaid renders diagrams, enabling new features like selecting layout and look. **Currently, this is supported for flowcharts and state diagrams**, with plans to extend support to all diagram types. + +### Selecting Diagram Looks + +Mermaid offers a variety of styles or ā€œlooksā€ for your diagrams, allowing you to tailor the visual appearance to match your specific needs or preferences. Whether you prefer a hand-drawn or classic style, you can easily customize your diagrams. + +**Available Looks:** + + • Hand-Drawn Look: For a more personal, creative touch, the hand-drawn look brings a sketch-like quality to your diagrams. This style is perfect for informal settings or when you want to add a bit of personality to your diagrams. + • Classic Look: If you prefer the traditional Mermaid style, the classic look maintains the original appearance that many users are familiar with. It’s great for consistency across projects or when you want to keep the familiar aesthetic. + +**How to Select a Look:** + +You can select a look by adding the look parameter in the metadata section of your Mermaid diagram code. Here’s an example: + +```mermaid +--- +config: + look: handDrawn + theme: neutral +--- +flowchart LR + A[Start] --> B{Decision} + B -->|Yes| C[Continue] + B -->|No| D[Stop] +``` + +#### Selecting Layout Algorithms + +In addition to customizing the look of your diagrams, Mermaid Chart now allows you to choose different layout algorithms to better organize and present your diagrams, especially when dealing with more complex structures. The layout algorithm dictates how nodes and edges are arranged on the page. + +#### Supported Layout Algorithms: + + • Dagre (default): This is the classic layout algorithm that has been used in Mermaid for a long time. It provides a good balance of simplicity and visual clarity, making it ideal for most diagrams. + • ELK: For those who need more sophisticated layout capabilities, especially when working with large or intricate diagrams, the ELK (Eclipse Layout Kernel) layout offers advanced options. It provides a more optimized arrangement, potentially reducing overlapping and improving readability. This is not included out the box but needs to be added when integrating mermaid for sites/applications that want to have elk support. + +#### How to Select a Layout Algorithm: + +You can specify the layout algorithm directly in the metadata section of your Mermaid diagram code. Here’s an example: + +```mermaid +--- +config: + layout: elk + look: handDrawn + theme: dark +--- +flowchart TB + A[Start] --> B{Decision} + B -->|Yes| C[Continue] + B -->|No| D[Stop] +``` + +In this example, the `layout: elk` line configures the diagram to use the ELK layout algorithm, along with the hand drawn look and forest theme. + +#### Customizing ELK Layout: + +When using the ELK layout, you can further refine the diagram’s configuration, such as how nodes are placed and whether parallel edges should be combined: + +- To combine parallel edges, use mergeEdges: true | false. +- To configure node placement, use nodePlacementStrategy with the following options: + - SIMPLE + - NETWORK_SIMPLEX + - LINEAR_SEGMENTS + - BRANDES_KOEPF (default) + +**Example configuration:** + +``` +--- +config: + layout: elk + elk: + mergeEdges: true + nodePlacementStrategy: LINEAR_SEGMENTS +--- +flowchart LR + A[Start] --> B{Choose Path} + B -->|Option 1| C[Path 1] + B -->|Option 2| D[Path 2] + +#### Using Dagre Layout with Classic Look: +``` + +Another example: + +``` +--- +config: + layout: dagre + look: classic + theme: default +--- + +flowchart LR +A[Start] --> B{Choose Path} +B -->|Option 1| C[Path 1] +B -->|Option 2| D[Path 2] + +``` + +These options give you the flexibility to create diagrams that not only look great but are also arranged to best suit your data’s structure and flow. + +When integrating Mermaid, you can include look and layout configuration with the initialize call. This is also where you add the loading of elk. diff --git a/packages/mermaid/src/docs/news/blog.md b/packages/mermaid/src/docs/news/blog.md index 4ddfbb39e..36a84396d 100644 --- a/packages/mermaid/src/docs/news/blog.md +++ b/packages/mermaid/src/docs/news/blog.md @@ -6,6 +6,24 @@ Discover the fresh new and unique Neo and Hand-Drawn looks for Mermaid Diagrams, while still offering the classic look you love. +## [Mermaid v11 is out!](https://www.mermaidchart.com/blog/posts/mermaid-v11/) + +23 August 2024 Ā· 2 mins + +Mermaid v11 introduces advanced layout options, new diagram types, and enhanced customization features, thanks to the incredible contributions from our community. + +## [Mermaid Innovation - Introducing New Looks for Mermaid Diagrams](https://www.mermaidchart.com/blog/posts/mermaid-innovation-introducing-new-looks-for-mermaid-diagrams/) + +6 August 2024 Ā·3 mins + +Discover the fresh new and unique Neo and Hand-Drawn looks for Mermaid Diagrams, while still offering the classic look you love. + +## [The Mermaid Chart Plugin for Jira: A How-To User Guide](https://www.mermaidchart.com/blog/posts/the-mermaid-chart-plugin-for-jira-a-how-to-user-guide/) + +31 July 2024 Ā· 5 mins + +The Mermaid Chart plugin for Jira has arrived! + ## [Mermaid AI Is Here to Change the Game For Diagram Creation](https://www.mermaidchart.com/blog/posts/mermaid-ai-is-here-to-change-the-game-for-diagram-creation/) 22 July 2024 Ā· 5 mins diff --git a/packages/mermaid/src/docs/syntax/architecture.md b/packages/mermaid/src/docs/syntax/architecture.md new file mode 100644 index 000000000..476c60532 --- /dev/null +++ b/packages/mermaid/src/docs/syntax/architecture.md @@ -0,0 +1,223 @@ +# Architecture Diagrams Documentation (v11.1.0+) + +> In the context of mermaid-js, the architecture diagram is used to show the relationship between services and resources commonly found within the Cloud or CI/CD deployments. In an architecture diagram, services (nodes) are connected by edges. Related services can be placed within groups to better illustrate how they are organized. + +## Example + +```mermaid-example +architecture-beta + group api(cloud)[API] + + service db(database)[Database] in api + service disk1(disk)[Storage] in api + service disk2(disk)[Storage] in api + service server(server)[Server] in api + + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db +``` + +## Syntax + +The building blocks of an architecture are `groups`, `services`, `edges`, and `junctions`. + +For supporting components, icons are declared by surrounding the icon name with `()`, while labels are declared by surrounding the text with `[]`. + +To begin an architecture diagram, use the keyword `architecture-beta`, followed by your groups, services, edges, and junctions. While each of the 3 building blocks can be declared in any order, care must be taken to ensure the identifier was previously declared by another component. + +### Groups + +The syntax for declaring a group is: + +``` +group {group id}({icon name})[{title}] (in {parent id})? +``` + +Put together: + +``` +group public_api(cloud)[Public API] +``` + +creates a group identified as `public_api`, uses the icon `cloud`, and has the label `Public API`. + +Additionally, groups can be placed within a group using the optional `in` keyword + +``` +group private_api(cloud)[Private API] in public_api +``` + +### Services + +The syntax for declaring a service is: + +``` +service {service id}({icon name})[{title}] (in {parent id})? +``` + +Put together: + +``` +service database(db)[Database] +``` + +creates the service identified as `database`, using the icon `db`, with the label `Database`. + +If the service belongs to a group, it can be placed inside it through the optional `in` keyword + +``` +service database(db)[Database] in private_api +``` + +### Edges + +The syntax for declaring an edge is: + +``` +{serviceId}{{group}}?:{T|B|L|R} {<}?--{>}? {T|B|L|R}:{serviceId}{{group}}? +``` + +#### Edge Direction + +The side of the service the edge comes out of is specified by adding a colon (`:`) to the side of the service connecting to the arrow and adding `L|R|T|B` + +For example: + +``` +db:R -- L:server +``` + +creates an edge between the services `db` and `server`, with the edge coming out of the right of `db` and the left of `server`. + +``` +db:T -- L:server +``` + +creates a 90 degree edge between the services `db` and `server`, with the edge coming out of the top of `db` and the left of `server`. + +#### Arrows + +Arrows can be added to each side of an edge by adding `<` before the direction on the left, and/or `>` after the direction on the right. + +For example: + +``` +subnet:R --> L:gateway +``` + +creates an edge with the arrow going into the `gateway` service + +#### Edges out of Groups + +To have an edge go from a group to another group or service within another group, the `{group}` modifier can be added after the `serviceId`. + +For example: + +``` +service server[Server] in groupOne +service subnet[Subnet] in groupTwo + +server{group}:B --> T:subnet{group} +``` + +creates an edge going out of `groupOne`, adjacent to `server`, and into `groupTwo`, adjacent to `subnet`. + +It's important to note that `groupId`s cannot be used for specifying edges and the `{group}` modifier can only be used for services within a group. + +### Junctions + +Junctions are a special type of node which acts as a potential 4-way split between edges. + +The syntax for declaring a junction is: + +``` +junction {junction id} (in {parent id})? +``` + +```mermaid-example +architecture-beta + service left_disk(disk)[Disk] + service top_disk(disk)[Disk] + service bottom_disk(disk)[Disk] + service top_gateway(internet)[Gateway] + service bottom_gateway(internet)[Gateway] + junction junctionCenter + junction junctionRight + + left_disk:R -- L:junctionCenter + top_disk:B -- T:junctionCenter + bottom_disk:T -- B:junctionCenter + junctionCenter:R -- L:junctionRight + top_gateway:B -- T:junctionRight + bottom_gateway:T -- B:junctionRight +``` + +## Icons + +By default, architecture diagram supports the following icons: `cloud`, `database`, `disk`, `internet`, `server`. +Users can use any of the 200,000+ icons available in iconify.design, or add their own custom icons, by following the steps below. + +The icon packs available can be found at [icones.js.org](https://icones.js.org/). +We use the name defined when registering the icon pack, to override the prefix field of the iconify pack. This allows the user to use shorter names for the icons. It also allows us to load a particular pack only when it is used in a diagram. + +Using JSON file directly from CDN: + +```js +import mermaid from 'CDN/mermaid.esm.mjs'; +mermaid.registerIconPacks([ + { + name: 'logos', + loader: () => + fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()), + }, +]); +``` + +Using packages and a bundler: + +```bash +npm install @iconify-json/logos +``` + +With lazy loading + +```js +import mermaid from 'mermaid'; + +mermaid.registerIconPacks([ + { + name: 'logos', + loader: () => import('@iconify-json/logos').then((module) => module.icons), + }, +]); +``` + +Without lazy loading + +```js +import mermaid from 'mermaid'; +import { icons } from '@iconify-json/logos'; +mermaid.registerIconPacks([ + { + name: icons.prefix, // To use the prefix defined in the icon pack + icons, + }, +]); +``` + +After the icons are installed, they can be used in the architecture diagram by using the format "name:icon-name", where name is the value used when registering the icon pack. + +```mermaid-example +architecture-beta + group api(logos:aws-lambda)[API] + + service db(logos:aws-aurora)[Database] in api + service disk1(logos:aws-glacier)[Storage] in api + service disk2(logos:aws-s3)[Storage] in api + service server(logos:aws-ec2)[Server] in api + + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db +``` diff --git a/packages/mermaid/src/docs/syntax/entityRelationshipDiagram.md b/packages/mermaid/src/docs/syntax/entityRelationshipDiagram.md index 3b874f689..8c83d2232 100644 --- a/packages/mermaid/src/docs/syntax/entityRelationshipDiagram.md +++ b/packages/mermaid/src/docs/syntax/entityRelationshipDiagram.md @@ -192,6 +192,7 @@ erDiagram - If you want the relationship label to be more than one word, you must use double quotes around the phrase - If you don't want a label at all on a relationship, you must use an empty double-quoted string +- (v11.1.0+) If you want a multi-line label on a relationship, use `
    ` between the two lines (`"first line
    second line"`) ## Styling diff --git a/packages/mermaid/src/mermaid.ts b/packages/mermaid/src/mermaid.ts index 43fc5bd31..52cca1cfe 100644 --- a/packages/mermaid/src/mermaid.ts +++ b/packages/mermaid/src/mermaid.ts @@ -2,6 +2,7 @@ * Web page integration module for the mermaid framework. It uses the mermaidAPI for mermaid * functionality and to render the diagrams to svg code! */ +import { registerIconPacks } from '$root/rendering-util/icons.js'; import { dedent } from 'ts-dedent'; import type { MermaidConfig } from './config.type.js'; import { detectType, registerLazyLoadedDiagrams } from './diagram-api/detectType.js'; @@ -435,6 +436,7 @@ export interface Mermaid { contentLoaded: typeof contentLoaded; setParseErrorHandler: typeof setParseErrorHandler; detectType: typeof detectType; + registerIconPacks: typeof registerIconPacks; } const mermaid: Mermaid = { @@ -451,6 +453,7 @@ const mermaid: Mermaid = { contentLoaded, setParseErrorHandler, detectType, + registerIconPacks, }; export default mermaid; diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index e1c4412b9..3fdd967f1 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -6,26 +6,26 @@ import { select } from 'd3'; import { compile, serialize, stringify } from 'stylis'; // @ts-ignore: TODO Fix ts errors +import DOMPurify from 'dompurify'; +import isEmpty from 'lodash-es/isEmpty.js'; import { version } from '../package.json'; +import { addSVGa11yTitleDescription, setA11yDiagramInfo } from './accessibility.js'; +import assignWithDepth from './assignWithDepth.js'; import * as configApi from './config.js'; +import type { MermaidConfig } from './config.type.js'; import { addDiagrams } from './diagram-api/diagram-orchestration.js'; +import type { DiagramMetadata, DiagramStyleClassDef } from './diagram-api/types.js'; import { Diagram } from './Diagram.js'; +import { evaluate } from './diagrams/common/common.js'; import errorRenderer from './diagrams/error/errorRenderer.js'; import { attachFunctions } from './interactionDb.js'; import { log, setLogLevel } from './logger.js'; +import { preprocessDiagram } from './preprocess.js'; import getStyles from './styles.js'; import theme from './themes/index.js'; -import DOMPurify from 'dompurify'; -import type { MermaidConfig } from './config.type.js'; -import { evaluate } from './diagrams/common/common.js'; -import isEmpty from 'lodash-es/isEmpty.js'; -import { setA11yDiagramInfo, addSVGa11yTitleDescription } from './accessibility.js'; -import type { DiagramMetadata, DiagramStyleClassDef } from './diagram-api/types.js'; -import { preprocessDiagram } from './preprocess.js'; +import type { D3Element, ParseOptions, ParseResult, RenderResult } from './types.js'; import { decodeEntities } from './utils.js'; import { toBase64 } from './utils/base64.js'; -import type { D3Element, ParseOptions, ParseResult, RenderResult } from './types.js'; -import assignWithDepth from './assignWithDepth.js'; const MAX_TEXTLENGTH = 50_000; const MAX_TEXTLENGTH_EXCEEDED_MSG = @@ -477,7 +477,7 @@ const render = async function ( * @param userOptions - Initial Mermaid options */ function initialize(userOptions: MermaidConfig = {}) { - const options = assignWithDepth({}, userOptions); + const options: MermaidConfig = assignWithDepth({}, userOptions); // Handle legacy location of font-family configuration if (options?.fontFamily && !options.themeVariables?.fontFamily) { if (!options.themeVariables) { diff --git a/packages/mermaid/src/rendering-util/icons.ts b/packages/mermaid/src/rendering-util/icons.ts new file mode 100644 index 000000000..27709b822 --- /dev/null +++ b/packages/mermaid/src/rendering-util/icons.ts @@ -0,0 +1,100 @@ +import { log } from '$root/logger.js'; +import type { ExtendedIconifyIcon, IconifyIcon, IconifyJSON } from '@iconify/types'; +import type { IconifyIconCustomisations } from '@iconify/utils'; +import { getIconData, iconToHTML, iconToSVG, replaceIDs, stringToIcon } from '@iconify/utils'; + +interface AsyncIconLoader { + name: string; + loader: () => Promise; +} + +interface SyncIconLoader { + name: string; + icons: IconifyJSON; +} + +export type IconLoader = AsyncIconLoader | SyncIconLoader; + +export const unknownIcon: IconifyIcon = { + body: '?', + height: 80, + width: 80, +}; + +const iconsStore = new Map(); +const loaderStore = new Map(); + +export const registerIconPacks = (iconLoaders: IconLoader[]) => { + for (const iconLoader of iconLoaders) { + if (!iconLoader.name) { + throw new Error( + 'Invalid icon loader. Must have a "name" property with non-empty string value.' + ); + } + log.debug('Registering icon pack:', iconLoader.name); + if ('loader' in iconLoader) { + loaderStore.set(iconLoader.name, iconLoader.loader); + } else if ('icons' in iconLoader) { + iconsStore.set(iconLoader.name, iconLoader.icons); + } else { + log.error('Invalid icon loader:', iconLoader); + throw new Error('Invalid icon loader. Must have either "icons" or "loader" property.'); + } + } +}; + +const getRegisteredIconData = async (iconName: string, fallbackPrefix?: string) => { + const data = stringToIcon(iconName, true, fallbackPrefix !== undefined); + if (!data) { + throw new Error(`Invalid icon name: ${iconName}`); + } + const prefix = data.prefix || fallbackPrefix; + if (!prefix) { + throw new Error(`Icon name must contain a prefix: ${iconName}`); + } + let icons = iconsStore.get(prefix); + if (!icons) { + const loader = loaderStore.get(prefix); + if (!loader) { + throw new Error(`Icon set not found: ${data.prefix}`); + } + try { + const loaded = await loader(); + icons = { ...loaded, prefix }; + iconsStore.set(prefix, icons); + } catch (e) { + log.error(e); + throw new Error(`Failed to load icon set: ${data.prefix}`); + } + } + const iconData = getIconData(icons, data.name); + if (!iconData) { + throw new Error(`Icon not found: ${iconName}`); + } + return iconData; +}; + +export const isIconAvailable = async (iconName: string) => { + try { + await getRegisteredIconData(iconName); + return true; + } catch { + return false; + } +}; + +export const getIconSVG = async ( + iconName: string, + customisations?: IconifyIconCustomisations & { fallbackPrefix?: string } +) => { + let iconData: ExtendedIconifyIcon; + try { + iconData = await getRegisteredIconData(iconName, customisations?.fallbackPrefix); + } catch (e) { + log.error(e); + iconData = unknownIcon; + } + const renderData = iconToSVG(iconData, customisations); + const svg = iconToHTML(replaceIDs(renderData.body), renderData.attributes); + return svg; +}; diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/dagre/index.js b/packages/mermaid/src/rendering-util/layout-algorithms/dagre/index.js index 2717eb717..307242675 100644 --- a/packages/mermaid/src/rendering-util/layout-algorithms/dagre/index.js +++ b/packages/mermaid/src/rendering-util/layout-algorithms/dagre/index.js @@ -28,7 +28,7 @@ import { getSubGraphTitleMargins } from '../../../utils/subGraphTitleMargins.js' import { getConfig } from '../../../diagram-api/diagramAPI.js'; const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, siteConfig) => { - log.info('Graph in recursive render: XXX', graphlibJson.write(graph), parentCluster); + log.warn('Graph in recursive render:XAX', graphlibJson.write(graph), parentCluster); const dir = graph.graph().rankdir; log.trace('Dir in recursive render - dir:', dir); @@ -111,7 +111,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit if (graph.children(v).length > 0) { // This is a cluster but not to be rendered recursively // Render as before - log.info( + log.trace( 'Cluster - the non recursive path XBX', v, node.id, @@ -120,11 +120,11 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit 'Graph:', graph ); - log.info(findNonClusterChild(node.id, graph)); + log.trace(findNonClusterChild(node.id, graph)); clusterDb.set(node.id, { id: findNonClusterChild(node.id, graph), node }); // insertCluster(clusters, graph.node(v)); } else { - log.trace('Node - the non recursive path XAX', v, node.id, node); + log.trace('Node - the non recursive path XAX', v, nodes, graph.node(v), dir); await insertNode(nodes, graph.node(v), dir); } } @@ -305,12 +305,64 @@ export const render = async (data4Layout, svg) => { log.debug('Edges:', data4Layout.edges); data4Layout.edges.forEach((edge) => { - graph.setEdge(edge.start, edge.end, { ...edge }, edge.id); + // Handle self-loops + if (edge.start === edge.end) { + const nodeId = edge.start; + const specialId1 = nodeId + '---' + nodeId + '---1'; + const specialId2 = nodeId + '---' + nodeId + '---2'; + const node = graph.node(nodeId); + graph.setNode(specialId1, { + domId: specialId1, + id: specialId1, + parentId: node.parentId, + labelStyle: '', + label: '', + padding: 0, + shape: 'labelRect', + // shape: 'rect', + style: '', + width: 10, + height: 10, + }); + graph.setParent(specialId1, node.parentId); + graph.setNode(specialId2, { + domId: specialId2, + id: specialId2, + parentId: node.parentId, + labelStyle: '', + padding: 0, + // shape: 'rect', + shape: 'labelRect', + label: '', + style: '', + width: 10, + height: 10, + }); + graph.setParent(specialId2, node.parentId); + + const edge1 = structuredClone(edge); + const edgeMid = structuredClone(edge); + const edge2 = structuredClone(edge); + edge1.label = ''; + edge1.arrowTypeEnd = 'none'; + edge1.id = nodeId + '-cyclic-special-1'; + edgeMid.arrowTypeEnd = 'none'; + edgeMid.id = nodeId + '-cyclic-special-mid'; + edge2.label = ''; + edge1.fromCluster = nodeId; + edge2.toCluster = nodeId; + edge2.id = nodeId + '-cyclic-special-2'; + graph.setEdge(nodeId, specialId1, edge1, nodeId + '-cyclic-special-0'); + graph.setEdge(specialId1, specialId2, edgeMid, nodeId + '-cyclic-special-1'); + graph.setEdge(specialId2, nodeId, edge2, nodeId + '-cyc { ' --- ', clusterDb.get(e.w) ); - if (clusterDb.get(e.v) && clusterDb.get(e.w) && clusterDb.get(e.v) === clusterDb.get(e.w)) { - log.warn('Fixing and trying link to self - removing XXX', e.v, e.w, e.name); - log.warn('Fixing and trying - removing XXX', e.v, e.w, e.name); - v = getAnchorId(e.v); - w = getAnchorId(e.w); - graph.removeEdge(e.v, e.w, e.name); - const specialId1 = e.w + '---' + e.v + '---1'; - const specialId2 = e.w + '---' + e.v + '---2'; - graph.setNode(specialId1, { - domId: specialId1, - id: specialId1, - labelStyle: '', - label: '', - padding: 0, - shape: 'labelRect', - style: '', - width: 10, - height: 10, - }); - graph.setNode(specialId2, { - domId: specialId2, - id: specialId2, - labelStyle: '', - padding: 0, - shape: 'labelRect', - style: '', - width: 10, - height: 10, - }); - const edge1 = structuredClone(edge); - const edgeMid = structuredClone(edge); - const edge2 = structuredClone(edge); - edge1.label = ''; - edge1.arrowTypeEnd = 'none'; - edge1.id = e.name + '-cyclic-special-1'; - edgeMid.arrowTypeEnd = 'none'; - edgeMid.id = e.name + '-cyclic-special-mid'; - edge2.label = ''; - edge1.fromCluster = e.v; - edge2.toCluster = e.v; - edge2.id = e.name + '-cyclic-special-2'; - graph.setEdge(v, specialId1, edge1, e.name + '-cyclic-special-0'); - graph.setEdge(specialId1, specialId2, edgeMid, e.name + '-cyclic-special-1'); - graph.setEdge(specialId2, w, edge2, e.name + '-cyclic-special-2'); - } else if (clusterDb.get(e.v) || clusterDb.get(e.w)) { + if (clusterDb.get(e.v) || clusterDb.get(e.w)) { log.warn('Fixing and trying - removing XXX', e.v, e.w, e.name); v = getAnchorId(e.v); w = getAnchorId(e.w); @@ -334,13 +290,6 @@ export const adjustClustersAndEdges = (graph, depth) => { extractor(graph, 0); log.trace(clusterDb); - - // Remove references to extracted cluster - // graph.edges().forEach((edge) => { - // if (isDescendant(edge.v, clusterId) || isDescendant(edge.w, clusterId)) { - // graph.removeEdge(edge); - // } - // }); }; export const extractor = (graph, depth) => { @@ -441,7 +390,7 @@ export const extractor = (graph, depth) => { for (const node of nodes) { const data = graph.node(node); log.warn(' Now next level', node, data); - if (data.clusterNode) { + if (data?.clusterNode) { extractor(data.graph, depth + 1); } } diff --git a/packages/mermaid/src/schemas/config.schema.yaml b/packages/mermaid/src/schemas/config.schema.yaml index 11c294ed9..a7b3549eb 100644 --- a/packages/mermaid/src/schemas/config.schema.yaml +++ b/packages/mermaid/src/schemas/config.schema.yaml @@ -46,6 +46,7 @@ required: - quadrantChart - xyChart - requirement + - architecture - mindmap - gitGraph - c4 @@ -119,6 +120,17 @@ properties: - LINEAR_SEGMENTS - BRANDES_KOEPF default: BRANDES_KOEPF + cycleBreakingStrategy: + description: | + This strategy decides how to find cycles in the graph and deciding which edges need adjustment to break loops. + type: string + enum: + - GREEDY + - DEPTH_FIRST + - INTERACTIVE + - MODEL_ORDER + - GREEDY_MODEL_ORDER + default: GREEDY_MODEL_ORDER darkMode: type: boolean default: false @@ -263,6 +275,8 @@ properties: $ref: '#/$defs/XYChartConfig' requirement: $ref: '#/$defs/RequirementDiagramConfig' + architecture: + $ref: '#/$defs/ArchitectureDiagramConfig' mindmap: $ref: '#/$defs/MindmapDiagramConfig' gitGraph: @@ -910,6 +924,28 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file) type: number default: 20 + ArchitectureDiagramConfig: + title: Architecture Diagram Config + allOf: [{ $ref: '#/$defs/BaseDiagramConfig' }] + description: The object containing configurations specific for architecture diagrams + type: object + unevaluatedProperties: false + required: + - useMaxWidth + - padding + - iconSize + - fontSize + properties: + padding: + type: number + default: 40 + iconSize: + type: number + default: 80 + fontSize: + type: number + default: 16 + MindmapDiagramConfig: title: Mindmap Diagram Config allOf: [{ $ref: '#/$defs/BaseDiagramConfig' }] diff --git a/packages/mermaid/src/themes/theme-base.js b/packages/mermaid/src/themes/theme-base.js index a92bd9e20..6e572ea5f 100644 --- a/packages/mermaid/src/themes/theme-base.js +++ b/packages/mermaid/src/themes/theme-base.js @@ -220,6 +220,13 @@ class Theme { this.pieOuterStrokeColor = this.pieOuterStrokeColor || 'black'; this.pieOpacity = this.pieOpacity || '0.7'; + /* architecture */ + this.archEdgeColor = this.archEdgeColor || '#777'; + this.archEdgeArrowColor = this.archEdgeArrowColor || '#777'; + this.archEdgeWidth = this.archEdgeWidth || '3'; + this.archGroupBorderColor = this.archGroupBorderColor || '#000'; + this.archGroupBorderWidth = this.archGroupBorderWidth || '2px'; + /* quadrant-graph */ this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; this.quadrant2Fill = this.quadrant2Fill || adjust(this.primaryColor, { r: 5, g: 5, b: 5 }); diff --git a/packages/mermaid/src/themes/theme-dark.js b/packages/mermaid/src/themes/theme-dark.js index 24ba128b5..cf223d989 100644 --- a/packages/mermaid/src/themes/theme-dark.js +++ b/packages/mermaid/src/themes/theme-dark.js @@ -84,6 +84,13 @@ class Theme { this.personBorder = this.primaryBorderColor; this.personBkg = this.mainBkg; + /* Architecture Diagram variables */ + this.archEdgeColor = 'calculated'; + this.archEdgeArrowColor = 'calculated'; + this.archEdgeWidth = '3'; + this.archGroupBorderColor = this.primaryBorderColor; + this.archGroupBorderWidth = '2px'; + /* state colors */ this.labelColor = 'calculated'; @@ -132,6 +139,10 @@ class Theme { this.doneTaskBkgColor = this.mainContrastColor; this.taskTextDarkColor = this.darkTextColor; + /* Architecture Diagram variables */ + this.archEdgeColor = this.lineColor; + this.archEdgeArrowColor = this.lineColor; + /* state colors */ this.transitionColor = this.transitionColor || this.lineColor; this.transitionLabelColor = this.transitionLabelColor || this.textColor; diff --git a/packages/mermaid/src/themes/theme-default.js b/packages/mermaid/src/themes/theme-default.js index 40acbb0f9..e0023758e 100644 --- a/packages/mermaid/src/themes/theme-default.js +++ b/packages/mermaid/src/themes/theme-default.js @@ -112,6 +112,13 @@ class Theme { this.personBorder = this.primaryBorderColor; this.personBkg = this.mainBkg; + /* Architecture Diagram variables */ + this.archEdgeColor = 'calculated'; + this.archEdgeArrowColor = 'calculated'; + this.archEdgeWidth = '3'; + this.archGroupBorderColor = this.primaryBorderColor; + this.archGroupBorderWidth = '2px'; + /* state colors */ this.labelColor = 'black'; this.errorBkgColor = '#552222'; @@ -194,6 +201,10 @@ class Theme { this.taskTextColor = this.taskTextLightColor; this.taskTextOutsideColor = this.taskTextDarkColor; + /* Architecture Diagram variables */ + this.archEdgeColor = this.lineColor; + this.archEdgeArrowColor = this.lineColor; + /* state colors */ this.transitionColor = this.transitionColor || this.lineColor; this.transitionLabelColor = this.transitionLabelColor || this.textColor; diff --git a/packages/mermaid/src/themes/theme-forest.js b/packages/mermaid/src/themes/theme-forest.js index 4bb7d2441..97c0682f3 100644 --- a/packages/mermaid/src/themes/theme-forest.js +++ b/packages/mermaid/src/themes/theme-forest.js @@ -86,6 +86,13 @@ class Theme { this.personBorder = this.primaryBorderColor; this.personBkg = this.mainBkg; + /* Architecture Diagram variables */ + this.archEdgeColor = 'calculated'; + this.archEdgeArrowColor = 'calculated'; + this.archEdgeWidth = '3'; + this.archGroupBorderColor = this.primaryBorderColor; + this.archGroupBorderWidth = '2px'; + /* state colors */ this.labelColor = 'black'; @@ -162,6 +169,10 @@ class Theme { this.activeTaskBorderColor = this.taskBorderColor; this.activeTaskBkgColor = this.mainBkg; + /* Architecture Diagram variables */ + this.archEdgeColor = this.lineColor; + this.archEdgeArrowColor = this.lineColor; + /* state colors */ this.transitionColor = this.transitionColor || this.lineColor; this.transitionLabelColor = this.transitionLabelColor || this.textColor; diff --git a/packages/mermaid/src/themes/theme-neutral.js b/packages/mermaid/src/themes/theme-neutral.js index 40963839e..4a622cbcc 100644 --- a/packages/mermaid/src/themes/theme-neutral.js +++ b/packages/mermaid/src/themes/theme-neutral.js @@ -98,6 +98,13 @@ class Theme { this.personBorder = this.primaryBorderColor; this.personBkg = this.mainBkg; + /* Architecture Diagram variables */ + this.archEdgeColor = 'calculated'; + this.archEdgeArrowColor = 'calculated'; + this.archEdgeWidth = '3'; + this.archGroupBorderColor = this.primaryBorderColor; + this.archGroupBorderWidth = '2px'; + /* state colors */ this.labelColor = 'black'; @@ -199,6 +206,10 @@ class Theme { this.todayLineColor = this.critBkgColor; + /* Architecture Diagram variables */ + this.archEdgeColor = this.lineColor; + this.archEdgeArrowColor = this.lineColor; + /* state colors */ this.transitionColor = this.transitionColor || '#000'; this.transitionLabelColor = this.transitionLabelColor || this.textColor; diff --git a/packages/mermaid/tsconfig.json b/packages/mermaid/tsconfig.json index bb3a8106b..0f06a1731 100644 --- a/packages/mermaid/tsconfig.json +++ b/packages/mermaid/tsconfig.json @@ -9,5 +9,10 @@ "$root/*": ["src/*"] } }, - "include": ["./src/**/*.ts", "./package.json"] + "include": [ + "./src/**/*.ts", + "./package.json", + "src/diagrams/gantt/ganttDb.js", + "src/diagrams/git/gitGraphRenderer.js" + ] } diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index f6b145b5a..4cf29b73a 100644 --- a/packages/parser/CHANGELOG.md +++ b/packages/parser/CHANGELOG.md @@ -1,5 +1,23 @@ # @mermaid-js/parser +## 0.3.0 + +### Minor Changes + +- [#5452](https://github.com/mermaid-js/mermaid/pull/5452) [`256a148`](https://github.com/mermaid-js/mermaid/commit/256a148bbf484fc7db6c19f94dd69d5d268ee048) Thanks [@NicolasNewman](https://github.com/NicolasNewman)! - New Diagram: Architecture + + Adds architecture diagrams which allows users to show relations between services. + +### Patch Changes + +- [#5793](https://github.com/mermaid-js/mermaid/pull/5793) [`7d8143b`](https://github.com/mermaid-js/mermaid/commit/7d8143b917ee3562149a0e0a821ed2d6f29cc05d) Thanks [@sidharthv96](https://github.com/sidharthv96)! - feat: Support - in architecture icons + +## 0.2.0 + +### Minor Changes + +- [#5664](https://github.com/mermaid-js/mermaid/pull/5664) [`5deaef4`](https://github.com/mermaid-js/mermaid/commit/5deaef456e74d796866431c26f69360e4e74dbff) Thanks [@Austin-Fulbright](https://github.com/Austin-Fulbright)! - chore: Migrate git graph to langium, use typescript for internals + ## 0.1.1 ### Patch Changes diff --git a/packages/parser/langium-config.json b/packages/parser/langium-config.json index c750f049d..bf64493ad 100644 --- a/packages/parser/langium-config.json +++ b/packages/parser/langium-config.json @@ -15,6 +15,16 @@ "id": "pie", "grammar": "src/language/pie/pie.langium", "fileExtensions": [".mmd", ".mermaid"] + }, + { + "id": "architecture", + "grammar": "src/language/architecture/architecture.langium", + "fileExtensions": [".mmd", ".mermaid"] + }, + { + "id": "gitGraph", + "grammar": "src/language/gitGraph/gitGraph.langium", + "fileExtensions": [".mmd", ".mermaid"] } ], "mode": "production", diff --git a/packages/parser/package.json b/packages/parser/package.json index 157d4dd05..a26a2a71e 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@mermaid-js/parser", - "version": "0.1.1", + "version": "0.3.0", "description": "MermaidJS parser", "author": "Yokozuna59", "contributors": [ diff --git a/packages/parser/src/language/architecture/architecture.langium b/packages/parser/src/language/architecture/architecture.langium new file mode 100644 index 000000000..11af26243 --- /dev/null +++ b/packages/parser/src/language/architecture/architecture.langium @@ -0,0 +1,55 @@ +grammar Architecture +import "../common/common"; + +entry Architecture: + NEWLINE* + "architecture-beta" + ( + NEWLINE* TitleAndAccessibilities + | NEWLINE* Statement* + | NEWLINE* + ) +; + +fragment Statement: + groups+=Group + | services+=Service + | junctions+=Junction + | edges+=Edge +; + +fragment LeftPort: + ':'lhsDir=ARROW_DIRECTION +; + +fragment RightPort: + rhsDir=ARROW_DIRECTION':' +; + +fragment Arrow: + LeftPort lhsInto?=ARROW_INTO? ('--' | '-' title=ARCH_TITLE '-') rhsInto?=ARROW_INTO? RightPort +; + +Group: + 'group' id=ARCH_ID icon=ARCH_ICON? title=ARCH_TITLE? ('in' in=ARCH_ID)? EOL +; + +Service: + 'service' id=ARCH_ID (iconText=ARCH_TEXT_ICON | icon=ARCH_ICON)? title=ARCH_TITLE? ('in' in=ARCH_ID)? EOL +; + +Junction: + 'junction' id=ARCH_ID ('in' in=ARCH_ID)? EOL +; + +Edge: + lhsId=ARCH_ID lhsGroup?=ARROW_GROUP? Arrow rhsId=ARCH_ID rhsGroup?=ARROW_GROUP? EOL +; + +terminal ARROW_DIRECTION: 'L' | 'R' | 'T' | 'B'; +terminal ARCH_ID: /[\w]+/; +terminal ARCH_TEXT_ICON: /\("[^"]+"\)/; +terminal ARCH_ICON: /\([\w-:]+\)/; +terminal ARCH_TITLE: /\[[\w ]+\]/; +terminal ARROW_GROUP: /\{group\}/; +terminal ARROW_INTO: /<|>/; diff --git a/packages/parser/src/language/architecture/index.ts b/packages/parser/src/language/architecture/index.ts new file mode 100644 index 000000000..fd3c604b0 --- /dev/null +++ b/packages/parser/src/language/architecture/index.ts @@ -0,0 +1 @@ +export * from './module.js'; diff --git a/packages/parser/src/language/architecture/module.ts b/packages/parser/src/language/architecture/module.ts new file mode 100644 index 000000000..d4e730c2c --- /dev/null +++ b/packages/parser/src/language/architecture/module.ts @@ -0,0 +1,79 @@ +import type { + DefaultSharedCoreModuleContext, + LangiumCoreServices, + LangiumSharedCoreServices, + Module, + PartialLangiumCoreServices, +} from 'langium'; +import { + EmptyFileSystem, + createDefaultCoreModule, + createDefaultSharedCoreModule, + inject, +} from 'langium'; + +import { MermaidGeneratedSharedModule, ArchitectureGeneratedModule } from '../generated/module.js'; +import { ArchitectureTokenBuilder } from './tokenBuilder.js'; +import { ArchitectureValueConverter } from './valueConverter.js'; + +/** + * Declaration of `Architecture` services. + */ +interface ArchitectureAddedServices { + parser: { + TokenBuilder: ArchitectureTokenBuilder; + ValueConverter: ArchitectureValueConverter; + }; +} + +/** + * Union of Langium default services and `Architecture` services. + */ +export type ArchitectureServices = LangiumCoreServices & ArchitectureAddedServices; + +/** + * Dependency injection module that overrides Langium default services and + * contributes the declared `Architecture` services. + */ +export const ArchitectureModule: Module< + ArchitectureServices, + PartialLangiumCoreServices & ArchitectureAddedServices +> = { + parser: { + TokenBuilder: () => new ArchitectureTokenBuilder(), + ValueConverter: () => new ArchitectureValueConverter(), + }, +}; + +/** + * Create the full set of services required by Langium. + * + * First inject the shared services by merging two modules: + * - Langium default shared services + * - Services generated by langium-cli + * + * Then inject the language-specific services by merging three modules: + * - Langium default language-specific services + * - Services generated by langium-cli + * - Services specified in this file + * @param context - Optional module context with the LSP connection + * @returns An object wrapping the shared services and the language-specific services + */ +export function createArchitectureServices( + context: DefaultSharedCoreModuleContext = EmptyFileSystem +): { + shared: LangiumSharedCoreServices; + Architecture: ArchitectureServices; +} { + const shared: LangiumSharedCoreServices = inject( + createDefaultSharedCoreModule(context), + MermaidGeneratedSharedModule + ); + const Architecture: ArchitectureServices = inject( + createDefaultCoreModule({ shared }), + ArchitectureGeneratedModule, + ArchitectureModule + ); + shared.ServiceRegistry.register(Architecture); + return { shared, Architecture }; +} diff --git a/packages/parser/src/language/architecture/tokenBuilder.ts b/packages/parser/src/language/architecture/tokenBuilder.ts new file mode 100644 index 000000000..6a7c6a37a --- /dev/null +++ b/packages/parser/src/language/architecture/tokenBuilder.ts @@ -0,0 +1,7 @@ +import { AbstractMermaidTokenBuilder } from '../common/index.js'; + +export class ArchitectureTokenBuilder extends AbstractMermaidTokenBuilder { + public constructor() { + super(['architecture']); + } +} diff --git a/packages/parser/src/language/architecture/valueConverter.ts b/packages/parser/src/language/architecture/valueConverter.ts new file mode 100644 index 000000000..c9475657b --- /dev/null +++ b/packages/parser/src/language/architecture/valueConverter.ts @@ -0,0 +1,20 @@ +import type { CstNode, GrammarAST, ValueType } from 'langium'; + +import { AbstractMermaidValueConverter } from '../common/index.js'; + +export class ArchitectureValueConverter extends AbstractMermaidValueConverter { + protected runCustomConverter( + rule: GrammarAST.AbstractRule, + input: string, + _cstNode: CstNode + ): ValueType | undefined { + if (rule.name === 'ARCH_ICON') { + return input.replace(/[()]/g, '').trim(); + } else if (rule.name === 'ARCH_TEXT_ICON') { + return input.replace(/["()]/g, ''); + } else if (rule.name === 'ARCH_TITLE') { + return input.replace(/[[\]]/g, '').trim(); + } + return undefined; + } +} diff --git a/packages/parser/src/language/gitGraph/gitGraph.langium b/packages/parser/src/language/gitGraph/gitGraph.langium new file mode 100644 index 000000000..1571ebba8 --- /dev/null +++ b/packages/parser/src/language/gitGraph/gitGraph.langium @@ -0,0 +1,87 @@ +grammar GitGraph + +interface Common { + accDescr?: string; + accTitle?: string; + title?: string; +} + +fragment TitleAndAccessibilities: + ((accDescr=ACC_DESCR | accTitle=ACC_TITLE | title=TITLE) EOL)+ +; + +fragment EOL returns string: + NEWLINE+ | EOF +; + +terminal NEWLINE: /\r?\n/; +terminal ACC_DESCR: /[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/; +terminal ACC_TITLE: /[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/; +terminal TITLE: /[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/; + +hidden terminal WHITESPACE: /[\t ]+/; +hidden terminal YAML: /---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/; +hidden terminal DIRECTIVE: /[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/; +hidden terminal SINGLE_LINE_COMMENT: /[\t ]*%%[^\n\r]*/; + +entry GitGraph: + NEWLINE* + ('gitGraph' | 'gitGraph' ':' | 'gitGraph:' | ('gitGraph' Direction ':')) + NEWLINE* + ( + NEWLINE* + (TitleAndAccessibilities | + statements+=Statement | + NEWLINE)* + ) +; + +Statement +: Commit +| Branch +| Merge +| Checkout +| CherryPicking +; + +Direction: + dir=('LR' | 'TB' | 'BT'); + +Commit: + 'commit' + ( + 'id:' id=STRING + |'msg:'? message=STRING + |'tag:' tags+=STRING + |'type:' type=('NORMAL' | 'REVERSE' | 'HIGHLIGHT') + )* EOL; +Branch: + 'branch' name=(ID|STRING) + ('order:' order=INT)? + EOL; + +Merge: + 'merge' branch=(ID|STRING) + ( + 'id:' id=STRING + |'tag:' tags+=STRING + |'type:' type=('NORMAL' | 'REVERSE' | 'HIGHLIGHT') + )* EOL; + +Checkout: + ('checkout'|'switch') branch=(ID|STRING) EOL; + +CherryPicking: + 'cherry-pick' + ( + 'id:' id=STRING + |'tag:' tags+=STRING + |'parent:' parent=STRING + )* EOL; + + + +terminal INT returns number: /[0-9]+(?=\s)/; +terminal ID returns string: /\w([-\./\w]*[-\w])?/; +terminal STRING: /"[^"]*"|'[^']*'/; + diff --git a/packages/parser/src/language/gitGraph/index.ts b/packages/parser/src/language/gitGraph/index.ts new file mode 100644 index 000000000..fd3c604b0 --- /dev/null +++ b/packages/parser/src/language/gitGraph/index.ts @@ -0,0 +1 @@ +export * from './module.js'; diff --git a/packages/parser/src/language/gitGraph/module.ts b/packages/parser/src/language/gitGraph/module.ts new file mode 100644 index 000000000..e2d45c8fa --- /dev/null +++ b/packages/parser/src/language/gitGraph/module.ts @@ -0,0 +1,52 @@ +import type { + DefaultSharedCoreModuleContext, + LangiumCoreServices, + LangiumSharedCoreServices, + Module, + PartialLangiumCoreServices, +} from 'langium'; +import { + inject, + createDefaultCoreModule, + createDefaultSharedCoreModule, + EmptyFileSystem, +} from 'langium'; +import { CommonValueConverter } from '../common/valueConverter.js'; +import { MermaidGeneratedSharedModule, GitGraphGeneratedModule } from '../generated/module.js'; +import { GitGraphTokenBuilder } from './tokenBuilder.js'; + +interface GitGraphAddedServices { + parser: { + TokenBuilder: GitGraphTokenBuilder; + ValueConverter: CommonValueConverter; + }; +} + +export type GitGraphServices = LangiumCoreServices & GitGraphAddedServices; + +export const GitGraphModule: Module< + GitGraphServices, + PartialLangiumCoreServices & GitGraphAddedServices +> = { + parser: { + TokenBuilder: () => new GitGraphTokenBuilder(), + ValueConverter: () => new CommonValueConverter(), + }, +}; + +export function createGitGraphServices(context: DefaultSharedCoreModuleContext = EmptyFileSystem): { + shared: LangiumSharedCoreServices; + GitGraph: GitGraphServices; +} { + const shared: LangiumSharedCoreServices = inject( + createDefaultSharedCoreModule(context), + MermaidGeneratedSharedModule + ); + const GitGraph: GitGraphServices = inject( + createDefaultCoreModule({ shared }), + GitGraphGeneratedModule, + GitGraphModule + ); + shared.ServiceRegistry.register(GitGraph); + return { shared, GitGraph }; +} diff --git a/packages/parser/src/language/gitGraph/tokenBuilder.ts b/packages/parser/src/language/gitGraph/tokenBuilder.ts new file mode 100644 index 000000000..ccadf1a1f --- /dev/null +++ b/packages/parser/src/language/gitGraph/tokenBuilder.ts @@ -0,0 +1,7 @@ +import { AbstractMermaidTokenBuilder } from '../common/index.js'; + +export class GitGraphTokenBuilder extends AbstractMermaidTokenBuilder { + public constructor() { + super(['gitGraph']); + } +} diff --git a/packages/parser/src/language/index.ts b/packages/parser/src/language/index.ts index 9f1d92ba8..c85a5a8b6 100644 --- a/packages/parser/src/language/index.ts +++ b/packages/parser/src/language/index.ts @@ -5,21 +5,37 @@ export { PacketBlock, Pie, PieSection, + Architecture, + GitGraph, + Branch, + Commit, + Merge, + Statement, isCommon, isInfo, isPacket, isPacketBlock, isPie, isPieSection, + isArchitecture, + isGitGraph, + isBranch, + isCommit, + isMerge, } from './generated/ast.js'; + export { InfoGeneratedModule, MermaidGeneratedSharedModule, PacketGeneratedModule, PieGeneratedModule, + ArchitectureGeneratedModule, + GitGraphGeneratedModule, } from './generated/module.js'; +export * from './gitGraph/index.js'; export * from './common/index.js'; export * from './info/index.js'; export * from './packet/index.js'; export * from './pie/index.js'; +export * from './architecture/index.js'; diff --git a/packages/parser/src/language/pie/valueConverter.ts b/packages/parser/src/language/pie/valueConverter.ts index 6e312e172..cc412d7a1 100644 --- a/packages/parser/src/language/pie/valueConverter.ts +++ b/packages/parser/src/language/pie/valueConverter.ts @@ -6,7 +6,6 @@ export class PieValueConverter extends AbstractMermaidValueConverter { protected runCustomConverter( rule: GrammarAST.AbstractRule, input: string, - _cstNode: CstNode ): ValueType | undefined { if (rule.name !== 'PIE_SECTION_LABEL') { diff --git a/packages/parser/src/parse.ts b/packages/parser/src/parse.ts index 992b96506..86713c2f1 100644 --- a/packages/parser/src/parse.ts +++ b/packages/parser/src/parse.ts @@ -1,8 +1,8 @@ import type { LangiumParser, ParseResult } from 'langium'; -import type { Info, Packet, Pie } from './index.js'; +import type { Info, Packet, Pie, Architecture, GitGraph } from './index.js'; -export type DiagramAST = Info | Packet | Pie; +export type DiagramAST = Info | Packet | Pie | Architecture | GitGraph; const parsers: Record = {}; const initializers = { @@ -21,11 +21,24 @@ const initializers = { const parser = createPieServices().Pie.parser.LangiumParser; parsers.pie = parser; }, + architecture: async () => { + const { createArchitectureServices } = await import('./language/architecture/index.js'); + const parser = createArchitectureServices().Architecture.parser.LangiumParser; + parsers.architecture = parser; + }, + gitGraph: async () => { + const { createGitGraphServices } = await import('./language/gitGraph/index.js'); + const parser = createGitGraphServices().GitGraph.parser.LangiumParser; + parsers.gitGraph = parser; + }, } as const; export async function parse(diagramType: 'info', text: string): Promise; export async function parse(diagramType: 'packet', text: string): Promise; export async function parse(diagramType: 'pie', text: string): Promise; +export async function parse(diagramType: 'architecture', text: string): Promise; +export async function parse(diagramType: 'gitGraph', text: string): Promise; + export async function parse( diagramType: keyof typeof initializers, text: string diff --git a/packages/parser/tests/gitGraph.test.ts b/packages/parser/tests/gitGraph.test.ts new file mode 100644 index 000000000..2d7c21bbe --- /dev/null +++ b/packages/parser/tests/gitGraph.test.ts @@ -0,0 +1,207 @@ +import { describe, expect, it } from 'vitest'; +import type { Branch, Merge } from '../src/language/index.js'; +import { gitGraphParse as parse } from './test-util.js'; +import type { Commit } from '../src/language/index.js'; +import type { Checkout, CherryPicking } from '../src/language/generated/ast.js'; + +describe('Parsing Commit Statements', () => { + it('should parse a simple commit', () => { + const result = parse(`gitGraph\n commit\n`); + expect(result.value.statements[0].$type).toBe('Commit'); + }); + + it('should parse multiple commits', () => { + const result = parse(`gitGraph\n commit\n commit\n commit\n`); + expect(result.value.statements).toHaveLength(3); + }); + + it('should parse commits with all properties', () => { + const result = parse(`gitGraph\n commit id:"1" msg:"Fix bug" tag:"v1.2" type:NORMAL\n`); + const commit = result.value.statements[0] as Commit; + expect(commit.$type).toBe('Commit'); + expect(commit.id).toBe('1'); + expect(commit.message).toBe('Fix bug'); + expect(commit.tags).toEqual(['v1.2']); + expect(commit.type).toBe('NORMAL'); + }); + + it('should handle commit messages with special characters', () => { + const result = parse(`gitGraph\n commit msg:"Fix issue #123: Handle errors"\n`); + const commit = result.value.statements[0] as Commit; + expect(commit.message).toBe('Fix issue #123: Handle errors'); + }); + + it('should parse commits with only a message and no other properties', () => { + const result = parse(`gitGraph\n commit msg:"Initial release"\n`); + const commit = result.value.statements[0] as Commit; + expect(commit.message).toBe('Initial release'); + expect(commit.id).toBeUndefined(); + expect(commit.type).toBeUndefined(); + }); + + it('should ignore malformed properties and not break parsing', () => { + const result = parse(`gitGraph\n commit id:"2" msg:"Malformed commit" oops:"ignored"\n`); + const commit = result.value.statements[0] as Commit; + expect(commit.id).toBe('2'); + expect(commit.message).toBe('Malformed commit'); + expect(commit.hasOwnProperty('oops')).toBe(false); + }); + + it('should parse multiple commits with different types', () => { + const result = parse(`gitGraph\n commit type:NORMAL\n commit type:REVERSE\n`); + const commit1 = result.value.statements[0] as Commit; + const commit2 = result.value.statements[1] as Commit; + expect(commit1.type).toBe('NORMAL'); + expect(commit2.type).toBe('REVERSE'); + }); +}); + +describe('Parsing Branch Statements', () => { + it('should parse a branch with a simple name', () => { + const result = parse(`gitGraph\n commit\n commit\n branch master\n`); + const branch = result.value.statements[2] as Branch; + expect(branch.name).toBe('master'); + }); + + it('should parse a branch with an order property', () => { + const result = parse(`gitGraph\n commit\n branch feature order:1\n`); + const branch = result.value.statements[1] as Branch; + expect(branch.name).toBe('feature'); + expect(branch.order).toBe(1); + }); + + it('should handle branch names with special characters', () => { + const result = parse(`gitGraph\n branch feature/test-branch\n`); + const branch = result.value.statements[0] as Branch; + expect(branch.name).toBe('feature/test-branch'); + }); + + it('should parse branches with hyphens and underscores', () => { + const result = parse(`gitGraph\n branch my-feature_branch\n`); + const branch = result.value.statements[0] as Branch; + expect(branch.name).toBe('my-feature_branch'); + }); + + it('should correctly handle branch without order property', () => { + const result = parse(`gitGraph\n branch feature\n`); + const branch = result.value.statements[0] as Branch; + expect(branch.name).toBe('feature'); + expect(branch.order).toBeUndefined(); + }); +}); + +describe('Parsing Merge Statements', () => { + it('should parse a merge with a branch name', () => { + const result = parse(`gitGraph\n merge master\n`); + const merge = result.value.statements[0] as Merge; + expect(merge.branch).toBe('master'); + }); + + it('should handle merges with additional properties', () => { + const result = parse(`gitGraph\n merge feature id:"m1" tag:"release" type:HIGHLIGHT\n`); + const merge = result.value.statements[0] as Merge; + expect(merge.branch).toBe('feature'); + expect(merge.id).toBe('m1'); + expect(merge.tags).toEqual(['release']); + expect(merge.type).toBe('HIGHLIGHT'); + }); + + it('should parse merge without any properties', () => { + const result = parse(`gitGraph\n merge feature\n`); + const merge = result.value.statements[0] as Merge; + expect(merge.branch).toBe('feature'); + }); + + it('should ignore malformed properties in merge statements', () => { + const result = parse(`gitGraph\n merge feature random:"ignored"\n`); + const merge = result.value.statements[0] as Merge; + expect(merge.branch).toBe('feature'); + expect(merge.hasOwnProperty('random')).toBe(false); + }); +}); + +describe('Parsing Checkout Statements', () => { + it('should parse a checkout to a named branch', () => { + const result = parse( + `gitGraph\n commit id:"1"\n branch develop\n branch fun\n checkout develop\n` + ); + const checkout = result.value.statements[3] as Checkout; + expect(checkout.branch).toBe('develop'); + }); + + it('should parse checkout to branches with complex names', () => { + const result = parse(`gitGraph\n checkout hotfix-123\n`); + const checkout = result.value.statements[0] as Checkout; + expect(checkout.branch).toBe('hotfix-123'); + }); + + it('should parse checkouts with hyphens and numbers', () => { + const result = parse(`gitGraph\n checkout release-2021\n`); + const checkout = result.value.statements[0] as Checkout; + expect(checkout.branch).toBe('release-2021'); + }); +}); + +describe('Parsing CherryPicking Statements', () => { + it('should parse cherry-picking with a commit id', () => { + const result = parse(`gitGraph\n commit id:"123" commit id:"321" cherry-pick id:"123"\n`); + const cherryPick = result.value.statements[2] as CherryPicking; + expect(cherryPick.id).toBe('123'); + }); + + it('should parse cherry-picking with multiple properties', () => { + const result = parse(`gitGraph\n cherry-pick id:"123" tag:"urgent" parent:"100"\n`); + const cherryPick = result.value.statements[0] as CherryPicking; + expect(cherryPick.id).toBe('123'); + expect(cherryPick.tags).toEqual(['urgent']); + expect(cherryPick.parent).toBe('100'); + }); + + describe('Parsing with Accessibility Titles and Descriptions', () => { + it('should parse accessibility titles', () => { + const result = parse(`gitGraph\n accTitle: Accessible Graph\n commit\n`); + expect(result.value.accTitle).toBe('Accessible Graph'); + }); + + it('should parse multiline accessibility descriptions', () => { + const result = parse( + `gitGraph\n accDescr {\n Detailed description\n across multiple lines\n }\n commit\n` + ); + expect(result.value.accDescr).toBe('Detailed description\nacross multiple lines'); + }); + }); + + describe('Integration Tests', () => { + it('should correctly parse a complex graph with various elements', () => { + const result = parse(` + gitGraph TB: + accTitle: Complex Example + commit id:"init" type:NORMAL + branch feature + commit id:"feat1" msg:"Add feature" + checkout main + merge feature tag:"v1.0" + cherry-pick id:"feat1" tag:"critical fix" + `); + expect(result.value.accTitle).toBe('Complex Example'); + expect(result.value.statements[0].$type).toBe('Commit'); + expect(result.value.statements[1].$type).toBe('Branch'); + expect(result.value.statements[2].$type).toBe('Commit'); + expect(result.value.statements[3].$type).toBe('Checkout'); + expect(result.value.statements[4].$type).toBe('Merge'); + expect(result.value.statements[5].$type).toBe('CherryPicking'); + }); + }); + + describe('Error Handling for Invalid Syntax', () => { + it('should report errors for unknown properties in commit', () => { + const result = parse(`gitGraph\n commit unknown:"oops"\n`); + expect(result.parserErrors).not.toHaveLength(0); + }); + + it('should report errors for invalid branch order', () => { + const result = parse(`gitGraph\n branch feature order:xyz\n`); + expect(result.parserErrors).not.toHaveLength(0); + }); + }); +}); diff --git a/packages/parser/tests/test-util.ts b/packages/parser/tests/test-util.ts index 9bdec348a..5cb487758 100644 --- a/packages/parser/tests/test-util.ts +++ b/packages/parser/tests/test-util.ts @@ -1,7 +1,18 @@ import type { LangiumParser, ParseResult } from 'langium'; import { expect, vi } from 'vitest'; -import type { Info, InfoServices, Pie, PieServices } from '../src/language/index.js'; -import { createInfoServices, createPieServices } from '../src/language/index.js'; +import type { + Info, + InfoServices, + Pie, + PieServices, + GitGraph, + GitGraphServices, +} from '../src/language/index.js'; +import { + createInfoServices, + createPieServices, + createGitGraphServices, +} from '../src/language/index.js'; const consoleMock = vi.spyOn(console, 'log').mockImplementation(() => undefined); @@ -40,3 +51,14 @@ export function createPieTestServices() { return { services: pieServices, parse }; } export const pieParse = createPieTestServices().parse; + +const gitGraphServices: GitGraphServices = createGitGraphServices().GitGraph; +const gitGraphParser: LangiumParser = gitGraphServices.parser.LangiumParser; +export function createGitGraphTestServices() { + const parse = (input: string) => { + return gitGraphParser.parse(input); + }; + + return { services: gitGraphServices, parse }; +} +export const gitGraphParse = createGitGraphTestServices().parse; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b377a1570..7fcdbb5f3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,10 +10,10 @@ importers: devDependencies: '@applitools/eyes-cypress': specifier: ^3.44.4 - version: 3.44.6(encoding@0.1.13)(typescript@5.4.5) + version: 3.44.7(encoding@0.1.13)(typescript@5.4.5) '@argos-ci/cypress': specifier: ^2.1.0 - version: 2.1.1(cypress@13.13.2) + version: 2.1.2(cypress@13.14.1) '@changesets/changelog-github': specifier: ^0.5.0 version: 0.5.0(encoding@0.1.13) @@ -22,16 +22,16 @@ importers: version: 2.27.7 '@cspell/eslint-plugin': specifier: ^8.8.4 - version: 8.13.1(eslint@9.8.0) + version: 8.13.3(eslint@9.8.0) '@cypress/code-coverage': specifier: ^3.12.30 - version: 3.12.44(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)))(cypress@13.13.2)(webpack@5.93.0(esbuild@0.21.5)) + version: 3.12.45(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)))(cypress@13.14.1)(webpack@5.93.0(esbuild@0.21.5)) '@eslint/js': specifier: ^9.4.0 version: 9.8.0 '@rollup/plugin-typescript': specifier: ^11.1.6 - version: 11.1.6(rollup@4.20.0)(tslib@2.6.3)(typescript@5.4.5) + version: 11.1.6(rollup@4.21.1)(tslib@2.7.0)(typescript@5.4.5) '@types/cors': specifier: ^2.8.17 version: 2.8.17 @@ -52,13 +52,13 @@ importers: version: 4.0.4 '@types/node': specifier: ^20.11.30 - version: 20.14.14 + version: 20.16.2 '@types/rollup-plugin-visualizer': specifier: ^4.2.4 version: 4.2.4 '@vitest/coverage-v8': specifier: ^1.4.0 - version: 1.6.0(vitest@1.6.0(@types/node@20.14.14)(@vitest/ui@1.6.0)(jsdom@24.1.1)(terser@5.31.3)) + version: 1.6.0(vitest@1.6.0(@types/node@20.16.2)(@vitest/ui@1.6.0)(jsdom@24.1.3)(terser@5.31.6)) '@vitest/spy': specifier: ^1.4.0 version: 1.6.0 @@ -85,13 +85,13 @@ importers: version: 7.0.3 cspell: specifier: ^8.6.0 - version: 8.13.1 + version: 8.14.2 cypress: - specifier: ^13.11.0 - version: 13.13.2 + specifier: ^13.14.1 + version: 13.14.1 cypress-image-snapshot: specifier: ^4.0.1 - version: 4.0.1(cypress@13.13.2)(jest@29.7.0(@types/node@20.14.14)) + version: 4.0.1(cypress@13.14.1)(jest@29.7.0(@types/node@20.16.2)) esbuild: specifier: ^0.21.5 version: 0.21.5 @@ -109,13 +109,13 @@ importers: version: 8.1.1 eslint-plugin-jest: specifier: ^28.6.0 - version: 28.7.0(@typescript-eslint/eslint-plugin@8.0.0(@typescript-eslint/parser@8.0.0(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(jest@29.7.0(@types/node@20.14.14))(typescript@5.4.5) + version: 28.7.0(@typescript-eslint/eslint-plugin@8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(jest@29.7.0(@types/node@20.16.2))(typescript@5.4.5) eslint-plugin-jsdoc: specifier: ^48.2.9 version: 48.11.0(eslint@9.8.0) eslint-plugin-json: specifier: ^4.0.0 - version: 4.0.0 + version: 4.0.1 eslint-plugin-lodash: specifier: ^8.0.0 version: 8.0.0(eslint@9.8.0) @@ -142,10 +142,10 @@ importers: version: 14.0.2 husky: specifier: ^9.0.11 - version: 9.1.4 + version: 9.1.5 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.14.14) + version: 29.7.0(@types/node@20.16.2) jison: specifier: ^0.4.18 version: 0.4.18 @@ -154,13 +154,13 @@ importers: version: 4.1.0 jsdom: specifier: ^24.0.0 - version: 24.1.1 + version: 24.1.3 langium-cli: specifier: 3.0.3 version: 3.0.3 lint-staged: specifier: ^15.2.2 - version: 15.2.8 + version: 15.2.9 markdown-table: specifier: ^3.0.3 version: 3.0.3 @@ -170,9 +170,6 @@ importers: path-browserify: specifier: ^1.0.1 version: 1.0.1 - pnpm: - specifier: ^8.15.5 - version: 8.15.9 prettier: specifier: ^3.2.5 version: 3.3.3 @@ -184,34 +181,37 @@ importers: version: 5.0.10 rollup-plugin-visualizer: specifier: ^5.12.0 - version: 5.12.0(rollup@4.20.0) + version: 5.12.0(rollup@4.21.1) start-server-and-test: specifier: ^2.0.3 version: 2.0.5 tsx: specifier: ^4.7.1 - version: 4.16.5 + version: 4.19.0 typescript: specifier: ~5.4.5 version: 5.4.5 typescript-eslint: specifier: ^8.0.0-alpha.34 - version: 8.0.0(eslint@9.8.0)(typescript@5.4.5) + version: 8.0.1(eslint@9.8.0)(typescript@5.4.5) vite: specifier: ^5.2.3 - version: 5.3.5(@types/node@20.14.14)(terser@5.31.3) + version: 5.4.2(@types/node@20.16.2)(terser@5.31.6) vite-plugin-istanbul: specifier: ^6.0.0 - version: 6.0.2(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3)) + version: 6.0.2(vite@5.4.2(@types/node@20.16.2)(terser@5.31.6)) vitest: specifier: ^1.4.0 - version: 1.6.0(@types/node@20.14.14)(@vitest/ui@1.6.0)(jsdom@24.1.1)(terser@5.31.3) + version: 1.6.0(@types/node@20.16.2)(@vitest/ui@1.6.0)(jsdom@24.1.3)(terser@5.31.6) packages/mermaid: dependencies: '@braintree/sanitize-url': specifier: ^7.0.1 version: 7.1.0 + '@iconify/utils': + specifier: ^2.1.32 + version: 2.1.32 '@mermaid-js/parser': specifier: workspace:^ version: link:../parser @@ -221,6 +221,9 @@ importers: cytoscape-cose-bilkent: specifier: ^4.1.0 version: 4.1.0(cytoscape@3.30.1) + cytoscape-fcose: + specifier: ^2.2.0 + version: 2.2.0(cytoscape@3.30.1) d3: specifier: ^7.9.0 version: 7.9.0 @@ -232,7 +235,7 @@ importers: version: 7.0.10 dayjs: specifier: ^1.11.10 - version: 1.11.12 + version: 1.11.13 dompurify: specifier: ^3.0.11 version: 3.1.6 @@ -253,7 +256,7 @@ importers: version: 4.6.6 stylis: specifier: ^4.3.1 - version: 4.3.2 + version: 4.3.4 ts-dedent: specifier: ^2.2.0 version: 2.2.0 @@ -264,9 +267,15 @@ importers: '@adobe/jsonschema2md': specifier: ^8.0.0 version: 8.0.2 + '@iconify/types': + specifier: ^2.0.0 + version: 2.0.0 '@types/cytoscape': specifier: ^3.21.4 version: 3.21.5 + '@types/cytoscape-fcose': + specifier: ^2.2.4 + version: 2.2.4 '@types/d3': specifier: ^7.4.3 version: 7.4.3 @@ -332,13 +341,13 @@ importers: version: 3.7.7 jsdom: specifier: ^24.0.0 - version: 24.1.1 + version: 24.1.3 json-schema-to-typescript: specifier: ^13.1.2 version: 13.1.2 micromatch: specifier: ^4.0.5 - version: 4.0.7 + version: 4.0.8 path-browserify: specifier: ^1.0.1 version: 1.0.1 @@ -362,7 +371,7 @@ importers: version: 2.0.5 type-fest: specifier: ^4.13.1 - version: 4.23.0 + version: 4.25.0 typedoc: specifier: ^0.25.12 version: 0.25.13(typescript@5.4.5) @@ -380,10 +389,10 @@ importers: version: 5.0.0 vitepress: specifier: ^1.0.1 - version: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.14)(axios@1.7.3)(postcss@8.4.40)(search-insights@2.15.0)(terser@5.31.3)(typescript@5.4.5) + version: 1.3.4(@algolia/client-search@4.24.0)(@types/node@22.5.1)(axios@1.7.5)(postcss@8.4.41)(search-insights@2.15.0)(terser@5.31.6)(typescript@5.4.5) vitepress-plugin-search: specifier: 1.0.4-alpha.22 - version: 1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.14)(axios@1.7.3)(postcss@8.4.40)(search-insights@2.15.0)(terser@5.31.3)(typescript@5.4.5))(vue@3.4.35(typescript@5.4.5)) + version: 1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.3.4(@algolia/client-search@4.24.0)(@types/node@22.5.1)(axios@1.7.5)(postcss@8.4.41)(search-insights@2.15.0)(terser@5.31.6)(typescript@5.4.5))(vue@3.4.38(typescript@5.4.5)) packages/mermaid-example-diagram: dependencies: @@ -427,7 +436,7 @@ importers: dependencies: '@zenuml/core': specifier: ^3.23.27 - version: 3.24.2(typescript@5.4.5) + version: 3.24.3(typescript@5.4.5) devDependencies: mermaid: specifier: workspace:^ @@ -440,7 +449,7 @@ importers: version: 7.4.47 '@vueuse/core': specifier: ^10.9.0 - version: 10.11.0(vue@3.4.35(typescript@5.4.5)) + version: 10.11.1(vue@3.4.38(typescript@5.4.5)) font-awesome: specifier: ^4.7.0 version: 4.7.0 @@ -452,7 +461,7 @@ importers: version: link:../.. vue: specifier: ^3.4.21 - version: 3.4.35(typescript@5.4.5) + version: 3.4.38(typescript@5.4.5) devDependencies: '@iconify-json/carbon': specifier: ^1.1.31 @@ -462,10 +471,10 @@ importers: version: 0.59.4 '@vite-pwa/vitepress': specifier: ^0.4.0 - version: 0.4.0(vite-plugin-pwa@0.19.8(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0)) + version: 0.4.0(vite-plugin-pwa@0.19.8(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0)) '@vitejs/plugin-vue': specifier: ^5.0.0 - version: 5.1.2(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(vue@3.4.35(typescript@5.4.5)) + version: 5.1.2(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))(vue@3.4.38(typescript@5.4.5)) fast-glob: specifier: ^3.3.2 version: 3.3.2 @@ -477,19 +486,80 @@ importers: version: 1.1.2 unocss: specifier: ^0.59.0 - version: 0.59.4(postcss@8.4.40)(rollup@2.79.1)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3)) + version: 0.59.4(postcss@8.4.41)(rollup@2.79.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6)) unplugin-vue-components: specifier: ^0.26.0 - version: 0.26.0(@babel/parser@7.25.3)(rollup@2.79.1)(vue@3.4.35(typescript@5.4.5)) + version: 0.26.0(@babel/parser@7.25.4)(rollup@2.79.1)(vue@3.4.38(typescript@5.4.5)) vite: specifier: ^5.0.0 - version: 5.3.5(@types/node@20.14.14)(terser@5.31.3) + version: 5.4.2(@types/node@22.5.1)(terser@5.31.6) vite-plugin-pwa: specifier: ^0.19.7 - version: 0.19.8(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0) + version: 0.19.8(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0) vitepress: specifier: 1.1.4 - version: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.14)(axios@1.7.3)(postcss@8.4.40)(search-insights@2.15.0)(terser@5.31.3)(typescript@5.4.5) + version: 1.1.4(@algolia/client-search@4.24.0)(@types/node@22.5.1)(axios@1.7.5)(postcss@8.4.41)(search-insights@2.15.0)(terser@5.31.6)(typescript@5.4.5) + workbox-window: + specifier: ^7.0.0 + version: 7.1.0 + + packages/mermaid/src/vitepress: + dependencies: + '@mdi/font': + specifier: ^7.0.0 + version: 7.4.47 + '@vueuse/core': + specifier: ^10.9.0 + version: 10.11.1(vue@3.4.38(typescript@5.4.5)) + font-awesome: + specifier: ^4.7.0 + version: 4.7.0 + jiti: + specifier: ^1.21.0 + version: 1.21.6 + mermaid: + specifier: workspace:^ + version: link:../.. + vue: + specifier: ^3.4.21 + version: 3.4.38(typescript@5.4.5) + devDependencies: + '@iconify-json/carbon': + specifier: ^1.1.31 + version: 1.1.37 + '@unocss/reset': + specifier: ^0.59.0 + version: 0.59.4 + '@vite-pwa/vitepress': + specifier: ^0.4.0 + version: 0.4.0(vite-plugin-pwa@0.19.8(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0)) + '@vitejs/plugin-vue': + specifier: ^5.0.0 + version: 5.1.2(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))(vue@3.4.38(typescript@5.4.5)) + fast-glob: + specifier: ^3.3.2 + version: 3.3.2 + https-localhost: + specifier: ^4.7.1 + version: 4.7.1 + pathe: + specifier: ^1.1.2 + version: 1.1.2 + unocss: + specifier: ^0.59.0 + version: 0.59.4(postcss@8.4.41)(rollup@4.21.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6)) + unplugin-vue-components: + specifier: ^0.26.0 + version: 0.26.0(@babel/parser@7.25.4)(rollup@4.21.1)(vue@3.4.38(typescript@5.4.5)) + vite: + specifier: ^5.0.0 + version: 5.4.2(@types/node@22.5.1)(terser@5.31.6) + vite-plugin-pwa: + specifier: ^0.19.7 + version: 0.19.8(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0) + vitepress: + specifier: 1.1.4 + version: 1.1.4(@algolia/client-search@4.24.0)(@types/node@22.5.1)(axios@1.7.5)(postcss@8.4.41)(search-insights@2.15.0)(terser@5.31.6)(typescript@5.4.5) workbox-window: specifier: ^7.0.0 version: 7.1.0 @@ -606,6 +676,9 @@ packages: '@antfu/install-pkg@0.1.1': resolution: {integrity: sha512-LyB/8+bSfa0DFGC06zpCEfs89/XoWZwws5ygEa5D+Xsm3OfI+aXQ86VgVG7Acyef+rSZ5HE7J8rrxzrQeM3PjQ==} + '@antfu/install-pkg@0.4.1': + resolution: {integrity: sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==} + '@antfu/utils@0.7.10': resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} @@ -619,8 +692,8 @@ packages: resolution: {integrity: sha512-6v5box6DqmvyfVNe0tjRSCIZpfkn6fc0DZMZI4+jKLczh4zm+Tlfey1ECavP3fRZayh79SGCpeIDqBNI9Ll7dA==} engines: {node: '>=12.13.0'} - '@applitools/core@4.18.0': - resolution: {integrity: sha512-GCW9pwPwXIieKLF5cdA2ezuwwzWHFFnq9mGNAfsvWc1/o2rchj7VRxMRo2566esaniOGVtY7klf9HzJaaZQubQ==} + '@applitools/core@4.18.1': + resolution: {integrity: sha512-Z0tW1PVOHcYL3NCn83B6lErKElHE9YkIJx+rQt8WqQw9PMpkUtiAwRA1sx6F10U0xSKbAJHRq7A9Yr4FMwnVog==} engines: {node: '>=12.13.0'} hasBin: true @@ -628,8 +701,8 @@ packages: resolution: {integrity: sha512-rH3aq/dkTweEUgS/MKuthD79CZDqpQVJlqmxqVxLZVAzbeFxYdTG/gnfG0zj6YJ025jzcPH2ktdW16Rl3QLutg==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - '@applitools/dom-capture@11.3.0': - resolution: {integrity: sha512-LGcNSPgzvlL/afQGUyykTfuPR6N+GYYQ5EaA/f5j4lgfYVxEyG/6t1W62GTImR86ZVHLEsKAQUKVE7jbKAZmVw==} + '@applitools/dom-capture@11.3.1': + resolution: {integrity: sha512-TylDa9uir7LnZ8e/E76rMRR5+1t927RQeATqIub0WnWMHMCs3reNlinbolQ4PWS1zIwkGkVXQzClSF/dRz6OWA==} engines: {node: '>=12.13.0'} '@applitools/dom-shared@1.0.15': @@ -662,13 +735,13 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - '@applitools/eyes-cypress@3.44.6': - resolution: {integrity: sha512-2RfdDNvF9qptTzMg2akjPsVXA/gQcmj0+2QHM+g8r+Shy5A6lfAhA2QAChTM7zvshTT8ZgxY8J1w+3TMbLdseQ==} + '@applitools/eyes-cypress@3.44.7': + resolution: {integrity: sha512-YgzUnc4YAoEQSNSLlwZvyP50Xua2U6h3WV7KAU+E9MbR2RwBBddUhI0y3PhnzJOZgvUJvSfdwqbw0Cxn0CfVjg==} engines: {node: '>=12.13.0'} hasBin: true - '@applitools/eyes@1.22.0': - resolution: {integrity: sha512-x4UQSDxWPOyEUGr9lrOYxhI+bHPpvzYbNAFr1C/99WY+T92GjNlXduBflx5NqOEFjbS1RzRuPDPq6Fbk/U/qhQ==} + '@applitools/eyes@1.22.1': + resolution: {integrity: sha512-+WOYGsRF3CuyLZQHJ2P4l3AjFFlHtW+s7kmjcyVswlvEcVJDxsiQzFnklo37IbbyX6oMi2Df8jOYPjXNCTQd7A==} engines: {node: '>=12.13.0'} '@applitools/functional-commons@1.6.0': @@ -730,22 +803,26 @@ packages: resolution: {integrity: sha512-qgJqx2yjlJBf79YyFehf1nSp4AXOdzJn3POQyg8CMWV0YH6HsjAfJjYaNrbXFcGYCSpPEJGhGehxC7GVKHX3YA==} engines: {node: '>=12.13.0'} - '@argos-ci/browser@2.1.2': - resolution: {integrity: sha512-4dpz76kW0KnXCYdxtkcdiaYUM4owmKtT9zPqrd1yPo+VuSNCNULyCZJ4mdy0aXWT716JLMMmIZ3AnQSkyaqvaA==} + '@argos-ci/api-client@0.2.0': + resolution: {integrity: sha512-stqugeAtbHjD2MwezvgJ4hU0HvlEGwGDVsJvUUD4YoRS0putS8yFjXuempkc90XGeHDEfYZgvG372rcK7/FClA==} engines: {node: '>=18.0.0'} - '@argos-ci/core@2.4.1': - resolution: {integrity: sha512-Sl+5Zq4LBZF+CDB0eXGdTOLv3E9bubO2tfpDncQku5/s/N+2Ptn0JAw/Vc3EdJAtMWj1G8uy+6cvtV+eaVNnFg==} + '@argos-ci/browser@2.1.3': + resolution: {integrity: sha512-C9/k32HrmkHkUT9KrhHuOvlli1ibPGE1nkToC6+t4cAmbJCIU8HFrVa6VG2UT3xJjLht+gpqvPwTbJAsCRXv3A==} engines: {node: '>=18.0.0'} - '@argos-ci/cypress@2.1.1': - resolution: {integrity: sha512-ftRjQBt4mfCecJ7jnnsVVbU/71c9mqlbsle+75D4vaDWnch06J4XbQEJ2K56S3eupj3iOHkGn+Nf9DNsOiT7PQ==} + '@argos-ci/core@2.5.0': + resolution: {integrity: sha512-xNHKWzuSLHXm/5fhdEQv8zUwExeEkYzw6CV/Ha9rUBBTPomZL5CNxpXQ/ww1AA4mRC5dq3CfgreRq8qj7HQMVQ==} + engines: {node: '>=18.0.0'} + + '@argos-ci/cypress@2.1.2': + resolution: {integrity: sha512-J+G7EoyQTz6ev9yZBKpl1lRuizUwrX4ZMfqfyB9fDtPhyNcW3V5nTZh2iMBsNW/4hDnL5SQ5Tge5eJfuGkffgA==} engines: {node: '>=18.0.0'} peerDependencies: cypress: ^12.0.0 || ^13.0.0 - '@argos-ci/util@2.1.0': - resolution: {integrity: sha512-/78zJjZJCh3i7Eh3/lo7ybXK2pzXFGUNHbK3SgJNKNbFiBDllNRfy+x0kccjvN2gCCDz877jnFOlSoZZuMK56A==} + '@argos-ci/util@2.1.1': + resolution: {integrity: sha512-UyACLQe9rvCPbo9muhrLte1AD75kQlcGBuecjmaotaF9MBMj+9Yz+TYs1jJrlLMgqowfIgbXjBYmkXRUn36tCg==} engines: {node: '>=18.0.0'} '@babel/code-frame@7.24.7': @@ -756,6 +833,10 @@ packages: resolution: {integrity: sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.25.4': + resolution: {integrity: sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==} + engines: {node: '>=6.9.0'} + '@babel/core@7.25.2': resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} engines: {node: '>=6.9.0'} @@ -764,6 +845,10 @@ packages: resolution: {integrity: sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==} engines: {node: '>=6.9.0'} + '@babel/generator@7.25.5': + resolution: {integrity: sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.24.7': resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==} engines: {node: '>=6.9.0'} @@ -782,6 +867,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-create-class-features-plugin@7.25.4': + resolution: {integrity: sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-create-regexp-features-plugin@7.25.2': resolution: {integrity: sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==} engines: {node: '>=6.9.0'} @@ -864,6 +955,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.25.4': + resolution: {integrity: sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.3': resolution: {integrity: sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==} engines: {node: '>=6.9.0'} @@ -1019,8 +1115,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-generator-functions@7.25.0': - resolution: {integrity: sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==} + '@babel/plugin-transform-async-generator-functions@7.25.4': + resolution: {integrity: sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1043,8 +1139,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-properties@7.24.7': - resolution: {integrity: sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==} + '@babel/plugin-transform-class-properties@7.25.4': + resolution: {integrity: sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1055,8 +1151,8 @@ packages: peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.25.0': - resolution: {integrity: sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==} + '@babel/plugin-transform-classes@7.25.4': + resolution: {integrity: sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1223,8 +1319,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-methods@7.24.7': - resolution: {integrity: sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==} + '@babel/plugin-transform-private-methods@7.25.4': + resolution: {integrity: sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1307,14 +1403,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-sets-regex@7.24.7': - resolution: {integrity: sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==} + '@babel/plugin-transform-unicode-sets-regex@7.25.4': + resolution: {integrity: sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.25.3': - resolution: {integrity: sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==} + '@babel/preset-env@7.25.4': + resolution: {integrity: sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1337,6 +1433,10 @@ packages: resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.25.4': + resolution: {integrity: sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==} + engines: {node: '>=6.9.0'} + '@babel/template@7.25.0': resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} engines: {node: '>=6.9.0'} @@ -1345,10 +1445,18 @@ packages: resolution: {integrity: sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.25.4': + resolution: {integrity: sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==} + engines: {node: '>=6.9.0'} + '@babel/types@7.25.2': resolution: {integrity: sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.25.4': + resolution: {integrity: sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==} + engines: {node: '>=6.9.0'} + '@bcherny/json-schema-ref-parser@10.0.5-fork': resolution: {integrity: sha512-E/jKbPoca1tfUPj3iSbitDZTGnq6FUFjkH6L8U2oDwSuwK1WhnnVtCG7oFOTg/DDnyoXbQYUiUiGOibHqaGVnw==} engines: {node: '>= 16'} @@ -1439,28 +1547,48 @@ packages: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} - '@cspell/cspell-bundled-dicts@8.13.1': - resolution: {integrity: sha512-ylAwnIdxBMJ9v6BHpFAQFZM+5zbybLtqVQJG7zQePts4e0/Qr2xjYFbC3F+fovZqyXPIx24BR+S6gFJNO1OdAw==} + '@cspell/cspell-bundled-dicts@8.13.3': + resolution: {integrity: sha512-OfCxUBMyayxKyeDaUZG3LQpiyH8MFUbg9nbIZCGh2x8U6N0fHaP9uR6R+gPzdi/bJp32Kr+RC/Yebojd+AQCGA==} engines: {node: '>=18'} - '@cspell/cspell-json-reporter@8.13.1': - resolution: {integrity: sha512-vYZTBRkYjpNBifGNbYQsgIXesDEdUa9QAwllDcLZGKbhh5mY/C1ygPnAVpYDYiJNt1WCeIqW286DUyjRjkmHeA==} + '@cspell/cspell-bundled-dicts@8.14.2': + resolution: {integrity: sha512-Kv2Utj/RTSxfufGXkkoTZ/3ErCsYWpCijtDFr/FwSsM7mC0PzLpdlcD9xjtgrJO5Kwp7T47iTG21U4Mwddyi8Q==} engines: {node: '>=18'} - '@cspell/cspell-pipe@8.13.1': - resolution: {integrity: sha512-acLWTQv3yWfeWXMds/cfQKZapslOrLHVL4VDp4rFyL/EnfgaCr7Ew9hQ7zAIARY3r/n0dByqWbOt2HKthdhx/g==} + '@cspell/cspell-json-reporter@8.14.2': + resolution: {integrity: sha512-TZavcnNIZKX1xC/GNj80RgFVKHCT4pHT0qm9jCsQFH2QJfyCrUlkEvotKGSQ04lAyCwWg6Enq95qhouF8YbKUQ==} engines: {node: '>=18'} - '@cspell/cspell-resolver@8.13.1': - resolution: {integrity: sha512-EGdb7KLYCklV3sLxf/895b7s6sExh8DCHZFpDos2hjKwMt+F4ynsu1+ceybQtqoUF/MsyLoJXrrmPvV2uGVmUQ==} + '@cspell/cspell-pipe@8.13.3': + resolution: {integrity: sha512-6a9Zd+fDltgXoJ0fosWqEMx0UdXBXZ7iakhslMNPRmv7GhVAoHBoIXzMVilOE4kYT2Mh/9NM/QW/NbNEpneZIQ==} engines: {node: '>=18'} - '@cspell/cspell-service-bus@8.13.1': - resolution: {integrity: sha512-oLFJfxuB1rwGXn3eD5qSF9nf0lHu6YjO0JcrjWhAZQ0r3AsO97gsX50wwCFCw6szVU3rd1cTUktW0KYEZUY6dA==} + '@cspell/cspell-pipe@8.14.2': + resolution: {integrity: sha512-aWMoXZAXEre0/M9AYWOW33YyOJZ06i4vvsEpWBDWpHpWQEmsR/7cMMgld8Pp3wlEjIUclUAKTYmrZ61PFWU/og==} engines: {node: '>=18'} - '@cspell/cspell-types@8.13.1': - resolution: {integrity: sha512-9dJdmyXLXJVesCJa/DWgwKsEC9p2RRFc6KORcLhNvtm1tE9TvCXiu5jV47sOmYXd6Hwan8IurBXXTz82CLVjPQ==} + '@cspell/cspell-resolver@8.13.3': + resolution: {integrity: sha512-vlwtMTEWsPPtWfktzT75eGQ0n+0M+9kN+89eSvUUYdCfvY9XAS6z+bTmhS2ULJgntgWtX6gUjABQK0PYYVedOg==} + engines: {node: '>=18'} + + '@cspell/cspell-resolver@8.14.2': + resolution: {integrity: sha512-pSyBsAvslaN0dx0pHdvECJEuFDDBJGAD6G8U4BVbIyj2OPk0Ox0HrZIj6csYxxoJERAgNO/q7yCPwa4j9NNFXg==} + engines: {node: '>=18'} + + '@cspell/cspell-service-bus@8.13.3': + resolution: {integrity: sha512-mFkeWXwGQSDxRiN6Kez77GaMNGNgG7T6o9UE42jyXEgf/bLJTpefbUy4fY5pU3p2mA0eoMzmnJX8l+TC5YJpbA==} + engines: {node: '>=18'} + + '@cspell/cspell-service-bus@8.14.2': + resolution: {integrity: sha512-WUF7xf3YgXYIqjmBwLcVugYIrYL4WfXchgSo9rmbbnOcAArzsK+HKfzb4AniZAJ1unxcIQ0JnVlRmnCAKPjjLg==} + engines: {node: '>=18'} + + '@cspell/cspell-types@8.13.3': + resolution: {integrity: sha512-lA5GbhLOL6FlKCWNMbooRFgNGfTsM6NJnHz60+EEN7XD9OgpFc7w+MBcK4aHsVCxcrIvnejIc8xQDqPnrdmN3w==} + engines: {node: '>=18'} + + '@cspell/cspell-types@8.14.2': + resolution: {integrity: sha512-MRY8MjBNOKGMDSkxAKueYAgVL43miO+lDcLCBBP+7cNXqHiUFMIZteONcGp3kJT0dWS04dN6lKAXvaNF0aWcng==} engines: {node: '>=18'} '@cspell/dict-ada@4.0.2': @@ -1469,14 +1597,23 @@ packages: '@cspell/dict-aws@4.0.3': resolution: {integrity: sha512-0C0RQ4EM29fH0tIYv+EgDQEum0QI6OrmjENC9u98pB8UcnYxGG/SqinuPxo+TgcEuInj0Q73MsBpJ1l5xUnrsw==} + '@cspell/dict-aws@4.0.4': + resolution: {integrity: sha512-6AWI/Kkf+RcX/J81VX8+GKLeTgHWEr/OMhGk3dHQzWK66RaqDJCGDqi7494ghZKcBB7dGa3U5jcKw2FZHL/u3w==} + '@cspell/dict-bash@4.1.3': resolution: {integrity: sha512-tOdI3QVJDbQSwPjUkOiQFhYcu2eedmX/PtEpVWg0aFps/r6AyjUQINtTgpqMYnYuq8O1QUIQqnpx21aovcgZCw==} + '@cspell/dict-bash@4.1.4': + resolution: {integrity: sha512-W/AHoQcJYn3Vn/tUiXX2+6D/bhfzdDshwcbQWv9TdiNlXP9P6UJjDKWbxyA5ogJCsR2D0X9Kx11oV8E58siGKQ==} + '@cspell/dict-companies@3.1.4': resolution: {integrity: sha512-y9e0amzEK36EiiKx3VAA+SHQJPpf2Qv5cCt5eTUSggpTkiFkCh6gRKQ97rVlrKh5GJrqinDwYIJtTsxuh2vy2Q==} - '@cspell/dict-cpp@5.1.12': - resolution: {integrity: sha512-6lXLOFIa+k/qBcu0bjaE/Kc6v3sh9VhsDOXD1Dalm3zgd0QIMjp5XBmkpSdCAK3pWCPV0Se7ysVLDfCea1BuXg==} + '@cspell/dict-cpp@5.1.15': + resolution: {integrity: sha512-5X8SouN/qIUrBTcDEevnKU6G3cRSm3Vm7dQEcjHaptIWp+/2YMknIfYbnhKeR1G9V/sbQaY4CVsVAKEaehY+7Q==} + + '@cspell/dict-cpp@5.1.16': + resolution: {integrity: sha512-32fU5RkuOM55IRcxjByiSoKbjr+C4danDfYjHaQNRWdvjzJzci3fLDGA2wTXiclkgDODxGiV8LCTUwCz+3TNWA==} '@cspell/dict-cryptocurrencies@5.0.0': resolution: {integrity: sha512-Z4ARIw5+bvmShL+4ZrhDzGhnc9znaAGHOEMaB/GURdS/jdoreEDY34wdN0NtdLHDO5KO7GduZnZyqGdRoiSmYA==} @@ -1484,8 +1621,8 @@ packages: '@cspell/dict-csharp@4.0.2': resolution: {integrity: sha512-1JMofhLK+4p4KairF75D3A924m5ERMgd1GvzhwK2geuYgd2ZKuGW72gvXpIV7aGf52E3Uu1kDXxxGAiZ5uVG7g==} - '@cspell/dict-css@4.0.12': - resolution: {integrity: sha512-vGBgPM92MkHQF5/2jsWcnaahOZ+C6OE/fPvd5ScBP72oFY9tn5GLuomcyO0z8vWCr2e0nUSX1OGimPtcQAlvSw==} + '@cspell/dict-css@4.0.13': + resolution: {integrity: sha512-WfOQkqlAJTo8eIQeztaH0N0P+iF5hsJVKFuhy4jmARPISy8Efcv8QXk2/IVbmjJH0/ZV7dKRdnY5JFVXuVz37g==} '@cspell/dict-dart@2.0.3': resolution: {integrity: sha512-cLkwo1KT5CJY5N5RJVHks2genFkNCl/WLfj+0fFjqNR+tk3tBI1LY7ldr9piCtSFSm4x9pO1x6IV3kRUY1lLiw==} @@ -1499,8 +1636,11 @@ packages: '@cspell/dict-docker@1.1.7': resolution: {integrity: sha512-XlXHAr822euV36GGsl2J1CkBIVg3fZ6879ZOg5dxTIssuhUOCiV2BuzKZmt6aIFmcdPmR14+9i9Xq+3zuxeX0A==} - '@cspell/dict-dotnet@5.0.2': - resolution: {integrity: sha512-UD/pO2A2zia/YZJ8Kck/F6YyDSpCMq0YvItpd4YbtDVzPREfTZ48FjZsbYi4Jhzwfvc6o8R56JusAE58P+4sNQ==} + '@cspell/dict-dotnet@5.0.4': + resolution: {integrity: sha512-FCjeagwME0f5pg7AjhqidsenKnskFN2S6JMaMu5TZn2w+wPVpLnsqmcl0dtW6K/mDrutTYwC/gQFlLbDzoRw4g==} + + '@cspell/dict-dotnet@5.0.5': + resolution: {integrity: sha512-gjg0L97ee146wX47dnA698cHm85e7EOpf9mVrJD8DmEaqoo/k1oPy2g7c7LgKxK9XnqwoXxhLNnngPrwXOoEtQ==} '@cspell/dict-elixir@4.0.3': resolution: {integrity: sha512-g+uKLWvOp9IEZvrIvBPTr/oaO6619uH/wyqypqvwpmnmpjcfi8+/hqZH8YNKt15oviK8k4CkINIqNhyndG9d9Q==} @@ -1532,8 +1672,11 @@ packages: '@cspell/dict-git@3.0.0': resolution: {integrity: sha512-simGS/lIiXbEaqJu9E2VPoYW1OTC2xrwPPXNXFMa2uo/50av56qOuaxDrZ5eH1LidFXwoc8HROCHYeKoNrDLSw==} - '@cspell/dict-golang@6.0.9': - resolution: {integrity: sha512-etDt2WQauyEQDA+qPS5QtkYTb2I9l5IfQftAllVoB1aOrT6bxxpHvMEpJ0Hsn/vezxrCqa/BmtUbRxllIxIuSg==} + '@cspell/dict-golang@6.0.11': + resolution: {integrity: sha512-BMFIDGh1HaFUe1cYBT1dotqyIQG2j3VkNntGQTBa/7i0aBnC5PBJDiAXnUeBHi0AVrz0hyAc7xtcK5KyKCEzwg==} + + '@cspell/dict-golang@6.0.12': + resolution: {integrity: sha512-LEPeoqd+4O+vceHF73S7D7+LYfrAjOvp4Dqzh4MT30ruzlQ77yHRSuYOJtrFN1GK5ntAt/ILSVOKg9sgsz1Llg==} '@cspell/dict-google@1.0.1': resolution: {integrity: sha512-dQr4M3n95uOhtloNSgB9tYYGXGGEGEykkFyRtfcp5pFuEecYUa0BSgtlGKx9RXVtJtKgR+yFT/a5uQSlt8WjqQ==} @@ -1577,23 +1720,35 @@ packages: '@cspell/dict-npm@5.0.18': resolution: {integrity: sha512-weMTyxWpzz19q4wv9n183BtFvdD5fCjtze+bFKpl+4rO/YlPhHL2cXLAeexJz/VDSBecwX4ybTZYoknd1h2J4w==} - '@cspell/dict-php@4.0.8': - resolution: {integrity: sha512-TBw3won4MCBQ2wdu7kvgOCR3dY2Tb+LJHgDUpuquy3WnzGiSDJ4AVelrZdE1xu7mjFJUr4q48aB21YT5uQqPZA==} + '@cspell/dict-npm@5.1.0': + resolution: {integrity: sha512-731gE9lO8hWb4gUytUk4kycEkAmSf0p8YejB4ZWoyQRvdNcu8p4U5S/yMS+uBPohHtxhSDq0K/vi/QaPm5HYFA==} + + '@cspell/dict-php@4.0.10': + resolution: {integrity: sha512-NfTZdp6kcZDF1PvgQ6cY0zE4FUO5rSwNmBH/iwCBuaLfJAFQ97rgjxo+D2bic4CFwNjyHutnHPtjJBRANO5XQw==} + + '@cspell/dict-php@4.0.9': + resolution: {integrity: sha512-Rg6+hc8zexiMyT5sXzYdkgr5irYCxl8Rn/OKgUOy7rMN7eD010VefGm62RG6jIBpUIUYFM00Qpc5RA+H4L0KyQ==} '@cspell/dict-powershell@5.0.5': resolution: {integrity: sha512-3JVyvMoDJesAATYGOxcUWPbQPUvpZmkinV3m8HL1w1RrjeMVXXuK7U1jhopSneBtLhkU+9HKFwgh9l9xL9mY2Q==} - '@cspell/dict-public-licenses@2.0.7': - resolution: {integrity: sha512-KlBXuGcN3LE7tQi/GEqKiDewWGGuopiAD0zRK1QilOx5Co8XAvs044gk4MNIQftc8r0nHeUI+irJKLGcR36DIQ==} + '@cspell/dict-powershell@5.0.6': + resolution: {integrity: sha512-BSi9tmnT7jgNsH5SaHSg70aw+4YwTjkkZBfhHtin0r6AMV2RaiLzsBPvzZGXOcm0yTvl975HYoKMqflXIlk2RA==} - '@cspell/dict-python@4.2.3': - resolution: {integrity: sha512-C1CPX9wwEGgcHv/p7KfjuIOp1G6KNyx5gWYweAd6/KPv+ZpeM1v572zFUTmpO8WDuAfKFf00nqYL8/GmCENWBw==} + '@cspell/dict-public-licenses@2.0.8': + resolution: {integrity: sha512-Sup+tFS7cDV0fgpoKtUqEZ6+fA/H+XUgBiqQ/Fbs6vUE3WCjJHOIVsP+udHuyMH7iBfJ4UFYOYeORcY4EaKdMg==} + + '@cspell/dict-python@4.2.4': + resolution: {integrity: sha512-sCtLBqMreb+8zRW2bXvFsfSnRUVU6IFm4mT6Dc4xbz0YajprbaPPh/kOUTw5IJRP8Uh+FFb7Xp2iH03CNWRq/A==} + + '@cspell/dict-python@4.2.6': + resolution: {integrity: sha512-Hkz399qDGEbfXi9GYa2hDl7GahglI86JmS2F1KP8sfjLXofUgtnknyC5NWc86nzHcP38pZiPqPbTigyDYw5y8A==} '@cspell/dict-r@2.0.1': resolution: {integrity: sha512-KCmKaeYMLm2Ip79mlYPc8p+B2uzwBp4KMkzeLd5E6jUlCL93Y5Nvq68wV5fRLDRTf7N1LvofkVFWfDcednFOgA==} - '@cspell/dict-ruby@5.0.2': - resolution: {integrity: sha512-cIh8KTjpldzFzKGgrqUX4bFyav5lC52hXDKo4LbRuMVncs3zg4hcSf4HtURY+f2AfEZzN6ZKzXafQpThq3dl2g==} + '@cspell/dict-ruby@5.0.3': + resolution: {integrity: sha512-V1xzv9hN6u8r6SM4CkYdsxs4ov8gjXXo0Twfx5kWhLXbEVxTXDMt7ohLTqpy2XlF5mutixZdbHMeFiAww8v+Ug==} '@cspell/dict-rust@4.0.5': resolution: {integrity: sha512-DIvlPRDemjKQy8rCqftAgGNZxY5Bg+Ps7qAIJjxkSjmMETyDgl0KTVuaJPt7EK4jJt6uCZ4ILy96npsHDPwoXA==} @@ -1601,11 +1756,14 @@ packages: '@cspell/dict-scala@5.0.3': resolution: {integrity: sha512-4yGb4AInT99rqprxVNT9TYb1YSpq58Owzq7zi3ZS5T0u899Y4VsxsBiOgHnQ/4W+ygi+sp+oqef8w8nABR2lkg==} - '@cspell/dict-software-terms@4.0.4': - resolution: {integrity: sha512-AHr3Wxa4pxbpKgxhyQseBmoJhdyeraeRGdQn0e8YD5pz4J6Mu47MLzKysasDKWK/yzmHQfwAsb2zm2k+ItMEUw==} + '@cspell/dict-software-terms@4.0.11': + resolution: {integrity: sha512-L4qtowZEnPGFz1BXIDwcdgF/Dgr4oMKO0HT98NqXafxZ8KGSHe/QBfisnHd/flceNHaw7Sd7iRW0ICJtXiWfLA==} - '@cspell/dict-sql@2.1.4': - resolution: {integrity: sha512-wsrNK6UBQ92IzQ4SqQqgM04BEYzqVsk3qZH3ZgascaqDtUgK6GI+z3Czi0rQ+9Qe2zKiklGnGMC8sJwYdlIw7g==} + '@cspell/dict-software-terms@4.1.1': + resolution: {integrity: sha512-JkXkULoOWYwtdsQ+/6dPtGi2pcrjDQ0NYsTkKt1uFCiITDb+QZkQhAG0kOP69jK6vhSQwUAH3z7jiYWqoK5TKw==} + + '@cspell/dict-sql@2.1.5': + resolution: {integrity: sha512-FmxanytHXss7GAWAXmgaxl3icTCW7YxlimyOSPNfm+njqeUDjw3kEv4mFNDDObBJv8Ec5AWCbUDkWIpkE3IpKg==} '@cspell/dict-svelte@1.0.2': resolution: {integrity: sha512-rPJmnn/GsDs0btNvrRBciOhngKV98yZ9SHmg8qI6HLS8hZKvcXc0LMsf9LLuMK1TmS2+WQFAan6qeqg6bBxL2Q==} @@ -1616,32 +1774,51 @@ packages: '@cspell/dict-terraform@1.0.0': resolution: {integrity: sha512-Ak+vy4HP/bOgzf06BAMC30+ZvL9mzv21xLM2XtfnBLTDJGdxlk/nK0U6QT8VfFLqJ0ZZSpyOxGsUebWDCTr/zQ==} + '@cspell/dict-terraform@1.0.1': + resolution: {integrity: sha512-29lmUUnZgPh+ieZ5hunick8hzNIpNRtiJh9vAusNskPCrig3RTW6u7F+GG1a8uyslbzSw+Irjf40PTOan1OJJA==} + '@cspell/dict-typescript@3.1.6': resolution: {integrity: sha512-1beC6O4P/j23VuxX+i0+F7XqPVc3hhiAzGJHEKqnWf5cWAXQtg0xz3xQJ5MvYx2a7iLaSa+lu7+05vG9UHyu9Q==} '@cspell/dict-vue@3.0.0': resolution: {integrity: sha512-niiEMPWPV9IeRBRzZ0TBZmNnkK3olkOPYxC1Ny2AX4TGlYRajcW0WUtoSHmvvjZNfWLSg2L6ruiBeuPSbjnG6A==} - '@cspell/dynamic-import@8.13.1': - resolution: {integrity: sha512-jMqJHWmQy+in99JMSFlaGV9P033gCx7DCZvGO/ZSeZ2EatrUTanJk3oTG1TZknZydb0nnxr1mgTWXN7PCAAXDg==} + '@cspell/dynamic-import@8.13.3': + resolution: {integrity: sha512-YN83CFWnMkt9B0q0RBadfEoptUaDRqBikh8b91MOQ0haEnUo6t57j4jAaLnbIEP4ynzMhgruWFKpIC/QaEtCuA==} engines: {node: '>=18.0'} - '@cspell/eslint-plugin@8.13.1': - resolution: {integrity: sha512-rn/9wrKj3dytuMzdqh29eBSicyzuMLUf7h4iNQn3ivl9YiwpLr6ShiRX4uS5Mid9wbcY6sgutWNAYqMl5/vqzg==} + '@cspell/dynamic-import@8.14.2': + resolution: {integrity: sha512-5MbqtIligU7yPwHWU/5yFCgMvur4i1bRAF1Cy8y2dDtHsa204S/w/SaXs+51EFLp2eNbCiBisCBrwJFT7R1RxA==} + engines: {node: '>=18.0'} + + '@cspell/eslint-plugin@8.13.3': + resolution: {integrity: sha512-wb4+WoirtqP4ijcLhCXWbgfE94ggU9D+ENMjTldBNABjcoDse1UyPIR1gSM/oNAAOLaSLtXbeOx7tnWuurjVaQ==} engines: {node: '>=18'} peerDependencies: eslint: ^7 || ^8 || ^9 - '@cspell/strong-weak-map@8.13.1': - resolution: {integrity: sha512-ga1ibI9ZLJWNszfP7e6qQ8gnoQOP9rE/clALMAim9ssO6cmMhEEm+i1ROH4nsDfThd6sVlUJ0IOtx5dEqPmWxw==} + '@cspell/filetypes@8.14.2': + resolution: {integrity: sha512-ZevArA0mWeVTTqHicxCPZIAeCibpY3NwWK/x6d1Lgu7RPk/daoGAM546Q2SLChFu+r10tIH7pRG212A6Q9ihPA==} engines: {node: '>=18'} - '@cspell/url@8.13.1': - resolution: {integrity: sha512-cCyojz5ovgGCexhez2urle4Q1UOEsp96lvl4pDmWNDHa/6n8dqiIn60SVzQIsAHzJ4yEV077RSaIrTlq/T+oSQ==} + '@cspell/strong-weak-map@8.13.3': + resolution: {integrity: sha512-/QYUEthesPuDarOHa6kcWKJmVq0HIotjPrmAWQ5QpH+dDik1Qin4G/9QdnWX75ueR4DC4WFjBNBU14C4TVSwHQ==} + engines: {node: '>=18'} + + '@cspell/strong-weak-map@8.14.2': + resolution: {integrity: sha512-7sRzJc392CQYNNrtdPEfOHJdRqsqf6nASCtbS5A9hL2UrdWQ4uN7r/D+Y1HpuizwY9eOkZvarcFfsYt5wE0Pug==} + engines: {node: '>=18'} + + '@cspell/url@8.13.3': + resolution: {integrity: sha512-hsxoTnZHwtdR2x9QEE6yfDBB1LUwAj67o1GyKTvI8A2OE/AfzAttirZs+9sxgOGWoBdTOxM9sMLtqB3SxtDB3A==} engines: {node: '>=18.0'} - '@cypress/code-coverage@3.12.44': - resolution: {integrity: sha512-5Eau3tnJqZJo1OddOOEMyWs1HCwlAOgDs1rFPaCXJ4a5Y2BR4PH4fAzCdMvIqpBnCiVSL8gOYv6JfND3aUuJJQ==} + '@cspell/url@8.14.2': + resolution: {integrity: sha512-YmWW+B/2XQcCynLpiAQF77Bitm5Cynw3/BICZkbdveKjJkUzEmXB+U2qWuwXOyU8xUYuwkP63YM8McnI567rUA==} + engines: {node: '>=18.0'} + + '@cypress/code-coverage@3.12.45': + resolution: {integrity: sha512-QRvdc9Zmner/CxQ1F5jcNVZR8P8VrRTyE8THcisxnB6D3AMIKKSmjYUGH6OnWBDF/vi3CqimuMSbrUfzmPzmhw==} peerDependencies: '@babel/core': ^7.0.1 '@babel/preset-env': ^7.0.0 @@ -1704,138 +1881,282 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.21.5': resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.21.5': resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.21.5': resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.21.5': resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.21.5': resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.21.5': resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.21.5': resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.21.5': resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.21.5': resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.21.5': resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.21.5': resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.21.5': resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.21.5': resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.21.5': resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.21.5': resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.21.5': resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.21.5': resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1868,17 +2189,17 @@ packages: '@fastify/error@2.0.0': resolution: {integrity: sha512-wI3fpfDT0t7p8E6dA2eTECzzOd+bZsZCJ2Hcv+Onn2b7ZwK3RwD27uW2QDaMtQhAfWQQP+WNK7nKf0twLsBf9w==} - '@floating-ui/core@1.6.5': - resolution: {integrity: sha512-8GrTWmoFhm5BsMZOTHeGD2/0FLKLQQHvO/ZmQga4tKempYRLz8aqJGqXVuQgisnMObq2YZ2SgkwctN1LOOxcqA==} + '@floating-ui/core@1.6.7': + resolution: {integrity: sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==} - '@floating-ui/dom@1.6.8': - resolution: {integrity: sha512-kx62rP19VZ767Q653wsP1XZCGIirkE09E0QUGNYTM/ttbbQHqcGPdSfWFxUyyNLc/W6aoJRBajOSXhP6GXjC0Q==} + '@floating-ui/dom@1.6.10': + resolution: {integrity: sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==} - '@floating-ui/utils@0.2.5': - resolution: {integrity: sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ==} + '@floating-ui/utils@0.2.7': + resolution: {integrity: sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==} - '@floating-ui/vue@1.1.2': - resolution: {integrity: sha512-7pq8HfhVhxOpV6iIMKSslI51fwFYy8G0BF0GjhlhpmUhVwL8jCByvcjzTwEtRWFVRrGD/I9kLp6eUHKumiUTjw==} + '@floating-ui/vue@1.1.4': + resolution: {integrity: sha512-ammH7T3vyCx7pmm9OF19Wc42zrGnUw0QvLoidgypWsCLJMtGXEwY7paYIHO+K+oLC3mbWpzIHzeTVienYenlNg==} '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} @@ -1886,8 +2207,8 @@ packages: '@hapi/topo@5.1.0': resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} - '@headlessui-float/vue@0.14.0': - resolution: {integrity: sha512-hx0IkJ7JPcwDeimco6fe0+IknknL1gUYIGu11OCn0JWlOoSAmO6sx2DxPwSEz1Wsq34X6Z8BwCwcPVuphZ1zMg==} + '@headlessui-float/vue@0.14.3': + resolution: {integrity: sha512-eIX+zawgy/8ijZM7t7GfNDeEZpL068wTJ3U+gxSepO5NprgOUijHzG3TBFhERbsP4dDq3i0Bs7EaKbk+W57FOg==} peerDependencies: '@headlessui/vue': ^1.0.0 vue: ^3.0.0 @@ -1921,116 +2242,111 @@ packages: '@iconify/utils@2.1.30': resolution: {integrity: sha512-bY0IO5xLOlbzJBnjWLxknp6Sss3yla03sVY9VeUz9nT6dbc+EGKlLfCt+6uytJnWm5CUvTF/BNotsLWF7kI61A==} - '@img/sharp-darwin-arm64@0.33.4': - resolution: {integrity: sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==} - engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@iconify/utils@2.1.32': + resolution: {integrity: sha512-LeifFZPPKu28O3AEDpYJNdEbvS4/ojAPyIW+pF/vUpJTYnbTiXUHkCh0bwgFRzKvdpb8H4Fbfd/742++MF4fPQ==} + + '@img/sharp-darwin-arm64@0.33.5': + resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.33.4': - resolution: {integrity: sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==} - engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-darwin-x64@0.33.5': + resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.0.2': - resolution: {integrity: sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==} - engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.0.2': - resolution: {integrity: sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==} - engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.0.2': - resolution: {integrity: sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==} - engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm@1.0.2': - resolution: {integrity: sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==} - engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-s390x@1.0.2': - resolution: {integrity: sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==} - engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-libvips-linux-s390x@1.0.4': + resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-x64@1.0.2': - resolution: {integrity: sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==} - engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.0.2': - resolution: {integrity: sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==} - engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.0.2': - resolution: {integrity: sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==} - engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.33.4': - resolution: {integrity: sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==} - engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-linux-arm64@0.33.5': + resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.33.4': - resolution: {integrity: sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==} - engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-linux-arm@0.33.5': + resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - '@img/sharp-linux-s390x@0.33.4': - resolution: {integrity: sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==} - engines: {glibc: '>=2.31', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-linux-s390x@0.33.5': + resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.33.4': - resolution: {integrity: sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==} - engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-linux-x64@0.33.5': + resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.33.4': - resolution: {integrity: sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==} - engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-linuxmusl-arm64@0.33.5': + resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.33.4': - resolution: {integrity: sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==} - engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-linuxmusl-x64@0.33.5': + resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.33.4': - resolution: {integrity: sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-wasm32@0.33.5': + resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-ia32@0.33.4': - resolution: {integrity: sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-win32-ia32@0.33.5': + resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.33.4': - resolution: {integrity: sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] @@ -2239,91 +2555,91 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.20.0': - resolution: {integrity: sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==} + '@rollup/rollup-android-arm-eabi@4.21.1': + resolution: {integrity: sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.20.0': - resolution: {integrity: sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==} + '@rollup/rollup-android-arm64@4.21.1': + resolution: {integrity: sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.20.0': - resolution: {integrity: sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==} + '@rollup/rollup-darwin-arm64@4.21.1': + resolution: {integrity: sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.20.0': - resolution: {integrity: sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==} + '@rollup/rollup-darwin-x64@4.21.1': + resolution: {integrity: sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.20.0': - resolution: {integrity: sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==} + '@rollup/rollup-linux-arm-gnueabihf@4.21.1': + resolution: {integrity: sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.20.0': - resolution: {integrity: sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==} + '@rollup/rollup-linux-arm-musleabihf@4.21.1': + resolution: {integrity: sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.20.0': - resolution: {integrity: sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==} + '@rollup/rollup-linux-arm64-gnu@4.21.1': + resolution: {integrity: sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.20.0': - resolution: {integrity: sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==} + '@rollup/rollup-linux-arm64-musl@4.21.1': + resolution: {integrity: sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.20.0': - resolution: {integrity: sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.21.1': + resolution: {integrity: sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.20.0': - resolution: {integrity: sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==} + '@rollup/rollup-linux-riscv64-gnu@4.21.1': + resolution: {integrity: sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.20.0': - resolution: {integrity: sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==} + '@rollup/rollup-linux-s390x-gnu@4.21.1': + resolution: {integrity: sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.20.0': - resolution: {integrity: sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==} + '@rollup/rollup-linux-x64-gnu@4.21.1': + resolution: {integrity: sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.20.0': - resolution: {integrity: sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==} + '@rollup/rollup-linux-x64-musl@4.21.1': + resolution: {integrity: sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.20.0': - resolution: {integrity: sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==} + '@rollup/rollup-win32-arm64-msvc@4.21.1': + resolution: {integrity: sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.20.0': - resolution: {integrity: sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==} + '@rollup/rollup-win32-ia32-msvc@4.21.1': + resolution: {integrity: sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.20.0': - resolution: {integrity: sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==} + '@rollup/rollup-win32-x64-msvc@4.21.1': + resolution: {integrity: sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==} cpu: [x64] os: [win32] - '@shikijs/core@1.12.1': - resolution: {integrity: sha512-biCz/mnkMktImI6hMfMX3H9kOeqsInxWEyCHbSlL8C/2TR1FqfmGxTLRNwYCKsyCyxWLbB8rEqXRVZuyxuLFmA==} + '@shikijs/core@1.14.1': + resolution: {integrity: sha512-KyHIIpKNaT20FtFPFjCQB5WVSTpLR/n+jQXhWHWVUMm9MaOaG9BGOG0MSyt7yA4+Lm+4c9rTc03tt3nYzeYSfw==} - '@shikijs/transformers@1.12.1': - resolution: {integrity: sha512-zOpj/S2thBvnJV4Ty3EE8aRs/VqCbV+lgtEYeBRkPxTW22uLADEIZq0qjt5W2Rfy2KSu29e73nRyzp4PefjUTg==} + '@shikijs/transformers@1.14.1': + resolution: {integrity: sha512-JJqL8QBVCJh3L61jqqEXgFq1cTycwjcGj7aSmqOEsbxnETM9hRlaB74QuXvY/fVJNjbNt8nvWo0VwAXKvMSLRg==} '@sideway/address@4.1.5': resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} @@ -2358,11 +2674,11 @@ packages: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} - '@tanstack/virtual-core@3.8.4': - resolution: {integrity: sha512-iO5Ujgw3O1yIxWDe9FgUPNkGjyT657b1WNX52u+Wv1DyBFEpdCdGkuVaky0M3hHFqNWjAmHWTn4wgj9rTr7ZQg==} + '@tanstack/virtual-core@3.10.4': + resolution: {integrity: sha512-yHyli4RHVsI+eJ0RjmOsjA9RpHp3/Zah9t+iRjmFa72dq00TeG/NwuLYuCV6CB4RkWD4i5RD421j1eb6BdKgvQ==} - '@tanstack/vue-virtual@3.8.5': - resolution: {integrity: sha512-JBHw3xFUslYgrbvNlCYtTWwFo8zjzRs7c2rs6B4JKFXWyP5yHuoeivgQgeZ34t6O6lJTNqc/K4ccmmcmKqpMPA==} + '@tanstack/vue-virtual@3.10.4': + resolution: {integrity: sha512-oikrjnC7BnUCmqh5ptemclUK6EtJj48AdLcJx1t2fTLQyu+60Alo6gPGC3cANgmbEP/1C9DptbeMcm5AAjyBVg==} peerDependencies: vue: ^2.7.0 || ^3.0.0 @@ -2406,6 +2722,9 @@ packages: '@types/cors@2.8.17': resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} + '@types/cytoscape-fcose@2.2.4': + resolution: {integrity: sha512-QwWtnT8HI9h+DHhG5krGc1ZY0Ex+cn85MvX96ZNAjSxuXiZDnjIZW/ypVkvvubTjIY4rSdkJY1D/Nsn8NDpmAw==} + '@types/cytoscape@3.21.5': resolution: {integrity: sha512-fzYT3vqY5J4gxVXDOsCgDpm0ZdU8bQq+wCv0ucS0MSTtvQdjs3lcb2VetJiUSAd4WBgouqizI+JT1f8Yc6eY7Q==} @@ -2628,12 +2947,18 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@18.19.43': - resolution: {integrity: sha512-Mw/YlgXnyJdEwLoFv2dpuJaDFriX+Pc+0qOBJ57jC1H6cDxIj2xc5yUrdtArDVG0m+KV6622a4p2tenEqB3C/g==} + '@types/node@18.19.47': + resolution: {integrity: sha512-1f7dB3BL/bpd9tnDJrrHb66Y+cVrhxSOTGorRNdHwYTUlTay3HuTDPKo9a/4vX9pMQkhYBcAbL4jQdNlhCFP9A==} '@types/node@20.14.14': resolution: {integrity: sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==} + '@types/node@20.16.2': + resolution: {integrity: sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==} + + '@types/node@22.5.1': + resolution: {integrity: sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==} + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -2704,6 +3029,9 @@ packages: '@types/unist@3.0.2': resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} @@ -2725,8 +3053,8 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@8.0.0': - resolution: {integrity: sha512-STIZdwEQRXAHvNUS6ILDf5z3u95Gc8jzywunxSNqX00OooIemaaNIA0vEgynJlycL5AjabYLLrIyHd4iazyvtg==} + '@typescript-eslint/eslint-plugin@8.0.1': + resolution: {integrity: sha512-5g3Y7GDFsJAnY4Yhvk8sZtFfV6YNF2caLzjrRPUBzewjPCaj0yokePB4LJSobyCzGMzjZZYFbwuzbfDHlimXbQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 @@ -2736,8 +3064,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.0.0': - resolution: {integrity: sha512-pS1hdZ+vnrpDIxuFXYQpLTILglTjSYJ9MbetZctrUawogUsPdz31DIIRZ9+rab0LhYNTsk88w4fIzVheiTbWOQ==} + '@typescript-eslint/parser@8.0.1': + resolution: {integrity: sha512-5IgYJ9EO/12pOUwiBKFkpU7rS3IU21mtXzB81TNwq2xEybcmAZrE9qwDtsb5uQd9aVO9o0fdabFyAmKveXyujg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2750,8 +3078,12 @@ packages: resolution: {integrity: sha512-V0aa9Csx/ZWWv2IPgTfY7T4agYwJyILESu/PVqFtTFz9RIS823mAze+NbnBI8xiwdX3iqeQbcTYlvB04G9wyQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.0.0': - resolution: {integrity: sha512-mJAFP2mZLTBwAn5WI4PMakpywfWFH5nQZezUQdSKV23Pqo6o9iShQg1hP2+0hJJXP2LnZkWPphdIq4juYYwCeg==} + '@typescript-eslint/scope-manager@8.0.1': + resolution: {integrity: sha512-NpixInP5dm7uukMiRyiHjRKkom5RIFA4dfiHvalanD2cF0CLUuQqxfg8PtEUo9yqJI2bBhF+pcSafqnG3UBnRQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@8.0.1': + resolution: {integrity: sha512-+/UT25MWvXeDX9YaHv1IS6KI1fiuTto43WprE7pgSMswHbn1Jm9GEM4Txp+X74ifOWV8emu2AWcbLhpJAvD5Ng==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -2763,6 +3095,10 @@ packages: resolution: {integrity: sha512-wgdSGs9BTMWQ7ooeHtu5quddKKs5Z5dS+fHLbrQI+ID0XWJLODGMHRfhwImiHoeO2S5Wir2yXuadJN6/l4JRxw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.0.1': + resolution: {integrity: sha512-PpqTVT3yCA/bIgJ12czBuE3iBlM3g4inRSC5J0QOdQFAn07TYrYEQBBKgXH1lQpglup+Zy6c1fxuwTk4MTNKIw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.0.0': resolution: {integrity: sha512-5b97WpKMX+Y43YKi4zVcCVLtK5F98dFls3Oxui8LbnmRsseKenbbDinmvxrWegKDMmlkIq/XHuyy0UGLtpCDKg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2772,16 +3108,35 @@ packages: typescript: optional: true + '@typescript-eslint/typescript-estree@8.0.1': + resolution: {integrity: sha512-8V9hriRvZQXPWU3bbiUV4Epo7EvgM6RTs+sUmxp5G//dBGy402S7Fx0W0QkB2fb4obCF8SInoUzvTYtc3bkb5w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@typescript-eslint/utils@8.0.0': resolution: {integrity: sha512-k/oS/A/3QeGLRvOWCg6/9rATJL5rec7/5s1YmdS0ZU6LHveJyGFwBvLhSRBv6i9xaj7etmosp+l+ViN1I9Aj/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/utils@8.0.1': + resolution: {integrity: sha512-CBFR0G0sCt0+fzfnKaciu9IBsKvEKYwN9UZ+eeogK1fYHg4Qxk1yf/wLQkLXlq8wbU2dFlgAesxt8Gi76E8RTA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/visitor-keys@8.0.0': resolution: {integrity: sha512-oN0K4nkHuOyF3PVMyETbpP5zp6wfyOvm7tWhTMfoqxSSsPmJIh6JNASuZDlODE8eE+0EB9uar+6+vxr9DBTYOA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.0.1': + resolution: {integrity: sha512-W5E+o0UfUcK5EgchLZsyVWqARmsM7v54/qEq6PY3YI5arkgmCzHiuk0zKSJJbm71V0xdRna4BGomkCTXz2/LkQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@unocss/astro@0.59.4': resolution: {integrity: sha512-DU3OR5MMR1Uvvec4/wB9EetDASHRg19Moy6z/MiIhn8JWJ0QzWYgSeJcfUX8exomMYv6WUEQJL+CyLI34Wmn8w==} peerDependencies: @@ -2909,57 +3264,60 @@ packages: '@vitest/utils@1.6.0': resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} - '@vue/compat@3.4.35': - resolution: {integrity: sha512-Z1OpqfQXWQurtOGxxYZdkgDtgWKxdQ+ZoQZ7MmAENaCFzmX80qRKGh5PVbOFqQTyTXnv0PRk3TqcF5bxddnp+g==} + '@vue/compat@3.4.38': + resolution: {integrity: sha512-CdX4/3I8hkodU6rG6r8uTi112gsKYVrZcTTEpmG7wP3G1n/lpHKbvl3wBzTc6ClTIqRyHABrYnFyTofiSommQA==} peerDependencies: - vue: 3.4.35 + vue: 3.4.38 - '@vue/compiler-core@3.4.35': - resolution: {integrity: sha512-gKp0zGoLnMYtw4uS/SJRRO7rsVggLjvot3mcctlMXunYNsX+aRJDqqw/lV5/gHK91nvaAAlWFgdVl020AW1Prg==} + '@vue/compiler-core@3.4.38': + resolution: {integrity: sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==} - '@vue/compiler-dom@3.4.35': - resolution: {integrity: sha512-pWIZRL76/oE/VMhdv/ovZfmuooEni6JPG1BFe7oLk5DZRo/ImydXijoZl/4kh2406boRQ7lxTYzbZEEXEhj9NQ==} + '@vue/compiler-dom@3.4.38': + resolution: {integrity: sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==} - '@vue/compiler-sfc@3.4.35': - resolution: {integrity: sha512-xacnRS/h/FCsjsMfxBkzjoNxyxEyKyZfBch/P4vkLRvYJwe5ChXmZZrj8Dsed/752H2Q3JE8kYu9Uyha9J6PgA==} + '@vue/compiler-sfc@3.4.38': + resolution: {integrity: sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==} - '@vue/compiler-ssr@3.4.35': - resolution: {integrity: sha512-7iynB+0KB1AAJKk/biENTV5cRGHRdbdaD7Mx3nWcm1W8bVD6QmnH3B4AHhQQ1qZHhqFwzEzMwiytXm3PX1e60A==} + '@vue/compiler-ssr@3.4.38': + resolution: {integrity: sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==} '@vue/devtools-api@6.6.3': resolution: {integrity: sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==} - '@vue/devtools-api@7.3.7': - resolution: {integrity: sha512-kvjQ6nmsqTp7SrmpwI2G0MgbC4ys0bPsgQirHXJM8y1m7siQ5RnWQUHJVfyUrHNguCySW1cevAdIw87zrPTl9g==} + '@vue/devtools-api@7.3.9': + resolution: {integrity: sha512-D+GTYtFg68bqSu66EugQUydsOqaDlPLNmYw5oYk8k81uBu9/bVTUrqlAJrAA9Am7MXhKz2gWdDkopY6sOBf/Bg==} - '@vue/devtools-kit@7.3.7': - resolution: {integrity: sha512-ktHhhjI4CoUrwdSUF5b/MFfjrtAtK8r4vhOkFyRN5Yp9kdXTwsRBYcwarHuP+wFPKf4/KM7DVBj2ELO8SBwdsw==} + '@vue/devtools-kit@7.3.9': + resolution: {integrity: sha512-Gr17nA+DaQzqyhNx1DUJr1CJRzTRfbIuuC80ZgU8MD/qNO302tv9la+ROi+Uaw+ULVwU9T71GnwLy4n8m9Lspg==} - '@vue/devtools-shared@7.3.7': - resolution: {integrity: sha512-M9EU1/bWi5GNS/+IZrAhwGOVZmUTN4MH22Hvh35nUZZg9AZP2R2OhfCb+MG4EtAsrUEYlu3R43/SIj3G7EZYtQ==} + '@vue/devtools-shared@7.3.9': + resolution: {integrity: sha512-CdfMRZKXyI8vw+hqOcQIiLihB6Hbbi7WNZGp7LsuH1Qe4aYAFmTaKjSciRZ301oTnwmU/knC/s5OGuV6UNiNoA==} - '@vue/reactivity@3.4.35': - resolution: {integrity: sha512-Ggtz7ZZHakriKioveJtPlStYardwQH6VCs9V13/4qjHSQb/teE30LVJNrbBVs4+aoYGtTQKJbTe4CWGxVZrvEw==} + '@vue/reactivity@3.4.38': + resolution: {integrity: sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==} - '@vue/runtime-core@3.4.35': - resolution: {integrity: sha512-D+BAjFoWwT5wtITpSxwqfWZiBClhBbR+bm0VQlWYFOadUUXFo+5wbe9ErXhLvwguPiLZdEF13QAWi2vP3ZD5tA==} + '@vue/runtime-core@3.4.38': + resolution: {integrity: sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==} - '@vue/runtime-dom@3.4.35': - resolution: {integrity: sha512-yGOlbos+MVhlS5NWBF2HDNgblG8e2MY3+GigHEyR/dREAluvI5tuUUgie3/9XeqhPE4LF0i2wjlduh5thnfOqw==} + '@vue/runtime-dom@3.4.38': + resolution: {integrity: sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==} - '@vue/server-renderer@3.4.35': - resolution: {integrity: sha512-iZ0e/u9mRE4T8tNhlo0tbA+gzVkgv8r5BX6s1kRbOZqfpq14qoIvCZ5gIgraOmYkMYrSEZgkkojFPr+Nyq/Mnw==} + '@vue/server-renderer@3.4.38': + resolution: {integrity: sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==} peerDependencies: - vue: 3.4.35 + vue: 3.4.38 - '@vue/shared@3.4.35': - resolution: {integrity: sha512-hvuhBYYDe+b1G8KHxsQ0diDqDMA8D9laxWZhNAjE83VZb5UDaXl9Xnz7cGdDSyiHM90qqI/CyGMcpBpiDy6VVQ==} + '@vue/shared@3.4.38': + resolution: {integrity: sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==} - '@vueuse/core@10.11.0': - resolution: {integrity: sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==} + '@vueuse/core@10.11.1': + resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==} - '@vueuse/integrations@10.11.0': - resolution: {integrity: sha512-Pp6MtWEIr+NDOccWd8j59Kpjy5YDXogXI61Kb1JxvSfVBO8NzFQkmrKmSZz47i+ZqHnIzxaT38L358yDHTncZg==} + '@vueuse/core@11.0.3': + resolution: {integrity: sha512-RENlh64+SYA9XMExmmH1a3TPqeIuJBNNB/63GT35MZI+zpru3oMRUA6cEFr9HmGqEgUisurwGwnIieF6qu3aXw==} + + '@vueuse/integrations@10.11.1': + resolution: {integrity: sha512-Y5hCGBguN+vuVYTZmdd/IMXLOdfS60zAmDmFYc4BKBcMUPZH1n4tdyDECCPjXm0bNT3ZRUy1xzTLGaUje8Xyaw==} peerDependencies: async-validator: ^4 axios: ^1 @@ -2999,11 +3357,58 @@ packages: universal-cookie: optional: true - '@vueuse/metadata@10.11.0': - resolution: {integrity: sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==} + '@vueuse/integrations@11.0.3': + resolution: {integrity: sha512-w6CDisaxs19S5Fd+NPPLFaA3GoX5gxuxrbTTBu0EYap7oH13w75L6C/+7e9mcoF9akhcR6GyYajwVMQEjdapJg==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true - '@vueuse/shared@10.11.0': - resolution: {integrity: sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==} + '@vueuse/metadata@10.11.1': + resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==} + + '@vueuse/metadata@11.0.3': + resolution: {integrity: sha512-+FtbO4SD5WpsOcQTcC0hAhNlOid6QNLzqedtquTtQ+CRNBoAt9GuV07c6KNHK1wCmlq8DFPwgiLF2rXwgSHX5Q==} + + '@vueuse/shared@10.11.1': + resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==} + + '@vueuse/shared@11.0.3': + resolution: {integrity: sha512-0rY2m6HS5t27n/Vp5cTDsKTlNnimCqsbh/fmT2LgE+aaU42EMfXo8+bNX91W9I7DDmxfuACXMmrd7d79JxkqWA==} '@wdio/config@7.31.1': resolution: {integrity: sha512-WAfswbCatwiaDVqy6kfF/5T8/WS/US/SRhBGUFrfBuGMIe+RRoHgy7jURFWSvUIE7CNHj8yvs46fLUcxhXjzcQ==} @@ -3105,8 +3510,8 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} - '@zenuml/core@3.24.2': - resolution: {integrity: sha512-yJKDAe7exo4lFwi0qvMncCyn2zrKrKKHG5f7TbYy/n1wy+/TUUdUvLrY4Qu8EbsIV4bqYyHWIjoTMqdktk+y2A==} + '@zenuml/core@3.24.3': + resolution: {integrity: sha512-ERWZgvVrgKyjzdJWlpiqzPzFNaNn6K439hX6bE9LZySKGbMq6uacKCmro7tLUGN+w9X/xhbQ+8goGzZtdIdOMQ==} engines: {node: '>=12.0.0'} JSONSelect@0.4.0: @@ -3328,8 +3733,8 @@ packages: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} - async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -3352,12 +3757,15 @@ packages: aws-sign2@0.7.0: resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} - aws4@1.13.0: - resolution: {integrity: sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==} + aws4@1.13.1: + resolution: {integrity: sha512-u5w79Rd7SU4JaIlA/zFqG+gOiuq25q5VLyZ8E+ijJeILuTxVzZgp2CaGw/UTw6pXYN9XMO9yiqj/nEHmhTG5CA==} axios@1.7.3: resolution: {integrity: sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==} + axios@1.7.5: + resolution: {integrity: sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==} + babel-jest@29.7.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3777,8 +4185,8 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - comment-json@4.2.4: - resolution: {integrity: sha512-E5AjpSW+O+N5T2GsOQMHLLsJvrYw6G/AFt9GvU6NguEAfzKShh7hRiLtVo6S9KbRpFMGqE5ojo0/hE+sdteWvQ==} + comment-json@4.2.5: + resolution: {integrity: sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==} engines: {node: '>= 6'} comment-parser@1.4.1: @@ -3858,6 +4266,9 @@ packages: core-js-compat@3.38.0: resolution: {integrity: sha512-75LAicdLa4OJVwFxFbQR3NdnZjNgX6ILpVcVzcC4T2smerB5lELMrJQQQoWV6TiuC/vlaFqgU2tKQx9w5s0e0A==} + core-js-compat@3.38.1: + resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==} + core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} @@ -3871,6 +4282,9 @@ packages: cose-base@1.0.3: resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} + cose-base@2.2.0: + resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} + cp-file@10.0.0: resolution: {integrity: sha512-vy2Vi1r2epK5WqxOLnskeKeZkdZvTKfFZQCplE3XWsP+SUJyd5XAUFC9lFgTjjXJF2GMne/UML14iEmkAaDfFg==} engines: {node: '>=14.16'} @@ -3909,42 +4323,71 @@ packages: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} - cspell-config-lib@8.13.1: - resolution: {integrity: sha512-sXUFOyxvk+qDkoQdFkVEqj1hfQWzMi+tbi6ksiotQaqpm7r+YitZLSgwJjN4xgDO/rTLyP70k9fagdZ67MVZbw==} + cspell-config-lib@8.13.3: + resolution: {integrity: sha512-dzVdar8Kenwxho0PnUxOxwjUvyFYn6Q9mQAMHcQNXQrvo32bdpoF+oNtWC/5FfrQgUgyl19CVQ607bRigYWoOQ==} engines: {node: '>=18'} - cspell-dictionary@8.13.1: - resolution: {integrity: sha512-Z0T4J4ahOJaHmWq83w24KXGik1zeauO5WvDRyzDyaSgpbA5MN2hN98LvxaIx72g3I+trtRK77XFcKginuME9EA==} + cspell-config-lib@8.14.2: + resolution: {integrity: sha512-yHP1BdcH5dbjb8qiZr6+bxEnJ+rxTULQ00wBz3eBPWCghJywEAYYvMWoYuxVtPpndlkKYC1wJAHsyNkweQyepA==} engines: {node: '>=18'} - cspell-gitignore@8.13.1: - resolution: {integrity: sha512-XyZ3X5d6x0gkWtNXSAQRcPMG41bEdLx9cTgZCYCJhEZCesU1VpNm60F3oc11dMLkO+BqPH3An+AO/YEIiaje3A==} + cspell-dictionary@8.13.3: + resolution: {integrity: sha512-DQ3Tee7LIoy+9Mu52ht32O/MNBZ6i4iUeSTY2sMDDwogno3361BLRyfEjyiYNo3Fqf0Pcnt5MqY2DqIhrF/H/Q==} + engines: {node: '>=18'} + + cspell-dictionary@8.14.2: + resolution: {integrity: sha512-gWuAvf6queGGUvGbfAxxUq55cZ0OevWPbjnCrSB0PpJ4tqdFd8dLcvVrIKzoE2sBXKPw2NDkmoEngs6iGavC0w==} + engines: {node: '>=18'} + + cspell-gitignore@8.14.2: + resolution: {integrity: sha512-lrO/49NaKBpkR7vFxv4OOY+oHmsG5+gNQejrBBWD9Nv9vvjJtz/G36X/rcN6M6tFcQQMWwa01kf04nxz8Ejuhg==} engines: {node: '>=18'} hasBin: true - cspell-glob@8.13.1: - resolution: {integrity: sha512-rW1A3t7YvPXxcC4z1pp1m9coeWzUVUmRjUw3vMNGlEDC2zecB39KKbEqesziBqnBceNAY7O5itllIGFKr03vqA==} + cspell-glob@8.13.3: + resolution: {integrity: sha512-+jGIMYyKDLmoOJIxNPXRdI7utcvw+9FMSmj1ApIdEff5dCkehi0gtzK4H7orXGYEvRdKQvfaXiyduVi79rXsZQ==} engines: {node: '>=18'} - cspell-grammar@8.13.1: - resolution: {integrity: sha512-HUkd24bulvBwee1UNBurxGlPUOiywb9pB34iXXoxFWuloHohZ/DuFlE8B/31ZtjW48ffEYIu3QZfWhcnD8e81w==} + cspell-glob@8.14.2: + resolution: {integrity: sha512-9Q1Kgoo1ev3fKTpp9y5n8M4RLxd8B0f5o4y5FQe4dBU0j/bt+/YDrLZNWDm77JViV606XQ6fimG1FTTq6pT9/g==} + engines: {node: '>=18'} + + cspell-grammar@8.13.3: + resolution: {integrity: sha512-xPSgKk9HY5EsI8lkMPC9hiZCeAUs+RY/IVliUBW1xEicAJhP4RZIGRdIwtDNNJGwKfNXazjqYhcS4LS0q7xPAQ==} engines: {node: '>=18'} hasBin: true - cspell-io@8.13.1: - resolution: {integrity: sha512-t2sgZuWGBzPSOAStfvz/U3KoFEfDxEt1cXZj0Kd0Vs36v2uoLktm6ihMe7XNFu7zIdOFSajsYQ8Bi4RSLPGPxQ==} + cspell-grammar@8.14.2: + resolution: {integrity: sha512-eYwceVP80FGYVJenE42ALnvEKOXaXjq4yVbb1Ni1umO/9qamLWNCQ1RP6rRACy5e/cXviAbhrQ5Mtw6n+pyPEQ==} + engines: {node: '>=18'} + hasBin: true + + cspell-io@8.13.3: + resolution: {integrity: sha512-AeMIkz7+4VuJaPKO/v1pUpyUSOOTyLOAfzeTRRAXEt+KRKOUe36MyUmBMza6gzNcX2yD04VgJukRL408TY9ntw==} engines: {node: '>=18'} - cspell-lib@8.13.1: - resolution: {integrity: sha512-H1HHG1pmATSeAaY0KmQ0xnkbSqJLvh9QpXWARDLWKUBvtE+/l44H4yVhIp/No3rM7PKMmb82GuSJzMaoIhHFLQ==} + cspell-io@8.14.2: + resolution: {integrity: sha512-uaKpHiY3DAgfdzgKMQml6U8F8o9udMuYxGqYa5FVfN7D5Ap7B2edQzSLTUYwxrFEn4skSfp6XY73+nzJvxzH4Q==} engines: {node: '>=18'} - cspell-trie-lib@8.13.1: - resolution: {integrity: sha512-2moCsIYDmMT7hp5Non3CvWatfXptFWCuxjbXQGDNvWJ2Cj3oso/oBe4802GJv5GEenv9QBWmEtum/E7rFcx4JA==} + cspell-lib@8.13.3: + resolution: {integrity: sha512-aEqxIILeqDtNoCa47/oSl5c926b50ue3PobYs4usn0Ymf0434RopCP+DCGsF7BPtog4j4XWnEmvkcJs57DYWDg==} engines: {node: '>=18'} - cspell@8.13.1: - resolution: {integrity: sha512-Bqppilpwx9xt3jZPaYcqe1JPteNmfKhx9pw9YglZEePDUzdiJQNVIfs31589GAnXjgdqqctR8N87ffLcaBNPXw==} + cspell-lib@8.14.2: + resolution: {integrity: sha512-d2oiIXHXnADmnhIuFLOdNE63L7OUfzgpLbYaqAWbkImCUDkevfGrOgnX8TJ03fUgZID4nvQ+3kgu/n2j4eLZjQ==} + engines: {node: '>=18'} + + cspell-trie-lib@8.13.3: + resolution: {integrity: sha512-Z0iLGi9HI+Vf+WhVVeru6dYgQdtaYCKWRlc1SayLfAZhw9BcjrXL8KTXDfAfv/lUgnRu6xwP1isLlDNZECsKVQ==} + engines: {node: '>=18'} + + cspell-trie-lib@8.14.2: + resolution: {integrity: sha512-rZMbaEBGoyy4/zxKECaMyVyGLbuUxYmZ5jlEgiA3xPtEdWwJ4iWRTo5G6dWbQsXoxPYdAXXZ0/q0GQ2y6Jt0kw==} + engines: {node: '>=18'} + + cspell@8.14.2: + resolution: {integrity: sha512-ii/W7fwO4chNQVYl1C/8k7RW8EXzLb69rvg08p8mSJx8B2UasVJ9tuJpTH2Spo1jX6N3H0dKPWUbd1fAmdAhPg==} engines: {node: '>=18'} hasBin: true @@ -3981,8 +4424,8 @@ packages: cypress-wait-until@3.0.2: resolution: {integrity: sha512-iemies796dD5CgjG5kV0MnpEmKSH+s7O83ZoJLVzuVbZmm4lheMsZqAVT73hlMx4QlkwhxbyUzhOBUOZwoOe0w==} - cypress@13.13.2: - resolution: {integrity: sha512-PvJQU33933NvS1StfzEb8/mu2kMy4dABwCF+yd5Bi7Qly1HOVf+Bufrygee/tlmty/6j5lX+KIi8j9Q3JUMbhA==} + cypress@13.14.1: + resolution: {integrity: sha512-Wo+byPmjps66hACEH5udhXINEiN3qS3jWNGRzJOjrRJF3D0+YrcP2LVB1T7oYaVQM/S+eanqEvBWYc8cf7Vcbg==} engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} hasBin: true @@ -3991,6 +4434,11 @@ packages: peerDependencies: cytoscape: ^3.2.0 + cytoscape-fcose@2.2.0: + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + cytoscape@3.30.1: resolution: {integrity: sha512-TRJc3HbBPkHd50u9YfJh2FxD1lDLZ+JXnJoyBn5LkncoeuT7fapO/Hq/Ed8TdFclaKshzInge2i30bg7VKeoPQ==} engines: {node: '>=0.10'} @@ -4175,6 +4623,9 @@ packages: dayjs@1.11.12: resolution: {integrity: sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -4209,15 +4660,6 @@ packages: supports-color: optional: true - debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.3.6: resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} engines: {node: '>=6.0'} @@ -4510,6 +4952,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} @@ -4572,8 +5019,8 @@ packages: peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 - eslint-plugin-json@4.0.0: - resolution: {integrity: sha512-l/P3WTzl2HI8PbwsbDIrZ+6jvwQI4TGuz20ReJkG3Y+gZS5ZurTgx+gBmuLpOgiqMyDJWyJ7+GCjevWtNYQcUg==} + eslint-plugin-json@4.0.1: + resolution: {integrity: sha512-3An5ISV5dq/kHfXdNyY5TUe2ONC3yXFSkLX2gu+W8xAhKhfvrRvkSAeKXCxZqZ0KJLX15ojBuLPyj+UikQMkOA==} engines: {node: '>=18.0'} eslint-plugin-lodash@8.0.0: @@ -4842,8 +5289,8 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - file-entry-cache@9.0.0: - resolution: {integrity: sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==} + file-entry-cache@9.1.0: + resolution: {integrity: sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==} engines: {node: '>=18'} file-saver@2.0.5: @@ -5339,8 +5786,8 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} - husky@9.1.4: - resolution: {integrity: sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA==} + husky@9.1.5: + resolution: {integrity: sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==} engines: {node: '>=18'} hasBin: true @@ -5362,6 +5809,10 @@ packages: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -5882,8 +6333,8 @@ packages: resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==} engines: {node: '>=12.0.0'} - jsdom@24.1.1: - resolution: {integrity: sha512-5O1wWV99Jhq4DV7rCLIoZ/UIhyQeDR7wHVyZAHAshbrvZsLs+Xzz7gtwnlJTJDjleiTKh54F4dXrX70vJQTyJQ==} + jsdom@24.1.3: + resolution: {integrity: sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==} engines: {node: '>=18'} peerDependencies: canvas: ^2.11.2 @@ -6008,6 +6459,9 @@ packages: layout-base@1.0.2: resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} + layout-base@2.0.1: + resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + lazy-ass@1.6.0: resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==} engines: {node: '> 0.8'} @@ -6040,8 +6494,8 @@ packages: linkify-it@4.0.1: resolution: {integrity: sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==} - lint-staged@15.2.8: - resolution: {integrity: sha512-PUWFf2zQzsd9EFU+kM1d7UP+AZDbKFKuj+9JNVTBkhUFhbg4MAt6WfyMMwBfM4lYqd4D2Jwac5iuTu9rVj4zCQ==} + lint-staged@15.2.9: + resolution: {integrity: sha512-BZAt8Lk3sEnxw7tfxM7jeZlPRuT4M68O0/CwZhhaw6eeWu0Lz5eERE3m386InivXB64fp/mDID452h48tvKlRQ==} engines: {node: '>=18.12.0'} hasBin: true @@ -6381,8 +6835,8 @@ packages: micromark@4.0.0: resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} - micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} mime-db@1.52.0: @@ -6450,6 +6904,9 @@ packages: minisearch@6.3.0: resolution: {integrity: sha512-ihFnidEeU8iXzcVHy74dhkxh/dn8Dc08ERl0xwoMMGqp4+LvRSCgicb+zGqWthVokQKvCSxITlh3P08OzdTYCQ==} + minisearch@7.1.0: + resolution: {integrity: sha512-tv7c/uefWdEhcu6hvrfTihflgeEi2tN6VV7HJnCjK6VxM75QQJh4t9FwJCsA2EsRS8LCnu3W87CuGPWMocOLCA==} + mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} @@ -6645,6 +7102,12 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + openapi-fetch@0.11.1: + resolution: {integrity: sha512-WtDQsrvxjXuCmo6u6WMQPfUaya8cLfL+ZCaXorPo9MMumqlU/Km/SrCXsEcJH234D4iykOkvJ6Q/iWBzK7+3rA==} + + openapi-typescript-helpers@0.0.12: + resolution: {integrity: sha512-FO+5kTWO6KDutigamr2MRwciYkAUYhqdctlyVRrQOe2uxif2/O2+GcS07jNnP36AUK6ubSsGu3GeBiYIc6eQzA==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -6754,6 +7217,9 @@ packages: package-json-from-dist@1.0.0: resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + package-manager-detector@0.2.0: + resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} + pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} @@ -6932,11 +7398,6 @@ packages: resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==} engines: {node: '>=12.13.0'} - pnpm@8.15.9: - resolution: {integrity: sha512-SZQ0ydj90aJ5Tr9FUrOyXApjOrzuW7Fee13pDzL0e1E6ypjNXP0AHDHw20VLw4BO3M1XhQHkyik6aBYWa72fgQ==} - engines: {node: '>=16.14'} - hasBin: true - points-on-curve@0.2.0: resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} @@ -6977,19 +7438,19 @@ packages: peerDependencies: postcss: ^8.2.14 - postcss-selector-parser@6.1.1: - resolution: {integrity: sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==} + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.4.40: - resolution: {integrity: sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==} + postcss@8.4.41: + resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} engines: {node: ^10 || ^12 || >=14} - preact@10.23.1: - resolution: {integrity: sha512-O5UdRsNh4vdZaTieWe3XOgSpdMAmkIYBCT3VhQDlKrzyCm8lUYsk0fmVEvoQQifoOjFRTaHZO69ylrzTW2BH+A==} + preact@10.23.2: + resolution: {integrity: sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==} preferred-pm@3.1.4: resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} @@ -7315,8 +7776,8 @@ packages: engines: {node: '>=10.0.0'} hasBin: true - rollup@4.20.0: - resolution: {integrity: sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==} + rollup@4.21.1: + resolution: {integrity: sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -7355,8 +7816,8 @@ packages: safe-regex2@2.0.0: resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} - safe-stable-stringify@2.4.3: - resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} safer-buffer@2.1.2: @@ -7447,9 +7908,9 @@ packages: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} - sharp@0.33.4: - resolution: {integrity: sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==} - engines: {libvips: '>=8.15.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.33.5: + resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} @@ -7473,8 +7934,8 @@ packages: shiki@0.14.7: resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} - shiki@1.12.1: - resolution: {integrity: sha512-nwmjbHKnOYYAe1aaQyEBHvQymJgfm86ZSS7fT8OaPRr4sbAcBNz7PbfAikMEFSDQ6se2j2zobkXvVKcBOm0ysg==} + shiki@1.14.1: + resolution: {integrity: sha512-FujAN40NEejeXdzPt+3sZ3F2dx1U24BY2XTY01+MG8mbxCiA2XukXdcbyMyLAHJ/1AUUnQd1tZlvIjefWWEJeA==} side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} @@ -7753,8 +8214,8 @@ packages: strip-literal@2.1.0: resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} - stylis@4.3.2: - resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==} + stylis@4.3.4: + resolution: {integrity: sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==} sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} @@ -7795,8 +8256,8 @@ packages: tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} - tailwindcss@3.4.7: - resolution: {integrity: sha512-rxWZbe87YJb4OcSopb7up2Ba4U82BoiSGUdoDr3Ydrg9ckxFS/YWsvhN323GMcddgU65QRy7JndC7ahhInhvlQ==} + tailwindcss@3.4.10: + resolution: {integrity: sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==} engines: {node: '>=14.0.0'} hasBin: true @@ -7845,6 +8306,11 @@ packages: engines: {node: '>=10'} hasBin: true + terser@5.31.6: + resolution: {integrity: sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==} + engines: {node: '>=10'} + hasBin: true + test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} @@ -7885,6 +8351,9 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinyexec@0.3.0: + resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + tinypool@0.8.4: resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} engines: {node: '>=14.0.0'} @@ -7963,8 +8432,11 @@ packages: tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} - tsx@4.16.5: - resolution: {integrity: sha512-ArsiAQHEW2iGaqZ8fTA1nX0a+lN5mNTyuGRRO6OW3H/Yno1y9/t1f9YOI1Cfoqz63VAthn++ZYcbDP7jPflc+A==} + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + + tsx@4.19.0: + resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==} engines: {node: '>=18.0.0'} hasBin: true @@ -8006,8 +8478,8 @@ packages: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} - type-fest@4.23.0: - resolution: {integrity: sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w==} + type-fest@4.25.0: + resolution: {integrity: sha512-bRkIGlXsnGBRBQRAY56UXBm//9qH4bmJfFvq83gSz41N282df+fjy8ofcEgc1sM8geNt5cl6mC2g9Fht1cs8Aw==} engines: {node: '>=16'} type-is@1.6.18: @@ -8048,8 +8520,8 @@ packages: peerDependencies: typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x - typescript-eslint@8.0.0: - resolution: {integrity: sha512-yQWBJutWL1PmpmDddIOl9/Mi6vZjqNCjqSGBMQ4vsc2Aiodk0SnbQQWPXbSy0HNuKCuGkw1+u4aQ2mO40TdhDQ==} + typescript-eslint@8.0.1: + resolution: {integrity: sha512-V3Y+MdfhawxEjE16dWpb7/IOgeXnLwAEEkS7v8oDqNcR1oYlqWhGH/iHqHdKVdpWme1VPZ0SoywXAkCqawj2eQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -8085,6 +8557,9 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + unicode-canonical-property-names-ecmascript@2.0.0: resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} engines: {node: '>=4'} @@ -8259,8 +8734,8 @@ packages: '@vite-pwa/assets-generator': optional: true - vite@5.3.5: - resolution: {integrity: sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==} + vite@5.4.2: + resolution: {integrity: sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -8268,6 +8743,7 @@ packages: less: '*' lightningcss: ^1.21.0 sass: '*' + sass-embedded: '*' stylus: '*' sugarss: '*' terser: ^5.4.0 @@ -8280,6 +8756,8 @@ packages: optional: true sass: optional: true + sass-embedded: + optional: true stylus: optional: true sugarss: @@ -8307,6 +8785,18 @@ packages: postcss: optional: true + vitepress@1.3.4: + resolution: {integrity: sha512-I1/F6OW1xl3kW4PaIMC6snxjWgf3qfziq2aqsDoFc/Gt41WbcRv++z8zjw8qGRIJ+I4bUW7ZcKFDHHN/jkH9DQ==} + hasBin: true + peerDependencies: + markdown-it-mathjax3: ^4 + postcss: ^8 + peerDependenciesMeta: + markdown-it-mathjax3: + optional: true + postcss: + optional: true + vitest@1.6.0: resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -8375,8 +8865,8 @@ packages: '@vue/composition-api': optional: true - vue@3.4.35: - resolution: {integrity: sha512-+fl/GLmI4GPileHftVlCdB7fUL4aziPcqTudpTGXCT8s+iZWuOCeNEB5haX6Uz2IpRrbEXOgIFbe+XciCuGbNQ==} + vue@3.4.38: + resolution: {integrity: sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -8652,18 +9142,6 @@ packages: utf-8-validate: optional: true - ws@8.5.0: - resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - xdg-basedir@5.1.0: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} @@ -8874,6 +9352,11 @@ snapshots: execa: 5.1.1 find-up: 5.0.0 + '@antfu/install-pkg@0.4.1': + dependencies: + package-manager-detector: 0.2.0 + tinyexec: 0.3.0 + '@antfu/utils@0.7.10': {} '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': @@ -8894,10 +9377,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@applitools/core@4.18.0(encoding@0.1.13)(typescript@5.4.5)': + '@applitools/core@4.18.1(encoding@0.1.13)(typescript@5.4.5)': dependencies: '@applitools/core-base': 1.16.0 - '@applitools/dom-capture': 11.3.0 + '@applitools/dom-capture': 11.3.1 '@applitools/dom-snapshot': 4.11.3 '@applitools/driver': 1.18.0 '@applitools/ec-client': 1.9.3(typescript@5.4.5) @@ -8930,7 +9413,7 @@ snapshots: mdn-data: 2.1.0 source-map-js: 1.0.1 - '@applitools/dom-capture@11.3.0': + '@applitools/dom-capture@11.3.1': dependencies: '@applitools/dom-shared': 1.0.15 '@applitools/functional-commons': 1.6.0 @@ -8996,10 +9479,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@applitools/eyes-cypress@3.44.6(encoding@0.1.13)(typescript@5.4.5)': + '@applitools/eyes-cypress@3.44.7(encoding@0.1.13)(typescript@5.4.5)': dependencies: - '@applitools/core': 4.18.0(encoding@0.1.13)(typescript@5.4.5) - '@applitools/eyes': 1.22.0(encoding@0.1.13)(typescript@5.4.5) + '@applitools/core': 4.18.1(encoding@0.1.13)(typescript@5.4.5) + '@applitools/eyes': 1.22.1(encoding@0.1.13)(typescript@5.4.5) '@applitools/functional-commons': 1.6.0 '@applitools/logger': 2.0.18 '@applitools/utils': 1.7.4 @@ -9007,7 +9490,7 @@ snapshots: chalk: 3.0.0 semver: 7.6.2 uuid: 8.3.2 - ws: 8.5.0 + ws: 8.17.1 transitivePeerDependencies: - bufferutil - encoding @@ -9015,9 +9498,9 @@ snapshots: - typescript - utf-8-validate - '@applitools/eyes@1.22.0(encoding@0.1.13)(typescript@5.4.5)': + '@applitools/eyes@1.22.1(encoding@0.1.13)(typescript@5.4.5)': dependencies: - '@applitools/core': 4.18.0(encoding@0.1.13)(typescript@5.4.5) + '@applitools/core': 4.18.1(encoding@0.1.13)(typescript@5.4.5) '@applitools/logger': 2.0.18 '@applitools/utils': 1.7.4 transitivePeerDependencies: @@ -9128,31 +9611,36 @@ snapshots: '@applitools/utils@1.7.4': {} - '@argos-ci/browser@2.1.2': {} - - '@argos-ci/core@2.4.1': + '@argos-ci/api-client@0.2.0': dependencies: - '@argos-ci/util': 2.1.0 - axios: 1.7.3(debug@4.3.6) + openapi-fetch: 0.11.1 + + '@argos-ci/browser@2.1.3': {} + + '@argos-ci/core@2.5.0': + dependencies: + '@argos-ci/api-client': 0.2.0 + '@argos-ci/util': 2.1.1 + axios: 1.7.5(debug@4.3.6) convict: 6.2.4 debug: 4.3.6(supports-color@8.1.1) fast-glob: 3.3.2 - sharp: 0.33.4 + sharp: 0.33.5 tmp: 0.2.3 transitivePeerDependencies: - supports-color - '@argos-ci/cypress@2.1.1(cypress@13.13.2)': + '@argos-ci/cypress@2.1.2(cypress@13.14.1)': dependencies: - '@argos-ci/browser': 2.1.2 - '@argos-ci/core': 2.4.1 - '@argos-ci/util': 2.1.0 - cypress: 13.13.2 + '@argos-ci/browser': 2.1.3 + '@argos-ci/core': 2.5.0 + '@argos-ci/util': 2.1.1 + cypress: 13.14.1 cypress-wait-until: 3.0.2 transitivePeerDependencies: - supports-color - '@argos-ci/util@2.1.0': {} + '@argos-ci/util@2.1.1': {} '@babel/code-frame@7.24.7': dependencies: @@ -9161,6 +9649,8 @@ snapshots: '@babel/compat-data@7.25.2': {} + '@babel/compat-data@7.25.4': {} + '@babel/core@7.25.2': dependencies: '@ampproject/remapping': 2.3.0 @@ -9169,7 +9659,7 @@ snapshots: '@babel/helper-compilation-targets': 7.25.2 '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) '@babel/helpers': 7.25.0 - '@babel/parser': 7.25.3 + '@babel/parser': 7.25.4 '@babel/template': 7.25.0 '@babel/traverse': 7.25.3 '@babel/types': 7.25.2 @@ -9188,14 +9678,21 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 + '@babel/generator@7.25.5': + dependencies: + '@babel/types': 7.25.4 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + '@babel/helper-annotate-as-pure@7.24.7': dependencies: - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': dependencies: - '@babel/traverse': 7.25.3 - '@babel/types': 7.25.2 + '@babel/traverse': 7.25.4 + '@babel/types': 7.25.4 transitivePeerDependencies: - supports-color @@ -9220,6 +9717,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-create-class-features-plugin@7.25.4(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.8 + '@babel/helper-optimise-call-expression': 7.24.7 + '@babel/helper-replace-supers': 7.25.0(@babel/core@7.25.2) + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/traverse': 7.25.4 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/helper-create-regexp-features-plugin@7.25.2(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -9241,7 +9751,7 @@ snapshots: '@babel/helper-member-expression-to-functions@7.24.8': dependencies: '@babel/traverse': 7.25.3 - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 transitivePeerDependencies: - supports-color @@ -9264,7 +9774,7 @@ snapshots: '@babel/helper-optimise-call-expression@7.24.7': dependencies: - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 '@babel/helper-plugin-utils@7.24.8': {} @@ -9273,7 +9783,7 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-annotate-as-pure': 7.24.7 '@babel/helper-wrap-function': 7.25.0 - '@babel/traverse': 7.25.3 + '@babel/traverse': 7.25.4 transitivePeerDependencies: - supports-color @@ -9289,14 +9799,14 @@ snapshots: '@babel/helper-simple-access@7.24.7': dependencies: '@babel/traverse': 7.25.3 - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.24.7': dependencies: '@babel/traverse': 7.25.3 - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 transitivePeerDependencies: - supports-color @@ -9309,8 +9819,8 @@ snapshots: '@babel/helper-wrap-function@7.25.0': dependencies: '@babel/template': 7.25.0 - '@babel/traverse': 7.25.3 - '@babel/types': 7.25.2 + '@babel/traverse': 7.25.4 + '@babel/types': 7.25.4 transitivePeerDependencies: - supports-color @@ -9330,11 +9840,15 @@ snapshots: dependencies: '@babel/types': 7.25.2 + '@babel/parser@7.25.4': + dependencies: + '@babel/types': 7.25.4 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.3(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/traverse': 7.25.3 + '@babel/traverse': 7.25.4 transitivePeerDependencies: - supports-color @@ -9361,7 +9875,7 @@ snapshots: dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/traverse': 7.25.3 + '@babel/traverse': 7.25.4 transitivePeerDependencies: - supports-color @@ -9480,13 +9994,13 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-async-generator-functions@7.25.0(@babel/core@7.25.2)': + '@babel/plugin-transform-async-generator-functions@7.25.4(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-remap-async-to-generator': 7.25.0(@babel/core@7.25.2) '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.2) - '@babel/traverse': 7.25.3 + '@babel/traverse': 7.25.4 transitivePeerDependencies: - supports-color @@ -9509,10 +10023,10 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-class-properties@7.25.4(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 - '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.25.2) + '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) '@babel/helper-plugin-utils': 7.24.8 transitivePeerDependencies: - supports-color @@ -9520,20 +10034,20 @@ snapshots: '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 - '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.25.2) + '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.25.0(@babel/core@7.25.2)': + '@babel/plugin-transform-classes@7.25.4(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-annotate-as-pure': 7.24.7 '@babel/helper-compilation-targets': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-replace-supers': 7.25.0(@babel/core@7.25.2) - '@babel/traverse': 7.25.3 + '@babel/traverse': 7.25.4 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -9599,7 +10113,7 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-compilation-targets': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/traverse': 7.25.3 + '@babel/traverse': 7.25.4 transitivePeerDependencies: - supports-color @@ -9648,7 +10162,7 @@ snapshots: '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-validator-identifier': 7.24.7 - '@babel/traverse': 7.25.3 + '@babel/traverse': 7.25.4 transitivePeerDependencies: - supports-color @@ -9719,10 +10233,10 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-private-methods@7.25.4(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 - '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.25.2) + '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) '@babel/helper-plugin-utils': 7.24.8 transitivePeerDependencies: - supports-color @@ -9731,7 +10245,7 @@ snapshots: dependencies: '@babel/core': 7.25.2 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.25.2) + '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.2) transitivePeerDependencies: @@ -9809,15 +10323,15 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-unicode-sets-regex@7.25.4(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) '@babel/helper-plugin-utils': 7.24.8 - '@babel/preset-env@7.25.3(@babel/core@7.25.2)': + '@babel/preset-env@7.25.4(@babel/core@7.25.2)': dependencies: - '@babel/compat-data': 7.25.2 + '@babel/compat-data': 7.25.4 '@babel/core': 7.25.2 '@babel/helper-compilation-targets': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 @@ -9847,13 +10361,13 @@ snapshots: '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.25.2) '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.25.2) '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-async-generator-functions': 7.25.0(@babel/core@7.25.2) + '@babel/plugin-transform-async-generator-functions': 7.25.4(@babel/core@7.25.2) '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-block-scoping': 7.25.0(@babel/core@7.25.2) - '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-class-properties': 7.25.4(@babel/core@7.25.2) '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-classes': 7.25.0(@babel/core@7.25.2) + '@babel/plugin-transform-classes': 7.25.4(@babel/core@7.25.2) '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-destructuring': 7.24.8(@babel/core@7.25.2) '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.25.2) @@ -9881,7 +10395,7 @@ snapshots: '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-optional-chaining': 7.24.8(@babel/core@7.25.2) '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-private-methods': 7.25.4(@babel/core@7.25.2) '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.25.2) @@ -9894,12 +10408,12 @@ snapshots: '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-unicode-sets-regex': 7.25.4(@babel/core@7.25.2) '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.25.2) babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.25.2) babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.25.2) babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.25.2) - core-js-compat: 3.38.0 + core-js-compat: 3.38.1 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -9908,7 +10422,7 @@ snapshots: dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 esutils: 2.0.3 '@babel/preset-typescript@7.24.7(@babel/core@7.25.2)': @@ -9928,17 +10442,21 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 + '@babel/runtime@7.25.4': + dependencies: + regenerator-runtime: 0.14.1 + '@babel/template@7.25.0': dependencies: '@babel/code-frame': 7.24.7 - '@babel/parser': 7.25.3 + '@babel/parser': 7.25.4 '@babel/types': 7.25.2 '@babel/traverse@7.25.3': dependencies: '@babel/code-frame': 7.24.7 '@babel/generator': 7.25.0 - '@babel/parser': 7.25.3 + '@babel/parser': 7.25.4 '@babel/template': 7.25.0 '@babel/types': 7.25.2 debug: 4.3.6(supports-color@8.1.1) @@ -9946,12 +10464,30 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.25.4': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.5 + '@babel/parser': 7.25.4 + '@babel/template': 7.25.0 + '@babel/types': 7.25.4 + debug: 4.3.6(supports-color@8.1.1) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + '@babel/types@7.25.2': dependencies: '@babel/helper-string-parser': 7.24.8 '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 + '@babel/types@7.25.4': + dependencies: + '@babel/helper-string-parser': 7.24.8 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + '@bcherny/json-schema-ref-parser@10.0.5-fork': dependencies: '@jsdevtools/ono': 7.1.3 @@ -10045,7 +10581,7 @@ snapshots: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 - micromatch: 4.0.7 + micromatch: 4.0.8 '@changesets/errors@0.2.0': dependencies: @@ -10085,7 +10621,7 @@ snapshots: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 is-subdir: 1.2.0 - micromatch: 4.0.7 + micromatch: 4.0.8 spawndamnit: 2.0.0 '@changesets/logger@0.1.0': @@ -10154,20 +10690,20 @@ snapshots: '@colors/colors@1.5.0': optional: true - '@cspell/cspell-bundled-dicts@8.13.1': + '@cspell/cspell-bundled-dicts@8.13.3': dependencies: '@cspell/dict-ada': 4.0.2 '@cspell/dict-aws': 4.0.3 '@cspell/dict-bash': 4.1.3 '@cspell/dict-companies': 3.1.4 - '@cspell/dict-cpp': 5.1.12 + '@cspell/dict-cpp': 5.1.15 '@cspell/dict-cryptocurrencies': 5.0.0 '@cspell/dict-csharp': 4.0.2 - '@cspell/dict-css': 4.0.12 + '@cspell/dict-css': 4.0.13 '@cspell/dict-dart': 2.0.3 '@cspell/dict-django': 4.1.0 '@cspell/dict-docker': 1.1.7 - '@cspell/dict-dotnet': 5.0.2 + '@cspell/dict-dotnet': 5.0.4 '@cspell/dict-elixir': 4.0.3 '@cspell/dict-en-common-misspellings': 2.0.4 '@cspell/dict-en-gb': 1.1.33 @@ -10178,7 +10714,7 @@ snapshots: '@cspell/dict-fullstack': 3.2.0 '@cspell/dict-gaming-terms': 1.0.5 '@cspell/dict-git': 3.0.0 - '@cspell/dict-golang': 6.0.9 + '@cspell/dict-golang': 6.0.11 '@cspell/dict-google': 1.0.1 '@cspell/dict-haskell': 4.0.1 '@cspell/dict-html': 4.0.5 @@ -10193,51 +10729,122 @@ snapshots: '@cspell/dict-monkeyc': 1.0.6 '@cspell/dict-node': 5.0.1 '@cspell/dict-npm': 5.0.18 - '@cspell/dict-php': 4.0.8 + '@cspell/dict-php': 4.0.9 '@cspell/dict-powershell': 5.0.5 - '@cspell/dict-public-licenses': 2.0.7 - '@cspell/dict-python': 4.2.3 + '@cspell/dict-public-licenses': 2.0.8 + '@cspell/dict-python': 4.2.4 '@cspell/dict-r': 2.0.1 - '@cspell/dict-ruby': 5.0.2 + '@cspell/dict-ruby': 5.0.3 '@cspell/dict-rust': 4.0.5 '@cspell/dict-scala': 5.0.3 - '@cspell/dict-software-terms': 4.0.4 - '@cspell/dict-sql': 2.1.4 + '@cspell/dict-software-terms': 4.0.11 + '@cspell/dict-sql': 2.1.5 '@cspell/dict-svelte': 1.0.2 '@cspell/dict-swift': 2.0.1 '@cspell/dict-terraform': 1.0.0 '@cspell/dict-typescript': 3.1.6 '@cspell/dict-vue': 3.0.0 - '@cspell/cspell-json-reporter@8.13.1': + '@cspell/cspell-bundled-dicts@8.14.2': dependencies: - '@cspell/cspell-types': 8.13.1 + '@cspell/dict-ada': 4.0.2 + '@cspell/dict-aws': 4.0.4 + '@cspell/dict-bash': 4.1.4 + '@cspell/dict-companies': 3.1.4 + '@cspell/dict-cpp': 5.1.16 + '@cspell/dict-cryptocurrencies': 5.0.0 + '@cspell/dict-csharp': 4.0.2 + '@cspell/dict-css': 4.0.13 + '@cspell/dict-dart': 2.0.3 + '@cspell/dict-django': 4.1.0 + '@cspell/dict-docker': 1.1.7 + '@cspell/dict-dotnet': 5.0.5 + '@cspell/dict-elixir': 4.0.3 + '@cspell/dict-en-common-misspellings': 2.0.4 + '@cspell/dict-en-gb': 1.1.33 + '@cspell/dict-en_us': 4.3.23 + '@cspell/dict-filetypes': 3.0.4 + '@cspell/dict-fonts': 4.0.0 + '@cspell/dict-fsharp': 1.0.1 + '@cspell/dict-fullstack': 3.2.0 + '@cspell/dict-gaming-terms': 1.0.5 + '@cspell/dict-git': 3.0.0 + '@cspell/dict-golang': 6.0.12 + '@cspell/dict-google': 1.0.1 + '@cspell/dict-haskell': 4.0.1 + '@cspell/dict-html': 4.0.5 + '@cspell/dict-html-symbol-entities': 4.0.0 + '@cspell/dict-java': 5.0.7 + '@cspell/dict-julia': 1.0.1 + '@cspell/dict-k8s': 1.0.6 + '@cspell/dict-latex': 4.0.0 + '@cspell/dict-lorem-ipsum': 4.0.0 + '@cspell/dict-lua': 4.0.3 + '@cspell/dict-makefile': 1.0.0 + '@cspell/dict-monkeyc': 1.0.6 + '@cspell/dict-node': 5.0.1 + '@cspell/dict-npm': 5.1.0 + '@cspell/dict-php': 4.0.10 + '@cspell/dict-powershell': 5.0.6 + '@cspell/dict-public-licenses': 2.0.8 + '@cspell/dict-python': 4.2.6 + '@cspell/dict-r': 2.0.1 + '@cspell/dict-ruby': 5.0.3 + '@cspell/dict-rust': 4.0.5 + '@cspell/dict-scala': 5.0.3 + '@cspell/dict-software-terms': 4.1.1 + '@cspell/dict-sql': 2.1.5 + '@cspell/dict-svelte': 1.0.2 + '@cspell/dict-swift': 2.0.1 + '@cspell/dict-terraform': 1.0.1 + '@cspell/dict-typescript': 3.1.6 + '@cspell/dict-vue': 3.0.0 - '@cspell/cspell-pipe@8.13.1': {} + '@cspell/cspell-json-reporter@8.14.2': + dependencies: + '@cspell/cspell-types': 8.14.2 - '@cspell/cspell-resolver@8.13.1': + '@cspell/cspell-pipe@8.13.3': {} + + '@cspell/cspell-pipe@8.14.2': {} + + '@cspell/cspell-resolver@8.13.3': dependencies: global-directory: 4.0.1 - '@cspell/cspell-service-bus@8.13.1': {} + '@cspell/cspell-resolver@8.14.2': + dependencies: + global-directory: 4.0.1 - '@cspell/cspell-types@8.13.1': {} + '@cspell/cspell-service-bus@8.13.3': {} + + '@cspell/cspell-service-bus@8.14.2': {} + + '@cspell/cspell-types@8.13.3': {} + + '@cspell/cspell-types@8.14.2': {} '@cspell/dict-ada@4.0.2': {} '@cspell/dict-aws@4.0.3': {} + '@cspell/dict-aws@4.0.4': {} + '@cspell/dict-bash@4.1.3': {} + '@cspell/dict-bash@4.1.4': {} + '@cspell/dict-companies@3.1.4': {} - '@cspell/dict-cpp@5.1.12': {} + '@cspell/dict-cpp@5.1.15': {} + + '@cspell/dict-cpp@5.1.16': {} '@cspell/dict-cryptocurrencies@5.0.0': {} '@cspell/dict-csharp@4.0.2': {} - '@cspell/dict-css@4.0.12': {} + '@cspell/dict-css@4.0.13': {} '@cspell/dict-dart@2.0.3': {} @@ -10247,7 +10854,9 @@ snapshots: '@cspell/dict-docker@1.1.7': {} - '@cspell/dict-dotnet@5.0.2': {} + '@cspell/dict-dotnet@5.0.4': {} + + '@cspell/dict-dotnet@5.0.5': {} '@cspell/dict-elixir@4.0.3': {} @@ -10269,7 +10878,9 @@ snapshots: '@cspell/dict-git@3.0.0': {} - '@cspell/dict-golang@6.0.9': {} + '@cspell/dict-golang@6.0.11': {} + + '@cspell/dict-golang@6.0.12': {} '@cspell/dict-google@1.0.1': {} @@ -10299,27 +10910,39 @@ snapshots: '@cspell/dict-npm@5.0.18': {} - '@cspell/dict-php@4.0.8': {} + '@cspell/dict-npm@5.1.0': {} + + '@cspell/dict-php@4.0.10': {} + + '@cspell/dict-php@4.0.9': {} '@cspell/dict-powershell@5.0.5': {} - '@cspell/dict-public-licenses@2.0.7': {} + '@cspell/dict-powershell@5.0.6': {} - '@cspell/dict-python@4.2.3': + '@cspell/dict-public-licenses@2.0.8': {} + + '@cspell/dict-python@4.2.4': + dependencies: + '@cspell/dict-data-science': 2.0.1 + + '@cspell/dict-python@4.2.6': dependencies: '@cspell/dict-data-science': 2.0.1 '@cspell/dict-r@2.0.1': {} - '@cspell/dict-ruby@5.0.2': {} + '@cspell/dict-ruby@5.0.3': {} '@cspell/dict-rust@4.0.5': {} '@cspell/dict-scala@5.0.3': {} - '@cspell/dict-software-terms@4.0.4': {} + '@cspell/dict-software-terms@4.0.11': {} - '@cspell/dict-sql@2.1.4': {} + '@cspell/dict-software-terms@4.1.1': {} + + '@cspell/dict-sql@2.1.5': {} '@cspell/dict-svelte@1.0.2': {} @@ -10327,36 +10950,48 @@ snapshots: '@cspell/dict-terraform@1.0.0': {} + '@cspell/dict-terraform@1.0.1': {} + '@cspell/dict-typescript@3.1.6': {} '@cspell/dict-vue@3.0.0': {} - '@cspell/dynamic-import@8.13.1': + '@cspell/dynamic-import@8.13.3': dependencies: import-meta-resolve: 4.1.0 - '@cspell/eslint-plugin@8.13.1(eslint@9.8.0)': + '@cspell/dynamic-import@8.14.2': dependencies: - '@cspell/cspell-types': 8.13.1 - '@cspell/url': 8.13.1 - cspell-lib: 8.13.1 + import-meta-resolve: 4.1.0 + + '@cspell/eslint-plugin@8.13.3(eslint@9.8.0)': + dependencies: + '@cspell/cspell-types': 8.13.3 + '@cspell/url': 8.13.3 + cspell-lib: 8.13.3 eslint: 9.8.0 synckit: 0.9.1 - '@cspell/strong-weak-map@8.13.1': {} + '@cspell/filetypes@8.14.2': {} - '@cspell/url@8.13.1': {} + '@cspell/strong-weak-map@8.13.3': {} - '@cypress/code-coverage@3.12.44(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)))(cypress@13.13.2)(webpack@5.93.0(esbuild@0.21.5))': + '@cspell/strong-weak-map@8.14.2': {} + + '@cspell/url@8.13.3': {} + + '@cspell/url@8.14.2': {} + + '@cypress/code-coverage@3.12.45(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)))(cypress@13.14.1)(webpack@5.93.0(esbuild@0.21.5))': dependencies: '@babel/core': 7.25.2 - '@babel/preset-env': 7.25.3(@babel/core@7.25.2) - '@cypress/webpack-preprocessor': 6.0.2(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)))(webpack@5.93.0(esbuild@0.21.5)) + '@babel/preset-env': 7.25.4(@babel/core@7.25.2) + '@cypress/webpack-preprocessor': 6.0.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)))(webpack@5.93.0(esbuild@0.21.5)) babel-loader: 9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)) chalk: 4.1.2 - cypress: 13.13.2 + cypress: 13.14.1 dayjs: 1.11.12 - debug: 4.3.5 + debug: 4.3.6(supports-color@8.1.1) execa: 4.1.0 globby: 11.1.0 istanbul-lib-coverage: 3.2.2 @@ -10369,7 +11004,7 @@ snapshots: '@cypress/request@3.0.1': dependencies: aws-sign2: 0.7.0 - aws4: 1.13.0 + aws4: 1.13.1 caseless: 0.12.0 combined-stream: 1.0.8 extend: 3.0.2 @@ -10387,13 +11022,13 @@ snapshots: tunnel-agent: 0.6.0 uuid: 8.3.2 - '@cypress/webpack-preprocessor@6.0.2(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)))(webpack@5.93.0(esbuild@0.21.5))': + '@cypress/webpack-preprocessor@6.0.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)))(webpack@5.93.0(esbuild@0.21.5))': dependencies: '@babel/core': 7.25.2 - '@babel/preset-env': 7.25.3(@babel/core@7.25.2) + '@babel/preset-env': 7.25.4(@babel/core@7.25.2) babel-loader: 9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)) bluebird: 3.7.1 - debug: 4.3.5 + debug: 4.3.6(supports-color@8.1.1) lodash: 4.17.21 webpack: 5.93.0(esbuild@0.21.5) transitivePeerDependencies: @@ -10413,7 +11048,7 @@ snapshots: '@docsearch/js@3.6.1(@algolia/client-search@4.24.0)(search-insights@2.15.0)': dependencies: '@docsearch/react': 3.6.1(@algolia/client-search@4.24.0)(search-insights@2.15.0) - preact: 10.23.1 + preact: 10.23.2 transitivePeerDependencies: - '@algolia/client-search' - '@types/react' @@ -10434,7 +11069,7 @@ snapshots: '@emnapi/runtime@1.2.0': dependencies: - tslib: 2.6.3 + tslib: 2.7.0 optional: true '@es-joy/jsdoccomment@0.46.0': @@ -10446,72 +11081,144 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true + '@esbuild/aix-ppc64@0.23.1': + optional: true + '@esbuild/android-arm64@0.21.5': optional: true + '@esbuild/android-arm64@0.23.1': + optional: true + '@esbuild/android-arm@0.21.5': optional: true + '@esbuild/android-arm@0.23.1': + optional: true + '@esbuild/android-x64@0.21.5': optional: true + '@esbuild/android-x64@0.23.1': + optional: true + '@esbuild/darwin-arm64@0.21.5': optional: true + '@esbuild/darwin-arm64@0.23.1': + optional: true + '@esbuild/darwin-x64@0.21.5': optional: true + '@esbuild/darwin-x64@0.23.1': + optional: true + '@esbuild/freebsd-arm64@0.21.5': optional: true + '@esbuild/freebsd-arm64@0.23.1': + optional: true + '@esbuild/freebsd-x64@0.21.5': optional: true + '@esbuild/freebsd-x64@0.23.1': + optional: true + '@esbuild/linux-arm64@0.21.5': optional: true + '@esbuild/linux-arm64@0.23.1': + optional: true + '@esbuild/linux-arm@0.21.5': optional: true + '@esbuild/linux-arm@0.23.1': + optional: true + '@esbuild/linux-ia32@0.21.5': optional: true + '@esbuild/linux-ia32@0.23.1': + optional: true + '@esbuild/linux-loong64@0.21.5': optional: true + '@esbuild/linux-loong64@0.23.1': + optional: true + '@esbuild/linux-mips64el@0.21.5': optional: true + '@esbuild/linux-mips64el@0.23.1': + optional: true + '@esbuild/linux-ppc64@0.21.5': optional: true + '@esbuild/linux-ppc64@0.23.1': + optional: true + '@esbuild/linux-riscv64@0.21.5': optional: true + '@esbuild/linux-riscv64@0.23.1': + optional: true + '@esbuild/linux-s390x@0.21.5': optional: true + '@esbuild/linux-s390x@0.23.1': + optional: true + '@esbuild/linux-x64@0.21.5': optional: true + '@esbuild/linux-x64@0.23.1': + optional: true + '@esbuild/netbsd-x64@0.21.5': optional: true + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + '@esbuild/openbsd-x64@0.21.5': optional: true + '@esbuild/openbsd-x64@0.23.1': + optional: true + '@esbuild/sunos-x64@0.21.5': optional: true + '@esbuild/sunos-x64@0.23.1': + optional: true + '@esbuild/win32-arm64@0.21.5': optional: true + '@esbuild/win32-arm64@0.23.1': + optional: true + '@esbuild/win32-ia32@0.21.5': optional: true + '@esbuild/win32-ia32@0.23.1': + optional: true + '@esbuild/win32-x64@0.21.5': optional: true + '@esbuild/win32-x64@0.23.1': + optional: true + '@eslint-community/eslint-utils@4.4.0(eslint@9.8.0)': dependencies: eslint: 9.8.0 @@ -10551,22 +11258,22 @@ snapshots: '@fastify/error@2.0.0': {} - '@floating-ui/core@1.6.5': + '@floating-ui/core@1.6.7': dependencies: - '@floating-ui/utils': 0.2.5 + '@floating-ui/utils': 0.2.7 - '@floating-ui/dom@1.6.8': + '@floating-ui/dom@1.6.10': dependencies: - '@floating-ui/core': 1.6.5 - '@floating-ui/utils': 0.2.5 + '@floating-ui/core': 1.6.7 + '@floating-ui/utils': 0.2.7 - '@floating-ui/utils@0.2.5': {} + '@floating-ui/utils@0.2.7': {} - '@floating-ui/vue@1.1.2(vue@3.4.35(typescript@5.4.5))': + '@floating-ui/vue@1.1.4(vue@3.4.38(typescript@5.4.5))': dependencies: - '@floating-ui/dom': 1.6.8 - '@floating-ui/utils': 0.2.5 - vue-demi: 0.14.10(vue@3.4.35(typescript@5.4.5)) + '@floating-ui/dom': 1.6.10 + '@floating-ui/utils': 0.2.7 + vue-demi: 0.14.10(vue@3.4.38(typescript@5.4.5)) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -10577,24 +11284,24 @@ snapshots: dependencies: '@hapi/hoek': 9.3.0 - '@headlessui-float/vue@0.14.0(@headlessui/vue@1.7.22(vue@3.4.35(typescript@5.4.5)))(vue@3.4.35(typescript@5.4.5))': + '@headlessui-float/vue@0.14.3(@headlessui/vue@1.7.22(vue@3.4.38(typescript@5.4.5)))(vue@3.4.38(typescript@5.4.5))': dependencies: - '@floating-ui/core': 1.6.5 - '@floating-ui/dom': 1.6.8 - '@floating-ui/vue': 1.1.2(vue@3.4.35(typescript@5.4.5)) - '@headlessui/vue': 1.7.22(vue@3.4.35(typescript@5.4.5)) - vue: 3.4.35(typescript@5.4.5) + '@floating-ui/core': 1.6.7 + '@floating-ui/dom': 1.6.10 + '@floating-ui/vue': 1.1.4(vue@3.4.38(typescript@5.4.5)) + '@headlessui/vue': 1.7.22(vue@3.4.38(typescript@5.4.5)) + vue: 3.4.38(typescript@5.4.5) transitivePeerDependencies: - '@vue/composition-api' - '@headlessui/tailwindcss@0.2.1(tailwindcss@3.4.7)': + '@headlessui/tailwindcss@0.2.1(tailwindcss@3.4.10)': dependencies: - tailwindcss: 3.4.7 + tailwindcss: 3.4.10 - '@headlessui/vue@1.7.22(vue@3.4.35(typescript@5.4.5))': + '@headlessui/vue@1.7.22(vue@3.4.38(typescript@5.4.5))': dependencies: - '@tanstack/vue-virtual': 3.8.5(vue@3.4.35(typescript@5.4.5)) - vue: 3.4.35(typescript@5.4.5) + '@tanstack/vue-virtual': 3.10.4(vue@3.4.38(typescript@5.4.5)) + vue: 3.4.38(typescript@5.4.5) '@humanwhocodes/module-importer@1.0.1': {} @@ -10618,79 +11325,91 @@ snapshots: transitivePeerDependencies: - supports-color - '@img/sharp-darwin-arm64@0.33.4': + '@iconify/utils@2.1.32': + dependencies: + '@antfu/install-pkg': 0.4.1 + '@antfu/utils': 0.7.10 + '@iconify/types': 2.0.0 + debug: 4.3.6(supports-color@8.1.1) + kolorist: 1.8.0 + local-pkg: 0.5.0 + mlly: 1.7.1 + transitivePeerDependencies: + - supports-color + + '@img/sharp-darwin-arm64@0.33.5': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.0.2 + '@img/sharp-libvips-darwin-arm64': 1.0.4 optional: true - '@img/sharp-darwin-x64@0.33.4': + '@img/sharp-darwin-x64@0.33.5': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.0.2 + '@img/sharp-libvips-darwin-x64': 1.0.4 optional: true - '@img/sharp-libvips-darwin-arm64@1.0.2': + '@img/sharp-libvips-darwin-arm64@1.0.4': optional: true - '@img/sharp-libvips-darwin-x64@1.0.2': + '@img/sharp-libvips-darwin-x64@1.0.4': optional: true - '@img/sharp-libvips-linux-arm64@1.0.2': + '@img/sharp-libvips-linux-arm64@1.0.4': optional: true - '@img/sharp-libvips-linux-arm@1.0.2': + '@img/sharp-libvips-linux-arm@1.0.5': optional: true - '@img/sharp-libvips-linux-s390x@1.0.2': + '@img/sharp-libvips-linux-s390x@1.0.4': optional: true - '@img/sharp-libvips-linux-x64@1.0.2': + '@img/sharp-libvips-linux-x64@1.0.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.0.2': + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.0.2': + '@img/sharp-libvips-linuxmusl-x64@1.0.4': optional: true - '@img/sharp-linux-arm64@0.33.4': + '@img/sharp-linux-arm64@0.33.5': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.0.2 + '@img/sharp-libvips-linux-arm64': 1.0.4 optional: true - '@img/sharp-linux-arm@0.33.4': + '@img/sharp-linux-arm@0.33.5': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.0.2 + '@img/sharp-libvips-linux-arm': 1.0.5 optional: true - '@img/sharp-linux-s390x@0.33.4': + '@img/sharp-linux-s390x@0.33.5': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.0.2 + '@img/sharp-libvips-linux-s390x': 1.0.4 optional: true - '@img/sharp-linux-x64@0.33.4': + '@img/sharp-linux-x64@0.33.5': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.0.2 + '@img/sharp-libvips-linux-x64': 1.0.4 optional: true - '@img/sharp-linuxmusl-arm64@0.33.4': + '@img/sharp-linuxmusl-arm64@0.33.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.0.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 optional: true - '@img/sharp-linuxmusl-x64@0.33.4': + '@img/sharp-linuxmusl-x64@0.33.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.0.2 + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 optional: true - '@img/sharp-wasm32@0.33.4': + '@img/sharp-wasm32@0.33.5': dependencies: '@emnapi/runtime': 1.2.0 optional: true - '@img/sharp-win32-ia32@0.33.4': + '@img/sharp-win32-ia32@0.33.5': optional: true - '@img/sharp-win32-x64@0.33.4': + '@img/sharp-win32-x64@0.33.5': optional: true '@isaacs/cliui@8.0.2': @@ -10715,7 +11434,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 20.14.14 + '@types/node': 20.16.2 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -10728,14 +11447,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.14 + '@types/node': 20.16.2 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.14.14) + jest-config: 29.7.0(@types/node@20.16.2) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -10747,7 +11466,7 @@ snapshots: jest-util: 29.7.0 jest-validate: 29.7.0 jest-watcher: 29.7.0 - micromatch: 4.0.7 + micromatch: 4.0.8 pretty-format: 29.7.0 slash: 3.0.0 strip-ansi: 6.0.1 @@ -10760,7 +11479,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.14 + '@types/node': 20.16.2 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -10778,7 +11497,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.14.14 + '@types/node': 20.16.2 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -10800,7 +11519,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 20.14.14 + '@types/node': 20.16.2 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -10858,7 +11577,7 @@ snapshots: jest-haste-map: 29.7.0 jest-regex-util: 29.6.3 jest-util: 29.7.0 - micromatch: 4.0.7 + micromatch: 4.0.8 pirates: 4.0.6 slash: 3.0.0 write-file-atomic: 4.0.2 @@ -10870,7 +11589,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.14.14 + '@types/node': 20.16.2 '@types/yargs': 17.0.32 chalk: 4.1.2 @@ -10978,18 +11697,18 @@ snapshots: dependencies: serialize-javascript: 6.0.2 smob: 1.5.0 - terser: 5.31.3 + terser: 5.31.6 optionalDependencies: rollup: 2.79.1 - '@rollup/plugin-typescript@11.1.6(rollup@4.20.0)(tslib@2.6.3)(typescript@5.4.5)': + '@rollup/plugin-typescript@11.1.6(rollup@4.21.1)(tslib@2.7.0)(typescript@5.4.5)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.20.0) + '@rollup/pluginutils': 5.1.0(rollup@4.21.1) resolve: 1.22.8 typescript: 5.4.5 optionalDependencies: - rollup: 4.20.0 - tslib: 2.6.3 + rollup: 4.21.1 + tslib: 2.7.0 '@rollup/pluginutils@3.1.0(rollup@2.79.1)': dependencies: @@ -11006,69 +11725,69 @@ snapshots: optionalDependencies: rollup: 2.79.1 - '@rollup/pluginutils@5.1.0(rollup@4.20.0)': + '@rollup/pluginutils@5.1.0(rollup@4.21.1)': dependencies: '@types/estree': 1.0.5 estree-walker: 2.0.2 picomatch: 2.3.1 optionalDependencies: - rollup: 4.20.0 + rollup: 4.21.1 - '@rollup/rollup-android-arm-eabi@4.20.0': + '@rollup/rollup-android-arm-eabi@4.21.1': optional: true - '@rollup/rollup-android-arm64@4.20.0': + '@rollup/rollup-android-arm64@4.21.1': optional: true - '@rollup/rollup-darwin-arm64@4.20.0': + '@rollup/rollup-darwin-arm64@4.21.1': optional: true - '@rollup/rollup-darwin-x64@4.20.0': + '@rollup/rollup-darwin-x64@4.21.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.20.0': + '@rollup/rollup-linux-arm-gnueabihf@4.21.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.20.0': + '@rollup/rollup-linux-arm-musleabihf@4.21.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.20.0': + '@rollup/rollup-linux-arm64-gnu@4.21.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.20.0': + '@rollup/rollup-linux-arm64-musl@4.21.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.20.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.21.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.20.0': + '@rollup/rollup-linux-riscv64-gnu@4.21.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.20.0': + '@rollup/rollup-linux-s390x-gnu@4.21.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.20.0': + '@rollup/rollup-linux-x64-gnu@4.21.1': optional: true - '@rollup/rollup-linux-x64-musl@4.20.0': + '@rollup/rollup-linux-x64-musl@4.21.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.20.0': + '@rollup/rollup-win32-arm64-msvc@4.21.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.20.0': + '@rollup/rollup-win32-ia32-msvc@4.21.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.20.0': + '@rollup/rollup-win32-x64-msvc@4.21.1': optional: true - '@shikijs/core@1.12.1': + '@shikijs/core@1.14.1': dependencies: '@types/hast': 3.0.4 - '@shikijs/transformers@1.12.1': + '@shikijs/transformers@1.14.1': dependencies: - shiki: 1.12.1 + shiki: 1.14.1 '@sideway/address@4.1.5': dependencies: @@ -11103,12 +11822,12 @@ snapshots: dependencies: defer-to-connect: 2.0.1 - '@tanstack/virtual-core@3.8.4': {} + '@tanstack/virtual-core@3.10.4': {} - '@tanstack/vue-virtual@3.8.5(vue@3.4.35(typescript@5.4.5))': + '@tanstack/vue-virtual@3.10.4(vue@3.4.38(typescript@5.4.5))': dependencies: - '@tanstack/virtual-core': 3.8.4 - vue: 3.4.35(typescript@5.4.5) + '@tanstack/virtual-core': 3.10.4 + vue: 3.4.38(typescript@5.4.5) '@tootallnate/once@2.0.0': {} @@ -11116,33 +11835,33 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.25.3 - '@babel/types': 7.25.2 + '@babel/parser': 7.25.4 + '@babel/types': 7.25.4 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.6 '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.25.3 - '@babel/types': 7.25.2 + '@babel/parser': 7.25.4 + '@babel/types': 7.25.4 '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 20.14.14 + '@types/node': 20.16.2 '@types/bonjour@3.5.13': dependencies: - '@types/node': 20.14.14 + '@types/node': 22.5.1 '@types/braces@3.0.4': {} @@ -11150,21 +11869,25 @@ snapshots: dependencies: '@types/http-cache-semantics': 4.0.4 '@types/keyv': 3.1.4 - '@types/node': 20.14.14 + '@types/node': 20.16.2 '@types/responselike': 1.0.3 '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.5 - '@types/node': 20.14.14 + '@types/node': 22.5.1 '@types/connect@3.4.38': dependencies: - '@types/node': 20.14.14 + '@types/node': 20.16.2 '@types/cors@2.8.17': dependencies: - '@types/node': 20.14.14 + '@types/node': 20.16.2 + + '@types/cytoscape-fcose@2.2.4': + dependencies: + '@types/cytoscape': 3.21.5 '@types/cytoscape@3.21.5': {} @@ -11319,7 +12042,7 @@ snapshots: '@types/express-serve-static-core@4.19.5': dependencies: - '@types/node': 20.14.14 + '@types/node': 20.16.2 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -11338,20 +12061,20 @@ snapshots: '@types/glob@7.2.0': dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.14.14 + '@types/node': 22.5.1 '@types/glob@8.1.0': dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.14.14 + '@types/node': 20.16.2 '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 20.14.14 + '@types/node': 20.16.2 '@types/hast@3.0.4': dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 '@types/http-cache-semantics@4.0.4': {} @@ -11359,7 +12082,7 @@ snapshots: '@types/http-proxy@1.17.14': dependencies: - '@types/node': 20.14.14 + '@types/node': 22.5.1 '@types/istanbul-lib-coverage@2.0.6': {} @@ -11385,7 +12108,7 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 20.14.14 + '@types/node': 20.16.2 '@types/linkify-it@5.0.0': {} @@ -11427,11 +12150,11 @@ snapshots: '@types/node-forge@1.3.11': dependencies: - '@types/node': 20.14.14 + '@types/node': 22.5.1 '@types/node@12.20.55': {} - '@types/node@18.19.43': + '@types/node@18.19.47': dependencies: undici-types: 5.26.5 @@ -11439,6 +12162,14 @@ snapshots: dependencies: undici-types: 5.26.5 + '@types/node@20.16.2': + dependencies: + undici-types: 6.19.8 + + '@types/node@22.5.1': + dependencies: + undici-types: 6.19.8 + '@types/normalize-package-data@2.4.4': {} '@types/prettier@2.7.3': {} @@ -11459,7 +12190,7 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 20.14.14 + '@types/node': 20.16.2 '@types/retry@0.12.0': {} @@ -11472,7 +12203,7 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 20.14.14 + '@types/node': 22.5.1 '@types/serve-index@1.9.4': dependencies: @@ -11481,7 +12212,7 @@ snapshots: '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 20.14.14 + '@types/node': 22.5.1 '@types/send': 0.17.4 '@types/sinonjs__fake-timers@8.1.1': {} @@ -11490,7 +12221,7 @@ snapshots: '@types/sockjs@0.3.36': dependencies: - '@types/node': 20.14.14 + '@types/node': 22.5.1 '@types/stack-utils@2.0.3': {} @@ -11504,17 +12235,19 @@ snapshots: '@types/unist@3.0.2': {} + '@types/unist@3.0.3': {} + '@types/uuid@9.0.8': {} '@types/web-bluetooth@0.0.20': {} '@types/ws@8.5.12': dependencies: - '@types/node': 20.14.14 + '@types/node': 22.5.1 '@types/ws@8.5.5': dependencies: - '@types/node': 20.14.14 + '@types/node': 20.16.2 '@types/yargs-parser@21.0.3': {} @@ -11524,20 +12257,20 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 20.14.14 + '@types/node': 20.16.2 optional: true - '@typescript-eslint/eslint-plugin@8.0.0(@typescript-eslint/parser@8.0.0(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(typescript@5.4.5)': + '@typescript-eslint/eslint-plugin@8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 8.0.0(eslint@9.8.0)(typescript@5.4.5) - '@typescript-eslint/scope-manager': 8.0.0 - '@typescript-eslint/type-utils': 8.0.0(eslint@9.8.0)(typescript@5.4.5) - '@typescript-eslint/utils': 8.0.0(eslint@9.8.0)(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 8.0.0 + '@typescript-eslint/parser': 8.0.1(eslint@9.8.0)(typescript@5.4.5) + '@typescript-eslint/scope-manager': 8.0.1 + '@typescript-eslint/type-utils': 8.0.1(eslint@9.8.0)(typescript@5.4.5) + '@typescript-eslint/utils': 8.0.1(eslint@9.8.0)(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 8.0.1 eslint: 9.8.0 graphemer: 1.4.0 - ignore: 5.3.1 + ignore: 5.3.2 natural-compare: 1.4.0 ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: @@ -11545,12 +12278,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.0.0(eslint@9.8.0)(typescript@5.4.5)': + '@typescript-eslint/parser@8.0.1(eslint@9.8.0)(typescript@5.4.5)': dependencies: - '@typescript-eslint/scope-manager': 8.0.0 - '@typescript-eslint/types': 8.0.0 - '@typescript-eslint/typescript-estree': 8.0.0(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 8.0.0 + '@typescript-eslint/scope-manager': 8.0.1 + '@typescript-eslint/types': 8.0.1 + '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 8.0.1 debug: 4.3.6(supports-color@8.1.1) eslint: 9.8.0 optionalDependencies: @@ -11563,10 +12296,15 @@ snapshots: '@typescript-eslint/types': 8.0.0 '@typescript-eslint/visitor-keys': 8.0.0 - '@typescript-eslint/type-utils@8.0.0(eslint@9.8.0)(typescript@5.4.5)': + '@typescript-eslint/scope-manager@8.0.1': dependencies: - '@typescript-eslint/typescript-estree': 8.0.0(typescript@5.4.5) - '@typescript-eslint/utils': 8.0.0(eslint@9.8.0)(typescript@5.4.5) + '@typescript-eslint/types': 8.0.1 + '@typescript-eslint/visitor-keys': 8.0.1 + + '@typescript-eslint/type-utils@8.0.1(eslint@9.8.0)(typescript@5.4.5)': + dependencies: + '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.4.5) + '@typescript-eslint/utils': 8.0.1(eslint@9.8.0)(typescript@5.4.5) debug: 4.3.6(supports-color@8.1.1) ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: @@ -11577,6 +12315,8 @@ snapshots: '@typescript-eslint/types@8.0.0': {} + '@typescript-eslint/types@8.0.1': {} + '@typescript-eslint/typescript-estree@8.0.0(typescript@5.4.5)': dependencies: '@typescript-eslint/types': 8.0.0 @@ -11592,6 +12332,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.0.1(typescript@5.4.5)': + dependencies: + '@typescript-eslint/types': 8.0.1 + '@typescript-eslint/visitor-keys': 8.0.1 + debug: 4.3.6(supports-color@8.1.1) + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.0.0(eslint@9.8.0)(typescript@5.4.5)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0) @@ -11603,18 +12358,44 @@ snapshots: - supports-color - typescript + '@typescript-eslint/utils@8.0.1(eslint@9.8.0)(typescript@5.4.5)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0) + '@typescript-eslint/scope-manager': 8.0.1 + '@typescript-eslint/types': 8.0.1 + '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.4.5) + eslint: 9.8.0 + transitivePeerDependencies: + - supports-color + - typescript + '@typescript-eslint/visitor-keys@8.0.0': dependencies: '@typescript-eslint/types': 8.0.0 eslint-visitor-keys: 3.4.3 - '@unocss/astro@0.59.4(rollup@2.79.1)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))': + '@typescript-eslint/visitor-keys@8.0.1': + dependencies: + '@typescript-eslint/types': 8.0.1 + eslint-visitor-keys: 3.4.3 + + '@unocss/astro@0.59.4(rollup@2.79.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))': dependencies: '@unocss/core': 0.59.4 '@unocss/reset': 0.59.4 - '@unocss/vite': 0.59.4(rollup@2.79.1)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3)) + '@unocss/vite': 0.59.4(rollup@2.79.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6)) optionalDependencies: - vite: 5.3.5(@types/node@20.14.14)(terser@5.31.3) + vite: 5.4.2(@types/node@22.5.1)(terser@5.31.6) + transitivePeerDependencies: + - rollup + + '@unocss/astro@0.59.4(rollup@4.21.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))': + dependencies: + '@unocss/core': 0.59.4 + '@unocss/reset': 0.59.4 + '@unocss/vite': 0.59.4(rollup@4.21.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6)) + optionalDependencies: + vite: 5.4.2(@types/node@22.5.1)(terser@5.31.6) transitivePeerDependencies: - rollup @@ -11636,6 +12417,24 @@ snapshots: transitivePeerDependencies: - rollup + '@unocss/cli@0.59.4(rollup@4.21.1)': + dependencies: + '@ampproject/remapping': 2.3.0 + '@rollup/pluginutils': 5.1.0(rollup@4.21.1) + '@unocss/config': 0.59.4 + '@unocss/core': 0.59.4 + '@unocss/preset-uno': 0.59.4 + cac: 6.7.14 + chokidar: 3.6.0 + colorette: 2.0.20 + consola: 3.2.3 + fast-glob: 3.3.2 + magic-string: 0.30.11 + pathe: 1.1.2 + perfect-debounce: 1.0.0 + transitivePeerDependencies: + - rollup + '@unocss/config@0.59.4': dependencies: '@unocss/core': 0.59.4 @@ -11654,7 +12453,7 @@ snapshots: gzip-size: 6.0.0 sirv: 2.0.4 - '@unocss/postcss@0.59.4(postcss@8.4.40)': + '@unocss/postcss@0.59.4(postcss@8.4.41)': dependencies: '@unocss/config': 0.59.4 '@unocss/core': 0.59.4 @@ -11662,7 +12461,7 @@ snapshots: css-tree: 2.3.1 fast-glob: 3.3.2 magic-string: 0.30.11 - postcss: 8.4.40 + postcss: 8.4.41 '@unocss/preset-attributify@0.59.4': dependencies: @@ -11745,7 +12544,7 @@ snapshots: dependencies: '@unocss/core': 0.59.4 - '@unocss/vite@0.59.4(rollup@2.79.1)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))': + '@unocss/vite@0.59.4(rollup@2.79.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))': dependencies: '@ampproject/remapping': 2.3.0 '@rollup/pluginutils': 5.1.0(rollup@2.79.1) @@ -11757,20 +12556,36 @@ snapshots: chokidar: 3.6.0 fast-glob: 3.3.2 magic-string: 0.30.11 - vite: 5.3.5(@types/node@20.14.14)(terser@5.31.3) + vite: 5.4.2(@types/node@22.5.1)(terser@5.31.6) transitivePeerDependencies: - rollup - '@vite-pwa/vitepress@0.4.0(vite-plugin-pwa@0.19.8(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0))': + '@unocss/vite@0.59.4(rollup@4.21.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))': dependencies: - vite-plugin-pwa: 0.19.8(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0) + '@ampproject/remapping': 2.3.0 + '@rollup/pluginutils': 5.1.0(rollup@4.21.1) + '@unocss/config': 0.59.4 + '@unocss/core': 0.59.4 + '@unocss/inspector': 0.59.4 + '@unocss/scope': 0.59.4 + '@unocss/transformer-directives': 0.59.4 + chokidar: 3.6.0 + fast-glob: 3.3.2 + magic-string: 0.30.11 + vite: 5.4.2(@types/node@22.5.1)(terser@5.31.6) + transitivePeerDependencies: + - rollup - '@vitejs/plugin-vue@5.1.2(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(vue@3.4.35(typescript@5.4.5))': + '@vite-pwa/vitepress@0.4.0(vite-plugin-pwa@0.19.8(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0))': dependencies: - vite: 5.3.5(@types/node@20.14.14)(terser@5.31.3) - vue: 3.4.35(typescript@5.4.5) + vite-plugin-pwa: 0.19.8(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0) - '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.14.14)(@vitest/ui@1.6.0)(jsdom@24.1.1)(terser@5.31.3))': + '@vitejs/plugin-vue@5.1.2(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))(vue@3.4.38(typescript@5.4.5))': + dependencies: + vite: 5.4.2(@types/node@22.5.1)(terser@5.31.6) + vue: 3.4.38(typescript@5.4.5) + + '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.16.2)(@vitest/ui@1.6.0)(jsdom@24.1.3)(terser@5.31.6))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -11785,7 +12600,7 @@ snapshots: std-env: 3.7.0 strip-literal: 2.1.0 test-exclude: 6.0.0 - vitest: 1.6.0(@types/node@20.14.14)(@vitest/ui@1.6.0)(jsdom@24.1.1)(terser@5.31.3) + vitest: 1.6.0(@types/node@20.16.2)(@vitest/ui@1.6.0)(jsdom@24.1.3)(terser@5.31.6) transitivePeerDependencies: - supports-color @@ -11820,7 +12635,7 @@ snapshots: pathe: 1.1.2 picocolors: 1.0.1 sirv: 2.0.4 - vitest: 1.6.0(@types/node@20.14.14)(@vitest/ui@1.6.0)(jsdom@24.1.1)(terser@5.31.3) + vitest: 1.6.0(@types/node@20.16.2)(@vitest/ui@1.6.0)(jsdom@24.1.3)(terser@5.31.6) '@vitest/utils@1.6.0': dependencies: @@ -11829,52 +12644,52 @@ snapshots: loupe: 2.3.7 pretty-format: 29.7.0 - '@vue/compat@3.4.35(vue@3.4.35(typescript@5.4.5))': + '@vue/compat@3.4.38(vue@3.4.38(typescript@5.4.5))': dependencies: - '@babel/parser': 7.25.3 + '@babel/parser': 7.25.4 estree-walker: 2.0.2 source-map-js: 1.2.0 - vue: 3.4.35(typescript@5.4.5) + vue: 3.4.38(typescript@5.4.5) - '@vue/compiler-core@3.4.35': + '@vue/compiler-core@3.4.38': dependencies: - '@babel/parser': 7.25.3 - '@vue/shared': 3.4.35 + '@babel/parser': 7.25.4 + '@vue/shared': 3.4.38 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.0 - '@vue/compiler-dom@3.4.35': + '@vue/compiler-dom@3.4.38': dependencies: - '@vue/compiler-core': 3.4.35 - '@vue/shared': 3.4.35 + '@vue/compiler-core': 3.4.38 + '@vue/shared': 3.4.38 - '@vue/compiler-sfc@3.4.35': + '@vue/compiler-sfc@3.4.38': dependencies: - '@babel/parser': 7.25.3 - '@vue/compiler-core': 3.4.35 - '@vue/compiler-dom': 3.4.35 - '@vue/compiler-ssr': 3.4.35 - '@vue/shared': 3.4.35 + '@babel/parser': 7.25.4 + '@vue/compiler-core': 3.4.38 + '@vue/compiler-dom': 3.4.38 + '@vue/compiler-ssr': 3.4.38 + '@vue/shared': 3.4.38 estree-walker: 2.0.2 magic-string: 0.30.11 - postcss: 8.4.40 + postcss: 8.4.41 source-map-js: 1.2.0 - '@vue/compiler-ssr@3.4.35': + '@vue/compiler-ssr@3.4.38': dependencies: - '@vue/compiler-dom': 3.4.35 - '@vue/shared': 3.4.35 + '@vue/compiler-dom': 3.4.38 + '@vue/shared': 3.4.38 '@vue/devtools-api@6.6.3': {} - '@vue/devtools-api@7.3.7': + '@vue/devtools-api@7.3.9': dependencies: - '@vue/devtools-kit': 7.3.7 + '@vue/devtools-kit': 7.3.9 - '@vue/devtools-kit@7.3.7': + '@vue/devtools-kit@7.3.9': dependencies: - '@vue/devtools-shared': 7.3.7 + '@vue/devtools-shared': 7.3.9 birpc: 0.2.17 hookable: 5.5.3 mitt: 3.0.1 @@ -11882,61 +12697,92 @@ snapshots: speakingurl: 14.0.1 superjson: 2.2.1 - '@vue/devtools-shared@7.3.7': + '@vue/devtools-shared@7.3.9': dependencies: rfdc: 1.4.1 - '@vue/reactivity@3.4.35': + '@vue/reactivity@3.4.38': dependencies: - '@vue/shared': 3.4.35 + '@vue/shared': 3.4.38 - '@vue/runtime-core@3.4.35': + '@vue/runtime-core@3.4.38': dependencies: - '@vue/reactivity': 3.4.35 - '@vue/shared': 3.4.35 + '@vue/reactivity': 3.4.38 + '@vue/shared': 3.4.38 - '@vue/runtime-dom@3.4.35': + '@vue/runtime-dom@3.4.38': dependencies: - '@vue/reactivity': 3.4.35 - '@vue/runtime-core': 3.4.35 - '@vue/shared': 3.4.35 + '@vue/reactivity': 3.4.38 + '@vue/runtime-core': 3.4.38 + '@vue/shared': 3.4.38 csstype: 3.1.3 - '@vue/server-renderer@3.4.35(vue@3.4.35(typescript@5.4.5))': + '@vue/server-renderer@3.4.38(vue@3.4.38(typescript@5.4.5))': dependencies: - '@vue/compiler-ssr': 3.4.35 - '@vue/shared': 3.4.35 - vue: 3.4.35(typescript@5.4.5) + '@vue/compiler-ssr': 3.4.38 + '@vue/shared': 3.4.38 + vue: 3.4.38(typescript@5.4.5) - '@vue/shared@3.4.35': {} + '@vue/shared@3.4.38': {} - '@vueuse/core@10.11.0(vue@3.4.35(typescript@5.4.5))': + '@vueuse/core@10.11.1(vue@3.4.38(typescript@5.4.5))': dependencies: '@types/web-bluetooth': 0.0.20 - '@vueuse/metadata': 10.11.0 - '@vueuse/shared': 10.11.0(vue@3.4.35(typescript@5.4.5)) - vue-demi: 0.14.10(vue@3.4.35(typescript@5.4.5)) + '@vueuse/metadata': 10.11.1 + '@vueuse/shared': 10.11.1(vue@3.4.38(typescript@5.4.5)) + vue-demi: 0.14.10(vue@3.4.38(typescript@5.4.5)) transitivePeerDependencies: - '@vue/composition-api' - vue - '@vueuse/integrations@10.11.0(axios@1.7.3)(focus-trap@7.5.4)(vue@3.4.35(typescript@5.4.5))': + '@vueuse/core@11.0.3(vue@3.4.38(typescript@5.4.5))': dependencies: - '@vueuse/core': 10.11.0(vue@3.4.35(typescript@5.4.5)) - '@vueuse/shared': 10.11.0(vue@3.4.35(typescript@5.4.5)) - vue-demi: 0.14.10(vue@3.4.35(typescript@5.4.5)) + '@types/web-bluetooth': 0.0.20 + '@vueuse/metadata': 11.0.3 + '@vueuse/shared': 11.0.3(vue@3.4.38(typescript@5.4.5)) + vue-demi: 0.14.10(vue@3.4.38(typescript@5.4.5)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/integrations@10.11.1(axios@1.7.5)(focus-trap@7.5.4)(vue@3.4.38(typescript@5.4.5))': + dependencies: + '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.4.5)) + '@vueuse/shared': 10.11.1(vue@3.4.38(typescript@5.4.5)) + vue-demi: 0.14.10(vue@3.4.38(typescript@5.4.5)) optionalDependencies: - axios: 1.7.3(debug@4.3.6) + axios: 1.7.5(debug@4.3.6) focus-trap: 7.5.4 transitivePeerDependencies: - '@vue/composition-api' - vue - '@vueuse/metadata@10.11.0': {} - - '@vueuse/shared@10.11.0(vue@3.4.35(typescript@5.4.5))': + '@vueuse/integrations@11.0.3(axios@1.7.5)(focus-trap@7.5.4)(vue@3.4.38(typescript@5.4.5))': dependencies: - vue-demi: 0.14.10(vue@3.4.35(typescript@5.4.5)) + '@vueuse/core': 11.0.3(vue@3.4.38(typescript@5.4.5)) + '@vueuse/shared': 11.0.3(vue@3.4.38(typescript@5.4.5)) + vue-demi: 0.14.10(vue@3.4.38(typescript@5.4.5)) + optionalDependencies: + axios: 1.7.5(debug@4.3.6) + focus-trap: 7.5.4 + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/metadata@10.11.1': {} + + '@vueuse/metadata@11.0.3': {} + + '@vueuse/shared@10.11.1(vue@3.4.38(typescript@5.4.5))': + dependencies: + vue-demi: 0.14.10(vue@3.4.38(typescript@5.4.5)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/shared@11.0.3(vue@3.4.38(typescript@5.4.5))': + dependencies: + vue-demi: 0.14.10(vue@3.4.38(typescript@5.4.5)) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -11963,7 +12809,7 @@ snapshots: '@wdio/types@7.30.2(typescript@5.4.5)': dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.47 got: 11.8.6 optionalDependencies: typescript: 5.4.5 @@ -12074,14 +12920,14 @@ snapshots: '@xtuc/long@4.2.2': {} - '@zenuml/core@3.24.2(typescript@5.4.5)': + '@zenuml/core@3.24.3(typescript@5.4.5)': dependencies: - '@headlessui-float/vue': 0.14.0(@headlessui/vue@1.7.22(vue@3.4.35(typescript@5.4.5)))(vue@3.4.35(typescript@5.4.5)) - '@headlessui/tailwindcss': 0.2.1(tailwindcss@3.4.7) - '@headlessui/vue': 1.7.22(vue@3.4.35(typescript@5.4.5)) + '@headlessui-float/vue': 0.14.3(@headlessui/vue@1.7.22(vue@3.4.38(typescript@5.4.5)))(vue@3.4.38(typescript@5.4.5)) + '@headlessui/tailwindcss': 0.2.1(tailwindcss@3.4.10) + '@headlessui/vue': 1.7.22(vue@3.4.38(typescript@5.4.5)) '@types/assert': 1.5.10 '@types/ramda': 0.28.25 - '@vue/compat': 3.4.35(vue@3.4.35(typescript@5.4.5)) + '@vue/compat': 3.4.38(vue@3.4.38(typescript@5.4.5)) antlr4: 4.11.0 color-string: 1.9.1 dom-to-image-more: 2.16.0 @@ -12092,11 +12938,11 @@ snapshots: lodash: 4.17.21 marked: 4.3.0 pino: 8.21.0 - postcss: 8.4.40 + postcss: 8.4.41 ramda: 0.28.0 - tailwindcss: 3.4.7 - vue: 3.4.35(typescript@5.4.5) - vuex: 4.1.0(vue@3.4.35(typescript@5.4.5)) + tailwindcss: 3.4.10 + vue: 3.4.38(typescript@5.4.5) + vuex: 4.1.0(vue@3.4.38(typescript@5.4.5)) transitivePeerDependencies: - '@vue/composition-api' - ts-node @@ -12313,7 +13159,7 @@ snapshots: astral-regex@2.0.0: {} - async@3.2.5: {} + async@3.2.6: {} asynckit@0.4.0: {} @@ -12336,7 +13182,7 @@ snapshots: aws-sign2@0.7.0: {} - aws4@1.13.0: {} + aws4@1.13.1: {} axios@1.7.3(debug@4.3.6): dependencies: @@ -12346,6 +13192,14 @@ snapshots: transitivePeerDependencies: - debug + axios@1.7.5(debug@4.3.6): + dependencies: + follow-redirects: 1.15.6(debug@4.3.6) + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + babel-jest@29.7.0(@babel/core@7.25.2): dependencies: '@babel/core': 7.25.2 @@ -12379,13 +13233,13 @@ snapshots: babel-plugin-jest-hoist@29.6.3: dependencies: '@babel/template': 7.25.0 - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.6 babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.25.2): dependencies: - '@babel/compat-data': 7.25.2 + '@babel/compat-data': 7.25.4 '@babel/core': 7.25.2 '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.2) semver: 6.3.1 @@ -12396,7 +13250,7 @@ snapshots: dependencies: '@babel/core': 7.25.2 '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.2) - core-js-compat: 3.38.0 + core-js-compat: 3.38.1 transitivePeerDependencies: - supports-color @@ -12815,7 +13669,7 @@ snapshots: commander@8.3.0: {} - comment-json@4.2.4: + comment-json@4.2.5: dependencies: array-timsort: 1.0.3 core-util-is: 1.0.3 @@ -12896,6 +13750,10 @@ snapshots: dependencies: browserslist: 4.23.3 + core-js-compat@3.38.1: + dependencies: + browserslist: 4.23.3 + core-util-is@1.0.2: {} core-util-is@1.0.3: {} @@ -12909,6 +13767,10 @@ snapshots: dependencies: layout-base: 1.0.2 + cose-base@2.2.0: + dependencies: + layout-base: 2.0.1 + cp-file@10.0.0: dependencies: graceful-fs: 4.2.11 @@ -12926,18 +13788,18 @@ snapshots: cp-file: 10.0.0 globby: 13.2.2 junk: 4.0.1 - micromatch: 4.0.7 + micromatch: 4.0.8 nested-error-stacks: 2.1.1 p-filter: 3.0.0 p-map: 6.0.0 - create-jest@29.7.0(@types/node@20.14.14): + create-jest@29.7.0(@types/node@20.16.2): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.14.14) + jest-config: 29.7.0(@types/node@20.16.2) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -12972,58 +13834,86 @@ snapshots: crypto-random-string@2.0.0: {} - cspell-config-lib@8.13.1: + cspell-config-lib@8.13.3: dependencies: - '@cspell/cspell-types': 8.13.1 - comment-json: 4.2.4 + '@cspell/cspell-types': 8.13.3 + comment-json: 4.2.5 yaml: 2.5.0 - cspell-dictionary@8.13.1: + cspell-config-lib@8.14.2: dependencies: - '@cspell/cspell-pipe': 8.13.1 - '@cspell/cspell-types': 8.13.1 - cspell-trie-lib: 8.13.1 + '@cspell/cspell-types': 8.14.2 + comment-json: 4.2.5 + yaml: 2.5.0 + + cspell-dictionary@8.13.3: + dependencies: + '@cspell/cspell-pipe': 8.13.3 + '@cspell/cspell-types': 8.13.3 + cspell-trie-lib: 8.13.3 fast-equals: 5.0.1 - cspell-gitignore@8.13.1: + cspell-dictionary@8.14.2: dependencies: - '@cspell/url': 8.13.1 - cspell-glob: 8.13.1 - cspell-io: 8.13.1 + '@cspell/cspell-pipe': 8.14.2 + '@cspell/cspell-types': 8.14.2 + cspell-trie-lib: 8.14.2 + fast-equals: 5.0.1 + + cspell-gitignore@8.14.2: + dependencies: + '@cspell/url': 8.14.2 + cspell-glob: 8.14.2 + cspell-io: 8.14.2 find-up-simple: 1.0.0 - cspell-glob@8.13.1: + cspell-glob@8.13.3: dependencies: - '@cspell/url': 8.13.1 - micromatch: 4.0.7 + '@cspell/url': 8.13.3 + micromatch: 4.0.8 - cspell-grammar@8.13.1: + cspell-glob@8.14.2: dependencies: - '@cspell/cspell-pipe': 8.13.1 - '@cspell/cspell-types': 8.13.1 + '@cspell/url': 8.14.2 + micromatch: 4.0.8 - cspell-io@8.13.1: + cspell-grammar@8.13.3: dependencies: - '@cspell/cspell-service-bus': 8.13.1 - '@cspell/url': 8.13.1 + '@cspell/cspell-pipe': 8.13.3 + '@cspell/cspell-types': 8.13.3 - cspell-lib@8.13.1: + cspell-grammar@8.14.2: dependencies: - '@cspell/cspell-bundled-dicts': 8.13.1 - '@cspell/cspell-pipe': 8.13.1 - '@cspell/cspell-resolver': 8.13.1 - '@cspell/cspell-types': 8.13.1 - '@cspell/dynamic-import': 8.13.1 - '@cspell/strong-weak-map': 8.13.1 - '@cspell/url': 8.13.1 + '@cspell/cspell-pipe': 8.14.2 + '@cspell/cspell-types': 8.14.2 + + cspell-io@8.13.3: + dependencies: + '@cspell/cspell-service-bus': 8.13.3 + '@cspell/url': 8.13.3 + + cspell-io@8.14.2: + dependencies: + '@cspell/cspell-service-bus': 8.14.2 + '@cspell/url': 8.14.2 + + cspell-lib@8.13.3: + dependencies: + '@cspell/cspell-bundled-dicts': 8.13.3 + '@cspell/cspell-pipe': 8.13.3 + '@cspell/cspell-resolver': 8.13.3 + '@cspell/cspell-types': 8.13.3 + '@cspell/dynamic-import': 8.13.3 + '@cspell/strong-weak-map': 8.13.3 + '@cspell/url': 8.13.3 clear-module: 4.1.2 - comment-json: 4.2.4 - cspell-config-lib: 8.13.1 - cspell-dictionary: 8.13.1 - cspell-glob: 8.13.1 - cspell-grammar: 8.13.1 - cspell-io: 8.13.1 - cspell-trie-lib: 8.13.1 + comment-json: 4.2.5 + cspell-config-lib: 8.13.3 + cspell-dictionary: 8.13.3 + cspell-glob: 8.13.3 + cspell-grammar: 8.13.3 + cspell-io: 8.13.3 + cspell-trie-lib: 8.13.3 env-paths: 3.0.0 fast-equals: 5.0.1 gensequence: 7.0.0 @@ -13033,30 +13923,63 @@ snapshots: vscode-uri: 3.0.8 xdg-basedir: 5.1.0 - cspell-trie-lib@8.13.1: + cspell-lib@8.14.2: dependencies: - '@cspell/cspell-pipe': 8.13.1 - '@cspell/cspell-types': 8.13.1 + '@cspell/cspell-bundled-dicts': 8.14.2 + '@cspell/cspell-pipe': 8.14.2 + '@cspell/cspell-resolver': 8.14.2 + '@cspell/cspell-types': 8.14.2 + '@cspell/dynamic-import': 8.14.2 + '@cspell/filetypes': 8.14.2 + '@cspell/strong-weak-map': 8.14.2 + '@cspell/url': 8.14.2 + clear-module: 4.1.2 + comment-json: 4.2.5 + cspell-config-lib: 8.14.2 + cspell-dictionary: 8.14.2 + cspell-glob: 8.14.2 + cspell-grammar: 8.14.2 + cspell-io: 8.14.2 + cspell-trie-lib: 8.14.2 + env-paths: 3.0.0 + fast-equals: 5.0.1 + gensequence: 7.0.0 + import-fresh: 3.3.0 + resolve-from: 5.0.0 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.0.8 + xdg-basedir: 5.1.0 + + cspell-trie-lib@8.13.3: + dependencies: + '@cspell/cspell-pipe': 8.13.3 + '@cspell/cspell-types': 8.13.3 gensequence: 7.0.0 - cspell@8.13.1: + cspell-trie-lib@8.14.2: dependencies: - '@cspell/cspell-json-reporter': 8.13.1 - '@cspell/cspell-pipe': 8.13.1 - '@cspell/cspell-types': 8.13.1 - '@cspell/dynamic-import': 8.13.1 - '@cspell/url': 8.13.1 + '@cspell/cspell-pipe': 8.14.2 + '@cspell/cspell-types': 8.14.2 + gensequence: 7.0.0 + + cspell@8.14.2: + dependencies: + '@cspell/cspell-json-reporter': 8.14.2 + '@cspell/cspell-pipe': 8.14.2 + '@cspell/cspell-types': 8.14.2 + '@cspell/dynamic-import': 8.14.2 + '@cspell/url': 8.14.2 chalk: 5.3.0 chalk-template: 1.1.0 commander: 12.1.0 - cspell-dictionary: 8.13.1 - cspell-gitignore: 8.13.1 - cspell-glob: 8.13.1 - cspell-io: 8.13.1 - cspell-lib: 8.13.1 + cspell-dictionary: 8.14.2 + cspell-gitignore: 8.14.2 + cspell-glob: 8.14.2 + cspell-io: 8.14.2 + cspell-lib: 8.14.2 fast-glob: 3.3.2 fast-json-stable-stringify: 2.1.0 - file-entry-cache: 9.0.0 + file-entry-cache: 9.1.0 get-stdin: 9.0.0 semver: 7.6.3 strip-ansi: 7.1.0 @@ -13082,13 +14005,13 @@ snapshots: cuint@0.2.2: {} - cypress-image-snapshot@4.0.1(cypress@13.13.2)(jest@29.7.0(@types/node@20.14.14)): + cypress-image-snapshot@4.0.1(cypress@13.14.1)(jest@29.7.0(@types/node@20.16.2)): dependencies: chalk: 2.4.2 - cypress: 13.13.2 + cypress: 13.14.1 fs-extra: 7.0.1 glob: 7.2.3 - jest-image-snapshot: 4.2.0(jest@29.7.0(@types/node@20.14.14)) + jest-image-snapshot: 4.2.0(jest@29.7.0(@types/node@20.16.2)) pkg-dir: 3.0.0 term-img: 4.1.0 transitivePeerDependencies: @@ -13096,7 +14019,7 @@ snapshots: cypress-wait-until@3.0.2: {} - cypress@13.13.2: + cypress@13.14.1: dependencies: '@cypress/request': 3.0.1 '@cypress/xvfb': 1.2.4(supports-color@8.1.1) @@ -13113,7 +14036,7 @@ snapshots: cli-table3: 0.6.5 commander: 6.2.1 common-tags: 1.8.2 - dayjs: 1.11.12 + dayjs: 1.11.13 debug: 4.3.6(supports-color@8.1.1) enquirer: 2.4.1 eventemitter2: 6.4.7 @@ -13146,6 +14069,11 @@ snapshots: cose-base: 1.0.3 cytoscape: 3.30.1 + cytoscape-fcose@2.2.0(cytoscape@3.30.1): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.30.1 + cytoscape@3.30.1: {} d3-array@2.12.1: @@ -13362,6 +14290,8 @@ snapshots: dayjs@1.11.12: {} + dayjs@1.11.13: {} + debug@2.6.9: dependencies: ms: 2.0.0 @@ -13380,10 +14310,6 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.3.5: - dependencies: - ms: 2.1.2 - debug@4.3.6(supports-color@8.1.1): dependencies: ms: 2.1.2 @@ -13697,6 +14623,33 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + escalade@3.1.2: {} escape-html@1.0.3: {} @@ -13730,13 +14683,13 @@ snapshots: dependencies: htmlparser2: 9.1.0 - eslint-plugin-jest@28.7.0(@typescript-eslint/eslint-plugin@8.0.0(@typescript-eslint/parser@8.0.0(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(jest@29.7.0(@types/node@20.14.14))(typescript@5.4.5): + eslint-plugin-jest@28.7.0(@typescript-eslint/eslint-plugin@8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(jest@29.7.0(@types/node@20.16.2))(typescript@5.4.5): dependencies: '@typescript-eslint/utils': 8.0.0(eslint@9.8.0)(typescript@5.4.5) eslint: 9.8.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.0.0(@typescript-eslint/parser@8.0.0(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(typescript@5.4.5) - jest: 29.7.0(@types/node@20.14.14) + '@typescript-eslint/eslint-plugin': 8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(typescript@5.4.5) + jest: 29.7.0(@types/node@20.16.2) transitivePeerDependencies: - supports-color - typescript @@ -13758,7 +14711,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-json@4.0.0: + eslint-plugin-json@4.0.1: dependencies: lodash: 4.17.21 vscode-json-languageservice: 4.2.1 @@ -14061,7 +15014,7 @@ snapshots: '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.7 + micromatch: 4.0.8 fast-json-stable-stringify@2.1.0: {} @@ -14148,7 +15101,7 @@ snapshots: dependencies: flat-cache: 4.0.1 - file-entry-cache@9.0.0: + file-entry-cache@9.1.0: dependencies: flat-cache: 5.0.0 @@ -14223,7 +15176,7 @@ snapshots: find-yarn-workspace-root2@1.2.16: dependencies: - micromatch: 4.0.7 + micromatch: 4.0.8 pkg-dir: 4.2.0 flat-cache@4.0.1: @@ -14397,7 +15350,7 @@ snapshots: getos@3.2.1: dependencies: - async: 3.2.5 + async: 3.2.6 getpass@0.1.7: dependencies: @@ -14474,7 +15427,7 @@ snapshots: array-union: 2.1.0 dir-glob: 3.0.1 fast-glob: 3.3.2 - ignore: 5.3.1 + ignore: 5.3.2 merge2: 1.4.1 slash: 3.0.0 @@ -14482,7 +15435,7 @@ snapshots: dependencies: dir-glob: 3.0.1 fast-glob: 3.3.2 - ignore: 5.3.1 + ignore: 5.3.2 merge2: 1.4.1 slash: 4.0.0 @@ -14641,7 +15594,7 @@ snapshots: http-proxy: 1.18.1 is-glob: 4.0.3 is-plain-obj: 3.0.0 - micromatch: 4.0.7 + micromatch: 4.0.8 optionalDependencies: '@types/express': 4.17.21 transitivePeerDependencies: @@ -14699,7 +15652,7 @@ snapshots: human-signals@5.0.0: {} - husky@9.1.4: {} + husky@9.1.5: {} iconv-lite@0.4.24: dependencies: @@ -14715,6 +15668,8 @@ snapshots: ignore@5.3.1: {} + ignore@5.3.2: {} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 @@ -14947,7 +15902,7 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: '@babel/core': 7.25.2 - '@babel/parser': 7.25.3 + '@babel/parser': 7.25.4 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -15013,7 +15968,7 @@ snapshots: jake@10.9.2: dependencies: - async: 3.2.5 + async: 3.2.6 chalk: 4.1.2 filelist: 1.0.4 minimatch: 3.1.2 @@ -15030,7 +15985,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.14 + '@types/node': 20.16.2 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.3 @@ -15050,16 +16005,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@20.14.14): + jest-cli@29.7.0(@types/node@20.16.2): dependencies: '@jest/core': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.14.14) + create-jest: 29.7.0(@types/node@20.16.2) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@20.14.14) + jest-config: 29.7.0(@types/node@20.16.2) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -15069,7 +16024,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@20.14.14): + jest-config@29.7.0(@types/node@20.16.2): dependencies: '@babel/core': 7.25.2 '@jest/test-sequencer': 29.7.0 @@ -15088,13 +16043,13 @@ snapshots: jest-runner: 29.7.0 jest-util: 29.7.0 jest-validate: 29.7.0 - micromatch: 4.0.7 + micromatch: 4.0.8 parse-json: 5.2.0 pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.14.14 + '@types/node': 20.16.2 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -15123,7 +16078,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.14 + '@types/node': 20.16.2 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -15133,24 +16088,24 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 20.14.14 + '@types/node': 20.16.2 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 jest-regex-util: 29.6.3 jest-util: 29.7.0 jest-worker: 29.7.0 - micromatch: 4.0.7 + micromatch: 4.0.8 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 - jest-image-snapshot@4.2.0(jest@29.7.0(@types/node@20.14.14)): + jest-image-snapshot@4.2.0(jest@29.7.0(@types/node@20.16.2)): dependencies: chalk: 1.1.3 get-stdin: 5.0.1 glur: 1.1.2 - jest: 29.7.0(@types/node@20.14.14) + jest: 29.7.0(@types/node@20.16.2) lodash: 4.17.21 mkdirp: 0.5.6 pixelmatch: 5.3.0 @@ -15177,7 +16132,7 @@ snapshots: '@types/stack-utils': 2.0.3 chalk: 4.1.2 graceful-fs: 4.2.11 - micromatch: 4.0.7 + micromatch: 4.0.8 pretty-format: 29.7.0 slash: 3.0.0 stack-utils: 2.0.6 @@ -15185,7 +16140,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.14.14 + '@types/node': 20.16.2 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -15220,7 +16175,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.14 + '@types/node': 20.16.2 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -15248,7 +16203,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.14 + '@types/node': 20.16.2 chalk: 4.1.2 cjs-module-lexer: 1.3.1 collect-v8-coverage: 1.0.2 @@ -15294,7 +16249,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.14.14 + '@types/node': 20.16.2 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -15313,7 +16268,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.14 + '@types/node': 20.16.2 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -15322,23 +16277,23 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 20.14.14 + '@types/node': 22.5.1 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 20.14.14 + '@types/node': 20.16.2 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@20.14.14): + jest@29.7.0(@types/node@20.16.2): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@20.14.14) + jest-cli: 29.7.0(@types/node@20.16.2) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -15394,7 +16349,7 @@ snapshots: jsdoc-type-pratt-parser@4.0.0: {} - jsdom@24.1.1: + jsdom@24.1.3: dependencies: cssstyle: 4.0.1 data-urls: 5.0.0 @@ -15539,6 +16494,8 @@ snapshots: layout-base@1.0.2: {} + layout-base@2.0.1: {} + lazy-ass@1.6.0: {} leven@3.1.0: {} @@ -15567,7 +16524,7 @@ snapshots: dependencies: uc.micro: 1.0.6 - lint-staged@15.2.8: + lint-staged@15.2.9: dependencies: chalk: 5.3.0 commander: 12.1.0 @@ -15575,7 +16532,7 @@ snapshots: execa: 8.0.1 lilconfig: 3.1.2 listr2: 8.2.4 - micromatch: 4.0.7 + micromatch: 4.0.8 pidtree: 0.6.0 string-argv: 0.3.2 yaml: 2.5.0 @@ -15774,7 +16731,7 @@ snapshots: mdast-util-from-markdown@2.0.1: dependencies: '@types/mdast': 4.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 decode-named-character-reference: 1.0.2 devlop: 1.1.0 mdast-util-to-string: 4.0.0 @@ -15864,7 +16821,7 @@ snapshots: mdast-util-to-markdown@2.1.0: dependencies: '@types/mdast': 4.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 longest-streak: 3.1.0 mdast-util-phrasing: 4.1.0 mdast-util-to-string: 4.0.0 @@ -16116,7 +17073,7 @@ snapshots: transitivePeerDependencies: - supports-color - micromatch@4.0.7: + micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.1 @@ -16163,6 +17120,8 @@ snapshots: minisearch@6.3.0: {} + minisearch@7.1.0: {} + mitt@3.0.1: {} mkdirp@0.5.6: @@ -16358,6 +17317,12 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + openapi-fetch@0.11.1: + dependencies: + openapi-typescript-helpers: 0.0.12 + + openapi-typescript-helpers@0.0.12: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -16457,6 +17422,8 @@ snapshots: package-json-from-dist@1.0.0: {} + package-manager-detector@0.2.0: {} + pako@1.0.11: {} parent-module@1.0.1: @@ -16578,7 +17545,7 @@ snapshots: process-warning: 3.0.0 quick-format-unescaped: 4.0.4 real-require: 0.2.0 - safe-stable-stringify: 2.4.3 + safe-stable-stringify: 2.5.0 sonic-boom: 3.8.1 thread-stream: 2.7.0 @@ -16620,8 +17587,6 @@ snapshots: pngjs@6.0.0: {} - pnpm@8.15.9: {} - points-on-curve@0.2.0: {} points-on-path@0.2.1: @@ -16631,44 +17596,44 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-import@15.1.0(postcss@8.4.40): + postcss-import@15.1.0(postcss@8.4.41): dependencies: - postcss: 8.4.40 + postcss: 8.4.41 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.8 - postcss-js@4.0.1(postcss@8.4.40): + postcss-js@4.0.1(postcss@8.4.41): dependencies: camelcase-css: 2.0.1 - postcss: 8.4.40 + postcss: 8.4.41 - postcss-load-config@4.0.2(postcss@8.4.40): + postcss-load-config@4.0.2(postcss@8.4.41): dependencies: lilconfig: 3.1.2 yaml: 2.5.0 optionalDependencies: - postcss: 8.4.40 + postcss: 8.4.41 - postcss-nested@6.2.0(postcss@8.4.40): + postcss-nested@6.2.0(postcss@8.4.41): dependencies: - postcss: 8.4.40 - postcss-selector-parser: 6.1.1 + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 - postcss-selector-parser@6.1.1: + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 postcss-value-parser@4.2.0: {} - postcss@8.4.40: + postcss@8.4.41: dependencies: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 - preact@10.23.1: {} + preact@10.23.2: {} preferred-pm@3.1.4: dependencies: @@ -16848,7 +17813,7 @@ snapshots: regenerator-transform@0.15.2: dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.25.4 regexp-tree@0.1.27: {} @@ -16994,39 +17959,39 @@ snapshots: robust-predicates@3.0.2: {} - rollup-plugin-visualizer@5.12.0(rollup@4.20.0): + rollup-plugin-visualizer@5.12.0(rollup@4.21.1): dependencies: open: 8.4.2 picomatch: 2.3.1 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 4.20.0 + rollup: 4.21.1 rollup@2.79.1: optionalDependencies: fsevents: 2.3.3 - rollup@4.20.0: + rollup@4.21.1: dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.20.0 - '@rollup/rollup-android-arm64': 4.20.0 - '@rollup/rollup-darwin-arm64': 4.20.0 - '@rollup/rollup-darwin-x64': 4.20.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.20.0 - '@rollup/rollup-linux-arm-musleabihf': 4.20.0 - '@rollup/rollup-linux-arm64-gnu': 4.20.0 - '@rollup/rollup-linux-arm64-musl': 4.20.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.20.0 - '@rollup/rollup-linux-riscv64-gnu': 4.20.0 - '@rollup/rollup-linux-s390x-gnu': 4.20.0 - '@rollup/rollup-linux-x64-gnu': 4.20.0 - '@rollup/rollup-linux-x64-musl': 4.20.0 - '@rollup/rollup-win32-arm64-msvc': 4.20.0 - '@rollup/rollup-win32-ia32-msvc': 4.20.0 - '@rollup/rollup-win32-x64-msvc': 4.20.0 + '@rollup/rollup-android-arm-eabi': 4.21.1 + '@rollup/rollup-android-arm64': 4.21.1 + '@rollup/rollup-darwin-arm64': 4.21.1 + '@rollup/rollup-darwin-x64': 4.21.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.21.1 + '@rollup/rollup-linux-arm-musleabihf': 4.21.1 + '@rollup/rollup-linux-arm64-gnu': 4.21.1 + '@rollup/rollup-linux-arm64-musl': 4.21.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.21.1 + '@rollup/rollup-linux-riscv64-gnu': 4.21.1 + '@rollup/rollup-linux-s390x-gnu': 4.21.1 + '@rollup/rollup-linux-x64-gnu': 4.21.1 + '@rollup/rollup-linux-x64-musl': 4.21.1 + '@rollup/rollup-win32-arm64-msvc': 4.21.1 + '@rollup/rollup-win32-ia32-msvc': 4.21.1 + '@rollup/rollup-win32-x64-msvc': 4.21.1 fsevents: 2.3.3 roughjs@4.6.6: @@ -17071,7 +18036,7 @@ snapshots: dependencies: ret: 0.2.2 - safe-stable-stringify@2.4.3: {} + safe-stable-stringify@2.5.0: {} safer-buffer@2.1.2: {} @@ -17184,31 +18149,31 @@ snapshots: dependencies: kind-of: 6.0.3 - sharp@0.33.4: + sharp@0.33.5: dependencies: color: 4.2.3 detect-libc: 2.0.3 semver: 7.6.3 optionalDependencies: - '@img/sharp-darwin-arm64': 0.33.4 - '@img/sharp-darwin-x64': 0.33.4 - '@img/sharp-libvips-darwin-arm64': 1.0.2 - '@img/sharp-libvips-darwin-x64': 1.0.2 - '@img/sharp-libvips-linux-arm': 1.0.2 - '@img/sharp-libvips-linux-arm64': 1.0.2 - '@img/sharp-libvips-linux-s390x': 1.0.2 - '@img/sharp-libvips-linux-x64': 1.0.2 - '@img/sharp-libvips-linuxmusl-arm64': 1.0.2 - '@img/sharp-libvips-linuxmusl-x64': 1.0.2 - '@img/sharp-linux-arm': 0.33.4 - '@img/sharp-linux-arm64': 0.33.4 - '@img/sharp-linux-s390x': 0.33.4 - '@img/sharp-linux-x64': 0.33.4 - '@img/sharp-linuxmusl-arm64': 0.33.4 - '@img/sharp-linuxmusl-x64': 0.33.4 - '@img/sharp-wasm32': 0.33.4 - '@img/sharp-win32-ia32': 0.33.4 - '@img/sharp-win32-x64': 0.33.4 + '@img/sharp-darwin-arm64': 0.33.5 + '@img/sharp-darwin-x64': 0.33.5 + '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-linux-arm': 0.33.5 + '@img/sharp-linux-arm64': 0.33.5 + '@img/sharp-linux-s390x': 0.33.5 + '@img/sharp-linux-x64': 0.33.5 + '@img/sharp-linuxmusl-arm64': 0.33.5 + '@img/sharp-linuxmusl-x64': 0.33.5 + '@img/sharp-wasm32': 0.33.5 + '@img/sharp-win32-ia32': 0.33.5 + '@img/sharp-win32-x64': 0.33.5 shebang-command@1.2.0: dependencies: @@ -17231,9 +18196,9 @@ snapshots: vscode-oniguruma: 1.7.0 vscode-textmate: 8.0.0 - shiki@1.12.1: + shiki@1.14.1: dependencies: - '@shikijs/core': 1.12.1 + '@shikijs/core': 1.14.1 '@types/hast': 3.0.4 side-channel@1.0.6: @@ -17555,7 +18520,7 @@ snapshots: dependencies: js-tokens: 9.0.0 - stylis@4.3.2: {} + stylis@4.3.4: {} sucrase@3.35.0: dependencies: @@ -17592,11 +18557,11 @@ snapshots: synckit@0.9.1: dependencies: '@pkgr/core': 0.1.1 - tslib: 2.6.3 + tslib: 2.7.0 tabbable@6.2.0: {} - tailwindcss@3.4.7: + tailwindcss@3.4.10: dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -17608,16 +18573,16 @@ snapshots: is-glob: 4.0.3 jiti: 1.21.6 lilconfig: 2.1.0 - micromatch: 4.0.7 + micromatch: 4.0.8 normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.0.1 - postcss: 8.4.40 - postcss-import: 15.1.0(postcss@8.4.40) - postcss-js: 4.0.1(postcss@8.4.40) - postcss-load-config: 4.0.2(postcss@8.4.40) - postcss-nested: 6.2.0(postcss@8.4.40) - postcss-selector-parser: 6.1.1 + postcss: 8.4.41 + postcss-import: 15.1.0(postcss@8.4.41) + postcss-js: 4.0.1(postcss@8.4.41) + postcss-load-config: 4.0.2(postcss@8.4.41) + postcss-nested: 6.2.0(postcss@8.4.41) + postcss-selector-parser: 6.1.2 resolve: 1.22.8 sucrase: 3.35.0 transitivePeerDependencies: @@ -17627,7 +18592,7 @@ snapshots: teen_process@1.16.0: dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.25.4 bluebird: 3.7.2 lodash: 4.17.21 shell-quote: 1.8.1 @@ -17679,6 +18644,13 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 + terser@5.31.6: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.12.1 + commander: 2.20.3 + source-map-support: 0.5.21 + test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 @@ -17716,6 +18688,8 @@ snapshots: tinybench@2.9.0: {} + tinyexec@0.3.0: {} + tinypool@0.8.4: {} tinyspy@2.2.1: {} @@ -17773,9 +18747,11 @@ snapshots: tslib@2.6.3: {} - tsx@4.16.5: + tslib@2.7.0: {} + + tsx@4.19.0: dependencies: - esbuild: 0.21.5 + esbuild: 0.23.1 get-tsconfig: 4.7.6 optionalDependencies: fsevents: 2.3.3 @@ -17804,7 +18780,7 @@ snapshots: type-fest@0.8.1: {} - type-fest@4.23.0: {} + type-fest@4.25.0: {} type-is@1.6.18: dependencies: @@ -17862,11 +18838,11 @@ snapshots: shiki: 0.14.7 typescript: 5.4.5 - typescript-eslint@8.0.0(eslint@9.8.0)(typescript@5.4.5): + typescript-eslint@8.0.1(eslint@9.8.0)(typescript@5.4.5): dependencies: - '@typescript-eslint/eslint-plugin': 8.0.0(@typescript-eslint/parser@8.0.0(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(typescript@5.4.5) - '@typescript-eslint/parser': 8.0.0(eslint@9.8.0)(typescript@5.4.5) - '@typescript-eslint/utils': 8.0.0(eslint@9.8.0)(typescript@5.4.5) + '@typescript-eslint/eslint-plugin': 8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.8.0)(typescript@5.4.5))(eslint@9.8.0)(typescript@5.4.5) + '@typescript-eslint/parser': 8.0.1(eslint@9.8.0)(typescript@5.4.5) + '@typescript-eslint/utils': 8.0.1(eslint@9.8.0)(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: @@ -17898,6 +18874,8 @@ snapshots: undici-types@5.26.5: {} + undici-types@6.19.8: {} + unicode-canonical-property-names-ecmascript@2.0.0: {} unicode-match-property-ecmascript@2.0.0: @@ -17951,7 +18929,7 @@ snapshots: unist-util-stringify-position@4.0.0: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-visit-parents@6.0.1: dependencies: @@ -17970,13 +18948,13 @@ snapshots: universalify@2.0.1: {} - unocss@0.59.4(postcss@8.4.40)(rollup@2.79.1)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3)): + unocss@0.59.4(postcss@8.4.41)(rollup@2.79.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6)): dependencies: - '@unocss/astro': 0.59.4(rollup@2.79.1)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3)) + '@unocss/astro': 0.59.4(rollup@2.79.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6)) '@unocss/cli': 0.59.4(rollup@2.79.1) '@unocss/core': 0.59.4 '@unocss/extractor-arbitrary-variants': 0.59.4 - '@unocss/postcss': 0.59.4(postcss@8.4.40) + '@unocss/postcss': 0.59.4(postcss@8.4.41) '@unocss/preset-attributify': 0.59.4 '@unocss/preset-icons': 0.59.4 '@unocss/preset-mini': 0.59.4 @@ -17991,9 +18969,38 @@ snapshots: '@unocss/transformer-compile-class': 0.59.4 '@unocss/transformer-directives': 0.59.4 '@unocss/transformer-variant-group': 0.59.4 - '@unocss/vite': 0.59.4(rollup@2.79.1)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3)) + '@unocss/vite': 0.59.4(rollup@2.79.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6)) optionalDependencies: - vite: 5.3.5(@types/node@20.14.14)(terser@5.31.3) + vite: 5.4.2(@types/node@22.5.1)(terser@5.31.6) + transitivePeerDependencies: + - postcss + - rollup + - supports-color + + unocss@0.59.4(postcss@8.4.41)(rollup@4.21.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6)): + dependencies: + '@unocss/astro': 0.59.4(rollup@4.21.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6)) + '@unocss/cli': 0.59.4(rollup@4.21.1) + '@unocss/core': 0.59.4 + '@unocss/extractor-arbitrary-variants': 0.59.4 + '@unocss/postcss': 0.59.4(postcss@8.4.41) + '@unocss/preset-attributify': 0.59.4 + '@unocss/preset-icons': 0.59.4 + '@unocss/preset-mini': 0.59.4 + '@unocss/preset-tagify': 0.59.4 + '@unocss/preset-typography': 0.59.4 + '@unocss/preset-uno': 0.59.4 + '@unocss/preset-web-fonts': 0.59.4 + '@unocss/preset-wind': 0.59.4 + '@unocss/reset': 0.59.4 + '@unocss/transformer-attributify-jsx': 0.59.4 + '@unocss/transformer-attributify-jsx-babel': 0.59.4 + '@unocss/transformer-compile-class': 0.59.4 + '@unocss/transformer-directives': 0.59.4 + '@unocss/transformer-variant-group': 0.59.4 + '@unocss/vite': 0.59.4(rollup@4.21.1)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6)) + optionalDependencies: + vite: 5.4.2(@types/node@22.5.1)(terser@5.31.6) transitivePeerDependencies: - postcss - rollup @@ -18001,7 +19008,7 @@ snapshots: unpipe@1.0.0: {} - unplugin-vue-components@0.26.0(@babel/parser@7.25.3)(rollup@2.79.1)(vue@3.4.35(typescript@5.4.5)): + unplugin-vue-components@0.26.0(@babel/parser@7.25.4)(rollup@2.79.1)(vue@3.4.38(typescript@5.4.5)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.1.0(rollup@2.79.1) @@ -18013,9 +19020,28 @@ snapshots: minimatch: 9.0.5 resolve: 1.22.8 unplugin: 1.12.0 - vue: 3.4.35(typescript@5.4.5) + vue: 3.4.38(typescript@5.4.5) optionalDependencies: - '@babel/parser': 7.25.3 + '@babel/parser': 7.25.4 + transitivePeerDependencies: + - rollup + - supports-color + + unplugin-vue-components@0.26.0(@babel/parser@7.25.4)(rollup@4.21.1)(vue@3.4.38(typescript@5.4.5)): + dependencies: + '@antfu/utils': 0.7.10 + '@rollup/pluginutils': 5.1.0(rollup@4.21.1) + chokidar: 3.6.0 + debug: 4.3.6(supports-color@8.1.1) + fast-glob: 3.3.2 + local-pkg: 0.4.3 + magic-string: 0.30.11 + minimatch: 9.0.5 + resolve: 1.22.8 + unplugin: 1.12.0 + vue: 3.4.38(typescript@5.4.5) + optionalDependencies: + '@babel/parser': 7.25.4 transitivePeerDependencies: - rollup - supports-color @@ -18075,33 +19101,34 @@ snapshots: vfile-message@4.0.2: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 vfile@6.0.2: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vite-node@1.6.0(@types/node@20.14.14)(terser@5.31.3): + vite-node@1.6.0(@types/node@20.16.2)(terser@5.31.6): dependencies: cac: 6.7.14 debug: 4.3.6(supports-color@8.1.1) pathe: 1.1.2 picocolors: 1.0.1 - vite: 5.3.5(@types/node@20.14.14)(terser@5.31.3) + vite: 5.4.2(@types/node@20.16.2)(terser@5.31.6) transitivePeerDependencies: - '@types/node' - less - lightningcss - sass + - sass-embedded - stylus - sugarss - supports-color - terser - vite-plugin-istanbul@6.0.2(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3)): + vite-plugin-istanbul@6.0.2(vite@5.4.2(@types/node@20.16.2)(terser@5.31.6)): dependencies: '@istanbuljs/load-nyc-config': 1.1.0 espree: 10.1.0 @@ -18109,60 +19136,70 @@ snapshots: picocolors: 1.0.1 source-map: 0.7.4 test-exclude: 6.0.0 - vite: 5.3.5(@types/node@20.14.14)(terser@5.31.3) + vite: 5.4.2(@types/node@20.16.2)(terser@5.31.6) transitivePeerDependencies: - supports-color - vite-plugin-pwa@0.19.8(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0): + vite-plugin-pwa@0.19.8(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0): dependencies: debug: 4.3.6(supports-color@8.1.1) fast-glob: 3.3.2 pretty-bytes: 6.1.1 - vite: 5.3.5(@types/node@20.14.14)(terser@5.31.3) + vite: 5.4.2(@types/node@22.5.1)(terser@5.31.6) workbox-build: 7.1.1(@types/babel__core@7.20.5) workbox-window: 7.1.0 transitivePeerDependencies: - supports-color - vite@5.3.5(@types/node@20.14.14)(terser@5.31.3): + vite@5.4.2(@types/node@20.16.2)(terser@5.31.6): dependencies: esbuild: 0.21.5 - postcss: 8.4.40 - rollup: 4.20.0 + postcss: 8.4.41 + rollup: 4.21.1 optionalDependencies: - '@types/node': 20.14.14 + '@types/node': 20.16.2 fsevents: 2.3.3 - terser: 5.31.3 + terser: 5.31.6 - vitepress-plugin-search@1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.14)(axios@1.7.3)(postcss@8.4.40)(search-insights@2.15.0)(terser@5.31.3)(typescript@5.4.5))(vue@3.4.35(typescript@5.4.5)): + vite@5.4.2(@types/node@22.5.1)(terser@5.31.6): + dependencies: + esbuild: 0.21.5 + postcss: 8.4.41 + rollup: 4.21.1 + optionalDependencies: + '@types/node': 22.5.1 + fsevents: 2.3.3 + terser: 5.31.6 + + vitepress-plugin-search@1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.3.4(@algolia/client-search@4.24.0)(@types/node@22.5.1)(axios@1.7.5)(postcss@8.4.41)(search-insights@2.15.0)(terser@5.31.6)(typescript@5.4.5))(vue@3.4.38(typescript@5.4.5)): dependencies: '@types/flexsearch': 0.7.6 '@types/markdown-it': 12.2.3 flexsearch: 0.7.43 glob-to-regexp: 0.4.1 markdown-it: 13.0.2 - vitepress: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.14)(axios@1.7.3)(postcss@8.4.40)(search-insights@2.15.0)(terser@5.31.3)(typescript@5.4.5) - vue: 3.4.35(typescript@5.4.5) + vitepress: 1.3.4(@algolia/client-search@4.24.0)(@types/node@22.5.1)(axios@1.7.5)(postcss@8.4.41)(search-insights@2.15.0)(terser@5.31.6)(typescript@5.4.5) + vue: 3.4.38(typescript@5.4.5) - vitepress@1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.14)(axios@1.7.3)(postcss@8.4.40)(search-insights@2.15.0)(terser@5.31.3)(typescript@5.4.5): + vitepress@1.1.4(@algolia/client-search@4.24.0)(@types/node@22.5.1)(axios@1.7.5)(postcss@8.4.41)(search-insights@2.15.0)(terser@5.31.6)(typescript@5.4.5): dependencies: '@docsearch/css': 3.6.1 '@docsearch/js': 3.6.1(@algolia/client-search@4.24.0)(search-insights@2.15.0) - '@shikijs/core': 1.12.1 - '@shikijs/transformers': 1.12.1 + '@shikijs/core': 1.14.1 + '@shikijs/transformers': 1.14.1 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.1.2(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(vue@3.4.35(typescript@5.4.5)) - '@vue/devtools-api': 7.3.7 - '@vueuse/core': 10.11.0(vue@3.4.35(typescript@5.4.5)) - '@vueuse/integrations': 10.11.0(axios@1.7.3)(focus-trap@7.5.4)(vue@3.4.35(typescript@5.4.5)) + '@vitejs/plugin-vue': 5.1.2(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))(vue@3.4.38(typescript@5.4.5)) + '@vue/devtools-api': 7.3.9 + '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.4.5)) + '@vueuse/integrations': 10.11.1(axios@1.7.5)(focus-trap@7.5.4)(vue@3.4.38(typescript@5.4.5)) focus-trap: 7.5.4 mark.js: 8.11.1 minisearch: 6.3.0 - shiki: 1.12.1 - vite: 5.3.5(@types/node@20.14.14)(terser@5.31.3) - vue: 3.4.35(typescript@5.4.5) + shiki: 1.14.1 + vite: 5.4.2(@types/node@22.5.1)(terser@5.31.6) + vue: 3.4.38(typescript@5.4.5) optionalDependencies: - postcss: 8.4.40 + postcss: 8.4.41 transitivePeerDependencies: - '@algolia/client-search' - '@types/node' @@ -18182,6 +19219,7 @@ snapshots: - react - react-dom - sass + - sass-embedded - search-insights - sortablejs - stylus @@ -18190,7 +19228,55 @@ snapshots: - typescript - universal-cookie - vitest@1.6.0(@types/node@20.14.14)(@vitest/ui@1.6.0)(jsdom@24.1.1)(terser@5.31.3): + vitepress@1.3.4(@algolia/client-search@4.24.0)(@types/node@22.5.1)(axios@1.7.5)(postcss@8.4.41)(search-insights@2.15.0)(terser@5.31.6)(typescript@5.4.5): + dependencies: + '@docsearch/css': 3.6.1 + '@docsearch/js': 3.6.1(@algolia/client-search@4.24.0)(search-insights@2.15.0) + '@shikijs/core': 1.14.1 + '@shikijs/transformers': 1.14.1 + '@types/markdown-it': 14.1.2 + '@vitejs/plugin-vue': 5.1.2(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))(vue@3.4.38(typescript@5.4.5)) + '@vue/devtools-api': 7.3.9 + '@vue/shared': 3.4.38 + '@vueuse/core': 11.0.3(vue@3.4.38(typescript@5.4.5)) + '@vueuse/integrations': 11.0.3(axios@1.7.5)(focus-trap@7.5.4)(vue@3.4.38(typescript@5.4.5)) + focus-trap: 7.5.4 + mark.js: 8.11.1 + minisearch: 7.1.0 + shiki: 1.14.1 + vite: 5.4.2(@types/node@22.5.1)(terser@5.31.6) + vue: 3.4.38(typescript@5.4.5) + optionalDependencies: + postcss: 8.4.41 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/node' + - '@types/react' + - '@vue/composition-api' + - async-validator + - axios + - change-case + - drauu + - fuse.js + - idb-keyval + - jwt-decode + - less + - lightningcss + - nprogress + - qrcode + - react + - react-dom + - sass + - sass-embedded + - search-insights + - sortablejs + - stylus + - sugarss + - terser + - typescript + - universal-cookie + + vitest@1.6.0(@types/node@20.16.2)(@vitest/ui@1.6.0)(jsdom@24.1.3)(terser@5.31.6): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -18209,17 +19295,18 @@ snapshots: strip-literal: 2.1.0 tinybench: 2.9.0 tinypool: 0.8.4 - vite: 5.3.5(@types/node@20.14.14)(terser@5.31.3) - vite-node: 1.6.0(@types/node@20.14.14)(terser@5.31.3) + vite: 5.4.2(@types/node@20.16.2)(terser@5.31.6) + vite-node: 1.6.0(@types/node@20.16.2)(terser@5.31.6) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 20.14.14 + '@types/node': 20.16.2 '@vitest/ui': 1.6.0(vitest@1.6.0) - jsdom: 24.1.1 + jsdom: 24.1.3 transitivePeerDependencies: - less - lightningcss - sass + - sass-embedded - stylus - sugarss - supports-color @@ -18256,24 +19343,24 @@ snapshots: vscode-uri@3.0.8: {} - vue-demi@0.14.10(vue@3.4.35(typescript@5.4.5)): + vue-demi@0.14.10(vue@3.4.38(typescript@5.4.5)): dependencies: - vue: 3.4.35(typescript@5.4.5) + vue: 3.4.38(typescript@5.4.5) - vue@3.4.35(typescript@5.4.5): + vue@3.4.38(typescript@5.4.5): dependencies: - '@vue/compiler-dom': 3.4.35 - '@vue/compiler-sfc': 3.4.35 - '@vue/runtime-dom': 3.4.35 - '@vue/server-renderer': 3.4.35(vue@3.4.35(typescript@5.4.5)) - '@vue/shared': 3.4.35 + '@vue/compiler-dom': 3.4.38 + '@vue/compiler-sfc': 3.4.38 + '@vue/runtime-dom': 3.4.38 + '@vue/server-renderer': 3.4.38(vue@3.4.38(typescript@5.4.5)) + '@vue/shared': 3.4.38 optionalDependencies: typescript: 5.4.5 - vuex@4.1.0(vue@3.4.35(typescript@5.4.5)): + vuex@4.1.0(vue@3.4.38(typescript@5.4.5)): dependencies: '@vue/devtools-api': 6.6.3 - vue: 3.4.35(typescript@5.4.5) + vue: 3.4.38(typescript@5.4.5) w3c-xmlserializer@5.0.0: dependencies: @@ -18306,7 +19393,7 @@ snapshots: webdriver@7.31.1(typescript@5.4.5): dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.47 '@wdio/config': 7.31.1(typescript@5.4.5) '@wdio/logger': 7.26.0 '@wdio/protocols': 7.27.0 @@ -18555,8 +19642,8 @@ snapshots: dependencies: '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1) '@babel/core': 7.25.2 - '@babel/preset-env': 7.25.3(@babel/core@7.25.2) - '@babel/runtime': 7.25.0 + '@babel/preset-env': 7.25.4(@babel/core@7.25.2) + '@babel/runtime': 7.25.4 '@rollup/plugin-babel': 5.3.1(@babel/core@7.25.2)(@types/babel__core@7.20.5)(rollup@2.79.1) '@rollup/plugin-node-resolve': 15.2.3(rollup@2.79.1) '@rollup/plugin-replace': 2.4.2(rollup@2.79.1) @@ -18697,8 +19784,6 @@ snapshots: ws@8.18.0: {} - ws@8.5.0: {} - xdg-basedir@5.1.0: {} xml-name-validator@5.0.0: {}