diff --git a/.build/common.ts b/.build/common.ts index 274977fa26..86ccd15d1d 100644 --- a/.build/common.ts +++ b/.build/common.ts @@ -1,3 +1,9 @@ +export interface PackageOptions { + name: string; + packageName: string; + file: string; +} + /** * Shared common options for both ESBuild and Vite */ @@ -22,9 +28,9 @@ export const packageOptions = { packageName: 'mermaid-zenuml', file: 'detector.ts', }, - 'mermaid-flowchart-elk': { - name: 'mermaid-flowchart-elk', - packageName: 'mermaid-flowchart-elk', - file: 'detector.ts', + 'mermaid-layout-elk': { + name: 'mermaid-layout-elk', + packageName: 'mermaid-layout-elk', + file: 'layouts.ts', }, -} as const; +} as const satisfies Record; diff --git a/.build/jisonTransformer.ts b/.build/jisonTransformer.ts index 314df8a33f..b603502306 100644 --- a/.build/jisonTransformer.ts +++ b/.build/jisonTransformer.ts @@ -1,6 +1,7 @@ import jison from 'jison'; export const transformJison = (src: string): string => { + // @ts-ignore - Jison is not typed properly const parser = new jison.Generator(src, { moduleType: 'js', 'token-stack': true, diff --git a/.build/jsonSchema.ts b/.build/jsonSchema.ts index 6fd8ca3f54..7a700c1e28 100644 --- a/.build/jsonSchema.ts +++ b/.build/jsonSchema.ts @@ -19,12 +19,14 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [ 'xyChart', 'requirement', 'mindmap', + 'kanban', 'timeline', 'gitGraph', 'c4', 'sankey', 'block', 'packet', + 'architecture', ] as const; /** diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000000..e5b6d8d6a6 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,8 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can +find the full documentation for it [in our repository](https://github.com/changesets/changesets) + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/angry-bags-brake.md b/.changeset/angry-bags-brake.md new file mode 100644 index 0000000000..472e486ec2 --- /dev/null +++ b/.changeset/angry-bags-brake.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: architecture diagrams no longer grow to extreme heights due to conflicting alignments diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000000..ffda055e88 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", + "changelog": ["@changesets/changelog-github", { "repo": "mermaid-js/mermaid" }], + "commit": false, + "fixed": [], + "linked": [], + "access": "public", + "baseBranch": "master", + "updateInternalDependencies": "patch", + "bumpVersionsWithWorkspaceProtocolOnly": true, + "ignore": ["@mermaid-js/docs", "@mermaid-js/webpack-test", "@mermaid-js/mermaid-example-diagram"] +} diff --git a/.cspell/code-terms.txt b/.cspell/code-terms.txt index 36d3b94c65..f4862006fe 100644 --- a/.cspell/code-terms.txt +++ b/.cspell/code-terms.txt @@ -26,8 +26,10 @@ concat controlx controly CSSCLASS +curv CYLINDEREND CYLINDERSTART +DAGA datakey DEND descr @@ -54,6 +56,7 @@ GENERICTYPE getBoundarys grammr graphtype +halign iife interp introdcued @@ -65,6 +68,7 @@ Kaufmann keyify LABELPOS LABELTYPE +layoutstop lcov LEFTOF Lexa @@ -90,6 +94,7 @@ reqs rewritelinks rgba RIGHTOF +roughjs sankey sequencenumber shrc @@ -109,6 +114,7 @@ strikethrough stringifying struct STYLECLASS +STYLEDEF STYLEOPTS subcomponent subcomponents @@ -117,6 +123,8 @@ SUBROUTINEEND SUBROUTINESTART Subschemas substr +SVGG +SVGSVG TAGEND TAGSTART techn @@ -133,6 +141,7 @@ tsdoc typeof typestr unshift +urlsafe verifymethod VERIFYMTHD WARN_DOCSDIR_DOESNT_MATCH diff --git a/.cspell/cspell.config.yaml b/.cspell/cspell.config.yaml index 33d6901936..b16040c8ce 100644 --- a/.cspell/cspell.config.yaml +++ b/.cspell/cspell.config.yaml @@ -28,6 +28,9 @@ dictionaryDefinitions: - name: suggestions words: - none + - disp + - subproc + - tria suggestWords: - seperator:separator - vertice:vertex diff --git a/.cspell/libraries.txt b/.cspell/libraries.txt index 71d2e18a44..73a2dceeb2 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 @@ -55,8 +57,10 @@ presetAttributify pyplot redmine rehype +roughjs rscratch shiki +Slidev sparkline sphinxcontrib ssim diff --git a/.cspell/mermaid-terms.txt b/.cspell/mermaid-terms.txt index 3fa5eff269..cb6db41dec 100644 --- a/.cspell/mermaid-terms.txt +++ b/.cspell/mermaid-terms.txt @@ -1,14 +1,18 @@ Adamiecki arrowend +Bendpoints bmatrix braintree catmull compositTitleSize +curv doublecircle elems gantt gitgraph gzipped +handDrawn +kanban knsv Knut marginx @@ -17,10 +21,12 @@ Markdownish mermaidjs mindmap mindmaps +mrtree multigraph nodesep NOTEGROUP Pinterest +procs rankdir ranksep rect diff --git a/.cspell/misc-terms.txt b/.cspell/misc-terms.txt index 0efd1dcc00..1820e3c863 100644 --- a/.cspell/misc-terms.txt +++ b/.cspell/misc-terms.txt @@ -1,4 +1,7 @@ BRANDES -handdrawn +circo +handDrawn KOEPF +neato newbranch +validify diff --git a/.esbuild/build.ts b/.esbuild/build.ts index 505c18405d..423e8f047c 100644 --- a/.esbuild/build.ts +++ b/.esbuild/build.ts @@ -8,8 +8,11 @@ import { defaultOptions, getBuildConfig } from './util.js'; const shouldVisualize = process.argv.includes('--visualize'); const buildPackage = async (entryName: keyof typeof packageOptions) => { - const commonOptions = { ...defaultOptions, entryName } as const; - const buildConfigs = [ + const commonOptions: MermaidBuildOptions = { + ...defaultOptions, + options: packageOptions[entryName], + } as const; + const buildConfigs: MermaidBuildOptions[] = [ // package.mjs { ...commonOptions }, // package.min.mjs @@ -40,7 +43,7 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => { continue; } const fileName = Object.keys(metafile.outputs) - .find((file) => !file.includes('chunks') && file.endsWith('js')) + .find((file) => !file.includes('chunks') && file.endsWith('js'))! .replace('dist/', ''); // Upload metafile into https://esbuild.github.io/analyze/ await writeFile(`stats/${fileName}.meta.json`, JSON.stringify(metafile)); diff --git a/.esbuild/server.ts b/.esbuild/server.ts index ef61ebec22..6e1bcb4602 100644 --- a/.esbuild/server.ts +++ b/.esbuild/server.ts @@ -9,13 +9,18 @@ import { generateLangium } from '../.build/generateLangium.js'; import { defaultOptions, getBuildConfig } from './util.js'; const configs = Object.values(packageOptions).map(({ packageName }) => - getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: packageName }) + getBuildConfig({ + ...defaultOptions, + minify: false, + core: false, + options: packageOptions[packageName], + }) ); const mermaidIIFEConfig = getBuildConfig({ ...defaultOptions, minify: false, core: false, - entryName: 'mermaid', + options: packageOptions.mermaid, format: 'iife', }); configs.push(mermaidIIFEConfig); diff --git a/.esbuild/util.ts b/.esbuild/util.ts index 6d424ab17c..6d6d1d59b9 100644 --- a/.esbuild/util.ts +++ b/.esbuild/util.ts @@ -3,20 +3,20 @@ import { fileURLToPath } from 'url'; import type { BuildOptions } from 'esbuild'; import { readFileSync } from 'fs'; import jsonSchemaPlugin from './jsonSchemaPlugin.js'; -import { packageOptions } from '../.build/common.js'; +import type { PackageOptions } from '../.build/common.js'; import { jisonPlugin } from './jisonPlugin.js'; const __dirname = fileURLToPath(new URL('.', import.meta.url)); -export interface MermaidBuildOptions { +export interface MermaidBuildOptions extends BuildOptions { minify: boolean; core: boolean; metafile: boolean; format: 'esm' | 'iife'; - entryName: keyof typeof packageOptions; + options: PackageOptions; } -export const defaultOptions: Omit = { +export const defaultOptions: Omit = { minify: false, metafile: false, core: false, @@ -52,9 +52,14 @@ const getFileName = (fileName: string, { core, format, minify }: MermaidBuildOpt }; export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => { - const { core, entryName, metafile, format, minify } = options; + const { + core, + metafile, + format, + minify, + options: { name, file, packageName }, + } = options; const external: string[] = ['require', 'fs', 'path']; - const { name, file, packageName } = packageOptions[entryName]; const outFileName = getFileName(name, options); const output: BuildOptions = buildOptions({ absWorkingDir: resolve(__dirname, `../packages/${packageName}`), diff --git a/.github/lychee.toml b/.github/lychee.toml index 2e3b08c413..288ab054a2 100644 --- a/.github/lychee.toml +++ b/.github/lychee.toml @@ -44,7 +44,10 @@ exclude = [ "https://chromewebstore.google.com", # Drupal 403 -"https://(www.)?drupal.org" +"https://(www.)?drupal.org", + +# Swimm returns 404, eventhough the link is valid +"https://docs.swimm.io" ] # Exclude all private IPs from checking. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index cfd22a2935..c221666199 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -15,4 +15,4 @@ Make sure you - [ ] :book: have read the [contribution guidelines](https://mermaid.js.org/community/contributing.html) - [ ] :computer: have added necessary unit/e2e tests. - [ ] :notebook: have added documentation. Make sure [`MERMAID_RELEASE_VERSION`](https://mermaid.js.org/community/contributing.html#update-documentation) is used for all new features. -- [ ] :bookmark: targeted `develop` branch +- [ ] :butterfly: If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running `pnpm changeset` and following the prompts. Changesets that add features should be `minor` and those that fix bugs should be `patch`. Please prefix changeset messages with `feat:`, `fix:`, or `chore:`. diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 83138c3d46..0000000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,36 +0,0 @@ -name-template: '$NEXT_PATCH_VERSION' -tag-template: '$NEXT_PATCH_VERSION' -categories: - - title: '🚨 **Breaking Changes**' - labels: - - 'Breaking Change' - - title: '🚀 Features' - labels: - - 'Type: Enhancement' - - 'feature' # deprecated, new PRs shouldn't have this - - title: '🐛 Bug Fixes' - labels: - - 'Type: Bug / Error' - - 'fix' # deprecated, new PRs shouldn't have this - - title: '🧰 Maintenance' - labels: - - 'Type: Other' - - 'chore' # deprecated, new PRs shouldn't have this - - title: '⚡ī¸ Performance' - labels: - - 'Type: Performance' - - title: '📚 Documentation' - labels: - - 'Area: Documentation' -change-template: '- $TITLE (#$NUMBER) @$AUTHOR' -sort-by: title -sort-direction: ascending -exclude-labels: - - 'Skip changelog' -no-changes-template: 'This release contains minor changes and bugfixes.' -template: | - # Release Notes - - $CHANGES - - 🎉 **Thanks to all contributors helping with this release!** 🎉 diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index e6ccf18856..13b913c111 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -2,20 +2,24 @@ name: autofix.ci # needed to securely identify the workflow on: pull_request: + branches-ignore: + - 'renovate/**' 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@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: cache: pnpm node-version-file: '.node-version' @@ -38,4 +42,4 @@ jobs: working-directory: ./packages/mermaid run: pnpm run docs:build - - uses: autofix-ci/action@dd55f44df8f7cdb7a6bf74c78677eb8acd40cd0a + - uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c # main diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 0ce7789573..79e9deea16 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@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 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 c6e96912ef..0000000000 --- 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 ad6df66b50..5c940c0875 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 012fbf19d5..0000000000 --- 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 764ec598cb..94ede60ab6 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@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 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@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 # ℹī¸ 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@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 0d4a01360d..521735e6e5 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 5e5407a23b..b1eb70674b 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@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 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-timings.yml b/.github/workflows/e2e-timings.yml new file mode 100644 index 0000000000..e3f724d815 --- /dev/null +++ b/.github/workflows/e2e-timings.yml @@ -0,0 +1,53 @@ +name: E2E - Generate Timings + +on: + # run this workflow every night at 3am + schedule: + - cron: '28 3 * * *' + # or when the user triggers it from GitHub Actions page + workflow_dispatch: + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +permissions: + contents: write + +jobs: + timings: + runs-on: ubuntu-latest + container: + 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 + - name: Setup Node.js + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 + with: + node-version-file: '.node-version' + - name: Install dependencies + uses: cypress-io/github-action@0da3c06ed8217b912deea9d8ee69630baed1737e # v6.7.6 + with: + runTests: false + - name: Cypress run + uses: cypress-io/github-action@0da3c06ed8217b912deea9d8ee69630baed1737e # v6.7.6 + id: cypress + with: + install: false + start: pnpm run dev:coverage + wait-on: 'http://localhost:9000' + browser: chrome + publish-summary: false + env: + VITEST_COVERAGE: true + CYPRESS_COMMIT: ${{ github.sha }} + SPLIT: 1 + SPLIT_INDEX: 0 + SPLIT_FILE: 'cypress/timings.json' + - name: Commit changes + uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4 + with: + add: 'cypress/timings.json' + author_name: 'github-actions[bot]' + author_email: '41898282+github-actions[bot]@users.noreply.github.com' + message: 'chore: update E2E timings' diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 2600b3fb84..c5bbc6e627 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 @@ -24,7 +28,6 @@ env: ) || github.event.before }} - shouldRunParallel: ${{ secrets.CYPRESS_RECORD_KEY != '' && !(github.event_name == 'push' && github.ref == 'refs/heads/develop') }} jobs: cache: runs-on: ubuntu-latest @@ -32,15 +35,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@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 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 +52,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@0da3c06ed8217b912deea9d8ee69630baed1737e # v6.7.6 with: # just perform install runTests: false @@ -76,28 +79,28 @@ jobs: strategy: fail-fast: false matrix: - containers: [1, 2, 3, 4] + containers: [1, 2, 3, 4, 5] 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@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 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@0da3c06ed8217b912deea9d8ee69630baed1737e # v6.7.6 with: runTests: false @@ -113,11 +116,8 @@ 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@0da3c06ed8217b912deea9d8ee69630baed1737e # v6.7.6 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 - if: ${{ env.shouldRunParallel == 'true' || ( matrix.containers == 1 ) }} with: install: false start: pnpm run dev:coverage @@ -125,19 +125,21 @@ jobs: browser: chrome # Disable recording if we don't have an API key # e.g. if this action was run from a fork - record: ${{ env.shouldRunParallel == 'true' }} - parallel: ${{ env.shouldRunParallel == 'true' }} + record: ${{ secrets.CYPRESS_RECORD_KEY != '' }} env: CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} VITEST_COVERAGE: true CYPRESS_COMMIT: ${{ github.sha }} ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }} - ARGOS_PARALLEL: ${{ env.shouldRunParallel == 'true' }} - ARGOS_PARALLEL_TOTAL: 4 + ARGOS_PARALLEL: true + ARGOS_PARALLEL_TOTAL: ${{ strategy.job-total }} ARGOS_PARALLEL_INDEX: ${{ matrix.containers }} + SPLIT: ${{ strategy.job-total }} + SPLIT_INDEX: ${{ strategy.job-index }} + SPLIT_FILE: 'cypress/timings.json' - 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 129bd62b67..87a6e958b2 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 bf54d7df28..0a2b74dfe7 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 632cd6ddc4..24e7ee35d2 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@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 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 0965903467..c9faaa0620 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 4ff5f41170..587ddae08b 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@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 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-draft.yml b/.github/workflows/release-draft.yml deleted file mode 100644 index 657bc767a3..0000000000 --- a/.github/workflows/release-draft.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Draft Release - -on: - push: - branches: - - master - -permissions: - contents: read - -jobs: - draft-release: - runs-on: ubuntu-latest - permissions: - contents: write # write permission is required to create a GitHub release - pull-requests: read # required to read PR titles/labels - steps: - - name: Draft Release - uses: release-drafter/release-drafter@v6 - with: - disable-autolabeler: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-preview-publish.yml b/.github/workflows/release-preview-publish.yml index 91e3ac9813..fa48d36594 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@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 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-preview.yml b/.github/workflows/release-preview.yml new file mode 100644 index 0000000000..7b7dba987e --- /dev/null +++ b/.github/workflows/release-preview.yml @@ -0,0 +1,43 @@ +name: Preview release + +on: + pull_request: + branches: [develop] + types: [opened, synchronize, labeled, ready_for_review] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.number }} + cancel-in-progress: true + +permissions: + contents: read + actions: write + +jobs: + preview: + if: ${{ github.repository_owner == 'mermaid-js' }} + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + issues: write + pull-requests: write + name: Publish preview release + timeout-minutes: 5 + steps: + - name: Checkout Repo + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 + + - name: Setup Node.js + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 + with: + cache: pnpm + node-version-file: '.node-version' + + - name: Install Packages + run: pnpm install --frozen-lockfile + + - name: Publish packages + run: pnpx pkg-pr-new publish --pnpm './packages/*' diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml deleted file mode 100644 index 4dcf709c01..0000000000 --- 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 new file mode 100644 index 0000000000..4e8e9cd838 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,46 @@ +name: Release + +on: + push: + branches: + - master + +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@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 + + - name: Setup Node.js + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 + with: + cache: pnpm + node-version-file: '.node-version' + + - name: Install Packages + run: pnpm install --frozen-lockfile + + - name: Create Release Pull Request or Publish to npm + id: changesets + uses: changesets/action@3de3850952bec538fde60aac71731376e57b9b57 # v1.4.8 + 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 0000000000..528e940452 --- /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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + persist-credentials: false + - name: Run analysis + uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 + 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@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + with: + sarif_file: results.sarif diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a0b284a68f..7323ec027e 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@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: cache: pnpm node-version-file: '.node-version' @@ -38,8 +38,12 @@ jobs: run: | pnpm exec vitest run ./packages/mermaid/src/diagrams/gantt/ganttDb.spec.ts --coverage + - name: Verify out-of-tree build with TypeScript + run: | + pnpm test:check:tsc + - 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 4c53797297..b854eeb4bb 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 f8f7696cde..1b26271f7c 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/.gitignore b/.gitignore index 7948faee49..7448f2a810 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,7 @@ cypress/snapshots/ .tsbuildinfo tsconfig.tsbuildinfo -knsv*.html +#knsv*.html local*.html stats/ diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 0000000000..280ff9592a --- /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 ad85fc42c2..cc173f4a57 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/.prettierignore b/.prettierignore index 7da0646e32..c700804260 100644 --- a/.prettierignore +++ b/.prettierignore @@ -16,3 +16,5 @@ generated/ # Ignore the files creates in /demos/dev except for example.html demos/dev/** !/demos/dev/example.html +# TODO: Lots of errors to fix +cypress/platform/state-refactor.html diff --git a/Dockerfile b/Dockerfile index 7bec3bd4b7..7be53d24ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,2 +1,13 @@ -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 + +RUN apk add --no-cache git~=2.43.4 \ + && git config --add --system safe.directory /mermaid + +ENV NODE_OPTIONS="--max_old_space_size=8192" + +EXPOSE 9000 3333 diff --git a/README.md b/README.md index 8d5eebfebe..456747132c 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.config.ts b/cypress.config.ts index 3346b5549b..b0257b9b2b 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,8 +1,9 @@ -import { defineConfig } from 'cypress'; -import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin'; -import coverage from '@cypress/code-coverage/task'; import eyesPlugin from '@applitools/eyes-cypress'; import { registerArgosTask } from '@argos-ci/cypress/task'; +import coverage from '@cypress/code-coverage/task'; +import { defineConfig } from 'cypress'; +import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin'; +import cypressSplit from 'cypress-split'; export default eyesPlugin( defineConfig({ @@ -13,6 +14,7 @@ export default eyesPlugin( specPattern: 'cypress/integration/**/*.{js,ts}', setupNodeEvents(on, config) { coverage(on, config); + cypressSplit(on, config); on('before:browser:launch', (browser, launchOptions) => { if (browser.name === 'chrome' && browser.isHeadless) { launchOptions.args.push('--window-size=1440,1024', '--force-device-scale-factor=1'); diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts index 133a350328..52da4a72ee 100644 --- a/cypress/helpers/util.ts +++ b/cypress/helpers/util.ts @@ -29,6 +29,7 @@ export const mermaidUrl = ( options: CypressMermaidConfig, api: boolean ): string => { + options.handDrawnSeed = 1; const codeObject: CodeObject = { code: graphStr, mermaid: options, @@ -73,7 +74,7 @@ export const imgSnapshotTest = ( export const urlSnapshotTest = ( url: string, - options: CypressMermaidConfig, + options: CypressMermaidConfig = {}, _api = false, validation?: any ): void => { diff --git a/cypress/integration/other/flowchart-elk.spec.js b/cypress/integration/other/flowchart-elk.spec.js deleted file mode 100644 index 22a6efc0f5..0000000000 --- a/cypress/integration/other/flowchart-elk.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -import { urlSnapshotTest, openURLAndVerifyRendering } from '../../helpers/util.ts'; - -describe('Flowchart elk', () => { - it('should use dagre as fallback', () => { - urlSnapshotTest('http://localhost:9000/flow-elk.html', { - name: 'flow-elk fallback to dagre', - }); - }); - it('should allow overriding with external package', () => { - urlSnapshotTest('http://localhost:9000/flow-elk.html?elk=true', { - name: 'flow-elk overriding dagre with elk', - }); - }); -}); diff --git a/cypress/integration/rendering/appli.spec.js b/cypress/integration/rendering/appli.spec.js index 5def968157..51eeb657eb 100644 --- a/cypress/integration/rendering/appli.spec.js +++ b/cypress/integration/rendering/appli.spec.js @@ -11,6 +11,27 @@ describe('Git Graph diagram', () => { {} ); }); + it('Should render subgraphs with title margins and edge labels', () => { + imgSnapshotTest( + `flowchart LR + + subgraph TOP + direction TB + subgraph B1 + direction RL + i1 --lb1-->f1 + end + subgraph B2 + direction BT + i2 --lb2-->f2 + end + end + A --lb3--> TOP --lb4--> B + B1 --lb5--> B2 + `, + { flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } } } + ); + }); // it(`ultraFastTest`, function () { // // Navigate to the url we want to test // // ⭐ī¸ Note to see visual bugs, run the test using the above URL for the 1st run. diff --git a/cypress/integration/rendering/architecture.spec.ts b/cypress/integration/rendering/architecture.spec.ts new file mode 100644 index 0000000000..25326ff804 --- /dev/null +++ b/cypress/integration/rendering/architecture.spec.ts @@ -0,0 +1,233 @@ +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} + ` + ); + }); + + it('should render an architecture diagram with a resonable height', () => { + imgSnapshotTest( + `architecture-beta + group federated(cloud)[Federated Environment] + service server1(server)[System] in federated + service edge(server)[Edge Device] in federated + server1:R -- L:edge + + group on_prem(cloud)[Hub] + service firewall(server)[Firewall Device] in on_prem + service server(server)[Server] in on_prem + firewall:R -- L:server + + service db1(database)[db1] in on_prem + service db2(database)[db2] in on_prem + service db3(database)[db3] in on_prem + service db4(database)[db4] in on_prem + service db5(database)[db5] in on_prem + service db6(database)[db6] in on_prem + + junction mid in on_prem + server:B -- T:mid + + junction 1Leftofmid in on_prem + 1Leftofmid:R -- L:mid + 1Leftofmid:B -- T:db1 + + junction 2Leftofmid in on_prem + 2Leftofmid:R -- L:1Leftofmid + 2Leftofmid:B -- T:db2 + + junction 3Leftofmid in on_prem + 3Leftofmid:R -- L:2Leftofmid + 3Leftofmid:B -- T:db3 + + junction 1RightOfMid in on_prem + mid:R -- L:1RightOfMid + 1RightOfMid:B -- T:db4 + + junction 2RightOfMid in on_prem + 1RightOfMid:R -- L:2RightOfMid + 2RightOfMid:B -- T:db5 + + junction 3RightOfMid in on_prem + 2RightOfMid:R -- L:3RightOfMid + 3RightOfMid:B -- T:db6 + + edge:R -- L:firewall + ` + ); + }); +}); + +// Skipped as the layout is not deterministic, and causes issues in E2E tests. +describe.skip('architecture - external', () => { + it('should allow adding external icons', () => { + urlSnapshotTest('http://localhost:9000/architecture-external.html'); + }); +}); diff --git a/cypress/integration/rendering/block.spec.js b/cypress/integration/rendering/block.spec.js index 9d62c642dc..f5d5103e89 100644 --- a/cypress/integration/rendering/block.spec.js +++ b/cypress/integration/rendering/block.spec.js @@ -236,7 +236,7 @@ describe('Block diagram', () => { ); }); - it('BL16: width alignment - blocks shold be equal in width', () => { + it('BL17: width alignment - blocks shold be equal in width', () => { imgSnapshotTest( `block-beta A("This is the text") @@ -247,7 +247,7 @@ describe('Block diagram', () => { ); }); - it('BL17: block types 1 - square, rounded and circle', () => { + it('BL18: block types 1 - square, rounded and circle', () => { imgSnapshotTest( `block-beta A["square"] @@ -258,7 +258,7 @@ describe('Block diagram', () => { ); }); - it('BL18: block types 2 - odd, diamond and hexagon', () => { + it('BL19: block types 2 - odd, diamond and hexagon', () => { imgSnapshotTest( `block-beta A>"rect_left_inv_arrow"] @@ -269,7 +269,7 @@ describe('Block diagram', () => { ); }); - it('BL19: block types 3 - stadium', () => { + it('BL20: block types 3 - stadium', () => { imgSnapshotTest( `block-beta A(["stadium"]) @@ -278,7 +278,7 @@ describe('Block diagram', () => { ); }); - it('BL20: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => { + it('BL21: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => { imgSnapshotTest( `block-beta A[/"lean right"/] @@ -290,7 +290,7 @@ describe('Block diagram', () => { ); }); - it('BL21: block types 1 - square, rounded and circle', () => { + it('BL22: block types 1 - square, rounded and circle', () => { imgSnapshotTest( `block-beta A["square"] @@ -301,7 +301,7 @@ describe('Block diagram', () => { ); }); - it('BL22: sizing - it should be possible to make a block wider', () => { + it('BL23: sizing - it should be possible to make a block wider', () => { imgSnapshotTest( `block-beta A("rounded"):2 @@ -312,7 +312,7 @@ describe('Block diagram', () => { ); }); - it('BL23: sizing - it should be possible to make a composite block wider', () => { + it('BL24: sizing - it should be possible to make a composite block wider', () => { imgSnapshotTest( `block-beta block:2 @@ -324,7 +324,7 @@ describe('Block diagram', () => { ); }); - it('BL24: block in the middle with space on each side', () => { + it('BL25: block in the middle with space on each side', () => { imgSnapshotTest( `block-beta columns 3 @@ -335,7 +335,7 @@ describe('Block diagram', () => { {} ); }); - it('BL25: space and an edge', () => { + it('BL26: space and an edge', () => { imgSnapshotTest( `block-beta columns 5 @@ -345,7 +345,7 @@ describe('Block diagram', () => { {} ); }); - it('BL26: block sizes for regular blocks', () => { + it('BL27: block sizes for regular blocks', () => { imgSnapshotTest( `block-beta columns 3 @@ -354,7 +354,7 @@ describe('Block diagram', () => { {} ); }); - it('BL27: composite block with a set width - f should use the available space', () => { + it('BL28: composite block with a set width - f should use the available space', () => { imgSnapshotTest( `block-beta columns 3 @@ -363,11 +363,12 @@ describe('Block diagram', () => { f end g - `, + `, {} ); }); - it('BL23: composite block with a set width - f and g should split the available space', () => { + + it('BL29: composite block with a set width - f and g should split the available space', () => { imgSnapshotTest( `block-beta columns 3 @@ -379,7 +380,7 @@ describe('Block diagram', () => { h i j - `, + `, {} ); }); diff --git a/cypress/integration/rendering/c4.spec.js b/cypress/integration/rendering/c4.spec.js index f699bd4298..00e71adec8 100644 --- a/cypress/integration/rendering/c4.spec.js +++ b/cypress/integration/rendering/c4.spec.js @@ -1,7 +1,7 @@ import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('C4 diagram', () => { - it('should render a simple C4Context diagram', () => { + it('C4.1 should render a simple C4Context diagram', () => { imgSnapshotTest( ` C4Context @@ -31,7 +31,7 @@ describe('C4 diagram', () => { {} ); }); - it('should render a simple C4Container diagram', () => { + it('C4.2 should render a simple C4Container diagram', () => { imgSnapshotTest( ` C4Container @@ -50,7 +50,7 @@ describe('C4 diagram', () => { {} ); }); - it('should render a simple C4Component diagram', () => { + it('C4.3 should render a simple C4Component diagram', () => { imgSnapshotTest( ` C4Component @@ -68,7 +68,7 @@ describe('C4 diagram', () => { {} ); }); - it('should render a simple C4Dynamic diagram', () => { + it('C4.4 should render a simple C4Dynamic diagram', () => { imgSnapshotTest( ` C4Dynamic @@ -91,7 +91,7 @@ describe('C4 diagram', () => { {} ); }); - it('should render a simple C4Deployment diagram', () => { + it('C4.5 should render a simple C4Deployment diagram', () => { imgSnapshotTest( ` C4Deployment diff --git a/cypress/integration/rendering/classDiagram-elk-v3.spec.js b/cypress/integration/rendering/classDiagram-elk-v3.spec.js new file mode 100644 index 0000000000..ee6ca0b2b4 --- /dev/null +++ b/cypress/integration/rendering/classDiagram-elk-v3.spec.js @@ -0,0 +1,1037 @@ +import { imgSnapshotTest } from '../../helpers/util.ts'; +describe('Class diagram V3 ELK', () => { + it('ELK-0: should render a simple class diagram', () => { + imgSnapshotTest( + ` + classDiagram + + classA -- classB : Inheritance + classA -- classC : link + classC -- classD : link + classB -- classD + + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-1: should render a simple class diagram', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-1.1: should render a simple class diagram without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: false, layout: 'elk' } + ); + }); + + it('ELK-2: should render a simple class diagrams with cardinality', () => { + imgSnapshotTest( + ` + classDiagram + Class01 "1" <|--|> "*" AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 "1" <--> "*" C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-2.1: should render a simple class diagrams with cardinality without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 "1" <|--|> "*" AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 "1" <--> "*" C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: false, layout: 'elk' } + ); + }); + + it('ELK-2.2 should render a simple class diagram with different visibilities', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class01 : -privateMethod() + Class01 : +publicMethod() + Class01 : #protectedMethod() + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK-2.3 should render a simple class diagram with different visibilities without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class01 : -privateMethod() + Class01 : +publicMethod() + Class01 : #protectedMethod() + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + `, + { logLevel: 1, htmlLabels: false, layout: 'elk' } + ); + }); + + it('ELK-3: should render multiple class diagrams', () => { + imgSnapshotTest( + [ + ` + classDiagram + Class01 "1" <|--|> "*" AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 "1" <--> "*" C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + ` + classDiagram + Class01 "1" <|--|> "*" AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 "1" <--> "*" C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + ], + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-4: should render a simple class diagram with comments', () => { + imgSnapshotTest( + ` + classDiagram + %% this is a comment + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-5: should render a simple class diagram with abstract method', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + Class01 : someMethod()* + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-5.1: should render a simple class diagram with abstract method without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + Class01 : someMethod()* + `, + { logLevel: 1, htmlLabels: false, layout: 'elk' } + ); + }); + + it('ELK-6: should render a simple class diagram with static method', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + Class01 : someMethod()$ + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-6.1: should render a simple class diagram with static method without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + Class01 : someMethod()$ + `, + { logLevel: 1, htmlLabels: false, layout: 'elk' } + ); + }); + + it('ELK-7: should render a simple class diagram with Generic class', () => { + imgSnapshotTest( + ` + classDiagram + class Class01~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-7.1: should render a simple class diagram with Generic class without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class01~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: false, layout: 'elk' } + ); + }); + + it('ELK-8: should render a simple class diagram with Generic class and relations', () => { + imgSnapshotTest( + ` + classDiagram + Class01~T~ <|-- AveryLongClass : Cool + Class03~T~ *-- Class04~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-9: should render a simple class diagram with clickable link', () => { + imgSnapshotTest( + ` + classDiagram + Class01~T~ <|-- AveryLongClass : Cool + Class03~T~ *-- Class04~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + link Class01 "google.com" "A Tooltip" + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-10: should render a simple class diagram with clickable callback', () => { + imgSnapshotTest( + ` + classDiagram + Class01~T~ <|-- AveryLongClass : Cool + Class03~T~ *-- Class04~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + callback Class01 "functionCall" "A Tooltip" + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-11: should render a simple class diagram with return type on method', () => { + imgSnapshotTest( + ` + classDiagram + class Class10~T~ { + int[] id + test(int[] ids) bool + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-11.1: should render a simple class diagram with return type on method without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10~T~ { + int[] id + test(int[] ids) bool + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: false, layout: 'elk' } + ); + }); + + it('ELK-12: should render a simple class diagram with generic types', () => { + imgSnapshotTest( + ` + classDiagram + class Class10~T~ { + int[] id + List~int~ ids + test(List~int~ ids) List~bool~ + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-12.1: should render a simple class diagram with generic types without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10~T~ { + int[] id + List~int~ ids + test(List~int~ ids) List~bool~ + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: false, layout: 'elk' } + ); + }); + + it('ELK-13: should render a simple class diagram with css classes applied', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 { + int[] id + List~int~ ids + test(List~int~ ids) List~bool~ + testArray() bool[] + } + + cssClass "Class10" exClass2 + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-14: should render a simple class diagram with css classes applied directly', () => { + imgSnapshotTest( + ` + classDiagram + class Class10:::exClass2 { + int[] id + List~int~ ids + test(List~int~ ids) List~bool~ + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-15: should render a simple class diagram with css classes applied two multiple classes', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + class Class20 + + cssClass "Class10, class20" exClass2 + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-16a: should render a simple class diagram with static field', () => { + imgSnapshotTest( + ` + classDiagram + class Foo { + +String bar$ + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-16b: should handle the direction statement with TB', () => { + imgSnapshotTest( + ` + classDiagram + direction TB + class Student { + -idCard : IdCard + } + class IdCard{ + -id : int + -name : string + } + class Bike{ + -id : int + -name : string + } + Student "1" --o "1" IdCard : carries + Student "1" --o "1" Bike : rides + + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK-17a: should handle the direction statement with BT', () => { + imgSnapshotTest( + ` + classDiagram + direction BT + class Student { + -idCard : IdCard + } + class IdCard{ + -id : int + -name : string + } + class Bike{ + -id : int + -name : string + } + Student "1" --o "1" IdCard : carries + Student "1" --o "1" Bike : rides + + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK-17b: should handle the direction statement with RL', () => { + imgSnapshotTest( + ` + classDiagram + direction RL + class Student { + -idCard : IdCard + } + class IdCard{ + -id : int + -name : string + } + class Bike{ + -id : int + -name : string + } + Student "1" --o "1" IdCard : carries + Student "1" --o "1" Bike : rides + + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-18a: should handle the direction statement with LR', () => { + imgSnapshotTest( + ` + classDiagram + direction LR + class Student { + -idCard : IdCard + } + class IdCard{ + -id : int + -name : string + } + class Bike{ + -id : int + -name : string + } + Student "1" --o "1" IdCard : carries + Student "1" --o "1" Bike : rides + + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-18b: should render a simple class diagram with notes', () => { + imgSnapshotTest( + ` + classDiagram + note "I love this diagram!\nDo you love it?" + class Class10 { + int id + size() + } + note for Class10 "Cool class\nI said it's very cool class!" + + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK-1433: should render a simple class with a title', () => { + imgSnapshotTest( + `--- +title: simple class diagram +--- +classDiagram +class Class10 +`, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK: should render a class with text label', () => { + imgSnapshotTest( + `classDiagram + class C1["Class 1 with text label"] + C1 --> C2`, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK: should render two classes with text labels', () => { + imgSnapshotTest( + `classDiagram + class C1["Class 1 with text label"] + class C2["Class 2 with chars @?"] + C1 --> C2`, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should render a class with a text label, members and annotation', () => { + imgSnapshotTest( + `classDiagram + class C1["Class 1 with text label"] { + <<interface>> + +member1 + } + C1 --> C2`, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should render multiple classes with same text labels', () => { + imgSnapshotTest( + `classDiagram + class C1["Class with text label"] + class C2["Class with text label"] + class C3["Class with text label"] + C1 --> C2 + C3 ..> C2 + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should render classes with different text labels', () => { + imgSnapshotTest( + `classDiagram + class C1["OneWord"] + class C2["With, Comma"] + class C3["With (Brackets)"] + class C4["With [Brackets]"] + class C5["With {Brackets}"] + class C7["With 1 number"] + class C8["With . period..."] + class C9["With - dash"] + class C10["With _ underscore"] + class C11["With ' single quote"] + class C12["With ~!@#$%^&*()_+=-/?"] + class C13["With Città foreign language"] + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + + it('ELK: should render classLabel if class has already been defined earlier', () => { + imgSnapshotTest( + `classDiagram + Animal <|-- Duck + class Duck["Duck with text label"] + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should add classes namespaces', function () { + imgSnapshotTest( + ` + classDiagram + namespace Namespace1 { + class C1 + class C2 + } + C1 --> C2 + class C3 + class C4 + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with no members', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with no members if hideEmptyMembersBox is enabled', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + `, + { logLevel: 1, class: { htmlLabels: true, hideEmptyMembersBox: true }, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with no attributes, only methods', () => { + imgSnapshotTest( + ` + classDiagram + class Duck { + +swim() + +quack() + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with no methods, only attributes', () => { + imgSnapshotTest( + ` + classDiagram + class Duck { + +String beakColor + +int age + +float weight + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with style definition', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + style Class10 fill:#f9f,stroke:#333,stroke-width:4px + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with style definition without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + style Class10 fill:#f9f,stroke:#333,stroke-width:4px + `, + { logLevel: 1, htmlLabels: false, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with classDef definitions', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + classDef pink fill:#f9f + classDef bold stroke:#333,stroke-width:6px,color:#fff + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with classDefs being applied', () => { + imgSnapshotTest( + ` + classDiagram + class Class10:::pink + cssClass "Class10" bold + classDef pink fill:#f9f + classDef bold stroke:#333,stroke-width:6px,color:#fff + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with classDefs being applied without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10:::pink + cssClass "Class10" bold + classDef pink fill:#f9f + classDef bold stroke:#333,stroke-width:6px,color:#fff + `, + { logLevel: 1, htmlLabels: false, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with markdown styling', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 { + +attribute *italic** + ~attribute **bold*** + _italicmethod_() + __boldmethod__() + _+_swim_()a_ + __+quack() test__ + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with markdown styling without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 { + +attribute *italic** + ~attribute **bold*** + _italicmethod_() + __boldmethod__() + _+_swim_()a_ + __+quack() test__ + } + `, + { logLevel: 1, htmlLabels: false, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with the handDrawn look', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn', layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with styles and the handDrawn look', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + style Class10 fill:#f9f,stroke:#333,stroke-width:4px,color:white + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn', layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with styles and the handDrawn look without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + style Class10 fill:#f9f,stroke:#333,stroke-width:4px,color:white + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn', layout: 'elk' } + ); + }); + it('ELK: should render a full class diagram with the handDrawn look', () => { + imgSnapshotTest( + ` + classDiagram + note "I love this diagram!\nDo you love it?" + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + note for Class10 "Cool class\nI said it's very cool class!" + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn', layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with a custom theme', () => { + imgSnapshotTest( + ` + %%{ + init: { + 'theme': 'base', + 'themeVariables': { + 'primaryColor': '#BB2528', + 'primaryTextColor': '#fff', + 'primaryBorderColor': '#7C0000', + 'lineColor': '#F83d29', + 'secondaryColor': '#006100', + 'tertiaryColor': '#fff' + } + } + }%% + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); + it('ELK: should render a simple class diagram with a custom theme and the handDrawn look', () => { + imgSnapshotTest( + ` + %%{ + init: { + 'theme': 'base', + 'themeVariables': { + 'primaryColor': '#BB2528', + 'primaryTextColor': '#fff', + 'primaryBorderColor': '#7C0000', + 'lineColor': '#F83d29', + 'secondaryColor': '#006100', + 'tertiaryColor': '#fff' + } + } + }%% + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn', layout: 'elk' } + ); + }); + it('ELK: should render a full class diagram using elk', () => { + imgSnapshotTest( + ` + classDiagram + note "I love this diagram!\nDo you love it?" + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + note for Class10 "Cool class\nI said it's very cool class!" + `, + { logLevel: 1, htmlLabels: true, layout: 'elk' } + ); + }); +}); diff --git a/cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js b/cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js new file mode 100644 index 0000000000..32a82c0897 --- /dev/null +++ b/cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js @@ -0,0 +1,1041 @@ +import { imgSnapshotTest } from '../../helpers/util.ts'; +describe('Class diagram V3 HD', () => { + it('HD-0: should render a simple class diagram', () => { + imgSnapshotTest( + ` + classDiagram + + classA -- classB : Inheritance + classA -- classC : link + classC -- classD : link + classB -- classD + + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-1: should render a simple class diagram', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-1.1: should render a simple class diagram without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + + it('HD-2: should render a simple class diagrams with cardinality', () => { + imgSnapshotTest( + ` + classDiagram + Class01 "1" <|--|> "*" AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 "1" <--> "*" C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-2.1: should render a simple class diagrams with cardinality without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 "1" <|--|> "*" AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 "1" <--> "*" C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + + it('HD-2.2 should render a simple class diagram with different visibilities', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class01 : -privateMethod() + Class01 : +publicMethod() + Class01 : #protectedMethod() + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD-2.3 should render a simple class diagram with different visibilities without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class01 : -privateMethod() + Class01 : +publicMethod() + Class01 : #protectedMethod() + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + + it('HD-3: should render multiple class diagrams', () => { + imgSnapshotTest( + [ + ` + classDiagram + Class01 "1" <|--|> "*" AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 "1" <--> "*" C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + ` + classDiagram + Class01 "1" <|--|> "*" AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 "1" <--> "*" C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + ], + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-4: should render a simple class diagram with comments', () => { + imgSnapshotTest( + ` + classDiagram + %% this is a comment + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-5: should render a simple class diagram with abstract method', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + Class01 : someMethod()* + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-5.1: should render a simple class diagram with abstract method without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + Class01 : someMethod()* + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + + it('HD-6: should render a simple class diagram with static method', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + Class01 : someMethod()$ + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-6.1: should render a simple class diagram with static method without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + Class01 : someMethod()$ + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + + it('HD-7: should render a simple class diagram with Generic class', () => { + imgSnapshotTest( + ` + classDiagram + class Class01~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-7.1: should render a simple class diagram with Generic class without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class01~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + + it('HD-8: should render a simple class diagram with Generic class and relations', () => { + imgSnapshotTest( + ` + classDiagram + Class01~T~ <|-- AveryLongClass : Cool + Class03~T~ *-- Class04~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-9: should render a simple class diagram with clickable link', () => { + imgSnapshotTest( + ` + classDiagram + Class01~T~ <|-- AveryLongClass : Cool + Class03~T~ *-- Class04~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + link Class01 "google.com" "A Tooltip" + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-10: should render a simple class diagram with clickable callback', () => { + imgSnapshotTest( + ` + classDiagram + Class01~T~ <|-- AveryLongClass : Cool + Class03~T~ *-- Class04~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + callback Class01 "functionCall" "A Tooltip" + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-11: should render a simple class diagram with return type on method', () => { + imgSnapshotTest( + ` + classDiagram + class Class10~T~ { + int[] id + test(int[] ids) bool + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-11.1: should render a simple class diagram with return type on method without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10~T~ { + int[] id + test(int[] ids) bool + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + + it('HD-12: should render a simple class diagram with generic types', () => { + imgSnapshotTest( + ` + classDiagram + class Class10~T~ { + int[] id + List~int~ ids + test(List~int~ ids) List~bool~ + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-12.1: should render a simple class diagram with generic types without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10~T~ { + int[] id + List~int~ ids + test(List~int~ ids) List~bool~ + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + + it('HD-13: should render a simple class diagram with css classes applied', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 { + int[] id + List~int~ ids + test(List~int~ ids) List~bool~ + testArray() bool[] + } + + cssClass "Class10" exClass2 + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-14: should render a simple class diagram with css classes applied directly', () => { + imgSnapshotTest( + ` + classDiagram + class Class10:::exClass2 { + int[] id + List~int~ ids + test(List~int~ ids) List~bool~ + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-15: should render a simple class diagram with css classes applied two multiple classes', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + class Class20 + + cssClass "Class10, class20" exClass2 + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-16a: should render a simple class diagram with static field', () => { + imgSnapshotTest( + ` + classDiagram + class Foo { + +String bar$ + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-16b: should handle the direction statement with TB', () => { + imgSnapshotTest( + ` + classDiagram + direction TB + class Student { + -idCard : IdCard + } + class IdCard{ + -id : int + -name : string + } + class Bike{ + -id : int + -name : string + } + Student "1" --o "1" IdCard : carries + Student "1" --o "1" Bike : rides + + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD-17a: should handle the direction statement with BT', () => { + imgSnapshotTest( + ` + classDiagram + direction BT + class Student { + -idCard : IdCard + } + class IdCard{ + -id : int + -name : string + } + class Bike{ + -id : int + -name : string + } + Student "1" --o "1" IdCard : carries + Student "1" --o "1" Bike : rides + + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD-17b: should handle the direction statement with RL', () => { + imgSnapshotTest( + ` + classDiagram + direction RL + class Student { + -idCard : IdCard + } + class IdCard{ + -id : int + -name : string + } + class Bike{ + -id : int + -name : string + } + Student "1" --o "1" IdCard : carries + Student "1" --o "1" Bike : rides + + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-18a: should handle the direction statement with LR', () => { + imgSnapshotTest( + ` + classDiagram + direction LR + class Student { + -idCard : IdCard + } + class IdCard{ + -id : int + -name : string + } + class Bike{ + -id : int + -name : string + } + Student "1" --o "1" IdCard : carries + Student "1" --o "1" Bike : rides + + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-18b: should render a simple class diagram with notes', () => { + imgSnapshotTest( + ` + classDiagram + note "I love this diagram!\nDo you love it?" + class Class10 { + int id + size() + } + note for Class10 "Cool class\nI said it's very cool class!" + + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD-1433: should render a simple class with a title', () => { + imgSnapshotTest( + `--- +title: simple class diagram +--- +classDiagram +class Class10 +`, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD: should render a class with text label', () => { + imgSnapshotTest( + `classDiagram + class C1["Class 1 with text label"] + C1 --> C2`, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD: should render two classes with text labels', () => { + imgSnapshotTest( + `classDiagram + class C1["Class 1 with text label"] + class C2["Class 2 with chars @?"] + C1 --> C2`, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a class with a text label, members and annotation', () => { + imgSnapshotTest( + `classDiagram + class C1["Class 1 with text label"] { + <<interface>> + +member1 + } + C1 --> C2`, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render multiple classes with same text labels', () => { + imgSnapshotTest( + `classDiagram +class C1["Class with text label"] +class C2["Class with text label"] +class C3["Class with text label"] +C1 --> C2 +C3 ..> C2 + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render classes with different text labels', () => { + imgSnapshotTest( + `classDiagram +class C1["OneWord"] +class C2["With, Comma"] +class C3["With (Brackets)"] +class C4["With [Brackets]"] +class C5["With {Brackets}"] +class C7["With 1 number"] +class C8["With . period..."] +class C9["With - dash"] +class C10["With _ underscore"] +class C11["With ' single quote"] +class C12["With ~!@#$%^&*()_+=-/?"] +class C13["With Città foreign language"] + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + + it('HD: should render classLabel if class has already been defined earlier', () => { + imgSnapshotTest( + `classDiagram + Animal <|-- Duck + class Duck["Duck with text label"] +`, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should add classes namespaces', function () { + imgSnapshotTest( + ` + classDiagram + namespace Namespace1 { + class C1 + class C2 + } + C1 --> C2 + class C3 + class C4 + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with no members', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with no members if hideEmptyMembersBox is enabled', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + `, + { logLevel: 1, class: { htmlLabels: true, hideEmptyMembersBox: true }, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with no attributes, only methods', () => { + imgSnapshotTest( + ` + classDiagram + class Duck { + +swim() + +quack() + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with no methods, only attributes', () => { + imgSnapshotTest( + ` + classDiagram + class Duck { + +String beakColor + +int age + +float weight + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with style definition', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + style Class10 fill:#f9f,stroke:#333,stroke-width:4px + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with style definition without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + style Class10 fill:#f9f,stroke:#333,stroke-width:4px + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with classDef definitions', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + classDef pink fill:#f9f + classDef bold stroke:#333,stroke-width:6px,color:#fff + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with classDefs being applied', () => { + imgSnapshotTest( + ` + classDiagram + class Class10:::pink + cssClass "Class10" bold + classDef pink fill:#f9f + classDef bold stroke:#333,stroke-width:6px,color:#fff + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with classDefs being applied without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10:::pink + cssClass "Class10" bold + classDef pink fill:#f9f + classDef bold stroke:#333,stroke-width:6px,color:#fff + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with markdown styling', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 { + +attribute *italic** + ~attribute **bold*** + _italicmethod_() + __boldmethod__() + _+_swim_()a_ + __+quack() test__ + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with markdown styling without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 { + +attribute *italic** + ~attribute **bold*** + _italicmethod_() + __boldmethod__() + _+_swim_()a_ + __+quack() test__ + } + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with the handDrawn look', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with styles and the handDrawn look', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + style Class10 fill:#f9f,stroke:#333,stroke-width:4px,color:white + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with styles and the handDrawn look without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + style Class10 fill:#f9f,stroke:#333,stroke-width:4px,color:white + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + it('HD: should render a full class diagram with the handDrawn look', () => { + imgSnapshotTest( + ` + classDiagram + note "I love this diagram!\nDo you love it?" + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + note for Class10 "Cool class\nI said it's very cool class!" + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with a custom theme', () => { + imgSnapshotTest( + ` + %%{ + init: { + 'theme': 'base', + 'themeVariables': { + 'primaryColor': '#BB2528', + 'primaryTextColor': '#fff', + 'primaryBorderColor': '#7C0000', + 'lineColor': '#F83d29', + 'secondaryColor': '#006100', + 'tertiaryColor': '#fff' + } + } + }%% + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a simple class diagram with a custom theme and the handDrawn look', () => { + imgSnapshotTest( + ` + %%{ + init: { + 'theme': 'base', + 'themeVariables': { + 'primaryColor': '#BB2528', + 'primaryTextColor': '#fff', + 'primaryBorderColor': '#7C0000', + 'lineColor': '#F83d29', + 'secondaryColor': '#006100', + 'tertiaryColor': '#fff' + } + } + }%% + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('HD: should render a full class diagram using elk', () => { + imgSnapshotTest( + ` +--- + config: + layout: elk +--- + classDiagram + note "I love this diagram!\nDo you love it?" + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + note for Class10 "Cool class\nI said it's very cool class!" + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); +}); diff --git a/cypress/integration/rendering/classDiagram-v2.spec.js b/cypress/integration/rendering/classDiagram-v2.spec.js index 20a1aea0ab..0c5dbc04b0 100644 --- a/cypress/integration/rendering/classDiagram-v2.spec.js +++ b/cypress/integration/rendering/classDiagram-v2.spec.js @@ -76,7 +76,7 @@ describe('Class diagram V2', () => { ); }); - it('should render a simple class diagram with different visibilities', () => { + it('2.1 should render a simple class diagram with different visibilities', () => { imgSnapshotTest( ` classDiagram-v2 @@ -93,7 +93,7 @@ describe('Class diagram V2', () => { ); }); - it('should render multiple class diagrams', () => { + it('3: should render multiple class diagrams', () => { imgSnapshotTest( [ ` @@ -581,4 +581,63 @@ class C13["With Città foreign language"] { logLevel: 1, flowchart: { htmlLabels: false } } ); }); + + it('renders a class diagram with a generic class in a namespace', () => { + const diagramDefinition = ` + classDiagram-v2 + namespace Company.Project.Module { + class GenericClass~T~ { + +addItem(item: T) + +getItem() T + } + } + `; + + imgSnapshotTest(diagramDefinition); + }); + + it('renders a class diagram with nested namespaces and relationships', () => { + const diagramDefinition = ` + classDiagram-v2 + namespace Company.Project.Module.SubModule { + class Report { + +generatePDF(data: List) + +generateCSV(data: List) + } + } + namespace Company.Project.Module { + class Admin { + +generateReport() + } + } + Admin --> Report : generates + `; + + imgSnapshotTest(diagramDefinition); + }); + + it('renders a class diagram with multiple classes and relationships in a namespace', () => { + const diagramDefinition = ` + classDiagram-v2 + namespace Company.Project.Module { + class User { + +login(username: String, password: String) + +logout() + } + class Admin { + +addUser(user: User) + +removeUser(user: User) + +generateReport() + } + class Report { + +generatePDF(reportData: List) + +generateCSV(reportData: List) + } + } + Admin --> User : manages + Admin --> Report : generates + `; + + imgSnapshotTest(diagramDefinition); + }); }); diff --git a/cypress/integration/rendering/classDiagram-v3.spec.js b/cypress/integration/rendering/classDiagram-v3.spec.js new file mode 100644 index 0000000000..626d6fcea4 --- /dev/null +++ b/cypress/integration/rendering/classDiagram-v3.spec.js @@ -0,0 +1,1031 @@ +import { imgSnapshotTest } from '../../helpers/util.ts'; +describe('Class diagram V3', () => { + it('0: should render a simple class diagram', () => { + imgSnapshotTest( + ` + classDiagram + + classA -- classB : Inheritance + classA -- classC : link + classC -- classD : link + classB -- classD + + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('1: should render a simple class diagram', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('1.1: should render a simple class diagram without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: false } + ); + }); + + it('2: should render a simple class diagrams with cardinality', () => { + imgSnapshotTest( + ` + classDiagram + Class01 "1" <|--|> "*" AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 "1" <--> "*" C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('2.1: should render a simple class diagrams with cardinality without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 "1" <|--|> "*" AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 "1" <--> "*" C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: false } + ); + }); + + it('2.2 should render a simple class diagram with different visibilities', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class01 : -privateMethod() + Class01 : +publicMethod() + Class01 : #protectedMethod() + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + `, + { logLevel: 1, htmlLabels: true } + ); + }); + it('2.3 should render a simple class diagram with different visibilities without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class01 : -privateMethod() + Class01 : +publicMethod() + Class01 : #protectedMethod() + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + `, + { logLevel: 1, htmlLabels: false } + ); + }); + + it('3: should render multiple class diagrams', () => { + imgSnapshotTest( + [ + ` + classDiagram + Class01 "1" <|--|> "*" AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 "1" <--> "*" C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + ` + classDiagram + Class01 "1" <|--|> "*" AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 "1" <--> "*" C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + ], + { logLevel: 1, htmlLabels: true } + ); + }); + + it('4: should render a simple class diagram with comments', () => { + imgSnapshotTest( + ` + classDiagram + %% this is a comment + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('5: should render a simple class diagram with abstract method', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + Class01 : someMethod()* + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('5.1: should render a simple class diagram with abstract method without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + Class01 : someMethod()* + `, + { logLevel: 1, htmlLabels: false } + ); + }); + + it('6: should render a simple class diagram with static method', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + Class01 : someMethod()$ + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('6.1: should render a simple class diagram with static method without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + Class01 : someMethod()$ + `, + { logLevel: 1, htmlLabels: false } + ); + }); + + it('7: should render a simple class diagram with Generic class', () => { + imgSnapshotTest( + ` + classDiagram + class Class01~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('7.1: should render a simple class diagram with Generic class without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class01~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: false } + ); + }); + + it('8: should render a simple class diagram with Generic class and relations', () => { + imgSnapshotTest( + ` + classDiagram + Class01~T~ <|-- AveryLongClass : Cool + Class03~T~ *-- Class04~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('9: should render a simple class diagram with clickable link', () => { + imgSnapshotTest( + ` + classDiagram + Class01~T~ <|-- AveryLongClass : Cool + Class03~T~ *-- Class04~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + link Class01 "google.com" "A Tooltip" + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('10: should render a simple class diagram with clickable callback', () => { + imgSnapshotTest( + ` + classDiagram + Class01~T~ <|-- AveryLongClass : Cool + Class03~T~ *-- Class04~T~ + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10~T~ { + <<service>> + int id + test() + } + callback Class01 "functionCall" "A Tooltip" + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('11: should render a simple class diagram with return type on method', () => { + imgSnapshotTest( + ` + classDiagram + class Class10~T~ { + int[] id + test(int[] ids) bool + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('11.1: should render a simple class diagram with return type on method without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10~T~ { + int[] id + test(int[] ids) bool + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: false } + ); + }); + + it('12: should render a simple class diagram with generic types', () => { + imgSnapshotTest( + ` + classDiagram + class Class10~T~ { + int[] id + List~int~ ids + test(List~int~ ids) List~bool~ + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('12.1: should render a simple class diagram with generic types without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10~T~ { + int[] id + List~int~ ids + test(List~int~ ids) List~bool~ + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: false } + ); + }); + + it('13: should render a simple class diagram with css classes applied', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 { + int[] id + List~int~ ids + test(List~int~ ids) List~bool~ + testArray() bool[] + } + + cssClass "Class10" exClass2 + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('14: should render a simple class diagram with css classes applied directly', () => { + imgSnapshotTest( + ` + classDiagram + class Class10:::exClass2 { + int[] id + List~int~ ids + test(List~int~ ids) List~bool~ + testArray() bool[] + } + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('15: should render a simple class diagram with css classes applied two multiple classes', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + class Class20 + + cssClass "Class10, class20" exClass2 + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('16a: should render a simple class diagram with static field', () => { + imgSnapshotTest( + ` + classDiagram + class Foo { + +String bar$ + } + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('16b: should handle the direction statement with TB', () => { + imgSnapshotTest( + ` + classDiagram + direction TB + class Student { + -idCard : IdCard + } + class IdCard{ + -id : int + -name : string + } + class Bike{ + -id : int + -name : string + } + Student "1" --o "1" IdCard : carries + Student "1" --o "1" Bike : rides + + `, + { logLevel: 1, htmlLabels: true } + ); + }); + it('17a: should handle the direction statement with BT', () => { + imgSnapshotTest( + ` + classDiagram + direction BT + class Student { + -idCard : IdCard + } + class IdCard{ + -id : int + -name : string + } + class Bike{ + -id : int + -name : string + } + Student "1" --o "1" IdCard : carries + Student "1" --o "1" Bike : rides + + `, + { logLevel: 1, htmlLabels: true } + ); + }); + it('17b: should handle the direction statement with RL', () => { + imgSnapshotTest( + ` + classDiagram + direction RL + class Student { + -idCard : IdCard + } + class IdCard{ + -id : int + -name : string + } + class Bike{ + -id : int + -name : string + } + Student "1" --o "1" IdCard : carries + Student "1" --o "1" Bike : rides + + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('18a: should handle the direction statement with LR', () => { + imgSnapshotTest( + ` + classDiagram + direction LR + class Student { + -idCard : IdCard + } + class IdCard{ + -id : int + -name : string + } + class Bike{ + -id : int + -name : string + } + Student "1" --o "1" IdCard : carries + Student "1" --o "1" Bike : rides + + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('18b: should render a simple class diagram with notes', () => { + imgSnapshotTest( + ` + classDiagram + note "I love this diagram!\nDo you love it?" + class Class10 { + int id + size() + } + note for Class10 "Cool class\nI said it's very cool class!" + + `, + { logLevel: 1, htmlLabels: true } + ); + }); + + it('1433: should render a simple class with a title', () => { + imgSnapshotTest( + `--- +title: simple class diagram +--- +classDiagram +class Class10 +` + ); + }); + + it('should render a class with text label', () => { + imgSnapshotTest( + `classDiagram + class C1["Class 1 with text label"] + C1 --> C2` + ); + }); + + it('should render two classes with text labels', () => { + imgSnapshotTest( + `classDiagram + class C1["Class 1 with text label"] + class C2["Class 2 with chars @?"] + C1 --> C2` + ); + }); + it('should render a class with a text label, members and annotation', () => { + imgSnapshotTest( + `classDiagram + class C1["Class 1 with text label"] { + <<interface>> + +member1 + } + C1 --> C2` + ); + }); + it('should render multiple classes with same text labels', () => { + imgSnapshotTest( + `classDiagram +class C1["Class with text label"] +class C2["Class with text label"] +class C3["Class with text label"] +C1 --> C2 +C3 ..> C2 + ` + ); + }); + it('should render classes with different text labels', () => { + imgSnapshotTest( + `classDiagram +class C1["OneWord"] +class C2["With, Comma"] +class C3["With (Brackets)"] +class C4["With [Brackets]"] +class C5["With {Brackets}"] +class C7["With 1 number"] +class C8["With . period..."] +class C9["With - dash"] +class C10["With _ underscore"] +class C11["With ' single quote"] +class C12["With ~!@#$%^&*()_+=-/?"] +class C13["With Città foreign language"] + ` + ); + }); + + it('should render classLabel if class has already been defined earlier', () => { + imgSnapshotTest( + `classDiagram + Animal <|-- Duck + class Duck["Duck with text label"] +` + ); + }); + it('should add classes namespaces', function () { + imgSnapshotTest( + ` + classDiagram + namespace Namespace1 { + class C1 + class C2 + } + C1 --> C2 + class C3 + class C4 + ` + ); + }); + it('should render a simple class diagram with no members', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + `, + { logLevel: 1, htmlLabels: true } + ); + }); + it('should render a simple class diagram with no members if hideEmptyMembersBox is enabled', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + `, + { logLevel: 1, class: { htmlLabels: true, hideEmptyMembersBox: true } } + ); + }); + it('should render a simple class diagram with no attributes, only methods', () => { + imgSnapshotTest( + ` + classDiagram + class Duck { + +swim() + +quack() + } + ` + ); + }); + it('should render a simple class diagram with no methods, only attributes', () => { + imgSnapshotTest( + ` + classDiagram + class Duck { + +String beakColor + +int age + +float weight + } + ` + ); + }); + it('should render a simple class diagram with style definition', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + style Class10 fill:#f9f,stroke:#333,stroke-width:4px + `, + { logLevel: 1, htmlLabels: true } + ); + }); + it('should render a simple class diagram with style definition without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + style Class10 fill:#f9f,stroke:#333,stroke-width:4px + `, + { logLevel: 1, htmlLabels: false } + ); + }); + it('should render a simple class diagram with classDef definitions', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + classDef pink fill:#f9f + classDef bold stroke:#333,stroke-width:6px,color:#fff + `, + { logLevel: 1, htmlLabels: true } + ); + }); + it('should render a simple class diagram with classDefs being applied', () => { + imgSnapshotTest( + ` + classDiagram + class Class10:::pink + cssClass "Class10" bold + classDef pink fill:#f9f + classDef bold stroke:#333,stroke-width:6px,color:#fff + `, + { logLevel: 1, htmlLabels: true } + ); + }); + it('should render a simple class diagram with classDefs being applied without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10:::pink + cssClass "Class10" bold + classDef pink fill:#f9f + classDef bold stroke:#333,stroke-width:6px,color:#fff + `, + { logLevel: 1, htmlLabels: false } + ); + }); + it('should render a simple class diagram with markdown styling', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 { + +attribute *italic** + ~attribute **bold*** + _italicmethod_() + __boldmethod__() + _+_swim_()a_ + __+quack() test__ + } + `, + { logLevel: 1, htmlLabels: true } + ); + }); + it('should render a simple class diagram with markdown styling without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 { + +attribute *italic** + ~attribute **bold*** + _italicmethod_() + __boldmethod__() + _+_swim_()a_ + __+quack() test__ + } + `, + { logLevel: 1, htmlLabels: false } + ); + }); + it('should render a simple class diagram with the handDrawn look', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('should render a simple class diagram with styles and the handDrawn look', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + style Class10 fill:#f9f,stroke:#333,stroke-width:4px,color:white + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('should render a simple class diagram with styles and the handDrawn look without htmlLabels', () => { + imgSnapshotTest( + ` + classDiagram + class Class10 + style Class10 fill:#f9f,stroke:#333,stroke-width:4px,color:white + `, + { logLevel: 1, htmlLabels: false, look: 'handDrawn' } + ); + }); + it('should render a full class diagram with the handDrawn look', () => { + imgSnapshotTest( + ` + classDiagram + note "I love this diagram!\nDo you love it?" + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + note for Class10 "Cool class\nI said it's very cool class!" + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('should render a simple class diagram with a custom theme', () => { + imgSnapshotTest( + ` + %%{ + init: { + 'theme': 'base', + 'themeVariables': { + 'primaryColor': '#BB2528', + 'primaryTextColor': '#fff', + 'primaryBorderColor': '#7C0000', + 'lineColor': '#F83d29', + 'secondaryColor': '#006100', + 'tertiaryColor': '#fff' + } + } + }%% + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true } + ); + }); + it('should render a simple class diagram with a custom theme and the handDrawn look', () => { + imgSnapshotTest( + ` + %%{ + init: { + 'theme': 'base', + 'themeVariables': { + 'primaryColor': '#BB2528', + 'primaryTextColor': '#fff', + 'primaryBorderColor': '#7C0000', + 'lineColor': '#F83d29', + 'secondaryColor': '#006100', + 'tertiaryColor': '#fff' + } + } + }%% + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + { logLevel: 1, htmlLabels: true, look: 'handDrawn' } + ); + }); + it('should render a full class diagram using elk', () => { + imgSnapshotTest( + ` +--- + config: + layout: elk +--- + classDiagram + note "I love this diagram!\nDo you love it?" + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 "1" *-- "*" Class04 + Class05 "1" o-- "many" Class06 + Class07 "1" .. "*" Class08 + Class09 "1" --> "*" C2 : Where am i? + Class09 "*" --* "*" C3 + Class09 "1" --|> "1" Class07 + Class12 <|.. Class08 + Class11 ..>Class12 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + note for Class10 "Cool class\nI said it's very cool class!" + `, + { logLevel: 1, htmlLabels: true } + ); + }); +}); diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js index 1a2340906a..aad9b1cf7f 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/errorDiagram.spec.js b/cypress/integration/rendering/errorDiagram.spec.js index e837565d3a..9eeb2e3106 100644 --- a/cypress/integration/rendering/errorDiagram.spec.js +++ b/cypress/integration/rendering/errorDiagram.spec.js @@ -3,7 +3,7 @@ import { imgSnapshotTest } from '../../helpers/util'; describe('Error Diagrams', () => { beforeEach(() => { cy.on('uncaught:exception', (err) => { - expect(err.message).to.include('Parse error'); + expect(err.message).to.include('error'); // return false to prevent the error from // failing this test return false; diff --git a/cypress/integration/rendering/flowchart-elk.spec.js b/cypress/integration/rendering/flowchart-elk.spec.js index e931025e91..38bfe6440e 100644 --- a/cypress/integration/rendering/flowchart-elk.spec.js +++ b/cypress/integration/rendering/flowchart-elk.spec.js @@ -1,6 +1,6 @@ import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; -describe.skip('Flowchart ELK', () => { +describe('Flowchart ELK', () => { it('1-elk: should render a simple flowchart', () => { imgSnapshotTest( `flowchart-elk TD @@ -837,6 +837,216 @@ subgraph "\`**Two**\`" in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog") end +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + it('Sub graphs and markdown strings', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- + +flowchart LR + subgraph subgraph_ko6czgs5u["Untitled subgraph"] + D["Option 1"] + end + C{"Evaluate"} -- One --> D + C -- Two --> E(("Option 2")) + D --> E + A["A"] + +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + it('6080: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- +flowchart LR + subgraph s1["Untitled subgraph"] + n1["Evaluate"] + n2["Option 1"] + n3["Option 2"] + n4["fa:fa-car Option 3"] + end + subgraph s2["Untitled subgraph"] + n5["Evaluate"] + n6["Option 1"] + n7["Option 2"] + n8["fa:fa-car Option 3"] + end + 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"] + n1 -- One --> n2 + n1 -- Two --> n3 + n1 -- Three --> n4 + n5 -- One --> n6 + n5 -- Two --> n7 + n5 -- Three --> n8 + n1@{ shape: diam} + n2@{ shape: rect} + n3@{ shape: rect} + n4@{ shape: rect} + n5@{ shape: diam} + n6@{ shape: rect} + n7@{ shape: rect} + n8@{ shape: rect} + +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + + it('6088-1: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- + flowchart LR + subgraph S2 + subgraph s1["APA"] + D{"Use the editor"} + end + + + D -- Mermaid js --> I{"fa:fa-code Text"} + D --> I + D --> I + + end +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + + it('6088-2: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- + flowchart LR + a + subgraph s0["APA"] + subgraph s8["APA"] + subgraph s1["APA"] + D{"X"} + E[Q] + end + subgraph s3["BAPA"] + F[Q] + I + end + D --> I + D --> I + D --> I + + I{"X"} + end + end + +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + + it('6088-3: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- + flowchart LR + a + D{"Use the editor"} + + D -- Mermaid js --> I{"fa:fa-code Text"} + D-->I + D-->I + +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + + it('6088-4: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- +flowchart LR + subgraph s1["Untitled subgraph"] + n1["Evaluate"] + n2["Option 1"] + n3["Option 2"] + n4["fa:fa-car Option 3"] + end + subgraph s2["Untitled subgraph"] + n5["Evaluate"] + n6["Option 1"] + n7["Option 2"] + n8["fa:fa-car Option 3"] + end + 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"] + n1 -- One --> n2 + n1 -- Two --> n3 + n1 -- Three --> n4 + n5 -- One --> n6 + n5 -- Two --> n7 + n5 -- Three --> n8 + n1@{ shape: diam} + n2@{ shape: rect} + n3@{ shape: rect} + n4@{ shape: rect} + n5@{ shape: diam} + n6@{ shape: rect} + n7@{ shape: rect} + n8@{ shape: rect} + +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + + it('6088-5: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- +flowchart LR + A{A} --> B & C + +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + it('6088-6: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- +flowchart LR + A{A} --> B & C + subgraph "subbe" + A + end + `, { flowchart: { titleTopMargin: 0 } } ); @@ -855,7 +1065,7 @@ describe('Title and arrow styling #4813', () => { flowchart LR A-->B A-->C`, - { flowchart: { defaultRenderer: 'elk' } } + { layout: 'elk' } ); cy.get('svg').should((svg) => { const title = svg[0].querySelector('text'); @@ -871,15 +1081,14 @@ describe('Title and arrow styling #4813', () => { B-.-oC C==xD D ~~~ A`, - { flowchart: { defaultRenderer: 'elk' } } + { layout: 'elk' } ); cy.get('svg').should((svg) => { const edges = svg[0].querySelectorAll('.edges path'); - console.log(edges); - expect(edges[0]).to.have.attr('pattern', 'solid'); - expect(edges[1]).to.have.attr('pattern', 'dotted'); - expect(edges[2]).to.have.css('stroke-width', '3.5px'); - expect(edges[3]).to.have.css('stroke-width', '1.5px'); + expect(edges[0].getAttribute('class')).to.contain('edge-pattern-solid'); + expect(edges[1].getAttribute('class')).to.contain('edge-pattern-dotted'); + expect(edges[2].getAttribute('class')).to.contain('edge-thickness-thick'); + expect(edges[3].getAttribute('class')).to.contain('edge-thickness-invisible'); }); }); }); diff --git a/cypress/integration/rendering/flowchart-handDrawn.spec.js b/cypress/integration/rendering/flowchart-handDrawn.spec.js new file mode 100644 index 0000000000..49c55c628e --- /dev/null +++ b/cypress/integration/rendering/flowchart-handDrawn.spec.js @@ -0,0 +1,1032 @@ +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; + +describe('Flowchart HandDrawn', () => { + it('FHD1: should render a simple flowchart no htmlLabels', () => { + imgSnapshotTest( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FHD2: should render a simple flowchart with htmlLabels', () => { + imgSnapshotTest( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: true }, + fontFamily: 'courier', + } + ); + }); + + it('FHD3: should render a simple flowchart with line breaks', () => { + imgSnapshotTest( + ` + graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me thinksssss
ssssssssssssssssssssss
sssssssssssssssssssssssssss} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[Car] + `, + { look: 'handDrawn', fontFamily: 'courier' } + ); + }); + + it('FHD4: should render a simple flowchart with trapezoid and inverse trapezoid vertex options.', () => { + imgSnapshotTest( + ` + graph TD + A[/Christmas\\] + A -->|Get money| B[\\Go shopping/] + B --> C{Let me thinksssss
ssssssssssssssssssssss
sssssssssssssssssssssssssss} + C -->|One| D[/Laptop/] + C -->|Two| E[\\iPhone\\] + C -->|Three| F[Car] + `, + { look: 'handDrawn', fontFamily: 'courier' } + ); + }); + + it('FHD5: should style nodes via a class.', () => { + imgSnapshotTest( + ` + graph TD + 1A --> 1B + 1B --> 1C + 1C --> D + 1C --> E + + classDef processHead fill:#888888,color:white,font-weight:bold,stroke-width:3px,stroke:#001f3f + class 1A,1B,D,E processHead + `, + { look: 'handDrawn', fontFamily: 'courier' } + ); + }); + + it('FHD6: should render a flowchart full of circles', () => { + imgSnapshotTest( + ` + graph LR + 47(SAM.CommonFA.FMESummary)-->48(SAM.CommonFA.CommonFAFinanceBudget) + 37(SAM.CommonFA.BudgetSubserviceLineVolume)-->48(SAM.CommonFA.CommonFAFinanceBudget) + 35(SAM.CommonFA.PopulationFME)-->47(SAM.CommonFA.FMESummary) + 41(SAM.CommonFA.MetricCost)-->47(SAM.CommonFA.FMESummary) + 44(SAM.CommonFA.MetricOutliers)-->47(SAM.CommonFA.FMESummary) + 46(SAM.CommonFA.MetricOpportunity)-->47(SAM.CommonFA.FMESummary) + 40(SAM.CommonFA.OPVisits)-->47(SAM.CommonFA.FMESummary) + 38(SAM.CommonFA.CommonFAFinanceRefund)-->47(SAM.CommonFA.FMESummary) + 43(SAM.CommonFA.CommonFAFinancePicuDays)-->47(SAM.CommonFA.FMESummary) + 42(SAM.CommonFA.CommonFAFinanceNurseryDays)-->47(SAM.CommonFA.FMESummary) + 45(SAM.CommonFA.MetricPreOpportunity)-->46(SAM.CommonFA.MetricOpportunity) + 35(SAM.CommonFA.PopulationFME)-->45(SAM.CommonFA.MetricPreOpportunity) + 41(SAM.CommonFA.MetricCost)-->45(SAM.CommonFA.MetricPreOpportunity) + 41(SAM.CommonFA.MetricCost)-->44(SAM.CommonFA.MetricOutliers) + 39(SAM.CommonFA.ChargeDetails)-->43(SAM.CommonFA.CommonFAFinancePicuDays) + 39(SAM.CommonFA.ChargeDetails)-->42(SAM.CommonFA.CommonFAFinanceNurseryDays) + 39(SAM.CommonFA.ChargeDetails)-->41(SAM.CommonFA.MetricCost) + 39(SAM.CommonFA.ChargeDetails)-->40(SAM.CommonFA.OPVisits) + 35(SAM.CommonFA.PopulationFME)-->39(SAM.CommonFA.ChargeDetails) + 36(SAM.CommonFA.PremetricCost)-->39(SAM.CommonFA.ChargeDetails) + `, + { look: 'handDrawn', fontFamily: 'courier' } + ); + }); + + it('FHD7: should render a flowchart full of icons', () => { + imgSnapshotTest( + ` + graph TD + 9e122290_1ec3_e711_8c5a_005056ad0002("fa:fa-creative-commons My System | Test Environment") + 82072290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Shared Business Logic Server:Service 1") + db052290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Shared Business Logic Server:Service 2") + 4e112290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Shared Report Server:Service 1") + 30122290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Shared Report Server:Service 2") + 5e112290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Dedicated Test Business Logic Server:Service 1") + c1112290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Dedicated Test Business Logic Server:Service 2") + b7042290_1ec3_e711_8c5a_005056ad0002("fa:fa-circle [DBServer\\SharedDbInstance].[SupportDb]") + 8f102290_1ec3_e711_8c5a_005056ad0002("fa:fa-circle [DBServer\\SharedDbInstance].[DevelopmentDb]") + 0e102290_1ec3_e711_8c5a_005056ad0002("fa:fa-circle [DBServer\\SharedDbInstance].[TestDb]") + 07132290_1ec3_e711_8c5a_005056ad0002("fa:fa-circle [DBServer\\SharedDbInstance].[SharedReportingDb]") + c7072290_1ec3_e711_8c5a_005056ad0002("fa:fa-server Shared Business Logic Server") + ca122290_1ec3_e711_8c5a_005056ad0002("fa:fa-server Shared Report Server") + 68102290_1ec3_e711_8c5a_005056ad0002("fa:fa-server Dedicated Test Business Logic Server") + f4112290_1ec3_e711_8c5a_005056ad0002("fa:fa-database [DBServer\\SharedDbInstance]") + d6072290_1ec3_e711_8c5a_005056ad0002("fa:fa-server DBServer") + 71082290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs DBServer\\:MSSQLSERVER") + c0102290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs DBServer\\:SQLAgent") + 9a072290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs DBServer\\:SQLBrowser") + 1d0a2290_1ec3_e711_8c5a_005056ad0002("fa:fa-server VmHost1") + 200a2290_1ec3_e711_8c5a_005056ad0002("fa:fa-server VmHost2") + 1c0a2290_1ec3_e711_8c5a_005056ad0002("fa:fa-server VmHost3") + 9e122290_1ec3_e711_8c5a_005056ad0002-->82072290_1ec3_e711_8c5a_005056ad0002 + 9e122290_1ec3_e711_8c5a_005056ad0002-->db052290_1ec3_e711_8c5a_005056ad0002 + 9e122290_1ec3_e711_8c5a_005056ad0002-->4e112290_1ec3_e711_8c5a_005056ad0002 + 9e122290_1ec3_e711_8c5a_005056ad0002-->30122290_1ec3_e711_8c5a_005056ad0002 + 9e122290_1ec3_e711_8c5a_005056ad0002-->5e112290_1ec3_e711_8c5a_005056ad0002 + 9e122290_1ec3_e711_8c5a_005056ad0002-->c1112290_1ec3_e711_8c5a_005056ad0002 + 82072290_1ec3_e711_8c5a_005056ad0002-->b7042290_1ec3_e711_8c5a_005056ad0002 + 82072290_1ec3_e711_8c5a_005056ad0002-->8f102290_1ec3_e711_8c5a_005056ad0002 + 82072290_1ec3_e711_8c5a_005056ad0002-->0e102290_1ec3_e711_8c5a_005056ad0002 + 82072290_1ec3_e711_8c5a_005056ad0002-->c7072290_1ec3_e711_8c5a_005056ad0002 + db052290_1ec3_e711_8c5a_005056ad0002-->c7072290_1ec3_e711_8c5a_005056ad0002 + db052290_1ec3_e711_8c5a_005056ad0002-->82072290_1ec3_e711_8c5a_005056ad0002 + 4e112290_1ec3_e711_8c5a_005056ad0002-->b7042290_1ec3_e711_8c5a_005056ad0002 + 4e112290_1ec3_e711_8c5a_005056ad0002-->8f102290_1ec3_e711_8c5a_005056ad0002 + 4e112290_1ec3_e711_8c5a_005056ad0002-->0e102290_1ec3_e711_8c5a_005056ad0002 + 4e112290_1ec3_e711_8c5a_005056ad0002-->07132290_1ec3_e711_8c5a_005056ad0002 + 4e112290_1ec3_e711_8c5a_005056ad0002-->ca122290_1ec3_e711_8c5a_005056ad0002 + 30122290_1ec3_e711_8c5a_005056ad0002-->ca122290_1ec3_e711_8c5a_005056ad0002 + 30122290_1ec3_e711_8c5a_005056ad0002-->4e112290_1ec3_e711_8c5a_005056ad0002 + 5e112290_1ec3_e711_8c5a_005056ad0002-->8f102290_1ec3_e711_8c5a_005056ad0002 + 5e112290_1ec3_e711_8c5a_005056ad0002-->68102290_1ec3_e711_8c5a_005056ad0002 + c1112290_1ec3_e711_8c5a_005056ad0002-->68102290_1ec3_e711_8c5a_005056ad0002 + c1112290_1ec3_e711_8c5a_005056ad0002-->5e112290_1ec3_e711_8c5a_005056ad0002 + b7042290_1ec3_e711_8c5a_005056ad0002-->f4112290_1ec3_e711_8c5a_005056ad0002 + 8f102290_1ec3_e711_8c5a_005056ad0002-->f4112290_1ec3_e711_8c5a_005056ad0002 + 0e102290_1ec3_e711_8c5a_005056ad0002-->f4112290_1ec3_e711_8c5a_005056ad0002 + 07132290_1ec3_e711_8c5a_005056ad0002-->f4112290_1ec3_e711_8c5a_005056ad0002 + c7072290_1ec3_e711_8c5a_005056ad0002-->1d0a2290_1ec3_e711_8c5a_005056ad0002 + ca122290_1ec3_e711_8c5a_005056ad0002-->200a2290_1ec3_e711_8c5a_005056ad0002 + 68102290_1ec3_e711_8c5a_005056ad0002-->1c0a2290_1ec3_e711_8c5a_005056ad0002 + f4112290_1ec3_e711_8c5a_005056ad0002-->d6072290_1ec3_e711_8c5a_005056ad0002 + f4112290_1ec3_e711_8c5a_005056ad0002-->71082290_1ec3_e711_8c5a_005056ad0002 + f4112290_1ec3_e711_8c5a_005056ad0002-->c0102290_1ec3_e711_8c5a_005056ad0002 + f4112290_1ec3_e711_8c5a_005056ad0002-->9a072290_1ec3_e711_8c5a_005056ad0002 + d6072290_1ec3_e711_8c5a_005056ad0002-->1c0a2290_1ec3_e711_8c5a_005056ad0002 + 71082290_1ec3_e711_8c5a_005056ad0002-->d6072290_1ec3_e711_8c5a_005056ad0002 + c0102290_1ec3_e711_8c5a_005056ad0002-->d6072290_1ec3_e711_8c5a_005056ad0002 + c0102290_1ec3_e711_8c5a_005056ad0002-->71082290_1ec3_e711_8c5a_005056ad0002 + 9a072290_1ec3_e711_8c5a_005056ad0002-->d6072290_1ec3_e711_8c5a_005056ad0002 + 9a072290_1ec3_e711_8c5a_005056ad0002-->71082290_1ec3_e711_8c5a_005056ad0002 + `, + { look: 'handDrawn', fontFamily: 'courier' } + ); + }); + + it('FHD8: should render labels with numbers at the start', () => { + imgSnapshotTest( + ` + graph TB;subgraph "number as labels";1;end; + `, + { look: 'handDrawn', fontFamily: 'courier' } + ); + }); + + it('FHD9: should render subgraphs', () => { + imgSnapshotTest( + ` + graph TB + subgraph One + a1-->a2 + end + `, + { look: 'handDrawn', fontFamily: 'courier' } + ); + }); + + it('FHD10: should render subgraphs with a title starting with a digit', () => { + imgSnapshotTest( + ` + graph TB + subgraph 2Two + a1-->a2 + end + `, + { look: 'handDrawn', fontFamily: 'courier' } + ); + }); + + it('FHD11: should render styled subgraphs', () => { + imgSnapshotTest( + ` + graph TB + A + B + subgraph foo[Foo SubGraph] + C + D + end + subgraph bar[Bar SubGraph] + E + F + end + G + + A-->B + B-->C + C-->D + B-->D + D-->E + E-->A + E-->F + F-->D + F-->G + B-->G + G-->D + + style foo fill:#F99,stroke-width:2px,stroke:#F0F,color:darkred + style bar fill:#999,stroke-width:10px,stroke:#0F0,color:blue + `, + { look: 'handDrawn', fontFamily: 'courier' } + ); + }); + + it('FHD12: should render a flowchart with long names and class definitions', () => { + imgSnapshotTest( + `graph LR + sid-B3655226-6C29-4D00-B685-3D5C734DC7E1[" + + 提äē¤į”ŗč¯ˇ + į†Šå¤§ + "]; + class sid-B3655226-6C29-4D00-B685-3D5C734DC7E1 node-executed; + sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A[" + 负č´ŖäēēåŽĄæ‰š + åŧē子 + "]; + class sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A node-executed; + sid-E27C0367-E6D6-497F-9736-3CDC21FDE221[" + DBAåŽĄæ‰š + åŧē子 + "]; + class sid-E27C0367-E6D6-497F-9736-3CDC21FDE221 node-executed; + sid-BED98281-9585-4D1B-934E-BD1AC6AC0EFD[" + SAåŽĄæ‰š + é˜ŋįžŽ + "]; + class sid-BED98281-9585-4D1B-934E-BD1AC6AC0EFD node-executed; + sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7[" + ä¸ģįŽĄåŽĄæ‰š + 光头åŧē + "]; + class sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7 node-executed; + sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89[" + DBAįĄŽčŽ¤ + åŧē子 + "]; + class sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89 node-executed; + sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937[" + SAįĄŽčŽ¤ + é˜ŋįžŽ + "]; + class sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937 node-executed; + sid-4FC27B48-A6F9-460A-A675-021F5854FE22[" + įģ“束 + "]; + class sid-4FC27B48-A6F9-460A-A675-021F5854FE22 node-executed; + sid-19DD9E9F-98C1-44EE-B604-842AFEE76F1E[" + SAæ‰§čĄŒ1 + åŧē子 + "]; + class sid-19DD9E9F-98C1-44EE-B604-842AFEE76F1E node-executed; + sid-6C2120F3-D940-4958-A067-0903DCE879C4[" + SAæ‰§čĄŒ2 + åŧē子 + "]; + class sid-6C2120F3-D940-4958-A067-0903DCE879C4 node-executed; + sid-9180E2A0-5C4B-435F-B42F-0D152470A338[" + DBAæ‰§čĄŒ1 + åŧē子 + "]; + class sid-9180E2A0-5C4B-435F-B42F-0D152470A338 node-executed; + sid-03A2C3AC-5337-48A5-B154-BB3FD0EC8DAD[" + DBAæ‰§čĄŒ3 + åŧē子 + "]; + class sid-03A2C3AC-5337-48A5-B154-BB3FD0EC8DAD node-executed; + sid-D5E1F2F4-306C-47A2-BF74-F66E3D769756[" + DBAæ‰§čĄŒ2 + åŧē子 + "]; + class sid-D5E1F2F4-306C-47A2-BF74-F66E3D769756 node-executed; + sid-8C3F2F1D-F014-4F99-B966-095DC1A2BD93[" + DBAæ‰§čĄŒ4 + åŧē子 + "]; + class sid-8C3F2F1D-F014-4F99-B966-095DC1A2BD93 node-executed; + sid-1897B30A-9C5C-4D5B-B80B-76A038785070[" + 负č´ŖäēēįĄŽčŽ¤ + æĸé™čŒš + "]; + class sid-1897B30A-9C5C-4D5B-B80B-76A038785070 node-executed; + sid-B3655226-6C29-4D00-B685-3D5C734DC7E1-->sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7; + sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A-->sid-1897B30A-9C5C-4D5B-B80B-76A038785070; + sid-E27C0367-E6D6-497F-9736-3CDC21FDE221-->sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89; + sid-BED98281-9585-4D1B-934E-BD1AC6AC0EFD-->sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937; + sid-19DD9E9F-98C1-44EE-B604-842AFEE76F1E-->sid-6C2120F3-D940-4958-A067-0903DCE879C4; + sid-9180E2A0-5C4B-435F-B42F-0D152470A338-->sid-D5E1F2F4-306C-47A2-BF74-F66E3D769756; + sid-03A2C3AC-5337-48A5-B154-BB3FD0EC8DAD-->sid-8C3F2F1D-F014-4F99-B966-095DC1A2BD93; + sid-6C2120F3-D940-4958-A067-0903DCE879C4-->sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A; + sid-1897B30A-9C5C-4D5B-B80B-76A038785070-->sid-4FC27B48-A6F9-460A-A675-021F5854FE22; + sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937-->sid-19DD9E9F-98C1-44EE-B604-842AFEE76F1E; + sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89-->sid-9180E2A0-5C4B-435F-B42F-0D152470A338; + sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89-->sid-03A2C3AC-5337-48A5-B154-BB3FD0EC8DAD; + sid-D5E1F2F4-306C-47A2-BF74-F66E3D769756-->sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A; + sid-8C3F2F1D-F014-4F99-B966-095DC1A2BD93-->sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A; + sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7-->sid-BED98281-9585-4D1B-934E-BD1AC6AC0EFD; + sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7-->sid-E27C0367-E6D6-497F-9736-3CDC21FDE221; + sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937-->sid-6C2120F3-D940-4958-A067-0903DCE879C4; + sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7-->sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A; + sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7-->sid-4FC27B48-A6F9-460A-A675-021F5854FE22; + `, + { look: 'handDrawn', fontFamily: 'courier' } + ); + }); + + it('FHD13: should render color of styled nodes', () => { + imgSnapshotTest( + ` + graph LR + foo-->bar + + classDef foo fill:lightblue,color:green,stroke:#FF9E2C,font-weight:bold + style foo fill:#F99,stroke-width:2px,stroke:#F0F + style bar fill:#999,color: #00ff00, stroke-width:10px,stroke:#0F0 + `, + { + look: 'handDrawn', + listUrl: false, + listId: 'color styling', + fontFamily: 'courier', + logLevel: 0, + } + ); + }); + + it('FHD14: should render hexagons', () => { + imgSnapshotTest( + ` + graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{{Let me think...
Do I want something for work,
something to spend every free second with,
or something to get around?}} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[Car] + click A "index.html#link-clicked" "link test" + click B testClick "click test" + classDef someclass fill:#f96; + class A someclass; + class C someclass; + `, + { + look: 'handDrawn', + listUrl: false, + listId: 'color styling', + fontFamily: 'courier', + logLevel: 0, + } + ); + }); + + it('FHD15: should render a simple flowchart with comments', () => { + imgSnapshotTest( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + %% this is a comment + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FHD16: Render Stadium shape', () => { + imgSnapshotTest( + ` graph TD + A([stadium shape test]) + A -->|Get money| B([Go shopping]) + B --> C([Let me think...
Do I want something for work,
something to spend every free second with,
or something to get around?]) + C -->|One| D([Laptop]) + C -->|Two| E([iPhone]) + C -->|Three| F([Car
wroom wroom]) + click A "index.html#link-clicked" "link test" + click B testClick "click test" + classDef someclass fill:#f96; + class A someclass; + class C someclass; + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FHD17: Render multiline texts', () => { + imgSnapshotTest( + `graph LR + A1[Multi
Line] -->|Multi
Line| B1(Multi
Line) + C1[Multi
Line] -->|Multi
Line| D1(Multi
Line) + E1[Multi
Line] -->|Multi
Line| F1(Multi
Line) + A2[Multi
Line] -->|Multi
Line| B2(Multi
Line) + C2[Multi
Line] -->|Multi
Line| D2(Multi
Line) + E2[Multi
Line] -->|Multi
Line| F2(Multi
Line) + linkStyle 0 stroke:DarkGray,stroke-width:2px + linkStyle 1 stroke:DarkGray,stroke-width:2px + linkStyle 2 stroke:DarkGray,stroke-width:2px + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FHD18: Chaining of nodes', () => { + imgSnapshotTest( + `graph LR + a --> b --> c + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FHD19: Multiple nodes and chaining in one statement', () => { + imgSnapshotTest( + `graph LR + a --> b & c--> d + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FHD20: Multiple nodes and chaining in one statement', () => { + imgSnapshotTest( + `graph TD + A[ h ] -- hello --> B[" test "]:::exClass & C --> D; + classDef exClass background:#bbb,border:1px solid red; + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FDH21: Render cylindrical shape', () => { + imgSnapshotTest( + `graph LR + A[(cylindrical
shape
test)] + A -->|Get money| B1[(Go shopping 1)] + A -->|Get money| B2[(Go shopping 2)] + A -->|Get money| B3[(Go shopping 3)] + C[(Let me think...
Do I want something for work,
something to spend every free second with,
or something to get around?)] + B1 --> C + B2 --> C + B3 --> C + C -->|One| D[(Laptop)] + C -->|Two| E[(iPhone)] + C -->|Three| F[(Car)] + click A "index.html#link-clicked" "link test" + click B testClick "click test" + classDef someclass fill:#f96; + class A someclass;`, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FDH22: Render a simple flowchart with nodeSpacing set to 100', () => { + imgSnapshotTest( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + %% this is a comment + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { look: 'handDrawn', flowchart: { nodeSpacing: 50 }, fontFamily: 'courier' } + ); + }); + + it('FDH23: Render a simple flowchart with rankSpacing set to 100', () => { + imgSnapshotTest( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + %% this is a comment + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { + look: 'handDrawn', + flowchart: { rankSpacing: '100' }, + fontFamily: 'courier', + } + ); + }); + + it('FDH24: Keep node label text (if already defined) when a style is applied', () => { + imgSnapshotTest( + `graph LR + A(( )) -->|step 1| B(( )) + B(( )) -->|step 2| C(( )) + C(( )) -->|step 3| D(( )) + linkStyle 1 stroke:greenyellow,stroke-width:2px + style C fill:greenyellow,stroke:green,stroke-width:4px + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FDH25: Handle link click events (link, anchor, mailto, other protocol, script)', () => { + imgSnapshotTest( + `graph TB + TITLE["Link Click Events
(click the nodes below)"] + A["link test (open in same tab)"] + B["link test (open in new tab)"] + C[anchor test] + D[mailto test] + E[other protocol test] + F[script test] + TITLE --> A & B & C & D & E & F + click A "https://mermaid-js.github.io/mermaid/#/" "link test (open in same tab)" + click B "https://mermaid-js.github.io/mermaid/#/" "link test (open in new tab)" _blank + click C "#link-clicked" + click D "mailto:user@user.user" "mailto test" + click E "notes://do-your-thing/id" "other protocol test" + click F "javascript:alert('test')" "script test" + `, + { look: 'handDrawn', securityLevel: 'loose', fontFamily: 'courier' } + ); + }); + + it('FDH26: Set text color of nodes and links according to styles when html labels are enabled', () => { + imgSnapshotTest( + `graph LR + A[red
text] -->|red
text| B(blue
text) + C[/red
text/] -->|blue
text| D{blue
text} + E{{default
style}} -->|default
style| F([default
style]) + linkStyle default color:Sienna; + linkStyle 0 color:red; + linkStyle 1 stroke:DarkGray,stroke-width:2px,color:#0000ff + style A color:red; + style B color:blue; + style C stroke:#ff0000,fill:#ffcccc,color:#ff0000 + style D stroke:#0000ff,fill:#ccccff,color:#0000ff + click B "index.html#link-clicked" "link test" + click D testClick "click test" + `, + { look: 'handDrawn', flowchart: { htmlLabels: true } } + ); + }); + + it('FDH27: Set text color of nodes and links according to styles when html labels are disabled', () => { + imgSnapshotTest( + `graph LR + A[red
text] -->|red
text| B(blue
text) + C[/red
text/] -->|blue
text| D{blue
text} + E{{default
style}} -->|default
style| F([default
style]) + linkStyle default color:Sienna; + linkStyle 0 color:red; + linkStyle 1 stroke:DarkGray,stroke-width:2px,color:#0000ff + style A color:red; + style B color:blue; + style C stroke:#ff0000,fill:#ffcccc,color:#ff0000 + style D stroke:#0000ff,fill:#ccccff,color:#0000ff + click B "index.html#link-clicked" "link test" + click D testClick "click test" + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FDH28: Apply default class to all nodes which do not have another class assigned (htmlLabels enabled)', () => { + imgSnapshotTest( + `graph TD + A[myClass1] --> B[default] & C[default] + B[default] & C[default] --> D[myClass2] + classDef default stroke-width:2px,fill:none,stroke:silver + classDef node color:red + classDef myClass1 color:#0000ff + classDef myClass2 stroke:#0000ff,fill:#ccccff + class A myClass1 + class D myClass2 + `, + { look: 'handDrawn', flowchart: { htmlLabels: true } } + ); + }); + + it('FDH29: Apply default class to all nodes which do not have another class assigned (htmlLabels disabled)', () => { + imgSnapshotTest( + `graph TD + A[myClass1] --> B[default] & C[default] + B[default] & C[default] --> D[myClass2] + classDef default stroke-width:2px,fill:none,stroke:silver + classDef node color:red + classDef myClass1 color:#0000ff + classDef myClass2 stroke:#0000ff,fill:#ccccff + class A myClass1 + class D myClass2 + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FDH30: Possibility to style text color of nodes and subgraphs as well as apply classes to subgraphs', () => { + imgSnapshotTest( + `graph LR + subgraph id1 [title is set] + A-->B + end + subgraph id2 [title] + E + end + + B-->C + + subgraph id3 + C-->D + end + class id3,id2,A redBg; + class id3,A whiteTxt; + classDef redBg fill:#622; + classDef whiteTxt color: white; + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FDH31: should not slice off edges that are to the left of the left-most vertex', () => { + imgSnapshotTest( + `graph TD + work --> sleep + sleep --> work + eat --> sleep + work --> eat + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FDH32: Render Subroutine shape', () => { + imgSnapshotTest( + `graph LR + A[[subroutine shape test]] + A -->|Get money| B[[Go shopping]] + B --> C[[Let me think...
Do I want something for work,
something to spend every free second with,
or something to get around?]] + C -->|One| D[[Laptop]] + C -->|Two| E[[iPhone]] + C -->|Three| F[[Car
wroom wroom]] + click A "index.html#link-clicked" "link test" + click B testClick "click test" + classDef someclass fill:#f96; + class A someclass; + class C someclass; + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: false }, + fontFamily: 'courier', + } + ); + }); + + it('FDH33: should render a simple flowchart with diagramPadding set to 0', () => { + imgSnapshotTest( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + %% this is a comment + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { look: 'handDrawn', flowchart: { diagramPadding: 0 } } + ); + }); + + it('FDH34: testing the label width in percy', () => { + imgSnapshotTest( + `graph TD + A[Christmas] + `, + { look: 'handDrawn', handDrawnSeed: 1 } + ); + }); + + it('FDH35: should honor minimum edge length as specified by the user', () => { + imgSnapshotTest( + `graph TD + L1 --- L2 + L2 --- C + M1 ---> C + R1 .-> R2 + R2 <.-> C + C -->|Label 1| E1 + C -- Label 2 ---> E2 + C ----> E3 + C -----> E4 + C ======> E5 + `, + { look: 'handDrawn', handDrawnSeed: 1 } + ); + }); + it('FDH36: should render escaped without html labels', () => { + imgSnapshotTest( + `graph TD + a["Haiya"]-->b + `, + { look: 'handDrawn', htmlLabels: false, flowchart: { htmlLabels: false } } + ); + }); + it('FDH37: should render non-escaped with html labels', () => { + imgSnapshotTest( + `graph TD + a["Haiya"]-->b + `, + { + look: 'handDrawn', + htmlLabels: true, + flowchart: { htmlLabels: true }, + securityLevel: 'loose', + } + ); + }); + it('FDH38: should render a flowchart when useMaxWidth is true (default)', () => { + renderGraph( + `flowchart TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { look: 'handDrawn', flowchart: { useMaxWidth: true } } + ); + cy.get('svg').should((svg) => { + expect(svg).to.have.attr('width', '100%'); + // expect(svg).to.have.attr('height'); + // use within because the absolute value can be slightly different depending on the environment Âą10% + // const height = parseFloat(svg.attr('height')); + // expect(height).to.be.within(446 * 0.95, 446 * 1.05); + 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.9, 446 * 1.1); + }); + }); + it('FDH39: should render a flowchart when useMaxWidth is false', () => { + renderGraph( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { look: 'handDrawn', flowchart: { useMaxWidth: false } } + ); + cy.get('svg').should((svg) => { + // const height = parseFloat(svg.attr('height')); + const width = parseFloat(svg.attr('width')); + // use within because the absolute value can be slightly different depending on the environment Âą10% + // expect(height).to.be.within(446 * 0.95, 446 * 1.05); + expect(width).to.be.within(446 * 0.9, 446 * 1.1); + expect(svg).to.not.have.attr('style'); + }); + }); + it('FDH40: handle styling with style expressions', () => { + imgSnapshotTest( + ` + graph LR + id1(Start)-->id2(Stop) + style id1 fill:#f9f,stroke:#333,stroke-width:4px + style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5 + `, + { + look: 'handDrawn', + htmlLabels: true, + flowchart: { htmlLabels: true }, + securityLevel: 'loose', + } + ); + }); + it('FDH41: handle styling for all node shapes', () => { + imgSnapshotTest( + ` + graph LR + A[red text] -->|default style| B(blue text) + C([red text]) -->|default style| D[[blue text]] + E[(red text)] -->|default style| F((blue text)) + G>red text] -->|default style| H{blue text} + I{{red text}} -->|default style| J[/blue text/] + linkStyle default color:Sienna; + style A stroke:#ff0000,fill:#ffcccc,color:#ff0000 + style B stroke:#0000ff,fill:#ccccff,color:#0000ff + style C stroke:#ff0000,fill:#ffcccc,color:#ff0000 + style D stroke:#0000ff,fill:#ccccff,color:#0000ff + style E stroke:#ff0000,fill:#ffcccc,color:#ff0000 + style F stroke:#0000ff,fill:#ccccff,color:#0000ff + style G stroke:#ff0000,fill:#ffcccc,color:#ff0000 + style H stroke:#0000ff,fill:#ccccff,color:#0000ff + style I stroke:#ff0000,fill:#ffcccc,color:#ff0000 + style J stroke:#0000ff,fill:#ccccff,color:#0000ff + `, + { + look: 'handDrawn', + htmlLabels: true, + flowchart: { htmlLabels: true }, + securityLevel: 'loose', + } + ); + }); + it('FDH42: fontawesome icons in edge labels', () => { + imgSnapshotTest( + ` +graph TD + C -->|fa:fa-car Car| F[fa:fa-car Car] + `, + { + look: 'handDrawn', + htmlLabels: true, + flowchart: { htmlLabels: true }, + securityLevel: 'loose', + } + ); + }); + it('FDH43: fontawesome icons in edge labels', () => { + imgSnapshotTest( + ` + graph TB + subgraph bar[Bar] + F + end + style bar fill:#999,stroke-width:10px,stroke:#0F0,color:blue + `, + { + look: 'handDrawn', + htmlLabels: true, + flowchart: { htmlLabels: true }, + securityLevel: 'loose', + } + ); + }); + it('FDH44: fontawesome icons in edge labels', () => { + imgSnapshotTest( + ` + graph TB + A + B + subgraph foo[Foo SubGraph] + C + D + end + subgraph bar[Bar SubGraph] + E + F + end + G + + A-->B + B-->C + C-->D + B-->D + D-->E + E-->A + E-->F + F-->D + F-->G + B-->G + G-->D + + style foo fill:#F99,stroke-width:2px,stroke:#F0F,color:darkred + style bar fill:#999,stroke-width:10px,stroke:#0F0,color:blue + `, + { + look: 'handDrawn', + htmlLabels: true, + flowchart: { htmlLabels: true }, + securityLevel: 'loose', + } + ); + }); + it('FDH45: fontawesome icons in edge labels', () => { + imgSnapshotTest( + ` + %%{init:{"theme":"base", "themeVariables": {"primaryColor":"#411d4e", "titleColor":"white", "darkMode":true}}}%% + flowchart LR + subgraph A + a --> b + end + subgraph B + i -->f + end + A --> B + `, + { + look: 'handDrawn', + htmlLabels: true, + flowchart: { htmlLabels: true }, + securityLevel: 'loose', + } + ); + }); + it('FDH46: text-color from classes', () => { + imgSnapshotTest( + ` + flowchart LR + classDef dark fill:#000,stroke:#000,stroke-width:4px,color:#fff + Lorem --> Ipsum --> Dolor + class Lorem,Dolor dark + `, + { + look: 'handDrawn', + htmlLabels: true, + flowchart: { htmlLabels: true }, + securityLevel: 'loose', + } + ); + }); + it('FDH47: apply class called default on node called default', () => { + imgSnapshotTest( + ` + graph TD + classDef default fill:#a34,stroke:#000,stroke-width:4px,color:#fff + hello --> default + `, + { + look: 'handDrawn', + htmlLabels: true, + flowchart: { htmlLabels: true }, + securityLevel: 'loose', + } + ); + }); + + it('FDH48: should be able to style default node independently', () => { + imgSnapshotTest( + ` + flowchart TD + classDef default fill:#a34 + hello --> default + + style default stroke:#000,stroke-width:4px + `, + { + look: 'handDrawn', + flowchart: { htmlLabels: true }, + securityLevel: 'loose', + } + ); + }); +}); diff --git a/cypress/integration/rendering/flowchart-shape-alias.spec.ts b/cypress/integration/rendering/flowchart-shape-alias.spec.ts new file mode 100644 index 0000000000..86aef718c2 --- /dev/null +++ b/cypress/integration/rendering/flowchart-shape-alias.spec.ts @@ -0,0 +1,142 @@ +import { imgSnapshotTest } from '../../helpers/util.ts'; + +const aliasSet1 = ['process', 'rect', 'proc', 'rectangle'] as const; + +const aliasSet2 = ['event', 'rounded'] as const; + +const aliasSet3 = ['stadium', 'pill', 'terminal'] as const; + +const aliasSet4 = ['fr-rect', 'subproc', 'subprocess', 'framed-rectangle', 'subroutine'] as const; + +const aliasSet5 = ['db', 'database', 'cylinder', 'cyl'] as const; + +const aliasSet6 = ['diam', 'decision', 'diamond'] as const; + +const aliasSet7 = ['hex', 'hexagon', 'prepare'] as const; + +const aliasSet8 = ['lean-r', 'lean-right', 'in-out'] as const; + +const aliasSet9 = ['lean-l', 'lean-left', 'out-in'] as const; + +const aliasSet10 = ['trap-b', 'trapezoid-bottom', 'priority'] as const; + +const aliasSet11 = ['trap-t', 'trapezoid-top', 'manual'] as const; + +const aliasSet12 = ['dbl-circ', 'double-circle'] as const; + +const aliasSet13 = ['notched-rectangle', 'card', 'notch-rect'] as const; + +const aliasSet14 = [ + 'lin-rect', + 'lined-rectangle', + 'lin-proc', + 'lined-process', + 'shaded-process', +] as const; + +const aliasSet15 = ['sm-circ', 'small-circle', 'start'] as const; + +const aliasSet16 = ['fr-circ', 'framed-circle', 'stop'] as const; + +const aliasSet17 = ['fork', 'join'] as const; +// brace-r', 'braces' +const aliasSet18 = ['comment', 'brace-l'] as const; + +const aliasSet19 = ['bolt', 'com-link', 'lightning-bolt'] as const; + +const aliasSet20 = ['doc', 'document'] as const; + +const aliasSet21 = ['delay', 'half-rounded-rectangle'] as const; + +const aliasSet22 = ['h-cyl', 'das', 'horizontal-cylinder'] as const; + +const aliasSet23 = ['lin-cyl', 'disk', 'lined-cylinder'] as const; + +const aliasSet24 = ['curv-trap', 'display', 'curved-trapezoid'] as const; + +const aliasSet25 = ['div-rect', 'div-proc', 'divided-rectangle', 'divided-process'] as const; + +const aliasSet26 = ['extract', 'tri', 'triangle'] as const; + +const aliasSet27 = ['win-pane', 'internal-storage', 'window-pane'] as const; + +const aliasSet28 = ['f-circ', 'junction', 'filled-circle'] as const; + +const aliasSet29 = ['lin-doc', 'lined-document'] as const; + +const aliasSet30 = ['notch-pent', 'loop-limit', 'notched-pentagon'] as const; + +const aliasSet31 = ['flip-tri', 'manual-file', 'flipped-triangle'] as const; + +const aliasSet32 = ['sl-rect', 'manual-input', 'sloped-rectangle'] as const; + +const aliasSet33 = ['docs', 'documents', 'st-doc', 'stacked-document'] as const; + +const aliasSet34 = ['procs', 'processes', 'st-rect', 'stacked-rectangle'] as const; + +const aliasSet35 = ['flag', 'paper-tape'] as const; + +const aliasSet36 = ['bow-rect', 'stored-data', 'bow-tie-rectangle'] as const; + +const aliasSet37 = ['cross-circ', 'summary', 'crossed-circle'] as const; + +const aliasSet38 = ['tag-doc', 'tagged-document'] as const; + +const aliasSet39 = ['tag-rect', 'tag-proc', 'tagged-rectangle', 'tagged-process'] as const; + +const aliasSet40 = ['collate', 'hourglass'] as const; + +// Aggregate all alias sets into a single array +const aliasSets = [ + aliasSet1, + aliasSet2, + aliasSet3, + aliasSet4, + aliasSet5, + aliasSet6, + aliasSet7, + aliasSet8, + aliasSet9, + aliasSet10, + aliasSet11, + aliasSet12, + aliasSet13, + aliasSet14, + aliasSet15, + aliasSet16, + aliasSet17, + aliasSet18, + aliasSet19, + aliasSet20, + aliasSet21, + aliasSet22, + aliasSet23, + aliasSet24, + aliasSet25, + aliasSet26, + aliasSet27, + aliasSet28, + aliasSet29, + aliasSet30, + aliasSet31, + aliasSet32, + aliasSet33, + aliasSet34, + aliasSet35, + aliasSet36, + aliasSet37, + aliasSet38, + aliasSet39, +] as const; + +aliasSets.forEach((aliasSet) => { + describe(`Test ${aliasSet.join(',')} `, () => { + it(`All ${aliasSet.join(',')} should render same shape`, () => { + let flowchartCode = `flowchart \n`; + aliasSet.forEach((alias, index) => { + flowchartCode += ` n${index}@{ shape: ${alias}, label: "${alias}" }\n`; + }); + imgSnapshotTest(flowchartCode); + }); + }); +}); diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js index 3eb2a0432a..66452f4b2f 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(290 * 0.95 - 1, 290 * 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(290 * 0.95 - 1, 290 * 1.05); + expect(width).to.be.within(417 * 0.95, 417 * 1.05); expect(svg).to.not.have.attr('style'); }); }); @@ -786,7 +786,7 @@ A ~~~ B `--- title: Subgraph nodeSpacing and rankSpacing example config: - flowchart: + flowchart: nodeSpacing: 250 rankSpacing: 250 --- @@ -1047,7 +1047,32 @@ end A --lb3--> TOP --lb4--> B B1 --lb5--> B2 `, - { flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } } } + { + flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } }, + } + ); + }); + it('Should render self-loops', () => { + imgSnapshotTest( + `flowchart + A --> A + subgraph B + B1 --> B1 + end + subgraph C + subgraph C1 + C2 --> C2 + subgraph D + D1 --> D1 + end + D --> D + end + C1 --> C1 + end + `, + { + flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } }, + } ); }); }); diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js index e4766e7923..d3a83ae5f2 100644 --- a/cypress/integration/rendering/flowchart.spec.js +++ b/cypress/integration/rendering/flowchart.spec.js @@ -733,7 +733,7 @@ describe('Graph', () => { }); it('38: should render a flowchart when useMaxWidth is true (default)', () => { renderGraph( - `graph TD + `flowchart TD A[Christmas] -->|Get money| B(Go shopping) B --> C{Let me think} C -->|One| D[Laptop] @@ -751,7 +751,7 @@ describe('Graph', () => { 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(300 * 0.9, 300 * 1.1); + expect(maxWidthValue).to.be.within(446 * 0.9, 446 * 1.1); }); }); it('39: should render a flowchart when useMaxWidth is false', () => { @@ -770,7 +770,7 @@ describe('Graph', () => { const width = parseFloat(svg.attr('width')); // use within because the absolute value can be slightly different depending on the environment Âą10% // expect(height).to.be.within(446 * 0.95, 446 * 1.05); - expect(width).to.be.within(300 * 0.9, 300 * 1.1); + expect(width).to.be.within(446 * 0.9, 446 * 1.1); expect(svg).to.not.have.attr('style'); }); }); @@ -905,13 +905,16 @@ graph TD it('67: should be able to style default node independently', () => { imgSnapshotTest( ` - flowchart TD + flowchart TD classDef default fill:#a34 hello --> default style default stroke:#000,stroke-width:4px `, - { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } + { + flowchart: { htmlLabels: true }, + securityLevel: 'loose', + } ); }); }); diff --git a/cypress/integration/rendering/iconShape.spec.ts b/cypress/integration/rendering/iconShape.spec.ts new file mode 100644 index 0000000000..4c12c3fa34 --- /dev/null +++ b/cypress/integration/rendering/iconShape.spec.ts @@ -0,0 +1,143 @@ +import { imgSnapshotTest } from '../../helpers/util'; + +const looks = ['classic', 'handDrawn'] as const; +const directions = [ + 'TB', + //'BT', + 'LR', + // 'RL' +] as const; +const forms = [undefined, 'square', 'circle', 'rounded'] as const; +const labelPos = [undefined, 't', 'b'] as const; + +looks.forEach((look) => { + directions.forEach((direction) => { + forms.forEach((form) => { + labelPos.forEach((pos) => { + describe(`Test iconShape in ${form ? `${form} form,` : ''} ${look} look and dir ${direction} with label position ${pos ? pos : 'not defined'}`, () => { + it(`without label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` nA --> nAA@{ icon: 'fa:bell'`; + if (form) { + flowchartCode += `, form: '${form}'`; + } + flowchartCode += ` }\n`; + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'This is a label for icon shape'`; + if (form) { + flowchartCode += `, form: '${form}'`; + } + if (pos) { + flowchartCode += `, pos: '${pos}'`; + } + flowchartCode += ` }\n`; + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with very long label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'This is a very very very very very long long long label for icon shape'`; + if (form) { + flowchartCode += `, form: '${form}'`; + } + if (pos) { + flowchartCode += `, pos: '${pos}'`; + } + flowchartCode += ` }\n`; + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with markdown htmlLabels:true`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'This is **bold**
and strong for icon shape'`; + if (form) { + flowchartCode += `, form: '${form}'`; + } + if (pos) { + flowchartCode += `, pos: '${pos}'`; + } + flowchartCode += ` }\n`; + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with markdown htmlLabels:false`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'This is **bold**
and strong for icon shape'`; + if (form) { + flowchartCode += `, form: '${form}'`; + } + if (pos) { + flowchartCode += `, pos: '${pos}'`; + } + flowchartCode += ` }\n`; + imgSnapshotTest(flowchartCode, { + look, + htmlLabels: false, + flowchart: { htmlLabels: false }, + }); + }); + + it(`with styles`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'new icon shape'`; + if (form) { + flowchartCode += `, form: '${form}'`; + } + if (pos) { + flowchartCode += `, pos: '${pos}'`; + } + flowchartCode += ` }\n`; + flowchartCode += ` style nAA fill:#f9f,stroke:#333,stroke-width:4px \n`; + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with classDef`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n`; + flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'new icon shape'`; + if (form) { + flowchartCode += `, form: '${form}'`; + } + if (pos) { + flowchartCode += `, pos: '${pos}'`; + } + flowchartCode += ` }\n`; + flowchartCode += ` nAA:::customClazz\n`; + imgSnapshotTest(flowchartCode, { look }); + }); + }); + }); + }); + }); +}); + +describe('Test iconShape with different h', () => { + it('with different h', () => { + let flowchartCode = `flowchart TB\n`; + const icon = 'fa:bell'; + const iconHeight = 64; + flowchartCode += ` nA --> nAA@{ icon: '${icon}', label: 'icon with different h', h: ${iconHeight} }\n`; + imgSnapshotTest(flowchartCode); + }); +}); + +describe('Test colored iconShape', () => { + it('with no styles', () => { + let flowchartCode = `flowchart TB\n`; + const icon = 'fluent-emoji:tropical-fish'; + flowchartCode += ` nA --> nAA@{ icon: '${icon}', form: 'square', label: 'icon with color' }\n`; + imgSnapshotTest(flowchartCode); + }); + + it('with styles', () => { + let flowchartCode = `flowchart TB\n`; + const icon = 'fluent-emoji:tropical-fish'; + flowchartCode += ` nA --> nAA@{ icon: '${icon}', form: 'square', label: 'icon with color' }\n`; + flowchartCode += ` style nAA fill:#f9f,stroke:#333,stroke-width:4px \n`; + imgSnapshotTest(flowchartCode); + }); +}); diff --git a/cypress/integration/rendering/imageShape.spec.ts b/cypress/integration/rendering/imageShape.spec.ts new file mode 100644 index 0000000000..d2e267149d --- /dev/null +++ b/cypress/integration/rendering/imageShape.spec.ts @@ -0,0 +1,103 @@ +import { imgSnapshotTest } from '../../helpers/util'; + +const looks = ['classic', 'handDrawn'] as const; +const directions = [ + 'TB', + //'BT', + 'LR', + // 'RL' +] as const; +const labelPos = [undefined, 't', 'b'] as const; + +looks.forEach((look) => { + directions.forEach((direction) => { + labelPos.forEach((pos) => { + describe(`Test imageShape in ${look} look and dir ${direction} with label position ${pos ? pos : 'not defined'}`, () => { + it(`without label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', w: '100', h: '100' }\n`; + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'This is a label for image shape'`; + + flowchartCode += `, w: '100', h: '200'`; + if (pos) { + flowchartCode += `, pos: '${pos}'`; + } + flowchartCode += ` }\n`; + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with very long label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'This is a very very very very very long long long label for image shape'`; + + flowchartCode += `, w: '100', h: '250'`; + if (pos) { + flowchartCode += `, pos: '${pos}'`; + } + flowchartCode += ` }\n`; + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with markdown htmlLabels:true`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'This is **bold**
and strong for image shape'`; + + flowchartCode += `, w: '550', h: '200'`; + if (pos) { + flowchartCode += `, pos: '${pos}'`; + } + flowchartCode += ` }\n`; + imgSnapshotTest(flowchartCode, { look, htmlLabels: true }); + }); + + it(`with markdown htmlLabels:false`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'This is **bold**
and strong for image shape'`; + flowchartCode += `, w: '250', h: '200'`; + + if (pos) { + flowchartCode += `, pos: '${pos}'`; + } + flowchartCode += ` }\n`; + imgSnapshotTest(flowchartCode, { + look, + htmlLabels: false, + flowchart: { htmlLabels: false }, + }); + }); + + it(`with styles`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'new image shape'`; + flowchartCode += `, w: '550', h: '200'`; + + if (pos) { + flowchartCode += `, pos: '${pos}'`; + } + flowchartCode += ` }\n`; + flowchartCode += ` style A fill:#f9f,stroke:#333,stroke-width:4px \n`; + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with classDef`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#000000,stroke-dasharray: 5 5\n`; + flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'new image shape'`; + + flowchartCode += `, w: '500', h: '550'`; + if (pos) { + flowchartCode += `, pos: '${pos}'`; + } + flowchartCode += ` }\n`; + flowchartCode += ` A:::customClazz\n`; + imgSnapshotTest(flowchartCode, { look }); + }); + }); + }); + }); +}); diff --git a/cypress/integration/rendering/kanban.spec.ts b/cypress/integration/rendering/kanban.spec.ts new file mode 100644 index 0000000000..6293776d6f --- /dev/null +++ b/cypress/integration/rendering/kanban.spec.ts @@ -0,0 +1,136 @@ +import { imgSnapshotTest } from '../../helpers/util.ts'; + +describe('Kanban diagram', () => { + it('1: should render a kanban with a single section', () => { + imgSnapshotTest( + `kanban + id1[Todo] + docs[Create Documentation] + docs[Create Blog about the new diagram] + `, + {} + ); + }); + it('2: should render a kanban with multiple sections', () => { + imgSnapshotTest( + `kanban + id1[Todo] + docs[Create Documentation] + id2 + docs[Create Blog about the new diagram] + `, + {} + ); + }); + it('3: should render a kanban with a single wrapping node', () => { + imgSnapshotTest( + `kanban + id1[Todo] + id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char, wrapping] + `, + {} + ); + }); + it('4: should handle the height of a section with a wrapping node at the end', () => { + imgSnapshotTest( + `kanban + id1[Todo] + id2[One line] + id3[Title of diagram is more than 100 chars when user duplicates diagram with 100 char, wrapping] + `, + {} + ); + }); + it('5: should handle the height of a section with a wrapping node at the top', () => { + imgSnapshotTest( + `kanban + id1[Todo] + id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char, wrapping] + id3[One line] + `, + {} + ); + }); + it('6: should handle the height of a section with a wrapping node in the middle', () => { + imgSnapshotTest( + `kanban + id1[Todo] + id2[One line] + id3[Title of diagram is more than 100 chars when user duplicates diagram with 100 char, wrapping] + id4[One line] + `, + {} + ); + }); + it('6: should handle assigments', () => { + imgSnapshotTest( + `kanban + id1[Todo] + docs[Create Documentation] + id2[In progress] + docs[Create Blog about the new diagram]@{ assigned: 'knsv' } + `, + {} + ); + }); + it('7: should handle prioritization', () => { + imgSnapshotTest( + `kanban + id2[In progress] + vh[Very High]@{ priority: 'Very High' } + h[High]@{ priority: 'High' } + m[Default priority] + l[Low]@{ priority: 'Low' } + vl[Very Low]@{ priority: 'Very Low' } + `, + {} + ); + }); + it('7: should handle external tickets', () => { + imgSnapshotTest( + `kanban + id1[Todo] + docs[Create Documentation] + id2[In progress] + docs[Create Blog about the new diagram]@{ ticket: MC-2037 } + `, + {} + ); + }); + it('8: should handle assignments, prioritization and tickets ids in the same item', () => { + imgSnapshotTest( + `kanban + id2[In progress] + docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' } + `, + {} + ); + }); + it('10: Full example', () => { + imgSnapshotTest( + `--- +config: + kanban: + ticketBaseUrl: 'https://abc123.atlassian.net/browse/#TICKET#' +--- +kanban + id1[Todo] + docs[Create Documentation] + docs[Create Blog about the new diagram] + id7[In progress] + id6[Create renderer so that it works in all cases. We also add som extra text here for testing purposes. And some more just for the extra flare.] + id8[Design grammar]@{ assigned: 'knsv' } + id9[Ready for deploy] + id10[Ready for test] + id11[Done] + id5[define getData] + id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char]@{ ticket: MC-2036, priority: 'Very High'} + id3[Update DB function]@{ ticket: MC-2037, assigned: knsv, priority: 'High' } + id4[Create parsing tests]@{ ticket: MC-2038, assigned: 'K.Sveidqvist', priority: 'High' } + id66[last item]@{ priority: 'Very Low', assigned: 'knsv' } + id12[Can't reproduce] + `, + {} + ); + }); +}); diff --git a/cypress/integration/rendering/newShapes.spec.ts b/cypress/integration/rendering/newShapes.spec.ts new file mode 100644 index 0000000000..6c71a38464 --- /dev/null +++ b/cypress/integration/rendering/newShapes.spec.ts @@ -0,0 +1,146 @@ +import { imgSnapshotTest } from '../../helpers/util.ts'; + +const looks = ['classic', 'handDrawn'] as const; +const directions = [ + 'TB', + //'BT', + 'LR', + //'RL' +] as const; +const newShapesSet1 = [ + 'triangle', + 'sloped-rectangle', + 'horizontal-cylinder', + 'flipped-triangle', + 'hourglass', +] as const; +const newShapesSet2 = [ + 'tagged-rectangle', + 'documents', + 'lightning-bolt', + 'filled-circle', + 'window-pane', +] as const; + +const newShapesSet3 = [ + 'curved-trapezoid', + 'bow-rect', + 'tagged-document', + 'divided-rectangle', + 'crossed-circle', +] as const; + +const newShapesSet4 = [ + 'document', + 'notched-pentagon', + 'lined-cylinder', + 'stacked-document', + 'half-rounded-rectangle', +] as const; + +const newShapesSet5 = [ + 'lined-document', + 'tagged-document', + 'brace-l', + 'comment', + 'braces', + 'brace-r', +] as const; + +const newShapesSet6 = ['brace-r', 'braces'] as const; +// Aggregate all shape sets into a single array +const newShapesSets = [ + newShapesSet1, + newShapesSet2, + newShapesSet3, + newShapesSet4, + newShapesSet5, + newShapesSet6, +]; + +looks.forEach((look) => { + directions.forEach((direction) => { + newShapesSets.forEach((newShapesSet) => { + describe(`Test ${newShapesSet.join(', ')} in ${look} look and dir ${direction}`, () => { + it(`without label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape} }\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is a label for ${newShape} shape' }\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`connect all shapes with each other`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index}${index}@{ shape: ${newShape}, label: 'This is a label for ${newShape} shape' }\n`; + }); + for (let i = 0; i < newShapesSet.length; i++) { + for (let j = i + 1; j < newShapesSet.length; j++) { + flowchartCode += ` n${i}${i} --> n${j}${j}\n`; + } + } + if (!(direction === 'TB' && look === 'handDrawn' && newShapesSet === newShapesSet1)) { + //skip this test, works in real. Need to look + imgSnapshotTest(flowchartCode, { look }); + } + }); + + it(`with very long label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is a very very very very very long long long label for ${newShape} shape' }\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with markdown htmlLabels:true`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is **bold**
and strong for ${newShape} shape' }\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with markdown htmlLabels:false`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is **bold**
and strong for ${newShape} shape' }\n`; + }); + imgSnapshotTest(flowchartCode, { + look, + htmlLabels: false, + flowchart: { htmlLabels: false }, + }); + }); + + it(`with styles`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new ${newShape} shape' }\n`; + flowchartCode += ` style n${index}${index} fill:#f9f,stroke:#333,stroke-width:4px \n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with classDef`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new ${newShape} shape' }\n`; + flowchartCode += ` n${index}${index}:::customClazz\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + }); + }); + }); +}); diff --git a/cypress/integration/rendering/oldShapes.spec.ts b/cypress/integration/rendering/oldShapes.spec.ts new file mode 100644 index 0000000000..628e70ea88 --- /dev/null +++ b/cypress/integration/rendering/oldShapes.spec.ts @@ -0,0 +1,107 @@ +import { imgSnapshotTest } from '../../helpers/util'; + +const looks = ['classic', 'handDrawn'] as const; +const directions = [ + 'TB', + //'BT', + 'LR', + //'RL' +] as const; + +const shapesSet1 = ['text', 'card', 'lin-rect', 'diamond', 'hexagon'] as const; + +// removing labelRect, need have alias for it +const shapesSet2 = ['rounded', 'rect', 'start', 'stop'] as const; + +const shapesSet3 = ['fork', 'choice', 'note', 'stadium', 'odd'] as const; + +const shapesSet4 = ['subroutine', 'cylinder', 'circle', 'doublecircle', 'odd'] as const; + +const shapesSet5 = ['anchor', 'lean-r', 'lean-l', 'trap-t', 'trap-b'] as const; + +// Aggregate all shape sets into a single array +const shapesSets = [shapesSet1, shapesSet2, shapesSet3, shapesSet4, shapesSet5] as const; + +looks.forEach((look) => { + directions.forEach((direction) => { + shapesSets.forEach((shapesSet) => { + describe(`Test ${shapesSet.join(', ')} in ${look} look and dir ${direction}`, () => { + it(`without label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + shapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape} }\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + shapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is a label for ${newShape} shape' }\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`connect all shapes with each other`, () => { + let flowchartCode = `flowchart ${direction}\n`; + shapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index}${index}@{ shape: ${newShape}, label: 'This is a label for ${newShape} shape' }\n`; + }); + for (let i = 0; i < shapesSet.length; i++) { + for (let j = i + 1; j < shapesSet.length; j++) { + flowchartCode += ` n${i}${i} --> n${j}${j}\n`; + } + } + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with very long label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + shapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is a very very very very very long long long label for ${newShape} shape' }\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with markdown htmlLabels:true`, () => { + let flowchartCode = `flowchart ${direction}\n`; + shapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is **bold**
and strong for ${newShape} shape' }\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with markdown htmlLabels:false`, () => { + let flowchartCode = `flowchart ${direction}\n`; + shapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is **bold**
and strong for ${newShape} shape' }\n`; + }); + imgSnapshotTest(flowchartCode, { + look, + htmlLabels: false, + flowchart: { htmlLabels: false }, + }); + }); + + it(`with styles`, () => { + let flowchartCode = `flowchart ${direction}\n`; + shapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new ${newShape} shape' }\n`; + flowchartCode += ` style n${index}${index} fill:#f9f,stroke:#333,stroke-width:4px \n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with classDef`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n`; + shapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new ${newShape} shape' }\n`; + flowchartCode += ` n${index}${index}:::customClazz\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + }); + }); + }); +}); diff --git a/cypress/integration/rendering/stateDiagram-v2.spec.js b/cypress/integration/rendering/stateDiagram-v2.spec.js index cb40aa8dc0..606a1a3f57 100644 --- a/cypress/integration/rendering/stateDiagram-v2.spec.js +++ b/cypress/integration/rendering/stateDiagram-v2.spec.js @@ -542,6 +542,43 @@ stateDiagram-v2 { logLevel: 0, fontFamily: 'courier' } ); }); + it(' can have styles applied ', () => { + imgSnapshotTest( + ` +stateDiagram-v2 +AState +style AState fill:#636,border:1px solid red,color:white; + `, + { logLevel: 0, fontFamily: 'courier' } + ); + }); + it(' should let styles take preceedence over classes', () => { + imgSnapshotTest( + ` +stateDiagram-v2 +AState: Should NOT be white +BState +classDef exampleStyleClass fill:#fff,color: blue; +class AState,BState exampleStyleClass +style AState fill:#636,border:1px solid red,color:white; + `, + { logLevel: 0, fontFamily: 'courier' } + ); + }); + it(' should allow styles to take effect in stubgraphs', () => { + imgSnapshotTest( + ` + stateDiagram + state roundWithTitle { + C: Black with white text + } + D: Black with white text + + style C,D stroke:#00f, fill:black, color:white + `, + { logLevel: 0, fontFamily: 'courier' } + ); + }); }); it('1433: should render a simple state diagram with a title', () => { imgSnapshotTest( @@ -551,6 +588,20 @@ title: simple state diagram stateDiagram-v2 [*] --> State1 State1 --> [*] +`, + {} + ); + }); + it('should align dividers correctly', () => { + imgSnapshotTest( + `stateDiagram-v2 + state s2 { + s3 + -- + s4 + -- + 55 + } `, {} ); diff --git a/cypress/platform/architecture-external.html b/cypress/platform/architecture-external.html new file mode 100644 index 0000000000..71770425d6 --- /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/flowchart-refactor.html b/cypress/platform/flowchart-refactor.html new file mode 100644 index 0000000000..6d9ce423f7 --- /dev/null +++ b/cypress/platform/flowchart-refactor.html @@ -0,0 +1,866 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DagreDagre with roughELKELK with rough
+ +
+
+
+              flowchart LR
+              id1([This is the text in the box])
+            
+
+
+
+
+flowchart LR
+    id1([This is the text in the box])
+
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+flowchart LR
+    id1([This is the text in the box])
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+flowchart LR
+    id1([This is the text in the box])
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+flowchart LR
+    id1([This is the text in the box])
+      
+
+ +
+
+
+      flowchart LR
+    id1[[This is the text in the box]]
+    
+
+
+
+
+flowchart LR
+    id1[[This is the text in the box]]
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+flowchart LR
+    id1[[This is the text in the box]]
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+flowchart LR
+    id1[[This is the text in the box]]
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+flowchart LR
+    id1[[This is the text in the box]]
+      
+
+ +
+
+
+              flowchart LR
+    id1[(Database)]
+    
+
+
+
+
+          flowchart LR
+    id1[(Database)]
+      
+
+
+          %%{init: {"look": "handDrawn"} }%%
+          flowchart LR
+    id1[(Database)]
+      
+
+
+          %%{init: {"handDrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1[(Database)]
+      
+
+
+          %%{init: {"look": "handDrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1[(Database)]
+      
+
+ +
+
+
+              flowchart LR
+    id1((This is the text in the circle))
+    
+
+
+
+
+          flowchart LR
+    id1((This is the text in the circle))
+      
+
+
+          %%{init: {"look": "handDrawn"} }%%
+          flowchart LR
+    id1((This is the text in the circle))
+      
+
+
+          %%{init: {"handDrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1((This is the text in the circle))
+      
+
+
+          %%{init: {"look": "handDrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1((This is the text in the circle))
+      
+
+ +
+
+
+              flowchart TD
+    id1(((This is the text in the circle)))
+    
+
+
+
+
+          flowchart TD
+    id1(((This is the text in the circle)))
+      
+
+
+          %%{init: {"look": "handDrawn"} }%%
+          flowchart TD
+    id1(((This is the text in the circle)))
+      
+
+
+          %%{init: {"handDrawn": false, "layout": "elk"} }%%
+          flowchart TD
+    id1(((This is the text in the circle)))
+      
+
+
+          %%{init: {"look": "handDrawn", "layout": "elk"} }%%
+          flowchart TD
+    id1(((This is the text in the circle)))
+      
+
+ +
+
+
+              flowchart LR
+    id1>This is the text in the box]
+    
+
+
+
+
+          flowchart LR
+    id1>This is the text in the box]
+      
+
+
+          %%{init: {"look": "handDrawn"} }%%
+          flowchart LR
+    id1>This is the text in the box]  
+      
+
+
+          %%{init: {"handDrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1>This is the text in the box]  
+      
+
+
+          %%{init: {"look": "handDrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1>This is the text in the box]
+      
+
+ +
+
+
+              flowchart LR
+    id1{This is the text in the box}
+    
+
+
+
+
+          flowchart LR
+    id1{This is the text in the box}
+      
+
+
+          %%{init: {"look": "handDrawn"} }%%
+          flowchart LR
+    id1{This is the text in the box}
+      
+
+
+          %%{init: {"handDrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1{This is the text in the box}
+      
+
+
+          %%{init: {"look": "handDrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1{This is the text in the box}
+      
+
+ +
+
+
+              flowchart LR
+    id1{{This is the text in the box}}
+    
+
+
+
+
+          flowchart LR
+    id1{{This is the text in the box}}
+      
+
+
+          %%{init: {"handDrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1{{This is the text in the box}}
+      
+
+
+          %%{init: {"look": "handDrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1{{This is the text in the box}}
+      
+
+ +
+
+
+              flowchart TD
+    id1[/This is the text in the box/]
+    
+
+
+
+
+          flowchart TD
+    id1[/This is the text in the box/]
+      
+
+
+          %%{init: {"look": "handDrawn"} }%%
+          flowchart TD
+    id1[/This is the text in the box/]  
+      
+
+
+          %%{init: {"handDrawn": false, "layout": "elk"} }%%
+          flowchart TD
+    id1[/This is the text in the box/] 
+      
+
+
+          %%{init: {"look": "handDrawn", "layout": "elk"} }%%
+          flowchart TD
+    id1[/This is the text in the box/]
+      
+
+ +
+
+
+              flowchart TD
+    id1[\This is the text in the box\]
+    
+
+
+
+
+          flowchart TD
+    id1[\This is the text in the box\]
+      
+
+
+          %%{init: {"look": "handDrawn"} }%%
+          flowchart TD
+    id1[\This is the text in the box\]
+      
+
+
+          %%{init: {"handDrawn": false, "layout": "elk"} }%%
+          flowchart TD
+    id1[\This is the text in the box\]
+      
+
+
+          %%{init: {"look": "handDrawn", "layout": "elk"} }%%
+          flowchart TD
+    id1[\This is the text in the box\]
+
+      
+
+ +
+
+
+              flowchart TD
+    A[/Christmas\]
+    
+
+
+
+
+          flowchart TD
+    A[/Christmas\]
+      
+
+
+          %%{init: {"look": "handDrawn"} }%%
+          flowchart TD
+    A[/Christmas\]
+      
+
+
+          %%{init: {"handDrawn": false, "layout": "elk"} }%%
+          flowchart TD
+    A[/Christmas\]
+      
+
+
+          %%{init: {"look": "handDrawn", "layout": "elk"} }%%
+          flowchart TD
+    A[/Christmas\]
+      
+
+ +
+
+
+              flowchart TD
+    A[\Christmas/]
+    
+
+
+
+
+          flowchart TD
+    A[\Christmas/]
+      
+
+
+          %%{init: {"look": "handDrawn"} }%%
+          flowchart TD
+    A[\Christmas/]
+      
+
+
+          %%{init: {"handDrawn": false, "layout": "elk"} }%%
+          flowchart TD
+    A[\Christmas/]
+      
+
+
+          %%{init: {"look": "handDrawn", "layout": "elk"} }%%
+          flowchart TD
+    A[\Christmas/]  
+      
+
+ +
+
+
+              flowchart LR
+    id1(This is the text in the box)
+    
+
+
+
+
+          flowchart LR
+    id1(This is the text in the box)
+      
+
+
+          %%{init: {"look": "handDrawn"} }%%
+          flowchart LR
+    id1(This is the text in the box)
+      
+
+
+          %%{init: {"handDrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1(This is the text in the box)
+      
+
+
+          %%{init: {"look": "handDrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1(This is the text in the box) 
+      
+
+ +
+
+
+              flowchart LR
+    id1[This is the text in the box]
+    
+
+
+
+
+          flowchart LR
+    id1[This is the text in the box]
+      
+
+
+          %%{init: {"look": "handDrawn"} }%%
+          flowchart LR
+    id1[This is the text in the box]
+      
+
+
+          %%{init: {"handDrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1[This is the text in the box]
+      
+
+
+          %%{init: {"look": "handDrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1[This is the text in the box]
+      
+
+ + + + diff --git a/cypress/platform/flowchart-sate.html b/cypress/platform/flowchart-sate.html new file mode 100644 index 0000000000..a85d2131c6 --- /dev/null +++ b/cypress/platform/flowchart-sate.html @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
State roughFlowchart rough
+ +
+
+
+      flowchart LR
+    id1([This is the text in the box])
+
+  
+
+
+
+
+%%{init: {"look": "handDrawn"} }%%
+stateDiagram-v2
+    stateA
+
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+flowchart LR
+    id1[[This is the text in the box]]
+
+
+      
+
+ + + + diff --git a/cypress/platform/knsv-4442.html b/cypress/platform/knsv-4442.html new file mode 100644 index 0000000000..fc15a1685f --- /dev/null +++ b/cypress/platform/knsv-4442.html @@ -0,0 +1,433 @@ + + + + + + + + + + +
+stateDiagram-v2
+    [*] --> Still
+    Still --> [*]
+    Still --> Moving
+    Moving --> Still
+    Moving --> Crash
+    Crash --> [*]    
+
+flowchart RL
+    subgraph "`one`"
+      a1 -- l1 --> a2
+      a1 -- l2 --> a2
+    end
+    
+
+flowchart RL
+    subgraph "`one`"
+      a1 -- l1 --> a2
+      a1 -- l2 --> a2
+    end
+    
+
+flowchart
+id["`A root with a long text that wraps to keep the node size in check. A root with a long text that wraps to keep the node size in check`"]
+
+flowchart LR
+    A[A text that needs to be wrapped wraps to another line]
+    B[A text that needs to be
wrapped wraps to another line] + C["`A text that needs to be wrapped to another line`"]
+
+flowchart LR
+    C["`A text
+        that needs
+        to be wrapped
+        in another
+        way`"]
+  
+
+      classDiagram-v2
+        note "I love this diagram!\nDo you love it?"
+    
+
+    stateDiagram-v2
+    State1: The state with a note with minus - and plus + in it
+    note left of State1
+      Important information! You can write
+      notes with . and  in them.
+    end note    
+
+mindmap
+root
+  Child3(A node with an icon and with a long text that wraps to keep the node size in check)
+
+
+      %%{init: {"theme": "forest"} }%%
+mindmap
+    id1[**Start2**
end] + id2[**Start2**
end] + %% Another comment + id3[**Start2**
end] %% Comment + id4[**Start2**
end
the very end] +
+
+mindmap
+    id1["`**Start2**
+    second line 😎 with long text that is wrapping to the next line`"]
+      id2["`Child **with bold** text`"]
+      id3["`Children of which some
+      is using *italic type of* text`"]
+      id4[Child]
+      id5["`Child
+      Row
+      and another
+      `"]
+    
+
+mindmap
+    id1("`**Root**`"]
+      id2["`A formatted text... with **bold** and *italics*`"]
+      id3[Regular labels works as usual]
+      id4["`Emojis and unicode works too: 🤓
+      ā¤ļā¤žā¤¨āĨā¤¤ā¤ŋā¤ƒ ØŗŲ„اŲ…  和åšŗ `"]
+
+    
+
+%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
+flowchart TB
+  %% I could not figure out how to use double quotes in labels in Mermaid
+  subgraph ibm[IBM Espresso CPU]
+    core0[IBM PowerPC Broadway Core 0]
+    core1[IBM PowerPC Broadway Core 1]
+    core2[IBM PowerPC Broadway Core 2]
+
+    rom[16 KB ROM]
+
+    core0 --- core2
+
+    rom --> core2
+  end
+
+  subgraph amd["`**AMD** Latte GPU`"]
+    mem[Memory & I/O Bridge]
+    dram[DRAM Controller]
+    edram[32 MB EDRAM MEM1]
+    rom[512 B SEEPROM]
+
+    sata[SATA IF]
+    exi[EXI]
+
+    subgraph gx[GX]
+      sram[3 MB 1T-SRAM]
+    end
+
+    radeon[AMD Radeon R7xx GX2]
+
+    mem --- gx
+    mem --- radeon
+
+    rom --- mem
+
+    mem --- sata
+    mem --- exi
+
+    dram --- sata
+    dram --- exi
+  end
+
+  ddr3[2 GB DDR3 RAM MEM2]
+
+  mem --- ddr3
+  dram --- ddr3
+  edram --- ddr3
+
+  core1 --- mem
+
+  exi --- rtc
+  rtc{{rtc}}
+
+
+%%{init: {"flowchart": {"defaultRenderer": "elk", "htmlLabels": false}} }%%
+flowchart TB
+  %% I could not figure out how to use double quotes in labels in Mermaid
+  subgraph ibm[IBM Espresso CPU]
+    core0[IBM PowerPC Broadway Core 0]
+    core1[IBM PowerPC Broadway Core 1]
+    core2[IBM PowerPC Broadway Core 2]
+
+    rom[16 KB ROM]
+
+    core0 --- core2
+
+    rom --> core2
+  end
+
+  subgraph amd["`**AMD** Latte GPU`"]
+    mem[Memory & I/O Bridge]
+    dram[DRAM Controller]
+    edram[32 MB EDRAM MEM1]
+    rom[512 B SEEPROM]
+
+    sata[SATA IF]
+    exi[EXI]
+
+    subgraph gx[GX]
+      sram[3 MB 1T-SRAM]
+    end
+
+    radeon[AMD Radeon R7xx GX2]
+
+    mem --- gx
+    mem --- radeon
+
+    rom --- mem
+
+    mem --- sata
+    mem --- exi
+
+    dram --- sata
+    dram --- exi
+  end
+
+  ddr3[2 GB DDR3 RAM MEM2]
+
+  mem --- ddr3
+  dram --- ddr3
+  edram --- ddr3
+
+  core1 --- mem
+
+  exi --- rtc
+  rtc{{rtc}}
+
+ +
+
+flowchart TB
+  %% I could not figure out how to use double quotes in labels in Mermaid
+  subgraph ibm[IBM Espresso CPU]
+    core0[IBM PowerPC Broadway Core 0]
+    core1[IBM PowerPC Broadway Core 1]
+    core2[IBM PowerPC Broadway Core 2]
+
+    rom[16 KB ROM]
+
+    core0 --- core2
+
+    rom --> core2
+  end
+
+  subgraph amd[AMD Latte GPU]
+    mem[Memory & I/O Bridge]
+    dram[DRAM Controller]
+    edram[32 MB EDRAM MEM1]
+    rom[512 B SEEPROM]
+
+    sata[SATA IF]
+    exi[EXI]
+
+    subgraph gx[GX]
+      sram[3 MB 1T-SRAM]
+    end
+
+    radeon[AMD Radeon R7xx GX2]
+
+    mem --- gx
+    mem --- radeon
+
+    rom --- mem
+
+    mem --- sata
+    mem --- exi
+
+    dram --- sata
+    dram --- exi
+  end
+
+  ddr3[2 GB DDR3 RAM MEM2]
+
+  mem --- ddr3
+  dram --- ddr3
+  edram --- ddr3
+
+  core1 --- mem
+
+  exi --- rtc
+  rtc{{rtc}}
+
+
+   +
+      flowchart LR
+  B1 --be be--x B2
+  B1 --bo bo--o B3
+  subgraph Ugge
+      B2
+      B3
+      subgraph inner
+          B4
+          B5
+      end
+      subgraph inner2
+        subgraph deeper
+          C4
+          C5
+        end
+        C6
+      end
+
+      B4 --> C4
+
+      B3 -- X --> B4
+      B2 --> inner
+
+      C4 --> C5
+  end
+
+  subgraph outer
+      B6
+  end
+  B6 --> B5
+  
+
+sequenceDiagram
+    Customer->>+Stripe: Makes a payment request
+    Stripe->>+Bank: Forwards the payment request to the bank
+    Bank->>+Customer: Asks for authorization
+    Customer->>+Bank: Provides authorization
+    Bank->>+Stripe: Sends a response with payment details
+    Stripe->>+Merchant: Sends a notification of payment receipt
+    Merchant->>+Stripe: Confirms the payment
+    Stripe->>+Customer: Sends a confirmation of payment
+    Customer->>+Merchant: Receives goods or services
+        
+
+mindmap
+  root((mindmap))
+    Origins
+      Long history
+      ::icon(fa fa-book)
+      Popularisation
+        British popular psychology author Tony Buzan
+    Research
+      On effectiveness
and features + On Automatic creation + Uses + Creative techniques + Strategic planning + Argument mapping + Tools + Pen and paper + Mermaid +
+
+
+  example-diagram
+    
+ + + + + + + + + // import mindmap from '../../packages/mermaid-mindmap/src/detector'; // import example from + '../../packages/mermaid-example-diagram/src/mermaid-example-diagram.core.mjs'; import mermaid + from './mermaid.esm.mjs'; // await mermaid.registerExternalDiagrams([example]); + mermaid.parseError = function (err, hash) { // console.error('Mermaid error: ', err); }; + mermaid.initialize({ // theme: 'forest', startOnLoad: true, logLevel: 0, flowchart: { // + defaultRenderer: 'elk', useMaxWidth: false, // htmlLabels: false, htmlLabels: true, }, // + htmlLabels: false, gantt: { useMaxWidth: false, }, useMaxWidth: false, }); function callback() + { alert('It worked'); } mermaid.parseError = function (err, hash) { console.error('In parse + error:'); console.error(err); }; // mermaid.test1('first_slow', 1200).then((r) => + console.info(r)); // mermaid.test1('second_fast', 200).then((r) => console.info(r)); // + mermaid.test1('third_fast', 200).then((r) => console.info(r)); // mermaid.test1('forth_slow', + 1200).then((r) => console.info(r)); + + + + + diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index b12a2e765d..1c7bda8e7d 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -4,44 +4,65 @@ + + + + + + + + -
-      block-beta
-  blockArrowId<["Label"]>(right)
-  blockArrowId2<["Label"]>(left)
-  blockArrowId3<["Label"]>(up)
-  blockArrowId4<["Label"]>(down)
-  blockArrowId5<["Label"]>(x)
-  blockArrowId6<["Label"]>(y)
-  blockArrowId6<["Label"]>(x, down)
-    
-
-block-beta
-  block:e:4
-    columns 2
-      f
-      g
-  end
+    
+---
+config:
+  layout: elk
+---
+      flowchart LR
+      subgraph S2
+      subgraph s1["APA"]
+      D{"Use the editor"}
+      end
+
+
+      D -- Mermaid js --> I{"fa:fa-code Text"}
+            D --> I
+            D --> I
 
+      end
     
-
-block-beta
-  block:e:4
-    columns 2
-      f
-      g
-      h
-  end
+    
+---
+config:
+  layout: elk
+---
+      flowchart LR
+      a
+      subgraph s0["APA"]
+      subgraph s8["APA"]
+      subgraph s1["APA"]
+        D{"X"}
+        E[Q]
+      end
+      subgraph s3["BAPA"]
+        F[Q]
+        I
+      end
+            D --> I
+            D --> I
+            D --> I
 
+      I{"X"}
+      end
+      end
     
-
-block-beta
-  columns 4
-  a b c d
-  block:e:4
-    columns 2
-      f
-      g
-      h
-  end
-  i:4
+    
+---
+config:
+  layout: elk
+---
+      flowchart LR
+      a
+        D{"Use the editor"}
 
+      D -- Mermaid js --> I{"fa:fa-code Text"}
+      D-->I
+      D-->I
     
-
+    
+---
+config:
+  layout: elk
+---
 flowchart LR
-  X-- "y" -->z
-    
-
-block-beta
-columns 5
-   A space B
-   A --x B
-    
-
-block-beta
-columns 3
-  a["A wide one"] b:2 c:2 d
-    
-
-block-beta
-  block:e
-      f
+ subgraph s1["Untitled subgraph"]
+        n1["Evaluate"]
+        n2["Option 1"]
+        n3["Option 2"]
+        n4["fa:fa-car Option 3"]
   end
-    
-
-block-beta
-  columns 3
-  a:3
-  block:e:3
-      f
-  end
-  g
-    
-
-block-beta
-  columns 3
-  a:3
-  block:e:3
-      f
-      g
+ subgraph s2["Untitled subgraph"]
+        n5["Evaluate"]
+        n6["Option 1"]
+        n7["Option 2"]
+        n8["fa:fa-car Option 3"]
   end
-  h
-  i
-  j
+    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"]
+    n1 -- One --> n2
+    n1 -- Two --> n3
+    n1 -- Three --> n4
+    n5 -- One --> n6
+    n5 -- Two --> n7
+    n5 -- Three --> n8
+    n1@{ shape: diam}
+    n2@{ shape: rect}
+    n3@{ shape: rect}
+    n4@{ shape: rect}
+    n5@{ shape: diam}
+    n6@{ shape: rect}
+    n7@{ shape: rect}
+    n8@{ shape: rect}
 
     
-
-block-beta
-columns 3
-  a b:2
-  block:e:3
-      f
-  end
-  g h i
-    
-
-block-beta
-columns 3
-  a b c
-  e:3
-  f g h
-    
-
-block-beta
-columns 1
-  db(("DB"))
-  blockArrowId6<["   "]>(down)
-  block:ID
-    A
-    B["A wide one in the middle"]
-    C
+    
+---
+config:
+  layout: elk
+---
+flowchart LR
+ subgraph s1["Untitled subgraph"]
+        n1["Evaluate"]
+        n2["Option 1"]
   end
-  space
-  D
-  ID --> D
-  C --> D
-  style B fill:#f9F,stroke:#333,stroke-width:4px
+    n1 -- One --> n2
+
+
+
+
     
-
-block-beta
-  columns 5
-  A1:3
-  A2:1
-  A3
-  B1 B2 B3:3
+    
+---
+config:
+  layout: elk
+---
+flowchart LR
+    A{A} --> B & C
+
+
+---
+config:
+  layout: elk
+---
+flowchart LR
+    A{A} --> B & C
+    subgraph "subbe"
+      A
+    end
+
+
+---
+config:
+  layout: elk
+---
+flowchart LR
+    n2@{ shape: rect}
+    n3@{ shape: rect}
+    n4@{ shape: rect}
+    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"]
+    %% C@{ shape: hexagon}
+
+
     
-
-block-beta
-  block
-    D
-    E
-  end
-  db("This is the text in the box")
+    
+---
+config:
+  kanban:
+    ticketBaseUrl: 'https://github.com/your-repo/issues/#TICKET#'
+---
+kanban
+  Backlog
+    task1[📝 Define project requirements]@{ ticket: a101 }
+  To Do
+    task2[🔍 Research technologies]@{ ticket: a102 }
+  Review
+    task4[🔍 Code review for login feature]@{ ticket: a104 }
+  Done
+    task5[✅ Deploy initial version]@{ ticket: a105 }
+  In Progress
+    task3[đŸ’ģ Develop login feature]@{ ticket: 103 }
+
     
-
-block-beta
+    
+flowchart LR
+nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
 
-      block
-        D
-      end
-      A["A: I am a wide one"]
     
-
-block-beta
-    A["square"]
-    B("rounded")
-    C(("circle"))
+    
+flowchart LR
+nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
+style A fill:#f9f,stroke:#333,stroke-width:4px
     
-
-block-beta
-    A>"rect_left_inv_arrow"]
-    B{"diamond"}
-    C{{"hexagon"}}
+    
+flowchart LR
+nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
+A:::AClass
+classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
     
-
-block-beta
-    A(["stadium"])
+    
+flowchart LR
+  nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
+
     
-
-block-beta
-    %% A[["subroutine"]]
-    %% B[("cylinder")]
-    C>"surprise"]
+    
+flowchart LR
+nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
+
     
-
-block-beta
-    A[/"lean right"/]
-    B[\"lean left"\]
-    C[/"trapezoid"\]
-    D[\"trapezoid"/]
+    
+flowchart LR
+nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
+style A fill:#f9f,stroke:#333,stroke-width:4px
     
- -
-flowchart
-      B
-      style B fill:#f9F,stroke:#333,stroke-width:4px
+    
+flowchart LR
+nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
+A:::AClass
+classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
     
+
+flowchart LR
+  nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
 
-    
-      flowchart LR
-      a1 -- apa --> b1
     
+
+flowchart LR
+nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
 
-    
-flowchart RL
-  subgraph "`one`"
-    id
-  end
-    
-
-flowchart RL
-    subgraph "`one`"
-      a1 -- l1 --> a2
-      a1 -- l2 --> a2
-    end
     
-
-flowchart
-id["`A root with a long text that wraps to keep the node size in check. A root with a long text that wraps to keep the node size in check`"]
-
-flowchart LR
-    A[A text that needs to be wrapped wraps to another line]
-    B[A text that needs to be
wrapped wraps to another line] - C["`A text that needs to be wrapped to another line`"]
-
+    
 flowchart LR
-    C["`A text
-        that needs
-        to be wrapped
-        in another
-        way`"]
-  
-
-      classDiagram-v2
-        note "I love this diagram!\nDo you love it?"
+nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
+style A fill:#f9f,stroke:#333,stroke-width:4px
     
-
-    stateDiagram-v2
-    State1: The state with a note with minus - and plus + in it
-    note left of State1
-      Important information! You can write
-      notes with . and  in them.
-    end note    
-
-mindmap
-root
-  Child3(A node with an icon and with a long text that wraps to keep the node size in check)
-
-
-      %%{init: {"theme": "forest"} }%%
-mindmap
-    id1[**Start2**
end] - id2[**Start2**
end] - %% Another comment - id3[**Start2**
end] %% Comment - id4[**Start2**
end
the very end] +
+flowchart LR
+nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
+A:::AClass
+classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
     
-
-mindmap
-    id1["`**Start2**
-    second line 😎 with long text that is wrapping to the next line`"]
-      id2["`Child **with bold** text`"]
-      id3["`Children of which some
-      is using *italic type of* text`"]
-      id4[Child]
-      id5["`Child
-      Row
-      and another
-      `"]
+    
+flowchart LR
+  nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
+  A:::AClass
+  classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
     
-
-mindmap
-    id1("`**Root**`"]
-      id2["`A formatted text... with **bold** and *italics*`"]
-      id3[Regular labels works as usual]
-      id4["`Emojis and unicode works too: 🤓
-      ā¤ļā¤žā¤¨āĨā¤¤ā¤ŋā¤ƒ ØŗŲ„اŲ…  和åšŗ `"]
-
+    
+flowchart LR
+  nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
+  style A fill:#f9f,stroke:#333,stroke-width:4px
     
-
-%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
-flowchart TB
-  %% I could not figure out how to use double quotes in labels in Mermaid
-  subgraph ibm[IBM Espresso CPU]
-    core0[IBM PowerPC Broadway Core 0]
-    core1[IBM PowerPC Broadway Core 1]
-    core2[IBM PowerPC Broadway Core 2]
-
-    rom[16 KB ROM]
-
-    core0 --- core2
-
-    rom --> core2
-  end
-
-  subgraph amd["`**AMD** Latte GPU`"]
-    mem[Memory & I/O Bridge]
-    dram[DRAM Controller]
-    edram[32 MB EDRAM MEM1]
-    rom[512 B SEEPROM]
-
-    sata[SATA IF]
-    exi[EXI]
-
-    subgraph gx[GX]
-      sram[3 MB 1T-SRAM]
-    end
-
-    radeon[AMD Radeon R7xx GX2]
-
-    mem --- gx
-    mem --- radeon
-
-    rom --- mem
-
-    mem --- sata
-    mem --- exi
-
-    dram --- sata
-    dram --- exi
-  end
-
-  ddr3[2 GB DDR3 RAM MEM2]
-
-  mem --- ddr3
-  dram --- ddr3
-  edram --- ddr3
-
-  core1 --- mem
-
-  exi --- rtc
-  rtc{{rtc}}
-
-
-%%{init: {"flowchart": {"defaultRenderer": "elk", "htmlLabels": false}} }%%
-flowchart TB
-  %% I could not figure out how to use double quotes in labels in Mermaid
-  subgraph ibm[IBM Espresso CPU]
-    core0[IBM PowerPC Broadway Core 0]
-    core1[IBM PowerPC Broadway Core 1]
-    core2[IBM PowerPC Broadway Core 2]
-
-    rom[16 KB ROM]
-
-    core0 --- core2
-
-    rom --> core2
-  end
-
-  subgraph amd["`**AMD** Latte GPU`"]
-    mem[Memory & I/O Bridge]
-    dram[DRAM Controller]
-    edram[32 MB EDRAM MEM1]
-    rom[512 B SEEPROM]
-
-    sata[SATA IF]
-    exi[EXI]
-
-    subgraph gx[GX]
-      sram[3 MB 1T-SRAM]
-    end
-
-    radeon[AMD Radeon R7xx GX2]
-
-    mem --- gx
-    mem --- radeon
-
-    rom --- mem
-
-    mem --- sata
-    mem --- exi
-
-    dram --- sata
-    dram --- exi
-  end
-
-  ddr3[2 GB DDR3 RAM MEM2]
-
-  mem --- ddr3
-  dram --- ddr3
-  edram --- ddr3
-
-  core1 --- mem
-
-  exi --- rtc
-  rtc{{rtc}}
-
- -
-
-flowchart TB
-  %% I could not figure out how to use double quotes in labels in Mermaid
-  subgraph ibm[IBM Espresso CPU]
-    core0[IBM PowerPC Broadway Core 0]
-    core1[IBM PowerPC Broadway Core 1]
-    core2[IBM PowerPC Broadway Core 2]
-
-    rom[16 KB ROM]
-
-    core0 --- core2
-
-    rom --> core2
-  end
-
-  subgraph amd[AMD Latte GPU]
-    mem[Memory & I/O Bridge]
-    dram[DRAM Controller]
-    edram[32 MB EDRAM MEM1]
-    rom[512 B SEEPROM]
-
-    sata[SATA IF]
-    exi[EXI]
-
-    subgraph gx[GX]
-      sram[3 MB 1T-SRAM]
-    end
-
-    radeon[AMD Radeon R7xx GX2]
-
-    mem --- gx
-    mem --- radeon
-
-    rom --- mem
-
-    mem --- sata
-    mem --- exi
-
-    dram --- sata
-    dram --- exi
-  end
-
-  ddr3[2 GB DDR3 RAM MEM2]
-
-  mem --- ddr3
-  dram --- ddr3
-  edram --- ddr3
-
-  core1 --- mem
-
-  exi --- rtc
-  rtc{{rtc}}
-
-
-   -
-      flowchart LR
-  B1 --be be--x B2
-  B1 --bo bo--o B3
-  subgraph Ugge
-      B2
-      B3
-      subgraph inner
-          B4
-          B5
-      end
-      subgraph inner2
-        subgraph deeper
-          C4
-          C5
-        end
-        C6
-      end
-
-      B4 --> C4
-
-      B3 -- X --> B4
-      B2 --> inner
-
-      C4 --> C5
-  end
-
-  subgraph outer
-      B6
-  end
-  B6 --> B5
-  
-
-sequenceDiagram
-    Customer->>+Stripe: Makes a payment request
-    Stripe->>+Bank: Forwards the payment request to the bank
-    Bank->>+Customer: Asks for authorization
-    Customer->>+Bank: Provides authorization
-    Bank->>+Stripe: Sends a response with payment details
-    Stripe->>+Merchant: Sends a notification of payment receipt
-    Merchant->>+Stripe: Confirms the payment
-    Stripe->>+Customer: Sends a confirmation of payment
-    Customer->>+Merchant: Receives goods or services
-        
-
-mindmap
-  root((mindmap))
-    Origins
-      Long history
-      ::icon(fa fa-book)
-      Popularisation
-        British popular psychology author Tony Buzan
-    Research
-      On effectiveness
and features - On Automatic creation - Uses - Creative techniques - Strategic planning - Argument mapping - Tools - Pen and paper - Mermaid +
+kanban
+  id2[In progress]
+    docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
     
-
-
-  example-diagram
+    
+---
+config:
+  kanban:
+    ticketBaseUrl: 'https://mermaidchart.atlassian.net/browse/#TICKET#'
+    # sectionWidth: 300
+---
+kanban
+  Todo
+    [Create Documentation]
+    docs[Create Blog about the new diagram]
+  id7[In progress]
+    id6[Create renderer so that it works in all cases. We also add som extra text here for testing purposes. And some more just for the extra flare.]
+  id9[Ready for deploy]
+    id8[Design grammar]@{ assigned: 'knsv' }
+  id10[Ready for test]
+    id4[Create parsing tests]@{ ticket: MC-2038, assigned: 'K.Sveidqvist', priority: 'High' }
+    id66[last item]@{ priority: 'Very Low', assigned: 'knsv' }
+  id11[Done]
+    id5[define getData]
+    id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char]@{ ticket: MC-2036, priority: 'Very High'}
+    id3[Update DB function]@{ ticket: MC-2037, assigned: knsv, priority: 'High' }
+
+  id12[Can't reproduce]
+    id3[Weird flickering in Firefox]
     
- - - - - - - diff --git a/cypress/platform/knsv3.html b/cypress/platform/knsv3.html index 26368a62a5..1d65c2ddfb 100644 --- a/cypress/platform/knsv3.html +++ b/cypress/platform/knsv3.html @@ -1,4 +1,4 @@ - + diff --git a/cypress/platform/saurabh.html b/cypress/platform/saurabh.html new file mode 100644 index 0000000000..89b314e68b --- /dev/null +++ b/cypress/platform/saurabh.html @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + +
+      flowchart LR
+      A@{ icon: "fa:window-minimize", form: circle }
+      E@{ icon: "fa:window-minimize", form: circle }
+      B@{ icon: "fa:bell", form: circle }
+      B2@{ icon: "fa:bell", form: circle }
+      C@{ icon: "fa:address-book",  form: square  }
+      D@{ icon: "fa:star-half",  form: square  }
+      A --> E
+      B --> B2
+
+
+    
+
+      flowchart TB
+       A --test2--> B2@{ icon: "fa:bell", form: "rounded", label: "B2 aiduaid uyawduad uaduabd uyduadb", pos: "b" }
+       B2 --test--> C
+       D --> B2 --> E
+       style B2 fill:#f9f,stroke:#333,stroke-width:4px
+  
+
+      flowchart BT
+       A --test2--> B2@{ icon: "fa:bell", form: "square", label: "B2", pos: "t", h: 40, w: 30 }
+       B2 --test--> C
+       D --> B2 --> E
+  
+
+      flowchart BT
+       A --test2--> B2@{ icon: "fa:bell", label: "B2 awiugdawu uydgayuiwd wuydguy", pos: "b", h: 40, w: 30 }
+       B2 --test--> C
+  
+
+      flowchart BT
+       A --test2--> B2@{ icon: "fa:bell", label: "B2 dawuygd ayuwgd uy", pos: "t", h: 40, w: 30 }
+       B2 --test--> C
+  
+
+      flowchart TB
+       A --> B2@{ icon: "fa:bell", form: "circle", label: "test augfuyfavf ydvaubfuac", pos: "t", w: 200, h: 100 } --> C
+  
+
+      flowchart TB
+       A --> B2@{ icon: "fa:bell", form: "circle", label: "test augfuyfavf ydvaubfuac", pos: "b", w: 200, h: 100 } --> C
+       D --> B2 --> E
+  
+ + + diff --git a/cypress/platform/state-refactor.html b/cypress/platform/state-refactor.html new file mode 100644 index 0000000000..8c04be7f6d --- /dev/null +++ b/cypress/platform/state-refactor.html @@ -0,0 +1,1271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DagreDagre with roughELKELK with rough
+ +
+
+
+      stateId
+  
+
+
+
+
+stateDiagram-v2
+    stateId
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+stateDiagram-v2
+    stateId
+
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+stateDiagram-v2
+    stateId
+
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+stateDiagram-v2
+    stateId
+
+      
+
+ +
+
+
+
+    state "description text" as s2
+  
+
+
+
+
+stateDiagram-v2
+    state "This is a state description" as s2
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+stateDiagram-v2
+    state "This is a state description" as s3
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+stateDiagram-v2
+    state "This is a state description" as s4
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+stateDiagram-v2
+    state "This is a state description" as s5
+      
+
+ +
+
+
+
+    s2 :  description text
+  
+
+
+
+
+stateDiagram-v2
+    s21 : This is a state description
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+stateDiagram-v2
+    s22 : This is a state description
+
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+stateDiagram-v2
+    s23 : This is a state description
+
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+stateDiagram-v2
+    s24 : This is a state description
+
+      
+
+ +
+
+
+
+    s1 --> s2
+
+  
+
+
+
+
+
+    stateDiagram-v2
+    s31 --> s32
+
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+
+   stateDiagram-v2
+    s41 --> s42
+
+
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+stateDiagram-v2
+    s51 --> s52
+
+
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+stateDiagram-v2
+    s61 --> s62
+
+
+      
+
+ +
+
+
+
+    s1 --> s2: A transition
+
+  
+
+
+
+
+
+    stateDiagram-v2
+    a1 --> a2: A transition
+
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+
+   stateDiagram-v2
+    a3 --> a4: A transition
+
+
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+stateDiagram-v2
+    a5 --> a6: A transition
+
+
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+stateDiagram-v2
+   a7 --> a8: A transition
+
+
+      
+
+ +
+
+
+
+      [*] --> test
+    test --> [*]
+
+  
+
+
+
+
+
+    stateDiagram-v2
+       [*] --> test
+    test --> [*]
+
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+
+   stateDiagram-v2
+       [*] --> test
+    test --> [*]
+
+
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+stateDiagram-v2
+       [*] --> test
+    test --> [*]
+
+
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+stateDiagram-v2
+      [*] --> test
+    test --> [*]
+
+
+      
+
+ +
+
+
+
+      [*] --> First
+    state First {
+        [*] --> second
+        second --> [*]
+    }
+
+  
+
+
+
+
+
+    stateDiagram-v2
+       [*] --> First
+    state First {
+        [*] --> second
+        second --> [*]
+    }
+
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+
+   stateDiagram-v2
+       [*] --> First
+    state First {
+        [*] --> second
+        second --> [*]
+    }
+
+
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+stateDiagram-v2
+       [*] --> First
+    state First {
+        [*] --> second
+        second --> [*]
+    }
+
+
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+stateDiagram-v2
+      [*] --> First
+    state First {
+        [*] --> second
+        second --> [*]
+    }
+
+
+      
+
+ +
+
+
+
+
+    [*] --> Level1
+
+    state Level1 {
+        [*] --> Level2
+
+        state Level2 {
+            [*] --> level2
+            level2 --> Level3
+
+            state Level3 {
+                [*] --> level3
+                level3 --> [*]
+            }
+        }
+    }
+
+
+  
+
+
+
+
+
+    stateDiagram-v2
+    [*] --> Level1
+
+    state Level1 {
+        [*] --> Level2
+
+        state Level2 {
+            [*] --> level2
+            level2 --> Level3
+
+            state Level3 {
+                [*] --> level3
+                level3 --> [*]
+            }
+        }
+    }
+
+
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+
+   stateDiagram-v2
+    [*] --> Level1
+
+    state Level1 {
+        [*] --> Level2
+
+        state Level2 {
+            [*] --> level2
+            level2 --> Level3
+
+            state Level3 {
+                [*] --> level3
+                level3 --> [*]
+            }
+        }
+    }
+
+
+
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+ stateDiagram-v2
+    [*] --> Level1
+
+    state Level1 {
+        [*] --> Level2
+
+        state Level2 {
+            [*] --> level2
+            level2 --> Level3
+
+            state Level3 {
+                [*] --> level3
+                level3 --> [*]
+            }
+        }
+    }
+
+
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+ stateDiagram-v2
+    [*] --> Level1
+
+    state Level1 {
+        [*] --> Level2
+
+        state Level2 {
+            [*] --> level2
+            level2 --> Level3
+
+            state Level3 {
+                [*] --> level3
+                level3 --> [*]
+            }
+        }
+    }
+
+      
+
+ +
+
+
+    [*] --> B1
+    B1 --> B2
+    B1 --> B3
+
+    state B1 {
+        [*] --> B11
+        B11 --> [*]
+    }
+    state B2 {
+        [*] --> B22
+        B22 --> [*]
+    }
+    state B3 {
+        [*] --> B33
+        B33 --> [*]
+    }
+
+
+
+  
+
+
+
+
+
+    stateDiagram-v2
+    [*] --> B1
+    B1 --> B2
+    B1 --> B3
+
+    state B1 {
+        [*] --> B11
+        B11 --> [*]
+    }
+    state B2 {
+        [*] --> B22
+        B22 --> [*]
+    }
+    state B3 {
+        [*] --> B33
+        B33 --> [*]
+    }
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+
+   stateDiagram-v2
+   [*] --> B1
+    B1 --> B2
+    B1 --> B3
+
+    state B1 {
+        [*] --> B11
+        B11 --> [*]
+    }
+    state B2 {
+        [*] --> B22
+        B22 --> [*]
+    }
+    state B3 {
+        [*] --> B33
+        B33 --> [*]
+    }
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+ stateDiagram-v2
+  [*] --> B1
+    B1 --> B2
+    B1 --> B3
+
+    state B1 {
+        [*] --> B11
+        B11 --> [*]
+    }
+    state B2 {
+        [*] --> B22
+        B22 --> [*]
+    }
+    state B3 {
+        [*] --> B33
+        B33 --> [*]
+    }
+
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+ stateDiagram-v2
+[*] --> B1
+    B1 --> B2
+    B1 --> B3
+
+    state B1 {
+        [*] --> B11
+        B11 --> [*]
+    }
+    state B2 {
+        [*] --> B22
+        B22 --> [*]
+    }
+    state B3 {
+        [*] --> B33
+        B33 --> [*]
+    }
+      
+
+ +
+
+
+    state if_state <<choice>>
+    [*] --> IsPositive
+    IsPositive --> if_state
+    if_state --> False: if n < 0
+    if_state --> True : if n >= 0
+
+
+
+
+  
+
+
+
+
+
+    stateDiagram-v2
+    state if_state <>
+    [*] --> IsPositive
+    IsPositive --> if_state
+    if_state --> False: if n < 0
+    if_state --> True : if n >= 0
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+
+   stateDiagram-v2
+   state if_state <>
+    [*] --> IsPositive
+    IsPositive --> if_state
+    if_state --> False: if n < 0
+    if_state --> True : if n >= 0
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+ stateDiagram-v2
+   state if_state <>
+    [*] --> IsPositive
+    IsPositive --> if_state
+    if_state --> False: if n < 0
+    if_state --> True : if n >= 0
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+ stateDiagram-v2
+ state if_state <>
+    [*] --> IsPositive
+    IsPositive --> if_state
+    if_state --> False: if n < 0
+    if_state --> True : if n >= 0
+      
+
+ +
+
+
+    state fork_state <<fork>>
+      [*] --> fork_state
+      fork_state --> State2
+      fork_state --> State3
+
+      state join_state <<join>>
+      State2 --> join_state
+      State3 --> join_state
+      join_state --> State4
+      State4 --> [*]
+
+
+
+  
+
+
+
+
+   stateDiagram-v2
+    state fork_state <>
+      [*] --> fork_state
+      fork_state --> State2
+      fork_state --> State3
+
+      state join_state <>
+      State2 --> join_state
+      State3 --> join_state
+      join_state --> State4
+      State4 --> [*]
+
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+   stateDiagram-v2
+    state fork_state <>
+      [*] --> fork_state
+      fork_state --> State2
+      fork_state --> State3
+
+      state join_state <>
+      State2 --> join_state
+      State3 --> join_state
+      join_state --> State4
+      State4 --> [*]
+
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+   stateDiagram-v2
+    state fork_state <>
+      [*] --> fork_state
+      fork_state --> State2
+      fork_state --> State3
+
+      state join_state <>
+      State2 --> join_state
+      State3 --> join_state
+      join_state --> State4
+      State4 --> [*]
+
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+   stateDiagram-v2
+    state fork_state <>
+      [*] --> fork_state
+      fork_state --> State2
+      fork_state --> State3
+
+      state join_state <>
+      State2 --> join_state
+      State3 --> join_state
+      join_state --> State4
+      State4 --> [*]
+
+      
+
+ +
+
+
+
+  TN1: The state with a note
+  note right of TN1
+      note text
+  end note
+  TN1 --> TN2
+  note left of TN2 : note text
+
+  
+
+
+
+
+   stateDiagram-v2
+     TN1: The state with a note
+        note right of TN1
+            Important information! You can write
+            notes.
+        end note
+        TN1 --> TN2
+        note left of TN2 : This is the note to the left.
+
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+   stateDiagram-v2
+     TN3: The state with a note
+        note right of TN3
+            Important information! You can write
+            notes.
+        end note
+        TN3 --> TN4
+        note left of TN4 : This is the note to the left.
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+   stateDiagram-v2
+     TN5: The state with a note
+        note right of TN5
+            Important information! You can write
+            notes.
+        end note
+        TN5 --> TN6
+        note left of TN6 : This is the note to the left.
+
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+   stateDiagram-v2
+     TN7: The state with a note
+        note right of TN7
+            Important information! You can write
+            notes.
+        end note
+        TN7 --> TN8
+        note left of TN8 : This is the note to the left.
+      
+
+ +
+
+
+[*] --> Active
+
+state Active {
+    [*] --> NumLockOff
+    NumLockOff --> NumLockOn : EvNumLockPressed
+    NumLockOn --> NumLockOff : EvNumLockPressed
+    --
+    [*] --> CapsLockOff
+    CapsLockOff --> CapsLockOn : EvCapsLockPressed
+    CapsLockOn --> CapsLockOff : EvCapsLockPressed
+    --
+    [*] --> ScrollLockOff
+    ScrollLockOff --> ScrollLockOn : EvScrollLockPressed
+    ScrollLockOn --> ScrollLockOff : EvScrollLockPressed
+}
+
+
+  
+
+
+
+
+     stateDiagram-v2
+    [*] --> Active
+
+    state Active {
+        [*] --> NumLockOff
+        NumLockOff --> NumLockOn : EvNumLockPressed
+        NumLockOn --> NumLockOff : EvNumLockPressed
+        --
+        [*] --> CapsLockOff
+        CapsLockOff --> CapsLockOn : EvCapsLockPressed
+        CapsLockOn --> CapsLockOff : EvCapsLockPressed
+        --
+        [*] --> ScrollLockOff
+        ScrollLockOff --> ScrollLockOn : EvScrollLockPressed
+        ScrollLockOn --> ScrollLockOff : EvScrollLockPressed
+    }
+
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+     stateDiagram-v2
+    [*] --> Active
+
+    state Active {
+        [*] --> NumLockOff
+        NumLockOff --> NumLockOn : EvNumLockPressed
+        NumLockOn --> NumLockOff : EvNumLockPressed
+        --
+        [*] --> CapsLockOff
+        CapsLockOff --> CapsLockOn : EvCapsLockPressed
+        CapsLockOn --> CapsLockOff : EvCapsLockPressed
+        --
+        [*] --> ScrollLockOff
+        ScrollLockOff --> ScrollLockOn : EvScrollLockPressed
+        ScrollLockOn --> ScrollLockOff : EvScrollLockPressed
+    }
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+     stateDiagram-v2
+    [*] --> Active
+
+    state Active {
+        [*] --> NumLockOff
+        NumLockOff --> NumLockOn : EvNumLockPressed
+        NumLockOn --> NumLockOff : EvNumLockPressed
+        --
+        [*] --> CapsLockOff
+        CapsLockOff --> CapsLockOn : EvCapsLockPressed
+        CapsLockOn --> CapsLockOff : EvCapsLockPressed
+        --
+        [*] --> ScrollLockOff
+        ScrollLockOff --> ScrollLockOn : EvScrollLockPressed
+        ScrollLockOn --> ScrollLockOff : EvScrollLockPressed
+    }
+
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+     stateDiagram-v2
+    [*] --> Active
+
+    state Active {
+        [*] --> NumLockOff
+        NumLockOff --> NumLockOn : EvNumLockPressed
+        NumLockOn --> NumLockOff : EvNumLockPressed
+        --
+        [*] --> CapsLockOff
+        CapsLockOff --> CapsLockOn : EvCapsLockPressed
+        CapsLockOn --> CapsLockOff : EvCapsLockPressed
+        --
+        [*] --> ScrollLockOff
+        ScrollLockOff --> ScrollLockOn : EvScrollLockPressed
+        ScrollLockOn --> ScrollLockOff : EvScrollLockPressed
+    }
+      
+
+ +
+
+
+direction LR
+    [*] --> D1
+    D1 --> D2
+    D2 --> D3
+    state D3 {
+      direction TB
+      D11 --> D22
+    }
+    D2 --> D4
+  
+
+
+
+
+     stateDiagram-v2
+    direction LR
+    [*] --> D1
+    D1 --> D2
+    D2 --> D3
+    state D3 {
+      direction TB
+      D11 --> D22
+    }
+    D2 --> D4
+      
+
+
+%%{init: {"look": "handDrawn"} }%%
+     stateDiagram-v2
+   direction LR
+    [*] --> D1
+    D1 --> D2
+    D2 --> D3
+    state D3 {
+      direction TB
+      D11 --> D22
+    }
+    D2 --> D4
+      
+
+
+%%{init: {"handDrawn": false, "layout": "elk"} }%%
+     stateDiagram-v2
+    direction LR
+    [*] --> D1
+    D1 --> D2
+    D2 --> D3
+    state D3 {
+      direction TB
+      D11 --> D22
+    }
+    D2 --> D4
+      
+
+
+%%{init: {"look": "handDrawn", "layout": "elk"} }%%
+     stateDiagram-v2
+    direction LR
+    [*] --> D1
+    D1 --> D2
+    D2 --> D3
+    state D3 {
+      direction TB
+      D11 --> D22
+    }
+    D2 --> D4
+      
+
Additional ContentNew content 1New content 2New content 3New content 4
+ + + + + + diff --git a/cypress/platform/viewer.js b/cypress/platform/viewer.js index c397f0e168..a9d4d2d81b 100644 --- a/cypress/platform/viewer.js +++ b/cypress/platform/viewer.js @@ -1,7 +1,7 @@ -import mermaid from './mermaid.esm.mjs'; -import flowchartELK from './mermaid-flowchart-elk.esm.mjs'; import externalExample from './mermaid-example-diagram.esm.mjs'; +import layouts from './mermaid-layout-elk.esm.mjs'; import zenUml from './mermaid-zenuml.esm.mjs'; +import mermaid from './mermaid.esm.mjs'; function b64ToUtf8(str) { return decodeURIComponent(escape(window.atob(str))); @@ -46,8 +46,64 @@ const contentLoaded = async function () { document.getElementsByTagName('body')[0].appendChild(div); } - await mermaid.registerExternalDiagrams([externalExample, zenUml, flowchartELK]); + await mermaid.registerExternalDiagrams([externalExample, zenUml]); + + mermaid.registerLayoutLoaders(layouts); mermaid.initialize(graphObj.mermaid); + const staticBellIconPack = { + prefix: 'fa6-regular', + icons: { + bell: { + body: '', + width: 448, + }, + }, + width: 512, + height: 512, + }; + /* MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE */ + const staticAwsLogoIconPack = { + prefix: 'fluent-emoji', + icons: { + 'tropical-fish': { + width: 32, + height: 32, + body: '', + }, + }, + width: 256, + height: 256, + }; + mermaid.registerIconPacks([ + { + name: 'fa', + loader: () => staticBellIconPack, + }, + { + name: 'fluent-emoji', + loader: () => staticAwsLogoIconPack, + }, + ]); await mermaid.run(); } }; diff --git a/cypress/platform/yari.html b/cypress/platform/yari.html new file mode 100644 index 0000000000..501541c3b8 --- /dev/null +++ b/cypress/platform/yari.html @@ -0,0 +1,663 @@ + + +

Class Nodes

+
+
+

Basic Class

+
+          ---
+            config:
+              htmlLabels: false
+          ---
+          classDiagram
+          class _Duck_ {
+            +String beakColor
+            _+_swim_()a_
+            __+quack() test__
+          }
+        
+
+
+

Basic Class

+
+          ---
+          config:
+            htmlLabels: false
+          ---
+          classDiagram
+          class Class10:::exClass2 {
+            int[] id
+            List~int~ ids
+            test(List~int~ ids) List~bool~
+            testArray() bool[]
+          }
+        
+
+
+

Basic Class

+
+          flowchart TD
+            Start --> Stop
+        
+
+
+

Complex Class

+
+                classDiagram
+                class Square~Shape~{
+                  int id
+                  List~int~ position
+                  setPoints(List~int~ points)
+                  getPoints() List~int~
+                }
+          
+          Square : -List~string~ messages
+          Square : +setMessages(List~string~ messages)
+          Square : +getMessages() List~string~
+          Square : +getDistanceMatrix() List~List~int~~
+              
+
+
+

No Attributes

+
+          classDiagram
+          class Duck {
+            +swim()
+            +quack()
+          }
+        
+
+
+

No Methods

+
+          classDiagram
+          class Duck {
+            +String beakColor
+          }
+        
+
+
+

Only Class Name

+

Empty line as attribute

+
+          ---
+            config:
+              class:
+                hideEmptyMembersBox: false
+          ---
+          classDiagram
+          class Duck {
+            
+          }
+        
+
+
+

Visibility and Types

+

(Further tilde testing)

+
+ classDiagram class Duck { ~interface~~~ +String beakColor #swim() ~quack()~~~ + -test()~~~~~~~ +deposit(amount) bool } +
+
+
+

Additional Classifiers

+

(* Abstract | $ Static)

+
+ classDiagram class Square~Shape~ { int id* List~int~ position* setPoints(List~int~points)* + getPoints()* List~int~ } Square : -List~string~ messages$ Square : + +setMessages(List~string~ messages)* Square : +getMessages()$ List~string~ Square : + +getDistanceMatrix() List~List~int~~$ +
+
+
+

Label

+
+          classDiagram
+          class Animal~test~["Animal with a label"]
+        
+
+
+

Spacing

+

(Fix ensures consistent spacing rules)

+

(No space or single space?)

+
+          classDiagram
+          class ClassName {
+          -attribute:type
+          -            attribute : type
+               test
+          
+          + GetAttribute() type                 
+          +     GetAttribute() type
+          }
+        
+
+
+

Annotation

+
+          classDiagram
+          class Shape
+          <<interface>> Shape
+          Shape : noOfVertices
+          Shape : draw()
+        
+
+
+

Long Class Name Text

+
+          classDiagram
+          class ThisIsATestForALongClassName {
+            <<interface>>
+            noOfLetters
+            delete()
+          }
+        
+
+
+

Long Annotation Text

+
+          classDiagram
+          class Shape
+          <<superlongannotationtext>> Shape
+          Shape : noOfVertices
+          Shape : draw()
+        
+
+
+

Long Member Text

+
+          classDiagram
+          class Shape
+          <<interface>> Shape
+          Shape : noOfVertices
+          Shape : longtexttestkeepgoingandgoing
+          Shape : draw()
+        
+
+
+

Link

+
+          classDiagram
+          class Shape
+          link Shape "https://www.github.com" "This is a tooltip for a link"
+        
+
+
+

Click

+
+          classDiagram
+          class Shape
+          click Shape href "https://www.github.com" "This is a tooltip for a link"
+        
+
+
+

Hand Drawn

+
+          ---
+          config:
+            look: handDrawn
+            htmlLabels: true
+          ---
+          classDiagram
+          class Hand {
+            +String beakColor
+            +swim()
+            +quack()
+          }
+          style Hand fill:#f9f,stroke:#29f,stroke-width:2px
+        
+
+
+

Neutral Theme

+
+          ---
+          config:
+            theme: neutral
+          ---
+          classDiagram
+          class Duck {
+            +String beakColor
+            +swim()
+            +quack()
+          }
+        
+
+
+

Dark Theme

+
+          ---
+          config:
+            theme: dark
+          ---
+          classDiagram
+          class Duck {
+            +String beakColor
+            +swim()
+            +quack()
+          }
+        
+
+
+

Forest Theme

+
+          ---
+          config:
+            theme: forest
+          ---
+          classDiagram
+          class Duck {
+            +String beakColor
+            +swim()
+            +quack()
+          }
+        
+
+
+

Base Theme

+
+          ---
+          config:
+            theme: base
+          ---
+          classDiagram
+          class Duck {
+            +String beakColor
+            +swim()
+            +quack()
+          }
+        
+
+
+

Custom Theme

+
+          %%{
+            init: {
+              'theme': 'base',
+              'themeVariables': {
+                'primaryColor': '#BB2528',
+                'primaryTextColor': '#fff',
+                'primaryBorderColor': '#7C0000',
+                'lineColor': '#F83d29',
+                'secondaryColor': '#006100',
+                'tertiaryColor': '#fff'
+              }
+            }
+          }%%
+          classDiagram
+          class Duck {
+            +String beakColor
+            +swim()
+            +quack()
+          }
+          Duck--Dog
+        
+
+
+

Styling within Diagram

+
+          classDiagram
+          class Duck {
+            +String beakColor
+            +swim()
+            +quack()
+          }
+          style Duck fill:#f9f,stroke:#333,stroke-width:8px
+        
+
+
+

Styling with classDef Statement

+
+          classDiagram
+          class Duck:::bold {
+            +String beakColor
+            +swim()
+            +quack()
+          }
+
+          class Dog {
+            +int numTeeth
+            +bark()
+          }
+
+          cssClass "Duck,Dog" pink
+
+          classDef pink fill:#f9f
+          classDef default color:#f1e
+          classDef bold stroke:#333,stroke-width:6px,color:#fff
+        
+
+
+

Styling with Class in Stylesheet

+
+          classDiagram
+          class Duck {
+            +String beakColor
+            +swim()
+            +quack()
+          }
+          class Duck:::styleClass
+        
+
+
+

Diagram Testing

+
+
+

Class Nodes Only

+
+          ---
+          title: Animal example
+          ---
+          classDiagram
+              Animal : +int age
+              Animal : +String gender
+              Animal: +isMammal()
+              Animal: +mate()
+              class Duck{
+                  +String beakColor
+                  +swim()
+                  +quack()
+              }
+              class Fish{
+                  -int sizeInFeet
+                  -canEat()
+              }
+              class Zebra{
+                  +bool is_wild
+                  +run()
+              }
+        
+
+
+

Class Nodes LR

+
+          ---
+          title: Animal example
+          ---
+          classDiagram
+              direction LR
+              Animal : +int age
+              Animal : +String gender
+              Animal: +isMammal()
+              Animal: +mate()
+              class Duck{
+                  +String beakColor
+                  +swim()
+                  +quack()
+              }
+              class Fish{
+                  -int sizeInFeet
+                  -canEat()
+              }
+              class Zebra{
+                  +bool is_wild
+                  +run()
+              }
+        
+
+
+

Relations

+
+          classDiagram
+            classA <|-- classB
+            classC *-- classD
+            classE o-- classF
+            classG <-- classH
+            classI -- classJ
+            classK <.. classL
+            classM <|.. classN
+            classO .. classP
+        
+
+
+

Two Way Relation

+
+          classDiagram
+          class Animal {
+            int size
+            walk()
+          }
+          class Zebra {
+            int size
+            walk()
+          }
+            Animal o--|> Zebra
+
+        
+
+
+

Relations with Labels

+
+          classDiagram
+            classA <|-- classB : implements
+            classC *-- classD : composition
+            classE o-- classF : aggregation
+        
+
+
+

Cardinality / Multiplicity

+
+          classDiagram
+            Customer "1" --> "*" Ticket
+            Student "1" --> "1..*" Course
+            Galaxy --> "many" Star : Contains
+        
+
+
+

Complex Relations with Theme

+
+          ---
+          config:
+            theme: forest
+            look: handDrawns
+            layout: elk
+          ---
+          classDiagram
+            direction RL
+            class Student {
+              -idCard : IdCard
+            }
+            class IdCard{
+              -id : int
+              -name : string
+            }
+            class Bike{
+              -id : int
+              -name : string
+            }
+            Student "1" o--o "1" IdCard : carries
+            Student "1" o--o "1" Bike : rides
+        
+
+
+

Notes

+
+          classDiagram
+            note "This is a general note"
+            note for MyClass "This is a note for a class"
+            class MyClass
+        
+
+
+

Namespaces

+
+          classDiagram
+            namespace BaseShapes {
+                class Triangle
+                class Rectangle {
+                  double width
+                  double height
+                }
+            }
+        
+
+
+

Namespaces

+
+          ---
+            config:
+              layout: elk
+          ---
+          classDiagram
+          namespace Namespace1 {
+            class C1
+            class C2
+          }
+          C1 --> C2
+          class C3
+          class C4
+        
+
+
+

Full Example

+
+          ---
+          title: Animal example
+          config:
+            layout: dagre
+          ---
+          classDiagram
+              note "From Duck till Zebra"
+              Animal <|--|> Duck
+              note for Duck "can fly
can swim
can dive
can help in debugging" + Animal <|-- Fish + Animal <|--|> Zebra + Animal : +int age + Animal : +String gender + Animal: +isMammal() + Animal: +mate() + class Duck{ + +String beakColor + +swim() + +quack() + } + class Fish{ + -int sizeInFeet + -canEat() + } + class Zebra{ + +bool is_wild + +run() + } + cssClass "Duck" test + classDef test fill:#f71 + %%classDef default fill:#f93 +
+
+
+

Full Example

+
+          ---
+            config:
+              theme: forest
+              look: handDrawn
+          ---
+          classDiagram
+          note for Outside "Note testing"
+          namespace Test {
+              class Outside
+          }
+          namespace BaseShapes {
+              class Triangle
+              class Rectangle {
+                double width
+                double height
+              }
+          }
+          Outside <|--|> Rectangle
+          style Triangle fill:#f9f,stroke:#333,stroke-width:4px
+        
+
+
+
+          ---
+            config:
+              look: handDrawn
+              layout: elk
+          ---
+          classDiagram
+            Class01 "1" <|--|> "*" AveryLongClass : Cool
+            <<interface>> Class01
+            Class03 "1" *-- "*" Class04
+            Class05 "1" o-- "many" Class06
+            Class07 "1" .. "*" Class08
+            Class09 "1" --> "*" C2 : Where am i?
+            Class09 "*" --* "*" C3
+            Class09 "1" --|> "1" Class07
+            NewClass ()--() Class04
+            Class09 <|--|> AveryLongClass
+            Class07  : equals()
+            Class07  : Object[] elementData
+            Class01  : size()
+            Class01  : int chimp
+            Class01  : int gorilla
+            Class08 "1" <--> "*" C2: Cool label
+            class Class10 {
+              <<service>>
+              int id
+              test()
+            }
+            Class10 o--o AveryLongClass
+            Class10 <--> Class07
+        
+
+
+
+          classDiagram
+            test ()--() test2
+        
+
+
+ + + + + diff --git a/cypress/timings.json b/cypress/timings.json new file mode 100644 index 0000000000..3455d82fc4 --- /dev/null +++ b/cypress/timings.json @@ -0,0 +1,152 @@ +{ + "durations": [ + { + "spec": "cypress/integration/other/configuration.spec.js", + "duration": 4989 + }, + { + "spec": "cypress/integration/other/external-diagrams.spec.js", + "duration": 1382 + }, + { + "spec": "cypress/integration/other/ghsa.spec.js", + "duration": 3178 + }, + { + "spec": "cypress/integration/other/iife.spec.js", + "duration": 1372 + }, + { + "spec": "cypress/integration/other/interaction.spec.js", + "duration": 8998 + }, + { + "spec": "cypress/integration/other/rerender.spec.js", + "duration": 1249 + }, + { + "spec": "cypress/integration/other/xss.spec.js", + "duration": 25664 + }, + { + "spec": "cypress/integration/rendering/appli.spec.js", + "duration": 1928 + }, + { + "spec": "cypress/integration/rendering/architecture.spec.ts", + "duration": 2330 + }, + { + "spec": "cypress/integration/rendering/block.spec.js", + "duration": 11156 + }, + { + "spec": "cypress/integration/rendering/c4.spec.js", + "duration": 3418 + }, + { + "spec": "cypress/integration/rendering/classDiagram-v2.spec.js", + "duration": 14866 + }, + { + "spec": "cypress/integration/rendering/classDiagram.spec.js", + "duration": 9894 + }, + { + "spec": "cypress/integration/rendering/conf-and-directives.spec.js", + "duration": 5778 + }, + { + "spec": "cypress/integration/rendering/current.spec.js", + "duration": 1690 + }, + { + "spec": "cypress/integration/rendering/erDiagram.spec.js", + "duration": 9144 + }, + { + "spec": "cypress/integration/rendering/errorDiagram.spec.js", + "duration": 1951 + }, + { + "spec": "cypress/integration/rendering/flowchart-elk.spec.js", + "duration": 2196 + }, + { + "spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js", + "duration": 21029 + }, + { + "spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts", + "duration": 16087 + }, + { + "spec": "cypress/integration/rendering/flowchart-v2.spec.js", + "duration": 27465 + }, + { + "spec": "cypress/integration/rendering/flowchart.spec.js", + "duration": 20035 + }, + { + "spec": "cypress/integration/rendering/gantt.spec.js", + "duration": 11366 + }, + { + "spec": "cypress/integration/rendering/gitGraph.spec.js", + "duration": 34025 + }, + { + "spec": "cypress/integration/rendering/iconShape.spec.ts", + "duration": 185902 + }, + { + "spec": "cypress/integration/rendering/imageShape.spec.ts", + "duration": 41631 + }, + { + "spec": "cypress/integration/rendering/info.spec.ts", + "duration": 1736 + }, + { + "spec": "cypress/integration/rendering/journey.spec.js", + "duration": 2247 + }, + { + "spec": "cypress/integration/rendering/katex.spec.js", + "duration": 2144 + }, + { + "spec": "cypress/integration/rendering/marker_unique_id.spec.js", + "duration": 1646 + }, + { + "spec": "cypress/integration/rendering/mindmap.spec.ts", + "duration": 6406 + }, + { + "spec": "cypress/integration/rendering/newShapes.spec.ts", + "duration": 107219 + }, + { + "spec": "cypress/integration/rendering/stateDiagram.spec.js", + "duration": 15834 + }, + { + "spec": "cypress/integration/rendering/theme.spec.js", + "duration": 33240 + }, + { + "spec": "cypress/integration/rendering/timeline.spec.ts", + "duration": 7122 + }, + { + "spec": "cypress/integration/rendering/xyChart.spec.js", + "duration": 11127 + }, + { + "spec": "cypress/integration/rendering/zenuml.spec.js", + "duration": 2391 + } + ] +} diff --git a/demos/architecture.html b/demos/architecture.html new file mode 100644 index 0000000000..6d978d9525 --- /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/classchart.html b/demos/classchart.html index f04fa5b5f9..980ea5098d 100644 --- a/demos/classchart.html +++ b/demos/classchart.html @@ -159,30 +159,87 @@

Class diagram demos

class People List~List~Person~~

-
     classDiagram
-      A1 --> B1
-      namespace A {
-        class A1 {
-          +foo : string
+      namespace Company.Project.Module {
+        class GenericClass~T~ {
+          +addItem(item: T)
+          +getItem() T
+        }
+      }
+    
+
+
+    classDiagram
+      namespace Company.Project.Module.SubModule {
+        class Report {
+          +generatePDF(data: List)
+          +generateCSV(data: List)
         }
-        class A2 {
-          +bar : int
+      }
+      namespace Company.Project.Module {
+        class Admin {
+          +generateReport()
         }
       }
-      namespace B {
-        class B1 {
-          +foo : bool
+      Admin --> Report : generates
+    
+
+    classDiagram
+      namespace Company.Project.Module {
+        class User {
+          +login(username: String, password: String)
+          +logout()
+        }
+        class Admin {
+          +addUser(user: User)
+          +removeUser(user: User)
+          +generateReport()
         }
-        class B2 {
-          +bar : float
+        class Report {
+          +generatePDF(reportData: List)
+          +generateCSV(reportData: List)
         }
       }
-      A2 --> B2
+      Admin --> User : manages
+      Admin --> Report : generates
     

- +
+    classDiagram
+      namespace Shapes {
+        class Shape {
+          +calculateArea() double
+        }
+        class Circle {
+          +double radius
+        }
+        class Square {
+          +double side
+        }
+      }
+      
+      Shape <|-- Circle
+      Shape <|-- Square
+      
+      namespace Vehicles {
+        class Vehicle {
+          +String brand
+        }
+        class Car {
+          +int horsepower
+        }
+        class Bike {
+          +boolean hasGears
+        }
+      }
+      
+      Vehicle <|-- Car
+      Vehicle <|-- Bike
+      Car --> Circle : "Logo Shape"
+      Bike --> Square : "Logo Shape"
+                  
+    
+``` + +## Supported layouts + +- `elk`: The default layout, which is `elk.layered`. +- `elk.layered`: Layered layout +- `elk.stress`: Stress layout +- `elk.force`: Force layout +- `elk.mrtree`: Multi-root tree layout +- `elk.sporeOverlap`: Spore overlap layout + + diff --git a/packages/mermaid-layout-elk/package.json b/packages/mermaid-layout-elk/package.json new file mode 100644 index 0000000000..a043d4c384 --- /dev/null +++ b/packages/mermaid-layout-elk/package.json @@ -0,0 +1,45 @@ +{ + "name": "@mermaid-js/layout-elk", + "version": "0.1.7", + "description": "ELK layout engine for mermaid", + "module": "dist/mermaid-layout-elk.core.mjs", + "types": "dist/layouts.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./dist/mermaid-layout-elk.core.mjs", + "types": "./dist/layouts.d.ts" + }, + "./*": "./*" + }, + "keywords": [ + "diagram", + "markdown", + "elk", + "mermaid" + ], + "scripts": {}, + "repository": { + "type": "git", + "url": "https://github.com/mermaid-js/mermaid" + }, + "contributors": [ + "Knut Sveidqvist", + "Sidharth Vinod" + ], + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "elkjs": "^0.9.3" + }, + "devDependencies": { + "@types/d3": "^7.4.3", + "mermaid": "workspace:^" + }, + "peerDependencies": { + "mermaid": "^11.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/packages/mermaid-flowchart-elk/src/render-utils.ts b/packages/mermaid-layout-elk/src/find-common-ancestor.ts similarity index 71% rename from packages/mermaid-flowchart-elk/src/render-utils.ts rename to packages/mermaid-layout-elk/src/find-common-ancestor.ts index ebdc01cf76..2d513b66dc 100644 --- a/packages/mermaid-flowchart-elk/src/render-utils.ts +++ b/packages/mermaid-layout-elk/src/find-common-ancestor.ts @@ -3,10 +3,15 @@ export interface TreeData { childrenById: Record; } -export const findCommonAncestor = (id1: string, id2: string, treeData: TreeData) => { - const { parentById } = treeData; +export const findCommonAncestor = (id1: string, id2: string, { parentById }: TreeData) => { const visited = new Set(); let currentId = id1; + + // Edge case with self edges + if (id1 === id2) { + return parentById[id1] || 'root'; + } + while (currentId) { visited.add(currentId); if (currentId === id2) { @@ -14,6 +19,7 @@ export const findCommonAncestor = (id1: string, id2: string, treeData: TreeData) } currentId = parentById[currentId]; } + currentId = id2; while (currentId) { if (visited.has(currentId)) { @@ -21,5 +27,6 @@ export const findCommonAncestor = (id1: string, id2: string, treeData: TreeData) } currentId = parentById[currentId]; } + return 'root'; }; diff --git a/packages/mermaid-layout-elk/src/layouts.ts b/packages/mermaid-layout-elk/src/layouts.ts new file mode 100644 index 0000000000..dc48ccbfae --- /dev/null +++ b/packages/mermaid-layout-elk/src/layouts.ts @@ -0,0 +1,19 @@ +import type { LayoutLoaderDefinition } from 'mermaid'; + +const loader = async () => await import(`./render.js`); +const algos = ['elk.stress', 'elk.force', 'elk.mrtree', 'elk.sporeOverlap']; + +const layouts: LayoutLoaderDefinition[] = [ + { + name: 'elk', + loader, + algorithm: 'elk.layered', + }, + ...algos.map((algo) => ({ + name: algo, + loader, + algorithm: algo, + })), +]; + +export default layouts; diff --git a/packages/mermaid-layout-elk/src/render.ts b/packages/mermaid-layout-elk/src/render.ts new file mode 100644 index 0000000000..59b97c557f --- /dev/null +++ b/packages/mermaid-layout-elk/src/render.ts @@ -0,0 +1,1023 @@ +import { curveLinear } from 'd3'; +import ELK from 'elkjs/lib/elk.bundled.js'; +import type { InternalHelpers, LayoutData, RenderOptions, SVG, SVGGroup } from 'mermaid'; +import { type TreeData, findCommonAncestor } from './find-common-ancestor.js'; + +type Node = LayoutData['nodes'][number]; + +interface LabelData { + width: number; + height: number; + wrappingWidth?: number; + labelNode?: SVGGElement | null; +} + +interface NodeWithVertex extends Omit { + children?: unknown[]; + labelData?: LabelData; + domId?: Node['domId'] | SVGGroup | d3.Selection; +} + +export const render = async ( + data4Layout: LayoutData, + svg: SVG, + { + common, + getConfig, + insertCluster, + insertEdge, + insertEdgeLabel, + insertMarkers, + insertNode, + interpolateToCurve, + labelHelper, + log, + positionEdgeLabel, + }: InternalHelpers, + { algorithm }: RenderOptions +) => { + const nodeDb: Record = {}; + const clusterDb: Record = {}; + + const addVertex = async ( + nodeEl: SVGGroup, + graph: { children: NodeWithVertex[] }, + nodeArr: Node[], + node: Node + ) => { + const labelData: LabelData = { width: 0, height: 0 }; + + const config = getConfig(); + + // Add the element to the DOM + if (!node.isGroup) { + const child: NodeWithVertex = { + ...node, + }; + graph.children.push(child); + nodeDb[node.id] = child; + + const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir }); + const boundingBox = childNodeEl.node()!.getBBox(); + child.domId = childNodeEl; + child.width = boundingBox.width; + child.height = boundingBox.height; + } else { + // A subgraph + const child: NodeWithVertex & { children: NodeWithVertex[] } = { + ...node, + children: [], + }; + graph.children.push(child); + nodeDb[node.id] = child; + await addVertices(nodeEl, nodeArr, child, node.id); + + if (node.label) { + // @ts-ignore TODO: fix this + const { shapeSvg, bbox } = await labelHelper(nodeEl, node, undefined, true); + labelData.width = bbox.width; + labelData.wrappingWidth = config.flowchart!.wrappingWidth; + // Give some padding for elk + labelData.height = bbox.height - 2; + labelData.labelNode = shapeSvg.node(); + // We need the label hight to be able to size the subgraph; + shapeSvg.remove(); + } else { + // Subgraph without label + labelData.width = 0; + labelData.height = 0; + } + child.labelData = labelData; + child.domId = nodeEl; + } + }; + + const addVertices = async function ( + nodeEl: SVGGroup, + nodeArr: Node[], + graph: { children: NodeWithVertex[] }, + parentId?: string + ) { + const siblings = nodeArr.filter((node) => node?.parentId === parentId); + log.info('addVertices APA12', siblings, parentId); + // Iterate through each item in the vertex object (containing all the vertices found) in the graph definition + await Promise.all( + siblings.map(async (node) => { + await addVertex(nodeEl, graph, nodeArr, node); + }) + ); + return graph; + }; + + const drawNodes = async ( + relX: number, + relY: number, + nodeArray: any[], + svg: any, + subgraphsEl: SVGGroup, + depth: number + ) => { + await Promise.all( + nodeArray.map(async function (node: { + id: string | number; + x: any; + y: any; + width: number; + labels: { width: any }[]; + height: number; + isGroup: any; + labelData: any; + offset: { posX: number; posY: number }; + shape: any; + domId: { node: () => any; attr: (arg0: string, arg1: string) => void }; + }) { + if (node) { + nodeDb[node.id] = node; + nodeDb[node.id].offset = { + posX: node.x + relX, + posY: node.y + relY, + x: relX, + y: relY, + depth, + width: Math.max(node.width, node.labels ? node.labels[0]?.width || 0 : 0), + height: node.height, + }; + if (node.isGroup) { + log.debug('Id abc88 subgraph = ', node.id, node.x, node.y, node.labelData); + const subgraphEl = subgraphsEl.insert('g').attr('class', 'subgraph'); + // TODO use faster way of cloning + const clusterNode = JSON.parse(JSON.stringify(node)); + clusterNode.x = node.offset.posX + node.width / 2; + clusterNode.y = node.offset.posY + node.height / 2; + clusterNode.width = Math.max(clusterNode.width, node.labelData.width); + await insertCluster(subgraphEl, clusterNode); + + log.debug('Id (UIO)= ', node.id, node.width, node.shape, node.labels); + } else { + log.info( + 'Id NODE = ', + node.id, + node.x, + node.y, + relX, + relY, + node.domId.node(), + `translate(${node.x + relX + node.width / 2}, ${node.y + relY + node.height / 2})` + ); + node.domId.attr( + 'transform', + `translate(${node.x + relX + node.width / 2}, ${node.y + relY + node.height / 2})` + ); + } + } + }) + ); + + await Promise.all( + nodeArray.map(async function (node: { isGroup: any; x: any; y: any; children: any }) { + if (node?.isGroup) { + await drawNodes(relX + node.x, relY + node.y, node.children, svg, subgraphsEl, depth + 1); + } + }) + ); + }; + + const addSubGraphs = (nodeArr: any[]): TreeData => { + const parentLookupDb: TreeData = { parentById: {}, childrenById: {} }; + const subgraphs = nodeArr.filter((node: { isGroup: any }) => node.isGroup); + log.info('Subgraphs - ', subgraphs); + subgraphs.forEach((subgraph: { id: string }) => { + const children = nodeArr.filter((node: { parentId: any }) => node.parentId === subgraph.id); + children.forEach((node: any) => { + parentLookupDb.parentById[node.id] = subgraph.id; + if (parentLookupDb.childrenById[subgraph.id] === undefined) { + parentLookupDb.childrenById[subgraph.id] = []; + } + parentLookupDb.childrenById[subgraph.id].push(node); + }); + }); + + subgraphs.forEach(function (subgraph: { id: string | number }) { + const data: any = { id: subgraph.id }; + if (parentLookupDb.parentById[subgraph.id] !== undefined) { + data.parent = parentLookupDb.parentById[subgraph.id]; + } + }); + return parentLookupDb; + }; + + const getEdgeStartEndPoint = (edge: any) => { + const source: any = edge.start; + const target: any = edge.end; + + // Save the original source and target + const sourceId = source; + const targetId = target; + + const startNode = nodeDb[edge.start.id]; + const endNode = nodeDb[edge.end.id]; + + if (!startNode || !endNode) { + return { source, target }; + } + + // Add the edge to the graph + return { source, target, sourceId, targetId }; + }; + + const calcOffset = function (src: string, dest: string, parentLookupDb: TreeData) { + const ancestor = findCommonAncestor(src, dest, parentLookupDb); + if (ancestor === undefined || ancestor === 'root') { + return { x: 0, y: 0 }; + } + + const ancestorOffset = nodeDb[ancestor].offset; + return { x: ancestorOffset.posX, y: ancestorOffset.posY }; + }; + + /** + * Add edges to graph based on parsed graph definition + */ + const addEdges = async function ( + dataForLayout: { edges: any; direction?: string }, + graph: { + id?: string; + layoutOptions?: { + 'elk.hierarchyHandling': string; + 'elk.algorithm': any; + 'nodePlacement.strategy': any; + 'elk.layered.mergeEdges': any; + 'elk.direction': string; + 'spacing.baseValue': number; + }; + children?: never[]; + edges: any; + }, + svg: SVG + ) { + log.info('abc78 DAGA edges = ', dataForLayout); + const edges = dataForLayout.edges; + const labelsEl = svg.insert('g').attr('class', 'edgeLabels'); + const linkIdCnt: any = {}; + const dir = dataForLayout.direction || 'DOWN'; + let defaultStyle: string | undefined; + let defaultLabelStyle: string | undefined; + + await Promise.all( + edges.map(async function (edge: { + id: string; + start: string; + end: string; + length: number; + text: undefined; + label: any; + type: string; + stroke: any; + interpolate: undefined; + style: undefined; + labelType: any; + startLabelRight?: string; + endLabelLeft?: string; + }) { + // Identify Link + const linkIdBase = edge.id; // 'L-' + edge.start + '-' + edge.end; + // count the links from+to the same node to give unique id + if (linkIdCnt[linkIdBase] === undefined) { + linkIdCnt[linkIdBase] = 0; + log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]); + } else { + linkIdCnt[linkIdBase]++; + log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]); + } + const linkId = linkIdBase + '_' + linkIdCnt[linkIdBase]; + edge.id = linkId; + log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]); + const linkNameStart = 'LS_' + edge.start; + const linkNameEnd = 'LE_' + edge.end; + + const edgeData: any = { style: '', labelStyle: '' }; + edgeData.minlen = edge.length || 1; + edge.text = edge.label; + // Set link type for rendering + if (edge.type === 'arrow_open') { + edgeData.arrowhead = 'none'; + } else { + edgeData.arrowhead = 'normal'; + } + + // Check of arrow types, placed here in order not to break old rendering + edgeData.arrowTypeStart = 'arrow_open'; + edgeData.arrowTypeEnd = 'arrow_open'; + + /* eslint-disable no-fallthrough */ + switch (edge.type) { + case 'double_arrow_cross': + edgeData.arrowTypeStart = 'arrow_cross'; + case 'arrow_cross': + edgeData.arrowTypeEnd = 'arrow_cross'; + break; + case 'double_arrow_point': + edgeData.arrowTypeStart = 'arrow_point'; + case 'arrow_point': + edgeData.arrowTypeEnd = 'arrow_point'; + break; + case 'double_arrow_circle': + edgeData.arrowTypeStart = 'arrow_circle'; + case 'arrow_circle': + edgeData.arrowTypeEnd = 'arrow_circle'; + break; + } + + let style = ''; + let labelStyle = ''; + + edgeData.startLabelRight = edge.startLabelRight; + edgeData.endLabelLeft = edge.endLabelLeft; + + switch (edge.stroke) { + case 'normal': + style = 'fill:none;'; + if (defaultStyle !== undefined) { + style = defaultStyle; + } + if (defaultLabelStyle !== undefined) { + labelStyle = defaultLabelStyle; + } + edgeData.thickness = 'normal'; + edgeData.pattern = 'solid'; + break; + case 'dotted': + edgeData.thickness = 'normal'; + edgeData.pattern = 'dotted'; + edgeData.style = 'fill:none;stroke-width:2px;stroke-dasharray:3;'; + break; + case 'thick': + edgeData.thickness = 'thick'; + edgeData.pattern = 'solid'; + edgeData.style = 'stroke-width: 3.5px;fill:none;'; + break; + } + + edgeData.style = edgeData.style += style; + edgeData.labelStyle = edgeData.labelStyle += labelStyle; + + const conf = getConfig(); + if (edge.interpolate !== undefined) { + edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear); + } else if (edges.defaultInterpolate !== undefined) { + edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear); + } else { + // @ts-ignore TODO: fix this + edgeData.curve = interpolateToCurve(conf.curve, curveLinear); + } + + if (edge.text === undefined) { + if (edge.style !== undefined) { + edgeData.arrowheadStyle = 'fill: #333'; + } + } else { + edgeData.arrowheadStyle = 'fill: #333'; + edgeData.labelpos = 'c'; + } + + edgeData.labelType = edge.labelType; + edgeData.label = (edge?.text || '').replace(common.lineBreakRegex, '\n'); + + if (edge.style === undefined) { + edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;'; + } + + edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:'); + + edgeData.id = linkId; + edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd; + + const labelEl = await insertEdgeLabel(labelsEl, edgeData); + + // calculate start and end points of the edge, note that the source and target + // can be modified for shapes that have ports + // @ts-ignore TODO: fix this + const { source, target, sourceId, targetId } = getEdgeStartEndPoint(edge, dir); + log.debug('abc78 source and target', source, target); + // Add the edge to the graph + graph.edges.push({ + // @ts-ignore TODO: fix this + id: 'e' + edge.start + edge.end, + ...edge, + sources: [source], + targets: [target], + sourceId, + targetId, + labelEl: labelEl, + labels: [ + { + width: edgeData.width, + height: edgeData.height, + orgWidth: edgeData.width, + orgHeight: edgeData.height, + text: edgeData.label, + layoutOptions: { + 'edgeLabels.inline': 'true', + 'edgeLabels.placement': 'CENTER', + }, + }, + ], + edgeData, + }); + }) + ); + return graph; + }; + + function dir2ElkDirection(dir: any) { + switch (dir) { + case 'LR': + return 'RIGHT'; + case 'RL': + return 'LEFT'; + case 'TB': + return 'DOWN'; + case 'BT': + return 'UP'; + default: + return 'DOWN'; + } + } + + function setIncludeChildrenPolicy(nodeId: string, ancestorId: string) { + const node = nodeDb[nodeId]; + + if (!node) { + return; + } + if (node?.layoutOptions === undefined) { + node.layoutOptions = {}; + } + node.layoutOptions['elk.hierarchyHandling'] = 'INCLUDE_CHILDREN'; + if (node.id !== ancestorId) { + setIncludeChildrenPolicy(node.parentId, ancestorId); + } + } + + function intersectLine( + p1: { y: number; x: number }, + p2: { y: number; x: number }, + q1: { x: any; y: any }, + q2: { x: any; y: any } + ) { + log.debug('UIO intersectLine', p1, p2, q1, q2); + // Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994, + // p7 and p473. + + // let a1, a2, b1, b2, c1, c2; + // let r1, r2, r3, r4; + // let denom, offset, num; + // let x, y; + + // Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x + + // b1 y + c1 = 0. + const a1 = p2.y - p1.y; + const b1 = p1.x - p2.x; + const c1 = p2.x * p1.y - p1.x * p2.y; + + // Compute r3 and r4. + const r3 = a1 * q1.x + b1 * q1.y + c1; + const r4 = a1 * q2.x + b1 * q2.y + c1; + + const epsilon = 1e-6; + + // Check signs of r3 and r4. If both point 3 and point 4 lie on + // same side of line 1, the line segments do not intersect. + if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) { + return /*DON'T_INTERSECT*/; + } + + // Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0 + const a2 = q2.y - q1.y; + const b2 = q1.x - q2.x; + const c2 = q2.x * q1.y - q1.x * q2.y; + + // Compute r1 and r2 + const r1 = a2 * p1.x + b2 * p1.y + c2; + const r2 = a2 * p2.x + b2 * p2.y + c2; + + // Check signs of r1 and r2. If both point 1 and point 2 lie + // on same side of second line segment, the line segments do + // not intersect. + if (Math.abs(r1) < epsilon && Math.abs(r2) < epsilon && sameSign(r1, r2)) { + return /*DON'T_INTERSECT*/; + } + + // Line segments intersect: compute intersection point. + const denom = a1 * b2 - a2 * b1; + if (denom === 0) { + return /*COLLINEAR*/; + } + + const offset = Math.abs(denom / 2); + + // The denom/2 is to get rounding instead of truncating. It + // is added or subtracted to the numerator, depending upon the + // sign of the numerator. + let num = b1 * c2 - b2 * c1; + const x = num < 0 ? (num - offset) / denom : (num + offset) / denom; + + num = a2 * c1 - a1 * c2; + const y = num < 0 ? (num - offset) / denom : (num + offset) / denom; + + return { x: x, y: y }; + } + + function sameSign(r1: number, r2: number) { + return r1 * r2 > 0; + } + const diamondIntersection = ( + bounds: { x: any; y: any; width: any; height: any }, + outsidePoint: { x: number; y: number }, + insidePoint: any + ) => { + const x1 = bounds.x; + const y1 = bounds.y; + + const w = bounds.width; //+ bounds.padding; + const h = bounds.height; // + bounds.padding; + + const polyPoints = [ + { x: x1, y: y1 - h / 2 }, + { x: x1 + w / 2, y: y1 }, + { x: x1, y: y1 + h / 2 }, + { x: x1 - w / 2, y: y1 }, + ]; + log.debug( + `APA16 diamondIntersection calc abc89: + outsidePoint: ${JSON.stringify(outsidePoint)} + insidePoint : ${JSON.stringify(insidePoint)} + node-bounds : x:${bounds.x} y:${bounds.y} w:${bounds.width} h:${bounds.height}`, + JSON.stringify(polyPoints) + ); + + const intersections = []; + + let minX = Number.POSITIVE_INFINITY; + let minY = Number.POSITIVE_INFINITY; + + polyPoints.forEach(function (entry) { + minX = Math.min(minX, entry.x); + minY = Math.min(minY, entry.y); + }); + + const left = x1 - w / 2 - minX; + const top = y1 - h / 2 - minY; + + for (let i = 0; i < polyPoints.length; i++) { + const p1 = polyPoints[i]; + const p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0]; + const intersect = intersectLine( + bounds, + outsidePoint, + { x: left + p1.x, y: top + p1.y }, + { x: left + p2.x, y: top + p2.y } + ); + + if (intersect) { + intersections.push(intersect); + } + } + + if (!intersections.length) { + return bounds; + } + + log.debug('UIO intersections', intersections); + + if (intersections.length > 1) { + // More intersections, find the one nearest to edge end point + intersections.sort(function (p, q) { + const pdx = p.x - outsidePoint.x; + const pdy = p.y - outsidePoint.y; + const distp = Math.sqrt(pdx * pdx + pdy * pdy); + + const qdx = q.x - outsidePoint.x; + const qdy = q.y - outsidePoint.y; + const distq = Math.sqrt(qdx * qdx + qdy * qdy); + + return distp < distq ? -1 : distp === distq ? 0 : 1; + }); + } + + return intersections[0]; + }; + + const intersection = ( + node: { x: any; y: any; width: number; height: number }, + outsidePoint: { x: number; y: number }, + insidePoint: { x: number; y: number } + ) => { + log.debug(`intersection calc abc89: + outsidePoint: ${JSON.stringify(outsidePoint)} + insidePoint : ${JSON.stringify(insidePoint)} + node : x:${node.x} y:${node.y} w:${node.width} h:${node.height}`); + const x = node.x; + const y = node.y; + + const dx = Math.abs(x - insidePoint.x); + // const dy = Math.abs(y - insidePoint.y); + const w = node.width / 2; + let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx; + const h = node.height / 2; + + const Q = Math.abs(outsidePoint.y - insidePoint.y); + const R = Math.abs(outsidePoint.x - insidePoint.x); + + if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) { + // Intersection is top or bottom of rect. + const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y; + r = (R * q) / Q; + const res = { + x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r, + y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q, + }; + + if (r === 0) { + res.x = outsidePoint.x; + res.y = outsidePoint.y; + } + if (R === 0) { + res.x = outsidePoint.x; + } + if (Q === 0) { + res.y = outsidePoint.y; + } + + log.debug(`abc89 topp/bott calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res); // cspell: disable-line + + return res; + } else { + // Intersection onn sides of rect + if (insidePoint.x < outsidePoint.x) { + r = outsidePoint.x - w - x; + } else { + // r = outsidePoint.x - w - x; + r = x - w - outsidePoint.x; + } + const q = (Q * r) / R; + // OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x + dx - w; + // OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r; + let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r; + // let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r; + let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q; + log.debug(`sides calc abc89, Q ${Q}, q ${q}, R ${R}, r ${r}`, { _x, _y }); + if (r === 0) { + _x = outsidePoint.x; + _y = outsidePoint.y; + } + if (R === 0) { + _x = outsidePoint.x; + } + if (Q === 0) { + _y = outsidePoint.y; + } + + return { x: _x, y: _y }; + } + }; + const outsideNode = ( + node: { x: any; y: any; width: number; height: number }, + point: { x: number; y: number } + ) => { + const x = node.x; + const y = node.y; + const dx = Math.abs(point.x - x); + const dy = Math.abs(point.y - y); + const w = node.width / 2; + const h = node.height / 2; + if (dx >= w || dy >= h) { + return true; + } + return false; + }; + /** + * This function will page a path and node where the last point(s) in the path is inside the node + * and return an update path ending by the border of the node. + */ + const cutPathAtIntersect = ( + _points: any[], + bounds: { x: any; y: any; width: any; height: any; padding: any }, + isDiamond: boolean + ) => { + log.debug('APA18 cutPathAtIntersect Points:', _points, 'node:', bounds, 'isDiamond', isDiamond); + const points: any[] = []; + let lastPointOutside = _points[0]; + let isInside = false; + _points.forEach((point: any) => { + // check if point is inside the boundary rect + if (!outsideNode(bounds, point) && !isInside) { + // First point inside the rect found + // Calc the intersection coord between the point anf the last point outside the rect + let inter; + + if (isDiamond) { + const inter2 = diamondIntersection(bounds, lastPointOutside, point); + const distance = Math.sqrt( + (lastPointOutside.x - inter2.x) ** 2 + (lastPointOutside.y - inter2.y) ** 2 + ); + if (distance > 1) { + inter = inter2; + } + } + if (!inter) { + inter = intersection(bounds, lastPointOutside, point); + } + + // Check case where the intersection is the same as the last point + let pointPresent = false; + points.forEach((p) => { + pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y); + }); + // if (!pointPresent) { + if (!points.some((e) => e.x === inter.x && e.y === inter.y)) { + points.push(inter); + } else { + log.debug('abc88 no intersect', inter, points); + } + // points.push(inter); + isInside = true; + } else { + // Outside + log.debug('abc88 outside', point, lastPointOutside, points); + lastPointOutside = point; + // points.push(point); + if (!isInside) { + points.push(point); + } + } + }); + return points; + }; + + // @ts-ignore - ELK is not typed + const elk = new ELK(); + const element = svg.select('g'); + // Add the arrowheads to the svg + insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId); + + // Setup the graph with the layout options and the data for the layout + let elkGraph: any = { + id: 'root', + layoutOptions: { + 'elk.hierarchyHandling': 'INCLUDE_CHILDREN', + 'elk.algorithm': algorithm, + 'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy, + 'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges, + 'elk.direction': 'DOWN', + '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: [], + }; + + log.info('Drawing flowchart using v4 renderer', elk); + + // Set the direction of the graph based on the parsed information + const dir = data4Layout.direction || 'DOWN'; + elkGraph.layoutOptions['elk.direction'] = dir2ElkDirection(dir); + + // Create the lookup db for the subgraphs and their children to used when creating + // the tree structured graph + const parentLookupDb: any = addSubGraphs(data4Layout.nodes); + + // Add elements in the svg to be used to hold the subgraphs container + // elements and the nodes + const subGraphsEl = svg.insert('g').attr('class', 'subgraphs'); + + const nodeEl = svg.insert('g').attr('class', 'nodes'); + + // Add the nodes to the graph, this will entail creating the actual nodes + // in order to get the size of the node. You can't get the size of a node + // that is not in the dom so we need to add it to the dom, get the size + // we will position the nodes when we get the layout from elkjs + elkGraph = await addVertices(nodeEl, data4Layout.nodes, elkGraph); + // Time for the edges, we start with adding an element in the node to hold the edges + const edgesEl = svg.insert('g').attr('class', 'edges edgePaths'); + + // Add the edges to the elk graph, this will entail creating the actual edges + elkGraph = await addEdges(data4Layout, elkGraph, svg); + + // Iterate through all nodes and add the top level nodes to the graph + const nodes = data4Layout.nodes; + nodes.forEach((n: { id: string | number }) => { + const node = nodeDb[n.id]; + + // Subgraph + if (parentLookupDb.childrenById[node.id] !== undefined) { + node.labels = [ + { + text: node.label, + width: node?.labelData?.width || 50, + height: node?.labelData?.height || 50, + }, + (node.width = node.width + 2 * node.padding), + log.debug('UIO node label', node?.labelData?.width, node.padding), + ]; + node.layoutOptions = { + 'spacing.baseValue': 30, + 'nodeLabels.placement': '[H_CENTER V_TOP, INSIDE]', + }; + if (node.dir) { + node.layoutOptions = { + ...node.layoutOptions, + 'elk.algorithm': algorithm, + 'elk.direction': dir2ElkDirection(node.dir), + 'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy, + 'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges, + 'elk.hierarchyHandling': 'SEPARATE_CHILDREN', + }; + } + delete node.x; + delete node.y; + delete node.width; + delete node.height; + } + }); + elkGraph.edges.forEach((edge: any) => { + const source = edge.sources[0]; + const target = edge.targets[0]; + + if (nodeDb[source].parentId !== nodeDb[target].parentId) { + const ancestorId = findCommonAncestor(source, target, parentLookupDb); + // an edge that breaks a subgraph has been identified, set configuration accordingly + setIncludeChildrenPolicy(source, ancestorId); + setIncludeChildrenPolicy(target, ancestorId); + } + }); + + const g = await elk.layout(elkGraph); + + // debugger; + await drawNodes(0, 0, g.children, svg, subGraphsEl, 0); + g.edges?.map( + (edge: { + sources: (string | number)[]; + targets: (string | number)[]; + start: any; + end: any; + sections: { startPoint: any; endPoint: any; bendPoints: any }[]; + points: any[]; + x: any; + labels: { height: number; width: number; x: number; y: number }[]; + y: any; + }) => { + // (elem, edge, clusterDb, diagramType, graph, id) + const startNode = nodeDb[edge.sources[0]]; + const startCluster = parentLookupDb[edge.sources[0]]; + const endNode = nodeDb[edge.targets[0]]; + const sourceId = edge.start; + const targetId = edge.end; + + const offset = calcOffset(sourceId, targetId, parentLookupDb); + log.debug( + 'APA18 offset', + offset, + sourceId, + ' ==> ', + targetId, + 'edge:', + edge, + 'cluster:', + startCluster, + startNode + ); + if (edge.sections) { + const src = edge.sections[0].startPoint; + const dest = edge.sections[0].endPoint; + const segments = edge.sections[0].bendPoints ? edge.sections[0].bendPoints : []; + + const segPoints = segments.map((segment: { x: any; y: any }) => { + return { x: segment.x + offset.x, y: segment.y + offset.y }; + }); + edge.points = [ + { x: src.x + offset.x, y: src.y + offset.y }, + ...segPoints, + { x: dest.x + offset.x, y: dest.y + offset.y }, + ]; + + let sw = startNode.width; + let ew = endNode.width; + if (startNode.isGroup) { + const bbox = startNode.domId.node().getBBox(); + // sw = Math.max(bbox.width, startNode.width, startNode.labels[0].width); + sw = Math.max(startNode.width, startNode.labels[0].width + startNode.padding); + // sw = startNode.width; + log.debug( + 'UIO width', + startNode.id, + startNode.with, + 'bbox.width=', + bbox.width, + 'lw=', + startNode.labels[0].width, + 'node:', + startNode.width, + 'SW = ', + sw + // 'HTML:', + // startNode.domId.node().innerHTML + ); + } + if (endNode.isGroup) { + const bbox = endNode.domId.node().getBBox(); + ew = Math.max(endNode.width, endNode.labels[0].width + endNode.padding); + + log.debug( + 'UIO width', + startNode.id, + startNode.with, + bbox.width, + 'EW = ', + ew, + 'HTML:', + startNode.innerHTML + ); + } + if (startNode.shape === 'diamond' || startNode.shape === 'diam') { + edge.points.unshift({ + x: startNode.offset.posX + startNode.width / 2, + y: startNode.offset.posY + startNode.height / 2, + }); + } + if (endNode.shape === 'diamond' || endNode.shape === 'diam') { + edge.points.push({ + x: endNode.offset.posX + endNode.width / 2, + y: endNode.offset.posY + endNode.height / 2, + }); + } + + edge.points = cutPathAtIntersect( + edge.points.reverse(), + { + x: startNode.offset.posX + startNode.width / 2, + y: startNode.offset.posY + startNode.height / 2, + width: sw, + height: startNode.height, + padding: startNode.padding, + }, + startNode.shape === 'diamond' || startNode.shape === 'diam' + ).reverse(); + + edge.points = cutPathAtIntersect( + edge.points, + { + x: endNode.offset.posX + endNode.width / 2, + y: endNode.offset.posY + endNode.height / 2, + width: ew, + height: endNode.height, + padding: endNode.padding, + }, + endNode.shape === 'diamond' || endNode.shape === 'diam' + ); + + const paths = insertEdge( + edgesEl, + edge, + clusterDb, + data4Layout.type, + startNode, + endNode, + data4Layout.diagramId + ); + log.info('APA12 edge points after insert', JSON.stringify(edge.points)); + + edge.x = edge.labels[0].x + offset.x + edge.labels[0].width / 2; + edge.y = edge.labels[0].y + offset.y + edge.labels[0].height / 2; + positionEdgeLabel(edge, paths); + } + } + ); +}; diff --git a/packages/mermaid-flowchart-elk/tsconfig.json b/packages/mermaid-layout-elk/tsconfig.json similarity index 89% rename from packages/mermaid-flowchart-elk/tsconfig.json rename to packages/mermaid-layout-elk/tsconfig.json index 5a41d06030..0d701ceded 100644 --- a/packages/mermaid-flowchart-elk/tsconfig.json +++ b/packages/mermaid-layout-elk/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "rootDir": "../..", + "rootDir": "./src", "outDir": "./dist", "types": ["vitest/importMeta", "vitest/globals"] }, diff --git a/packages/mermaid-zenuml/package.json b/packages/mermaid-zenuml/package.json index 1a47a99d8a..192f4e0c4f 100644 --- a/packages/mermaid-zenuml/package.json +++ b/packages/mermaid-zenuml/package.json @@ -19,8 +19,7 @@ "mermaid" ], "scripts": { - "clean": "rimraf dist", - "prepublishOnly": "pnpm -w run build" + "clean": "rimraf dist" }, "repository": { "type": "git", @@ -40,7 +39,7 @@ "mermaid": "workspace:^" }, "peerDependencies": { - "mermaid": "workspace:>=10.0.0" + "mermaid": "^10 || ^11" }, "files": [ "dist" diff --git a/packages/mermaid/CHANGELOG.md b/packages/mermaid/CHANGELOG.md new file mode 100644 index 0000000000..221d7c735d --- /dev/null +++ b/packages/mermaid/CHANGELOG.md @@ -0,0 +1,138 @@ +# mermaid + +## 11.4.1 + +### Patch Changes + +- [#6059](https://github.com/mermaid-js/mermaid/pull/6059) [`01b5079`](https://github.com/mermaid-js/mermaid/commit/01b5079562ec8d34ce9964910f168873843c68f8) Thanks [@knsv](https://github.com/knsv)! - fix: Kanban diagrams will not render when adding a number as ticket id or assigned for a task + +- [#6038](https://github.com/mermaid-js/mermaid/pull/6038) [`1388662`](https://github.com/mermaid-js/mermaid/commit/1388662132cc829f9820c2e9970ae04e2dd90588) Thanks [@knsv](https://github.com/knsv)! - fix: Intersection calculations for tilted cylinder/DAS when using handdrawn look. Some random seeds could cause the calculations to break. + +- [#6079](https://github.com/mermaid-js/mermaid/pull/6079) [`fe3cffb`](https://github.com/mermaid-js/mermaid/commit/fe3cffbb673a25b81989aacb06e5d0eda35326db) Thanks [@aloisklink](https://github.com/aloisklink)! - Bump dompurify to `^3.2.1`. This removes the need for `@types/dompurify`. + +## 11.4.0 + +### Minor Changes + +- [#5999](https://github.com/mermaid-js/mermaid/pull/5999) [`742ad7c`](https://github.com/mermaid-js/mermaid/commit/742ad7c130964df1fb5544e909d9556081285f68) Thanks [@knsv](https://github.com/knsv)! - Adding Kanban board, a new diagram type + +- [#5880](https://github.com/mermaid-js/mermaid/pull/5880) [`bdf145f`](https://github.com/mermaid-js/mermaid/commit/bdf145ffe362462176d9c1e68d5f3ff5c9d962b0) Thanks [@yari-dewalt](https://github.com/yari-dewalt)! - Class diagram changes: + + - Updates the class diagram to the new unified way of rendering. + - Includes a new "classBox" shape to be used in diagrams + - Other updates such as: + - the option to hide the empty members box in class diagrams, + - support for handDrawn look, + - the introduction of the classDef statement into class diagrams, + - support for styling the default class, + - support lollipop interfaces. + - Includes fixes / additions for #5562 #3139 and #4037 + +### Patch Changes + +- [#5937](https://github.com/mermaid-js/mermaid/pull/5937) [`17b7831`](https://github.com/mermaid-js/mermaid/commit/17b783135f9b2b7748b620dbf81d0f56ab4755f1) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - fix: Jagged edge fix for icon shape + +- [#5933](https://github.com/mermaid-js/mermaid/pull/5933) [`72d60d2`](https://github.com/mermaid-js/mermaid/commit/72d60d2633584eb59bccdb6cf30b9522db645db2) Thanks [@remcohaszing](https://github.com/remcohaszing)! - Add missing TypeScript dependencies + +- [#5937](https://github.com/mermaid-js/mermaid/pull/5937) [`17b7831`](https://github.com/mermaid-js/mermaid/commit/17b783135f9b2b7748b620dbf81d0f56ab4755f1) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - fix: Icon color fix for colored icons. + +- [#6002](https://github.com/mermaid-js/mermaid/pull/6002) [`5fabd41`](https://github.com/mermaid-js/mermaid/commit/5fabd414fbee01e43bf6c900907ffc1511ca7440) Thanks [@aloisklink](https://github.com/aloisklink)! - fix: error `mermaid.parse` on an invalid shape, so that it matches the errors thrown by `mermaid.render` + +## 11.3.0 + +### Minor Changes + +- [#5825](https://github.com/mermaid-js/mermaid/pull/5825) [`9e3aa70`](https://github.com/mermaid-js/mermaid/commit/9e3aa705ae21fd4898504ab22d775a9e437b898e) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - New Flowchart Shapes (with new syntax) + +### Patch Changes + +- [#5849](https://github.com/mermaid-js/mermaid/pull/5849) [`6c5b7ce`](https://github.com/mermaid-js/mermaid/commit/6c5b7ce9f41c0fbd59fe03dbefc8418d97697f0a) Thanks [@ReneLombard](https://github.com/ReneLombard)! - Fixed an issue when the mermaid classdiagram crashes when adding a . to the namespace. + Forexample + + ```mermaid + + classDiagram + namespace Company.Project.Module { + class GenericClass~T~ { + +addItem(item: T) + +getItem() T + } + } + ``` + +- [#5914](https://github.com/mermaid-js/mermaid/pull/5914) [`de2c05c`](https://github.com/mermaid-js/mermaid/commit/de2c05cd5463af68d19dd7b6b3f1303d69ddb2dd) Thanks [@aloisklink](https://github.com/aloisklink)! - Ban DOMPurify v3.1.7 as a dependency + +## 11.2.1 + +### Patch Changes + +- [#5856](https://github.com/mermaid-js/mermaid/pull/5856) [`bfd8c63`](https://github.com/mermaid-js/mermaid/commit/bfd8c63daaa8420e57da9953922b9f0c94123064) Thanks [@knsv](https://github.com/knsv)! - Fix for issue with calculation of label width when using in firefox + +## 11.2.0 + +### Minor Changes + +- [#5831](https://github.com/mermaid-js/mermaid/pull/5831) [`64abf29`](https://github.com/mermaid-js/mermaid/commit/64abf29ea870eaa47148197f95ce714f85bd7eea) Thanks [@sidharthv96](https://github.com/sidharthv96)! - feat: Return parsed config from mermaid.parse + +### Patch Changes + +- [#5838](https://github.com/mermaid-js/mermaid/pull/5838) [`5e75320`](https://github.com/mermaid-js/mermaid/commit/5e75320d49eab65aca630dcc3c04c8d620a8bbf7) Thanks [@bollwyvl](https://github.com/bollwyvl)! - fix: Replace $root with relative paths + +## 11.1.1 + +### Patch Changes + +- [#5828](https://github.com/mermaid-js/mermaid/pull/5828) [`4c43d21`](https://github.com/mermaid-js/mermaid/commit/4c43d21196f784b6f483ae635fc462329f3d176f) Thanks [@knsv](https://github.com/knsv)! - fix: Fix for issue where self-loops in the root of diagrams break the rendering + +## 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 + +- Updated dependencies [[`83926c9`](https://github.com/mermaid-js/mermaid/commit/83926c9707b09c34e300888186250191ee8ae30a)]: + - @mermaid-js/parser@0.1.1 + +## 11.0.1 + +### Patch Changes + +- [#5744](https://github.com/mermaid-js/mermaid/pull/5744) [`5013484`](https://github.com/mermaid-js/mermaid/commit/50134849246141ec400e33e08c12c10539b84de9) Thanks [@sidharthv96](https://github.com/sidharthv96)! - Release parser, test changesets + +- Updated dependencies [[`5013484`](https://github.com/mermaid-js/mermaid/commit/50134849246141ec400e33e08c12c10539b84de9)]: + - @mermaid-js/parser@0.1.0 diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json index 585a29ce3e..71abdfdb4b 100644 --- a/packages/mermaid/package.json +++ b/packages/mermaid/package.json @@ -1,6 +1,6 @@ { "name": "mermaid", - "version": "11.0.0-alpha.7", + "version": "11.4.1", "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", @@ -35,8 +35,8 @@ "clean": "rimraf dist", "dev": "pnpm -w dev", "docs:code": "typedoc src/defaultConfig.ts src/config.ts src/mermaid.ts && prettier --write ./src/docs/config/setup", - "docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && tsx scripts/docs.cli.mts", - "docs:verify": "pnpm docs:spellcheck && pnpm docs:code && tsx scripts/docs.cli.mts --verify", + "docs:build": "rimraf ../../docs && pnpm docs:code && pnpm docs:spellcheck && tsx scripts/docs.cli.mts", + "docs:verify": "pnpm docs:code && pnpm docs:spellcheck && tsx scripts/docs.cli.mts --verify", "docs:pre:vitepress": "pnpm --filter ./src/docs prefetch && rimraf src/vitepress && pnpm docs:code && tsx scripts/docs.cli.mts --vitepress && pnpm --filter ./src/vitepress install --no-frozen-lockfile --ignore-scripts", "docs:build:vitepress": "pnpm docs:pre:vitepress && (cd src/vitepress && pnpm run build) && cpy --flat src/docs/landing/ ./src/vitepress/.vitepress/dist/landing", "docs:dev": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./src/vitepress dev\" \"tsx scripts/docs.cli.mts --watch --vitepress\"", @@ -48,8 +48,7 @@ "types:build-config": "tsx scripts/create-types-from-json-schema.mts", "types:verify-config": "tsx scripts/create-types-from-json-schema.mts --verify", "checkCircle": "npx madge --circular ./src", - "release": "pnpm build", - "prepublishOnly": "cpy '../../README.*' ./ --cwd=. && pnpm docs:release-version && pnpm -w run build" + "prepublishOnly": "pnpm docs:verify-version" }, "repository": { "type": "git", @@ -69,32 +68,36 @@ }, "dependencies": { "@braintree/sanitize-url": "^7.0.1", + "@iconify/utils": "^2.1.32", "@mermaid-js/parser": "workspace:^", + "@types/d3": "^7.4.3", "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", + "dagre-d3-es": "7.0.11", "dayjs": "^1.11.10", - "dompurify": "^3.0.11", + "dompurify": "^3.2.1", "katex": "^0.16.9", "khroma": "^2.1.0", "lodash-es": "^4.17.21", "marked": "^13.0.2", + "roughjs": "^4.6.6", "stylis": "^4.3.1", "ts-dedent": "^2.2.0", "uuid": "^9.0.1" }, "devDependencies": { "@adobe/jsonschema2md": "^8.0.0", + "@iconify/types": "^2.0.0", "@types/cytoscape": "^3.21.4", - "@types/d3": "^7.4.3", + "@types/cytoscape-fcose": "^2.2.4", "@types/d3-sankey": "^0.12.4", "@types/d3-scale": "^4.0.8", "@types/d3-scale-chromatic": "^3.0.3", "@types/d3-selection": "^3.0.10", "@types/d3-shape": "^3.1.6", - "@types/dompurify": "^3.0.5", "@types/jsdom": "^21.1.6", "@types/katex": "^0.16.7", "@types/lodash-es": "^4.17.12", @@ -105,7 +108,6 @@ "ajv": "^8.12.0", "chokidar": "^3.6.0", "concurrently": "^8.2.2", - "cpy-cli": "^5.0.0", "csstree-validator": "^3.0.0", "globby": "^14.0.1", "jison": "^0.4.18", @@ -133,7 +135,6 @@ "dist/", "README.md" ], - "sideEffects": false, "publishConfig": { "access": "public" } diff --git a/packages/mermaid/scripts/docs.mts b/packages/mermaid/scripts/docs.mts index 374e788706..073a3c1a9c 100644 --- a/packages/mermaid/scripts/docs.mts +++ b/packages/mermaid/scripts/docs.mts @@ -41,7 +41,8 @@ import { exec } from 'child_process'; import { globby } from 'globby'; import { JSDOM } from 'jsdom'; import { dump, load, JSON_SCHEMA } from 'js-yaml'; -import type { Code, ListItem, Root, Text, YAML } from 'mdast'; +import type { Code, ListItem, PhrasingContent, Root, Text, YAML } from 'mdast'; +import { register } from 'node:module'; import { posix, dirname, relative, join } from 'path'; import prettier from 'prettier'; import { remark } from 'remark'; @@ -53,6 +54,10 @@ import mm from 'micromatch'; import flatmap from 'unist-util-flatmap'; import { visit } from 'unist-util-visit'; +// short-circuit `.schema.yaml` imports, so that we can safely import `shapes.js` +register('./loadHook.mjs', import.meta.url); +const { shapesDefs } = await import('../src/rendering-util/rendering-elements/shapes.js'); + export const MERMAID_RELEASE_VERSION = JSON.parse(readFileSync('../mermaid/package.json', 'utf8')) .version as string; const MERMAID_MAJOR_VERSION = MERMAID_RELEASE_VERSION.split('.')[0]; @@ -103,6 +108,60 @@ const generateHeader = (file: string): string => { > ## Please edit the corresponding file in [${filePathFromRoot}](${sourcePathRelativeToGenerated}).`; }; +/** + * Builds a markdown list of shapes supported in flowcharts. + */ +export function buildShapeDoc() { + const data = shapesDefs + .sort((a, b) => a.semanticName.localeCompare(b.semanticName)) + .map((shape): PhrasingContent[][] => { + const { name, semanticName, description, shortName, aliases = [] } = shape; + return [ + [{ type: 'text', value: semanticName }], + [{ type: 'text', value: name }], + [{ type: 'inlineCode', value: shortName }], + [{ type: 'text', value: description }], + aliases.sort().flatMap((alias, index) => [ + ...(index !== 0 ? ([{ type: 'text', value: ', ' }] as const) : []), + { + type: 'inlineCode', + value: alias, + }, + ]), + ]; + }); + + // don't prettify this table, since we'd do it later + return remark() + .use(remarkGfm) + .stringify({ + type: 'root', + children: [ + { + type: 'table', + children: [ + ['Semantic Name', 'Shape Name', 'Short Name', 'Description', 'Alias Supported'].map( + (s): PhrasingContent[] => [ + { + type: 'strong', + children: [{ type: 'text', value: s }], + }, + ] + ), + ...data, + ].map((row) => ({ + type: 'tableRow', + children: row.map((cell) => ({ + type: 'tableCell', + children: cell, + })), + })), + }, + ], + }) + .toString(); +} + /** * Given a source file name and path, return the documentation destination full path and file name * Create the destination path if it does not already exist. @@ -192,10 +251,22 @@ export const transformToBlockQuote = ( const injectPlaceholders = (text: string): string => text.replace(//g, MERMAID_MAJOR_VERSION).replace(//g, CDN_URL); +const virtualGenerators: Record string> = { + shapesTable: buildShapeDoc, +}; + const transformIncludeStatements = (file: string, text: string): string => { // resolve includes - src https://github.com/vuejs/vitepress/blob/428eec3750d6b5648a77ac52d88128df0554d4d1/src/node/markdownToVue.ts#L65-L76 - return text.replace(includesRE, (m, m1) => { + return text.replace(includesRE, (m, m1: string) => { try { + if (m1.startsWith('virtual:')) { + const key = m1.replace('virtual:', ''); + const generator = virtualGenerators[key]; + if (!generator) { + throw new Error(`Unknown virtual generator: ${key} in "${file}"`); + } + return generator(); + } const includePath = join(dirname(file), m1).replaceAll('\\', '/'); const content = readSyncedUTF8file(includePath); includedFiles.add(changeToFinalDocDir(includePath)); diff --git a/packages/mermaid/scripts/docs.spec.ts b/packages/mermaid/scripts/docs.spec.ts index c84bc1bac9..68677d4c9c 100644 --- a/packages/mermaid/scripts/docs.spec.ts +++ b/packages/mermaid/scripts/docs.spec.ts @@ -1,4 +1,4 @@ -import { transformMarkdownAst, transformToBlockQuote } from './docs.mjs'; +import { buildShapeDoc, transformMarkdownAst, transformToBlockQuote } from './docs.mjs'; import { remark } from 'remark'; // import it this way so we can mock it import remarkFrontmatter from 'remark-frontmatter'; @@ -165,4 +165,59 @@ This Markdown should be kept. }); }); }); + + describe('buildShapeDoc', () => { + it('should build shapesTable based on the shapeDefs', () => { + expect(buildShapeDoc()).toMatchInlineSnapshot(` + "| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** | + | --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- | + | Card | Notched Rectangle | \`notch-rect\` | Represents a card | \`card\`, \`notched-rectangle\` | + | Collate | Hourglass | \`hourglass\` | Represents a collate operation | \`collate\`, \`hourglass\` | + | Com Link | Lightning Bolt | \`bolt\` | Communication link | \`com-link\`, \`lightning-bolt\` | + | Comment | Curly Brace | \`brace\` | Adds a comment | \`brace-l\`, \`comment\` | + | Comment Right | Curly Brace | \`brace-r\` | Adds a comment | | + | Comment with braces on both sides | Curly Braces | \`braces\` | Adds a comment | | + | Data Input/Output | Lean Right | \`lean-r\` | Represents input or output | \`in-out\`, \`lean-right\` | + | Data Input/Output | Lean Left | \`lean-l\` | Represents output or input | \`lean-left\`, \`out-in\` | + | Database | Cylinder | \`cyl\` | Database storage | \`cylinder\`, \`database\`, \`db\` | + | Decision | Diamond | \`diam\` | Decision-making step | \`decision\`, \`diamond\`, \`question\` | + | Delay | Half-Rounded Rectangle | \`delay\` | Represents a delay | \`half-rounded-rectangle\` | + | Direct Access Storage | Horizontal Cylinder | \`h-cyl\` | Direct access storage | \`das\`, \`horizontal-cylinder\` | + | Disk Storage | Lined Cylinder | \`lin-cyl\` | Disk storage | \`disk\`, \`lined-cylinder\` | + | Display | Curved Trapezoid | \`curv-trap\` | Represents a display | \`curved-trapezoid\`, \`display\` | + | Divided Process | Divided Rectangle | \`div-rect\` | Divided process shape | \`div-proc\`, \`divided-process\`, \`divided-rectangle\` | + | Document | Document | \`doc\` | Represents a document | \`doc\`, \`document\` | + | Event | Rounded Rectangle | \`rounded\` | Represents an event | \`event\` | + | Extract | Triangle | \`tri\` | Extraction process | \`extract\`, \`triangle\` | + | Fork/Join | Filled Rectangle | \`fork\` | Fork or join in process flow | \`join\` | + | Internal Storage | Window Pane | \`win-pane\` | Internal storage | \`internal-storage\`, \`window-pane\` | + | Junction | Filled Circle | \`f-circ\` | Junction point | \`filled-circle\`, \`junction\` | + | Lined Document | Lined Document | \`lin-doc\` | Lined document | \`lined-document\` | + | Lined/Shaded Process | Lined Rectangle | \`lin-rect\` | Lined process shape | \`lin-proc\`, \`lined-process\`, \`lined-rectangle\`, \`shaded-process\` | + | Loop Limit | Trapezoidal Pentagon | \`notch-pent\` | Loop limit step | \`loop-limit\`, \`notched-pentagon\` | + | Manual File | Flipped Triangle | \`flip-tri\` | Manual file operation | \`flipped-triangle\`, \`manual-file\` | + | Manual Input | Sloped Rectangle | \`sl-rect\` | Manual input step | \`manual-input\`, \`sloped-rectangle\` | + | Manual Operation | Trapezoid Base Top | \`trap-t\` | Represents a manual task | \`inv-trapezoid\`, \`manual\`, \`trapezoid-top\` | + | Multi-Document | Stacked Document | \`docs\` | Multiple documents | \`documents\`, \`st-doc\`, \`stacked-document\` | + | Multi-Process | Stacked Rectangle | \`st-rect\` | Multiple processes | \`processes\`, \`procs\`, \`stacked-rectangle\` | + | Odd | Odd | \`odd\` | Odd shape | | + | Paper Tape | Flag | \`flag\` | Paper tape | \`paper-tape\` | + | Prepare Conditional | Hexagon | \`hex\` | Preparation or condition step | \`hexagon\`, \`prepare\` | + | Priority Action | Trapezoid Base Bottom | \`trap-b\` | Priority action | \`priority\`, \`trapezoid\`, \`trapezoid-bottom\` | + | Process | Rectangle | \`rect\` | Standard process shape | \`proc\`, \`process\`, \`rectangle\` | + | Start | Circle | \`circle\` | Starting point | \`circ\` | + | Start | Small Circle | \`sm-circ\` | Small starting point | \`small-circle\`, \`start\` | + | Stop | Double Circle | \`dbl-circ\` | Represents a stop point | \`double-circle\` | + | Stop | Framed Circle | \`fr-circ\` | Stop point | \`framed-circle\`, \`stop\` | + | Stored Data | Bow Tie Rectangle | \`bow-rect\` | Stored data | \`bow-tie-rectangle\`, \`stored-data\` | + | Subprocess | Framed Rectangle | \`fr-rect\` | Subprocess | \`framed-rectangle\`, \`subproc\`, \`subprocess\`, \`subroutine\` | + | Summary | Crossed Circle | \`cross-circ\` | Summary | \`crossed-circle\`, \`summary\` | + | Tagged Document | Tagged Document | \`tag-doc\` | Tagged document | \`tag-doc\`, \`tagged-document\` | + | Tagged Process | Tagged Rectangle | \`tag-rect\` | Tagged process | \`tag-proc\`, \`tagged-process\`, \`tagged-rectangle\` | + | Terminal Point | Stadium | \`stadium\` | Terminal point | \`pill\`, \`terminal\` | + | Text Block | Text Block | \`text\` | Text block | | + " + `); + }); + }); }); diff --git a/packages/mermaid/scripts/loadHook.mjs b/packages/mermaid/scripts/loadHook.mjs new file mode 100644 index 0000000000..50129861bc --- /dev/null +++ b/packages/mermaid/scripts/loadHook.mjs @@ -0,0 +1,22 @@ +import { fileURLToPath } from 'node:url'; +/** @import import { LoadHook } from "node:module"; */ +/** + * @type {LoadHook} + * + * Load hook that short circuits the loading of `.schema.yaml` files with `export default {}`. + * These would normally be loaded using ESBuild, but that doesn't work for these local scripts. + * + * @see https://nodejs.org/api/module.html#loadurl-context-nextload + */ +export const load = async (url, context, nextLoad) => { + const filePath = url.startsWith('file://') ? fileURLToPath(url) : url; + if (filePath.endsWith('.schema.yaml')) { + return { + format: 'module', + shortCircuit: true, + source: `export default {}`, + }; + } else { + return await nextLoad(url, context); + } +}; diff --git a/packages/mermaid/scripts/update-release-version.mts b/packages/mermaid/scripts/update-release-version.mts index a5943b37bd..0459d34443 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.ts b/packages/mermaid/src/config.ts index 1229bcd22b..31f0592de9 100644 --- a/packages/mermaid/src/config.ts +++ b/packages/mermaid/src/config.ts @@ -190,7 +190,10 @@ export const addDirective = (directive: MermaidConfig) => { // If the directive has a fontFamily, but no themeVariables, add the fontFamily to the themeVariables if (directive.fontFamily && !directive.themeVariables?.fontFamily) { - directive.themeVariables = { fontFamily: directive.fontFamily }; + directive.themeVariables = { + ...directive.themeVariables, + fontFamily: directive.fontFamily, + }; } directives.push(directive); diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index b7cf27e72b..86281cd522 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -61,9 +61,24 @@ export interface MermaidConfig { * You may also use `themeCSS` to override this value. * */ - theme?: 'default' | 'forest' | 'dark' | 'neutral' | 'null'; + theme?: 'default' | 'base' | 'dark' | 'forest' | 'neutral' | 'null'; themeVariables?: any; themeCSS?: string; + /** + * Defines which main look to use for the diagram. + * + */ + look?: 'classic' | 'handDrawn'; + /** + * Defines the seed to be used when using handDrawn look. This is important for the automated tests as they will always find differences without the seed. The default value is 0 which gives a random seed. + * + */ + handDrawnSeed?: number; + /** + * Defines which layout algorithm to use for rendering the diagram. + * + */ + layout?: string; /** * The maximum allowed size of the users text diagram */ @@ -73,6 +88,28 @@ export interface MermaidConfig { * */ maxEdges?: number; + elk?: { + /** + * 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. + * + */ + mergeEdges?: boolean; + /** + * Elk specific option affecting how nodes are placed. + * + */ + 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; /** @@ -154,7 +191,9 @@ export interface MermaidConfig { quadrantChart?: QuadrantChartConfig; xyChart?: XYChartConfig; requirement?: RequirementDiagramConfig; + architecture?: ArchitectureDiagramConfig; mindmap?: MindmapDiagramConfig; + kanban?: KanbanDiagramConfig; gitGraph?: GitGraphDiagramConfig; c4?: C4DiagramConfig; sankey?: SankeyDiagramConfig; @@ -678,6 +717,7 @@ export interface ClassDiagramConfig extends BaseDiagramConfig { */ diagramPadding?: number; htmlLabels?: boolean; + hideEmptyMembersBox?: boolean; } /** * The object containing configurations specific for entity relationship diagrams @@ -697,6 +737,8 @@ export interface StateDiagramConfig extends BaseDiagramConfig { textHeight?: number; titleShift?: number; noteMargin?: number; + nodeSpacing?: number; + rankSpacing?: number; forkWidth?: number; forkHeight?: number; miniPadding?: number; @@ -962,6 +1004,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 * @@ -972,6 +1025,17 @@ export interface MindmapDiagramConfig extends BaseDiagramConfig { padding?: number; maxNodeWidth?: number; } +/** + * The object containing configurations specific for kanban diagrams + * + * This interface was referenced by `MermaidConfig`'s JSON-Schema + * via the `definition` "KanbanDiagramConfig". + */ +export interface KanbanDiagramConfig extends BaseDiagramConfig { + padding?: number; + sectionWidth?: number; + ticketBaseUrl?: string; +} /** * This interface was referenced by `MermaidConfig`'s JSON-Schema * via the `definition` "GitGraphDiagramConfig". diff --git a/packages/mermaid/src/dagre-wrapper/index.js b/packages/mermaid/src/dagre-wrapper/index.js index c870566a7f..86ae7e2842 100644 --- a/packages/mermaid/src/dagre-wrapper/index.js +++ b/packages/mermaid/src/dagre-wrapper/index.js @@ -87,7 +87,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit // insertCluster(clusters, graph.node(v)); } else { log.info('Node - the non recursive path', v, node.id, node); - await insertNode(nodes, graph.node(v), dir); + await insertNode(nodes, graph.node(v), { config: siteConfig, dir }); } } }) @@ -97,25 +97,26 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit // Also figure out which edges point to/from clusters and adjust them accordingly // Edges from/to clusters really points to the first child in the cluster. // TODO: pick optimal child in the cluster to us as link anchor - graph.edges().forEach(function (e) { + graph.edges().forEach(async function (e) { const edge = graph.edge(e.v, e.w, e.name); log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e)); log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e))); // Check if link is either from or to a cluster log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translating: ', clusterDb[e.v], clusterDb[e.w]); - insertEdgeLabel(edgeLabels, edge); + await insertEdgeLabel(edgeLabels, edge); }); graph.edges().forEach(function (e) { log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e)); }); + log.info('Graph before layout:', JSON.stringify(graphlibJson.write(graph))); log.info('#############################################'); log.info('### Layout ###'); log.info('#############################################'); log.info(graph); dagreLayout(graph); - log.info('Graph after layout:', graphlibJson.write(graph)); + log.info('Graph after layout:', JSON.stringify(graphlibJson.write(graph))); // Move the nodes to the correct place let diff = 0; const { subGraphTitleTotalMargin } = getSubGraphTitleMargins(siteConfig); diff --git a/packages/mermaid/src/dagre-wrapper/nodes.js b/packages/mermaid/src/dagre-wrapper/nodes.js index 611cc5b6ee..2677fd7857 100644 --- a/packages/mermaid/src/dagre-wrapper/nodes.js +++ b/packages/mermaid/src/dagre-wrapper/nodes.js @@ -1,12 +1,12 @@ import { select } from 'd3'; -import { log } from '../logger.js'; -import { labelHelper, updateNodeBounds, insertPolygonShape } from './shapes/util.js'; import { getConfig } from '../diagram-api/diagramAPI.js'; -import intersect from './intersect/index.js'; -import createLabel from './createLabel.js'; -import note from './shapes/note.js'; import { evaluate } from '../diagrams/common/common.js'; +import { log } from '../logger.js'; import { getArrowPoints } from './blockArrowHelper.js'; +import createLabel from './createLabel.js'; +import intersect from './intersect/index.js'; +import note from './shapes/note.js'; +import { insertPolygonShape, labelHelper, updateNodeBounds } from './shapes/util.js'; const formatClass = (str) => { if (str) { @@ -395,6 +395,7 @@ const rect = async (parent, node) => { // add the rect const rect = shapeSvg.insert('rect', ':first-child'); + // console.log('Rect node:', node, 'bbox:', bbox, 'halfPadding:', halfPadding, 'node.padding:', node.padding); // const totalWidth = bbox.width + node.padding * 2; // const totalHeight = bbox.height + node.padding * 2; const totalWidth = node.positioned ? node.width : bbox.width + node.padding; @@ -1130,7 +1131,7 @@ const shapes = { let nodeElems = {}; -export const insertNode = async (elem, node, dir) => { +export const insertNode = async (elem, node, renderOptions) => { let newEl; let el; @@ -1143,9 +1144,9 @@ export const insertNode = async (elem, node, dir) => { target = node.linkTarget || '_blank'; } newEl = elem.insert('svg:a').attr('xlink:href', node.link).attr('target', target); - el = await shapes[node.shape](newEl, node, dir); + el = await shapes[node.shape](newEl, node, renderOptions); } else { - el = await shapes[node.shape](elem, node, dir); + el = await shapes[node.shape](elem, node, renderOptions); newEl = el; } if (node.tooltip) { @@ -1154,9 +1155,6 @@ export const insertNode = async (elem, node, dir) => { if (node.class) { el.attr('class', 'node default ' + node.class); } - // MC Special - newEl.attr('data-node', 'true'); - newEl.attr('data-id', node.id); nodeElems[node.id] = newEl; diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index 727842bba1..a3dab2ddbb 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -20,6 +20,11 @@ const config: RequiredDeep = { // Set, even though they're `undefined` so that `configKeys` finds these keys // TODO: Should we replace these with `null` so that they can go in the JSON Schema? deterministicIDSeed: undefined, + elk: { + // mergeEdges is needed here to be considered + mergeEdges: false, + nodePlacementStrategy: 'BRANDES_KOEPF', + }, themeCSS: undefined, // add non-JSON default config values @@ -48,6 +53,9 @@ const config: RequiredDeep = { }; }, }, + class: { + hideEmptyMembersBox: false, + }, gantt: { ...defaultConfigJson.gantt, tickInterval: undefined, @@ -244,19 +252,6 @@ const config: RequiredDeep = { ...defaultConfigJson.requirement, useWidth: undefined, }, - gitGraph: { - ...defaultConfigJson.gitGraph, - // TODO: This is a temporary override for `gitGraph`, since every other - // diagram does have `useMaxWidth`, but instead sets it to `true`. - // Should we set this to `true` instead? - useMaxWidth: false, - }, - sankey: { - ...defaultConfigJson.sankey, - // this is false, unlike every other diagram (other than gitGraph) - // TODO: can we make this default to `true` instead? - useMaxWidth: false, - }, packet: { ...defaultConfigJson.packet, }, diff --git a/packages/mermaid/src/diagram-api/diagram-orchestration.ts b/packages/mermaid/src/diagram-api/diagram-orchestration.ts index 55d05c9aaa..5b8cfc3fe9 100644 --- a/packages/mermaid/src/diagram-api/diagram-orchestration.ts +++ b/packages/mermaid/src/diagram-api/diagram-orchestration.ts @@ -19,9 +19,11 @@ import errorDiagram from '../diagrams/error/errorDiagram.js'; import flowchartElk from '../diagrams/flowchart/elk/detector.js'; import timeline from '../diagrams/timeline/detector.js'; import mindmap from '../diagrams/mindmap/detector.js'; +import kanban from '../diagrams/kanban/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'; @@ -69,6 +71,7 @@ export const addDiagrams = () => { // Ordering of detectors is important. The first one to return true will be used. registerLazyLoadedDiagrams( c4, + kanban, classDiagramV2, classDiagram, er, @@ -90,6 +93,7 @@ export const addDiagrams = () => { sankey, packet, xychart, - block + block, + architecture ); }; diff --git a/packages/mermaid/src/diagram-api/types.ts b/packages/mermaid/src/diagram-api/types.ts index 4556c1d6eb..fdb175e520 100644 --- a/packages/mermaid/src/diagram-api/types.ts +++ b/packages/mermaid/src/diagram-api/types.ts @@ -129,6 +129,6 @@ export type HTML = d3.Selection; -export type Group = d3.Selection; +export type SVGGroup = d3.Selection; export type DiagramStylesProvider = (options?: any) => string; diff --git a/packages/mermaid/src/diagrams/architecture/architectureDb.ts b/packages/mermaid/src/diagrams/architecture/architectureDb.ts new file mode 100644 index 0000000000..2174ebe199 --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureDb.ts @@ -0,0 +1,356 @@ +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 { + ArchitectureAlignment, + ArchitectureDB, + ArchitectureDirectionPair, + ArchitectureDirectionPairMap, + ArchitectureEdge, + ArchitectureGroup, + ArchitectureJunction, + ArchitectureNode, + ArchitectureService, + ArchitectureSpatialMap, + ArchitectureState, +} from './architectureTypes.js'; +import { + getArchitectureDirectionAlignment, + 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, & group alignments. + * If they have not been created, run the algorithms to generate them. + * @returns + */ +const getDataStructures = () => { + if (state.records.dataStructures === undefined) { + // Tracks how groups are aligned with one another. Generated while creating the adj list + const groupAlignments: Record< + string, + Record> + > = {}; + + // 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) => { + // track the direction groups connect to one another + const lhsGroupId = getNode(edge.lhsId)?.in; + const rhsGroupId = getNode(edge.rhsId)?.in; + if (lhsGroupId && rhsGroupId && lhsGroupId !== rhsGroupId) { + const alignment = getArchitectureDirectionAlignment(edge.lhsDir, edge.rhsDir); + if (alignment !== 'bend') { + groupAlignments[lhsGroupId] ??= {}; + groupAlignments[lhsGroupId][rhsGroupId] = alignment; + groupAlignments[rhsGroupId] ??= {}; + groupAlignments[rhsGroupId][lhsGroupId] = alignment; + } + } + + 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 }; + // If a key is present in this object, it has not been visited + 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, + groupAlignments, + }; + } + 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 0000000000..c15b474abc --- /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 0000000000..82dacd3e19 --- /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 0000000000..fac4a7b122 --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureIcons.ts @@ -0,0 +1,43 @@ +import { unknownIcon } from '../../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 0000000000..a7159d9076 --- /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 0000000000..768c174b0e --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -0,0 +1,534 @@ +import { registerIconPacks } from '../../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 { + ArchitectureAlignment, + ArchitectureDataStructures, + ArchitectureGroupAlignments, + 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( + db: ArchitectureDB, + spatialMaps: ArchitectureSpatialMap[], + groupAlignments: ArchitectureGroupAlignments +): fcose.FcoseAlignmentConstraint { + /** + * Flattens the alignment object so nodes in different groups will be in the same alignment array IFF their groups don't connect in a conflicting alignment + * + * i.e., two groups which connect horizontally should not have nodes with vertical alignments to one another + * + * See: #5952 + * + * @param alignmentObj - alignment object with the outer key being the row/col # and the inner key being the group name mapped to the nodes on that axis in the group + * @param alignmentDir - alignment direction + * @returns flattened alignment object with an arbitrary key mapping to nodes in the same row/col + */ + const flattenAlignments = ( + alignmentObj: Record>, + alignmentDir: ArchitectureAlignment + ): Record => { + return Object.entries(alignmentObj).reduce( + (prev, [dir, alignments]) => { + // prev is the mapping of x/y coordinate to an array of the nodes in that row/column + let cnt = 0; + const arr = Object.entries(alignments); // [group name, array of nodes within the group on axis dir] + if (arr.length === 1) { + // If only one group exists in the row/column, we don't need to do anything else + prev[dir] = arr[0][1]; + return prev; + } + for (let i = 0; i < arr.length - 1; i++) { + for (let j = i + 1; j < arr.length; j++) { + const [aGroupId, aNodeIds] = arr[i]; + const [bGroupId, bNodeIds] = arr[j]; + const alignment = groupAlignments[aGroupId]?.[bGroupId]; // Get how the two groups are intended to align (undefined if they aren't) + + if (alignment === alignmentDir) { + // If the intended alignment between the two groups is the same as the alignment we are parsing + prev[dir] ??= []; + prev[dir] = [...prev[dir], ...aNodeIds, ...bNodeIds]; // add the node ids of both groups to the axis array in prev + } else if (aGroupId === 'default' || bGroupId === 'default') { + // If either of the groups are in the default space (not in a group), use the same behavior as above + prev[dir] ??= []; + prev[dir] = [...prev[dir], ...aNodeIds, ...bNodeIds]; + } else { + // Otherwise, the nodes in the two groups are not intended to align + const keyA = `${dir}-${cnt++}`; + prev[keyA] = aNodeIds; + const keyB = `${dir}-${cnt++}`; + prev[keyB] = bNodeIds; + } + } + } + + return prev; + }, + {} as Record + ); + }; + + 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]]) => { + const nodeGroup = db.getNode(id)?.in ?? 'default'; + + horizontalAlignments[y] ??= {}; + horizontalAlignments[y][nodeGroup] ??= []; + horizontalAlignments[y][nodeGroup].push(id); + + verticalAlignments[x] ??= {}; + verticalAlignments[x][nodeGroup] ??= []; + verticalAlignments[x][nodeGroup].push(id); + }); + + // Merge the values of each object into a list if the inner list has at least 2 elements + return { + horiz: Object.values(flattenAlignments(horizontalAlignments, 'horizontal')).filter( + (arr) => arr.length > 1 + ), + vert: Object.values(flattenAlignments(verticalAlignments, 'vertical')).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[], + db: ArchitectureDB, + { spatialMaps, groupAlignments }: 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(db, spatialMaps, groupAlignments); + + // 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, db, 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 0000000000..7f494ecd13 --- /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 0000000000..cad2c5c369 --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts @@ -0,0 +1,383 @@ +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 ArchitectureAlignment = 'vertical' | 'horizontal' | 'bend'; + +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 const getArchitectureDirectionAlignment = function ( + a: ArchitectureDirection, + b: ArchitectureDirection +): ArchitectureAlignment { + if (isArchitectureDirectionXY(a, b)) { + return 'bend'; + } else if (isArchitectureDirectionX(a)) { + return 'horizontal'; + } + return 'vertical'; +}; + +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; + +/** + * Maps the direction that groups connect from. + * + * **Outer key**: ID of group A + * + * **Inner key**: ID of group B + * + * **Value**: 'vertical' or 'horizontal' + * + * Note: tmp[groupA][groupB] == tmp[groupB][groupA] + */ +export type ArchitectureGroupAlignments = Record< + string, + Record> +>; + +export interface ArchitectureDataStructures { + adjList: ArchitectureAdjacencyList; + spatialMaps: ArchitectureSpatialMap[]; + groupAlignments: ArchitectureGroupAlignments; +} + +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 0000000000..b10a451fea --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/svgDraw.ts @@ -0,0 +1,370 @@ +import { getIconSVG } from '../../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/block/blockRenderer.ts b/packages/mermaid/src/diagrams/block/blockRenderer.ts index 99b89ceeb2..509b5b7150 100644 --- a/packages/mermaid/src/diagrams/block/blockRenderer.ts +++ b/packages/mermaid/src/diagrams/block/blockRenderer.ts @@ -8,13 +8,6 @@ import type { BlockDB } from './blockDB.js'; import { layout } from './layout.js'; import { calculateBlockSizes, insertBlocks, insertEdges } from './renderHelpers.js'; -/** - * Returns the all the styles from classDef statements in the graph definition. - * - * @param text - The text with the classes - * @param diagObj - The diagram object - * @returns ClassDef - The styles - */ export const getClasses = function (text: any, diagObj: any) { return diagObj.db.getClasses(); }; @@ -45,8 +38,6 @@ export const draw = async function ( const markers = ['point', 'circle', 'cross']; // Add the marker definitions to the svg as marker tags - // insertMarkers(svg, markers, diagObj.type, diagObj.arrowMarkerAbsolute); - // insertMarkers(svg, markers, diagObj.type, true); insertMarkers(svg, markers, diagObj.type, id); const bl = db.getBlocks(); @@ -59,11 +50,7 @@ export const draw = async function ( await insertBlocks(nodes, bl, db); await insertEdges(nodes, edges, blArr, db, id); - // log.debug('Here', bl); - // Establish svg dimensions and get width and height - // - // const bounds2 = nodes.node().getBoundingClientRect(); // Why, oh why ???? if (bounds) { const bounds2 = bounds; diff --git a/packages/mermaid/src/diagrams/block/renderHelpers.ts b/packages/mermaid/src/diagrams/block/renderHelpers.ts index 97eca40748..8957b4d8fa 100644 --- a/packages/mermaid/src/diagrams/block/renderHelpers.ts +++ b/packages/mermaid/src/diagrams/block/renderHelpers.ts @@ -124,7 +124,8 @@ async function calculateBlockSize( } // Add the element to the DOM to size it - const nodeEl = await insertNode(elem, node); + const config = getConfig(); + const nodeEl = await insertNode(elem, node, { config }); const boundingBox = nodeEl.node().getBBox(); const obj = db.getBlock(node.id); obj.size = { width: boundingBox.width, height: boundingBox.height, x: 0, y: 0, node: nodeEl }; @@ -138,7 +139,8 @@ export async function insertBlockPositioned(elem: any, block: Block, db: any) { // Add the element to the DOM to size it const obj = db.getBlock(node.id); if (obj.type !== 'space') { - await insertNode(elem, node); + const config = getConfig(); + await insertNode(elem, node, { config }); block.intersect = node?.intersect; positionNode(node); } diff --git a/packages/mermaid/src/diagrams/class/classDb.ts b/packages/mermaid/src/diagrams/class/classDb.ts index a59c1eada1..5699437367 100644 --- a/packages/mermaid/src/diagrams/class/classDb.ts +++ b/packages/mermaid/src/diagrams/class/classDb.ts @@ -1,9 +1,8 @@ -import type { Selection } from 'd3'; -import { select } from 'd3'; +import { select, type Selection } from 'd3'; import { log } from '../../logger.js'; import { getConfig } from '../../diagram-api/diagramAPI.js'; import common from '../common/common.js'; -import utils from '../../utils.js'; +import utils, { getEdgeId } from '../../utils.js'; import { setAccTitle, getAccTitle, @@ -21,13 +20,18 @@ import type { ClassMap, NamespaceMap, NamespaceNode, + StyleClass, + Interface, } from './classTypes.js'; +import type { Node, Edge } from '../../rendering-util/types.js'; const MERMAID_DOM_ID_PREFIX = 'classId-'; let relations: ClassRelation[] = []; let classes = new Map(); +const styleClasses = new Map(); let notes: ClassNote[] = []; +let interfaces: Interface[] = []; let classCounter = 0; let namespaces = new Map(); let namespaceCounter = 0; @@ -58,6 +62,8 @@ export const setClassLabel = function (_id: string, label: string) { const { className } = splitClassNameAndType(id); classes.get(className)!.label = label; + classes.get(className)!.text = + `${label}${classes.get(className)!.type ? `<${classes.get(className)!.type}>` : ''}`; }; /** @@ -80,7 +86,9 @@ export const addClass = function (_id: string) { id: name, type: type, label: name, - cssClasses: [], + text: `${name}${type ? `<${type}>` : ''}`, + shape: 'classBox', + cssClasses: 'default', methods: [], members: [], annotations: [], @@ -91,6 +99,16 @@ export const addClass = function (_id: string) { classCounter++; }; +const addInterface = function (label: string, classId: string) { + const classInterface: Interface = { + id: `interface${interfaces.length}`, + label, + classId, + }; + + interfaces.push(classInterface); +}; + /** * Function to lookup domId from id in the graph definition. * @@ -109,6 +127,7 @@ export const clear = function () { relations = []; classes = new Map(); notes = []; + interfaces = []; functions = []; functions.push(setupToolTips); namespaces = new Map(); @@ -133,19 +152,50 @@ export const getNotes = function () { return notes; }; -export const addRelation = function (relation: ClassRelation) { - log.debug('Adding relation: ' + JSON.stringify(relation)); - addClass(relation.id1); - addClass(relation.id2); +export const addRelation = function (classRelation: ClassRelation) { + log.debug('Adding relation: ' + JSON.stringify(classRelation)); + // Due to relationType cannot just check if it is equal to 'none' or it complains, can fix this later + const invalidTypes = [ + relationType.LOLLIPOP, + relationType.AGGREGATION, + relationType.COMPOSITION, + relationType.DEPENDENCY, + relationType.EXTENSION, + ]; + + if ( + classRelation.relation.type1 === relationType.LOLLIPOP && + !invalidTypes.includes(classRelation.relation.type2) + ) { + addClass(classRelation.id2); + addInterface(classRelation.id1, classRelation.id2); + classRelation.id1 = `interface${interfaces.length - 1}`; + } else if ( + classRelation.relation.type2 === relationType.LOLLIPOP && + !invalidTypes.includes(classRelation.relation.type1) + ) { + addClass(classRelation.id1); + addInterface(classRelation.id2, classRelation.id1); + classRelation.id2 = `interface${interfaces.length - 1}`; + } else { + addClass(classRelation.id1); + addClass(classRelation.id2); + } - relation.id1 = splitClassNameAndType(relation.id1).className; - relation.id2 = splitClassNameAndType(relation.id2).className; + classRelation.id1 = splitClassNameAndType(classRelation.id1).className; + classRelation.id2 = splitClassNameAndType(classRelation.id2).className; - relation.relationTitle1 = common.sanitizeText(relation.relationTitle1.trim(), getConfig()); + classRelation.relationTitle1 = common.sanitizeText( + classRelation.relationTitle1.trim(), + getConfig() + ); - relation.relationTitle2 = common.sanitizeText(relation.relationTitle2.trim(), getConfig()); + classRelation.relationTitle2 = common.sanitizeText( + classRelation.relationTitle2.trim(), + getConfig() + ); - relations.push(relation); + relations.push(classRelation); }; /** @@ -229,11 +279,37 @@ export const setCssClass = function (ids: string, className: string) { } const classNode = classes.get(id); if (classNode) { - classNode.cssClasses.push(className); + classNode.cssClasses += ' ' + className; } }); }; +export const defineClass = function (ids: string[], style: string[]) { + for (const id of ids) { + let styleClass = styleClasses.get(id); + if (styleClass === undefined) { + styleClass = { id, styles: [], textStyles: [] }; + styleClasses.set(id, styleClass); + } + + if (style) { + style.forEach(function (s) { + if (/color/.exec(s)) { + const newStyle = s.replace('fill', 'bgFill'); // .replace('color', 'fill'); + styleClass.textStyles.push(newStyle); + } + styleClass.styles.push(s); + }); + } + + classes.forEach((value) => { + if (value.cssClasses.includes(id)) { + value.styles.push(...style.flatMap((s) => s.split(','))); + } + }); + } +}; + /** * Called by parser when a tooltip is found, e.g. a clickable element. * @@ -389,7 +465,6 @@ const setupToolTips = function (element: Element) { // @ts-ignore - getBoundingClientRect is not part of the d3 type definition const rect = this.getBoundingClientRect(); - // @ts-expect-error - Incorrect types tooltipElem.transition().duration(200).style('opacity', '.9'); tooltipElem .text(el.attr('title')) @@ -399,7 +474,6 @@ const setupToolTips = function (element: Element) { el.classed('hover', true); }) .on('mouseout', function () { - // @ts-expect-error - Incorrect types tooltipElem.transition().duration(500).style('opacity', 0); const el = select(this); el.classed('hover', false); @@ -474,6 +548,152 @@ export const setCssStyle = function (id: string, styles: string[]) { } }; +/** + * Gets the arrow marker for a type index + * + * @param type - The type to look for + * @returns The arrow marker + */ +function getArrowMarker(type: number) { + let marker; + switch (type) { + case 0: + marker = 'aggregation'; + break; + case 1: + marker = 'extension'; + break; + case 2: + marker = 'composition'; + break; + case 3: + marker = 'dependency'; + break; + case 4: + marker = 'lollipop'; + break; + default: + marker = 'none'; + } + return marker; +} + +export const getData = () => { + const nodes: Node[] = []; + const edges: Edge[] = []; + const config = getConfig(); + + for (const namespaceKey of namespaces.keys()) { + const namespace = namespaces.get(namespaceKey); + if (namespace) { + const node: Node = { + id: namespace.id, + label: namespace.id, + isGroup: true, + padding: config.class!.padding ?? 16, + // parent node must be one of [rect, roundedWithTitle, noteGroup, divider] + shape: 'rect', + cssStyles: ['fill: none', 'stroke: black'], + look: config.look, + }; + nodes.push(node); + } + } + + for (const classKey of classes.keys()) { + const classNode = classes.get(classKey); + if (classNode) { + const node = classNode as unknown as Node; + node.parentId = classNode.parent; + node.look = config.look; + nodes.push(node); + } + } + + let cnt = 0; + for (const note of notes) { + cnt++; + const noteNode: Node = { + id: note.id, + label: note.text, + isGroup: false, + shape: 'note', + padding: config.class!.padding ?? 6, + cssStyles: [ + 'text-align: left', + 'white-space: nowrap', + `fill: ${config.themeVariables.noteBkgColor}`, + `stroke: ${config.themeVariables.noteBorderColor}`, + ], + look: config.look, + }; + nodes.push(noteNode); + + const noteClassId = classes.get(note.class)?.id ?? ''; + + if (noteClassId) { + const edge: Edge = { + id: `edgeNote${cnt}`, + start: note.id, + end: noteClassId, + type: 'normal', + thickness: 'normal', + classes: 'relation', + arrowTypeStart: 'none', + arrowTypeEnd: 'none', + arrowheadStyle: '', + labelStyle: [''], + style: ['fill: none'], + pattern: 'dotted', + look: config.look, + }; + edges.push(edge); + } + } + + for (const _interface of interfaces) { + const interfaceNode: Node = { + id: _interface.id, + label: _interface.label, + isGroup: false, + shape: 'rect', + cssStyles: ['opacity: 0;'], + look: config.look, + }; + nodes.push(interfaceNode); + } + + cnt = 0; + for (const classRelation of relations) { + cnt++; + const edge: Edge = { + id: getEdgeId(classRelation.id1, classRelation.id2, { + prefix: 'id', + counter: cnt, + }), + start: classRelation.id1, + end: classRelation.id2, + type: 'normal', + label: classRelation.title, + labelpos: 'c', + thickness: 'normal', + classes: 'relation', + arrowTypeStart: getArrowMarker(classRelation.relation.type1), + arrowTypeEnd: getArrowMarker(classRelation.relation.type2), + startLabelRight: classRelation.relationTitle1 === 'none' ? '' : classRelation.relationTitle1, + endLabelLeft: classRelation.relationTitle2 === 'none' ? '' : classRelation.relationTitle2, + arrowheadStyle: '', + labelStyle: ['display: inline-block'], + style: classRelation.style || '', + pattern: classRelation.relation.lineType == 1 ? 'dashed' : 'solid', + look: config.look, + }; + edges.push(edge); + } + + return { nodes, edges, other: {}, config, direction: getDirection() }; +}; + export default { setAccTitle, getAccTitle, @@ -499,6 +719,7 @@ export default { relationType, setClickEvent, setCssClass, + defineClass, setLink, getTooltip, setTooltip, @@ -511,4 +732,5 @@ export default { getNamespace, getNamespaces, setCssStyle, + getData, }; diff --git a/packages/mermaid/src/diagrams/class/classDiagram-styles.spec.js b/packages/mermaid/src/diagrams/class/classDiagram-styles.spec.js index 9571884014..18bdaade59 100644 --- a/packages/mermaid/src/diagrams/class/classDiagram-styles.spec.js +++ b/packages/mermaid/src/diagrams/class/classDiagram-styles.spec.js @@ -13,7 +13,7 @@ describe('class diagram, ', function () { parser.parse(str); - expect(parser.yy.getClass('Class01').cssClasses[0]).toBe('exClass'); + expect(parser.yy.getClass('Class01').cssClasses).toBe('default exClass'); }); it('should be possible to apply a css class to a class directly with struct', function () { @@ -28,7 +28,7 @@ describe('class diagram, ', function () { parser.parse(str); const testClass = parser.yy.getClass('Class1'); - expect(testClass.cssClasses[0]).toBe('exClass'); + expect(testClass.cssClasses).toBe('default exClass'); }); it('should be possible to apply a css class to a class with relations', function () { @@ -36,7 +36,7 @@ describe('class diagram, ', function () { parser.parse(str); - expect(parser.yy.getClass('Class01').cssClasses[0]).toBe('exClass'); + expect(parser.yy.getClass('Class01').cssClasses).toBe('default exClass'); }); it('should be possible to apply a cssClass to a class', function () { @@ -44,7 +44,7 @@ describe('class diagram, ', function () { parser.parse(str); - expect(parser.yy.getClass('Class01').cssClasses[0]).toBe('exClass'); + expect(parser.yy.getClass('Class01').cssClasses).toBe('default exClass'); }); it('should be possible to apply a cssClass to a comma separated list of classes', function () { @@ -53,8 +53,8 @@ describe('class diagram, ', function () { parser.parse(str); - expect(parser.yy.getClass('Class01').cssClasses[0]).toBe('exClass'); - expect(parser.yy.getClass('Class02').cssClasses[0]).toBe('exClass'); + expect(parser.yy.getClass('Class01').cssClasses).toBe('default exClass'); + expect(parser.yy.getClass('Class02').cssClasses).toBe('default exClass'); }); it('should be possible to apply a style to an individual node', function () { const str = @@ -69,5 +69,47 @@ describe('class diagram, ', function () { expect(styleElements[1]).toBe('stroke:#333'); expect(styleElements[2]).toBe('stroke-width:4px'); }); + it('should be possible to define and assign a class inside the diagram', function () { + const str = + 'classDiagram\n' + 'class Class01\n cssClass "Class01" pink\n classDef pink fill:#f9f'; + + parser.parse(str); + + expect(parser.yy.getClass('Class01').cssClasses).toBe('default pink'); + }); + it('should be possible to define and assign a class using shorthand inside the diagram', function () { + const str = 'classDiagram\n' + 'class Class01:::pink\n classDef pink fill:#f9f'; + + parser.parse(str); + + expect(parser.yy.getClass('Class01').cssClasses).toBe('default pink'); + }); + it('should properly assign styles from a class defined inside the diagram', function () { + const str = + 'classDiagram\n' + + 'class Class01:::pink\n classDef pink fill:#f9f,stroke:#333,stroke-width:6px'; + + parser.parse(str); + + expect(parser.yy.getClass('Class01').styles).toStrictEqual([ + 'fill:#f9f', + 'stroke:#333', + 'stroke-width:6px', + ]); + }); + it('should properly assign multiple classes and styles from classes defined inside the diagram', function () { + const str = + 'classDiagram\n' + + 'class Class01:::pink\n cssClass "Class01" bold\n classDef pink fill:#f9f\n classDef bold stroke:#333,stroke-width:6px'; + + parser.parse(str); + + expect(parser.yy.getClass('Class01').styles).toStrictEqual([ + 'fill:#f9f', + 'stroke:#333', + 'stroke-width:6px', + ]); + expect(parser.yy.getClass('Class01').cssClasses).toBe('default pink bold'); + }); }); }); diff --git a/packages/mermaid/src/diagrams/class/classDiagram-v2.ts b/packages/mermaid/src/diagrams/class/classDiagram-v2.ts index ec5398d29d..6a3747e418 100644 --- a/packages/mermaid/src/diagrams/class/classDiagram-v2.ts +++ b/packages/mermaid/src/diagrams/class/classDiagram-v2.ts @@ -3,7 +3,7 @@ import type { DiagramDefinition } from '../../diagram-api/types.js'; import parser from './parser/classDiagram.jison'; import db from './classDb.js'; import styles from './styles.js'; -import renderer from './classRenderer-v2.js'; +import renderer from './classRenderer-v3-unified.js'; export const diagram: DiagramDefinition = { parser, diff --git a/packages/mermaid/src/diagrams/class/classDiagram.spec.ts b/packages/mermaid/src/diagrams/class/classDiagram.spec.ts index 9804b325ef..40027f27ec 100644 --- a/packages/mermaid/src/diagrams/class/classDiagram.spec.ts +++ b/packages/mermaid/src/diagrams/class/classDiagram.spec.ts @@ -14,6 +14,78 @@ describe('given a basic class diagram, ', function () { classDb.clear(); parser.yy = classDb; }); + it('should handle classes within namespaces', () => { + const str = `classDiagram + namespace Company.Project { + class User { + +login(username: String, password: String) + +logout() + } + } + namespace Company.Project.Module { + class Admin { + +addUser(user: User) + +removeUser(user: User) + } + }`; + + parser.parse(str); + + const user = classDb.getClass('User'); + const admin = classDb.getClass('Admin'); + + // Check if the classes are correctly registered under their respective namespaces + expect(user.parent).toBe('Company.Project'); + expect(admin.parent).toBe('Company.Project.Module'); + expect(user.methods.length).toBe(2); + expect(admin.methods.length).toBe(2); + }); + + it('should handle generic classes within namespaces', () => { + const str = `classDiagram + namespace Company.Project.Module { + class GenericClass~T~ { + +addItem(item: T) + +getItem() T + } + }`; + + parser.parse(str); + + const genericClass = classDb.getClass('GenericClass'); + expect(genericClass.type).toBe('T'); + expect(genericClass.methods[0].getDisplayDetails().displayText).toBe('+addItem(item: T)'); + expect(genericClass.methods[1].getDisplayDetails().displayText).toBe('+getItem() : T'); + }); + + it('should handle nested namespaces and relationships', () => { + const str = ` classDiagram + namespace Company.Project.Module.SubModule { + class Report { + +generatePDF(data: List) + +generateCSV(data: List) + } + } + namespace Company.Project.Module { + class Admin { + +generateReport() + } + } + Admin --> Report : generates`; + + parser.parse(str); + + const report = classDb.getClass('Report'); + const admin = classDb.getClass('Admin'); + const relations = classDb.getRelations(); + + expect(report.parent).toBe('Company.Project.Module.SubModule'); + expect(admin.parent).toBe('Company.Project.Module'); + expect(relations[0].id1).toBe('Admin'); + expect(relations[0].id2).toBe('Report'); + expect(relations[0].title).toBe('generates'); + }); + it('should handle accTitle and accDescr', function () { const str = `classDiagram accTitle: My Title @@ -48,6 +120,22 @@ describe('given a basic class diagram, ', function () { } }); + it('should handle fully qualified class names in relationships', () => { + const str = `classDiagram + namespace Company.Project.Module { + class User + class Admin + } + Admin --> User : manages`; + + parser.parse(str); + + const relations = classDb.getRelations(); + expect(relations[0].id1).toBe('Admin'); + expect(relations[0].id2).toBe('User'); + expect(relations[0].title).toBe('manages'); + }); + it('should handle backquoted class names', function () { const str = 'classDiagram\n' + 'class `Car`'; @@ -158,7 +246,7 @@ describe('given a basic class diagram, ', function () { const c1 = classDb.getClass('C1'); expect(c1.label).toBe('Class 1 with text label'); - expect(c1.cssClasses[0]).toBe('styleClass'); + expect(c1.cssClasses).toBe('default styleClass'); }); it('should parse a class with text label and css class', () => { @@ -173,7 +261,7 @@ describe('given a basic class diagram, ', function () { const c1 = classDb.getClass('C1'); expect(c1.label).toBe('Class 1 with text label'); expect(c1.members[0].getDisplayDetails().displayText).toBe('int member1'); - expect(c1.cssClasses[0]).toBe('styleClass'); + expect(c1.cssClasses).toBe('default styleClass'); }); it('should parse two classes with text labels and css classes', () => { @@ -188,11 +276,11 @@ describe('given a basic class diagram, ', function () { const c1 = classDb.getClass('C1'); expect(c1.label).toBe('Class 1 with text label'); - expect(c1.cssClasses[0]).toBe('styleClass'); + expect(c1.cssClasses).toBe('default styleClass'); const c2 = classDb.getClass('C2'); expect(c2.label).toBe('Long long long long long long long long long long label'); - expect(c2.cssClasses[0]).toBe('styleClass'); + expect(c2.cssClasses).toBe('default styleClass'); }); it('should parse two classes with text labels and css class shorthands', () => { @@ -205,11 +293,11 @@ describe('given a basic class diagram, ', function () { const c1 = classDb.getClass('C1'); expect(c1.label).toBe('Class 1 with text label'); - expect(c1.cssClasses[0]).toBe('styleClass1'); + expect(c1.cssClasses).toBe('default styleClass1'); const c2 = classDb.getClass('C2'); expect(c2.label).toBe('Class 2 !@#$%^&*() label'); - expect(c2.cssClasses[0]).toBe('styleClass2'); + expect(c2.cssClasses).toBe('default styleClass2'); }); it('should parse multiple classes with same text labels', () => { @@ -393,12 +481,27 @@ class C13["With Città foreign language"] Student "1" --o "1" IdCard : carries Student "1" --o "1" Bike : rides`); - expect(classDb.getClasses().size).toBe(3); + const studentClass = classDb.getClasses().get('Student'); + // Check that the important properties match, but ignore the domId + expect(studentClass).toMatchObject({ + id: 'Student', + label: 'Student', + members: [ + expect.objectContaining({ + id: 'idCard : IdCard', + visibility: '-', + }), + ], + methods: [], + annotations: [], + cssClasses: 'default', + }); + expect(classDb.getClasses().get('Student')).toMatchInlineSnapshot(` { "annotations": [], - "cssClasses": [], - "domId": "classId-Student-134", + "cssClasses": "default", + "domId": "classId-Student-141", "id": "Student", "label": "Student", "members": [ @@ -406,11 +509,14 @@ class C13["With Città foreign language"] "classifier": "", "id": "idCard : IdCard", "memberType": "attribute", + "text": "\\-idCard : IdCard", "visibility": "-", }, ], "methods": [], + "shape": "classBox", "styles": [], + "text": "Student", "type": "", } `); @@ -654,7 +760,7 @@ foo() const actual = parser.yy.getClass('Class1'); expect(actual.link).toBe('google.com'); - expect(actual.cssClasses[0]).toBe('clickable'); + expect(actual.cssClasses).toBe('default clickable'); }); it('should handle href link with tooltip', function () { @@ -670,7 +776,7 @@ foo() const actual = parser.yy.getClass('Class1'); expect(actual.link).toBe('google.com'); expect(actual.tooltip).toBe('A Tooltip'); - expect(actual.cssClasses[0]).toBe('clickable'); + expect(actual.cssClasses).toBe('default clickable'); }); it('should handle href link with tooltip and target', function () { @@ -689,7 +795,7 @@ foo() const actual = parser.yy.getClass('Class1'); expect(actual.link).toBe('google.com'); expect(actual.tooltip).toBe('A tooltip'); - expect(actual.cssClasses[0]).toBe('clickable'); + expect(actual.cssClasses).toBe('default clickable'); }); it('should handle function call', function () { @@ -1384,8 +1490,7 @@ describe('given a class diagram with relationships, ', function () { const testClass = parser.yy.getClass('Class1'); expect(testClass.link).toBe('google.com'); - expect(testClass.cssClasses.length).toBe(1); - expect(testClass.cssClasses[0]).toBe('clickable'); + expect(testClass.cssClasses).toBe('default clickable'); }); it('should associate click and href link and css appropriately', function () { @@ -1398,8 +1503,7 @@ describe('given a class diagram with relationships, ', function () { const testClass = parser.yy.getClass('Class1'); expect(testClass.link).toBe('google.com'); - expect(testClass.cssClasses.length).toBe(1); - expect(testClass.cssClasses[0]).toBe('clickable'); + expect(testClass.cssClasses).toBe('default clickable'); }); it('should associate link with tooltip', function () { @@ -1413,8 +1517,7 @@ describe('given a class diagram with relationships, ', function () { const testClass = parser.yy.getClass('Class1'); expect(testClass.link).toBe('google.com'); expect(testClass.tooltip).toBe('A tooltip'); - expect(testClass.cssClasses.length).toBe(1); - expect(testClass.cssClasses[0]).toBe('clickable'); + expect(testClass.cssClasses).toBe('default clickable'); }); it('should associate click and href link with tooltip', function () { @@ -1428,8 +1531,7 @@ describe('given a class diagram with relationships, ', function () { const testClass = parser.yy.getClass('Class1'); expect(testClass.link).toBe('google.com'); expect(testClass.tooltip).toBe('A tooltip'); - expect(testClass.cssClasses.length).toBe(1); - expect(testClass.cssClasses[0]).toBe('clickable'); + expect(testClass.cssClasses).toBe('default clickable'); }); it('should associate click and href link with tooltip and target appropriately', function () { @@ -1686,8 +1788,7 @@ C1 --> C2 const c1 = classDb.getClass('C1'); expect(c1.label).toBe('Class 1 with text label'); - expect(c1.cssClasses.length).toBe(1); - expect(c1.cssClasses[0]).toBe('styleClass'); + expect(c1.cssClasses).toBe('default styleClass'); const member = c1.members[0]; expect(member.getDisplayDetails().displayText).toBe('+member1'); }); @@ -1703,8 +1804,7 @@ cssClass "C1" styleClass const c1 = classDb.getClass('C1'); expect(c1.label).toBe('Class 1 with text label'); - expect(c1.cssClasses.length).toBe(1); - expect(c1.cssClasses[0]).toBe('styleClass'); + expect(c1.cssClasses).toBe('default styleClass'); const member = c1.members[0]; expect(member.getDisplayDetails().displayText).toBe('+member1'); }); @@ -1721,13 +1821,11 @@ cssClass "C1,C2" styleClass const c1 = classDb.getClass('C1'); expect(c1.label).toBe('Class 1 with text label'); - expect(c1.cssClasses.length).toBe(1); - expect(c1.cssClasses[0]).toBe('styleClass'); + expect(c1.cssClasses).toBe('default styleClass'); const c2 = classDb.getClass('C2'); expect(c2.label).toBe('Long long long long long long long long long long label'); - expect(c2.cssClasses.length).toBe(1); - expect(c2.cssClasses[0]).toBe('styleClass'); + expect(c2.cssClasses).toBe('default styleClass'); }); it('should parse two classes with text labels and css class shorthands', () => { @@ -1741,13 +1839,11 @@ C1 --> C2 const c1 = classDb.getClass('C1'); expect(c1.label).toBe('Class 1 with text label'); - expect(c1.cssClasses.length).toBe(1); - expect(c1.cssClasses[0]).toBe('styleClass1'); + expect(c1.cssClasses).toBe('default styleClass1'); const c2 = classDb.getClass('C2'); expect(c2.label).toBe('Class 2 !@#$%^&*() label'); - expect(c2.cssClasses.length).toBe(1); - expect(c2.cssClasses[0]).toBe('styleClass2'); + expect(c2.cssClasses).toBe('default styleClass2'); }); it('should parse multiple classes with same text labels', () => { diff --git a/packages/mermaid/src/diagrams/class/classDiagram.ts b/packages/mermaid/src/diagrams/class/classDiagram.ts index 7f027c186e..6a3747e418 100644 --- a/packages/mermaid/src/diagrams/class/classDiagram.ts +++ b/packages/mermaid/src/diagrams/class/classDiagram.ts @@ -3,7 +3,7 @@ import type { DiagramDefinition } from '../../diagram-api/types.js'; import parser from './parser/classDiagram.jison'; import db from './classDb.js'; import styles from './styles.js'; -import renderer from './classRenderer.js'; +import renderer from './classRenderer-v3-unified.js'; export const diagram: DiagramDefinition = { parser, diff --git a/packages/mermaid/src/diagrams/class/classRenderer-v3-unified.ts b/packages/mermaid/src/diagrams/class/classRenderer-v3-unified.ts new file mode 100644 index 0000000000..670f93f165 --- /dev/null +++ b/packages/mermaid/src/diagrams/class/classRenderer-v3-unified.ts @@ -0,0 +1,79 @@ +import { getConfig } from '../../diagram-api/diagramAPI.js'; +import type { DiagramStyleClassDef } from '../../diagram-api/types.js'; +import { log } from '../../logger.js'; +import { getDiagramElement } from '../../rendering-util/insertElementsForSize.js'; +import { getRegisteredLayoutAlgorithm, render } from '../../rendering-util/render.js'; +import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js'; +import type { LayoutData } from '../../rendering-util/types.js'; +import utils from '../../utils.js'; + +/** + * Get the direction from the statement items. + * Look through all of the documents (docs) in the parsedItems + * Because is a _document_ direction, the default direction is not necessarily the same as the overall default _diagram_ direction. + * @param parsedItem - the parsed statement item to look through + * @param defaultDir - the direction to use if none is found + * @returns The direction to use + */ +export const getDir = (parsedItem: any, defaultDir = 'TB') => { + if (!parsedItem.doc) { + return defaultDir; + } + + let dir = defaultDir; + + for (const parsedItemDoc of parsedItem.doc) { + if (parsedItemDoc.stmt === 'dir') { + dir = parsedItemDoc.value; + } + } + + return dir; +}; + +export const getClasses = function ( + text: string, + diagramObj: any +): Map { + return diagramObj.db.getClasses(); +}; + +export const draw = async function (text: string, id: string, _version: string, diag: any) { + log.info('REF0:'); + log.info('Drawing class diagram (v3)', id); + const { securityLevel, state: conf, layout } = getConfig(); + // Extracting the data from the parsed structure into a more usable form + // Not related to the refactoring, but this is the first step in the rendering process + // diag.db.extract(diag.db.getRootDocV2()); + + // The getData method provided in all supported diagrams is used to extract the data from the parsed structure + // into the Layout data format + const data4Layout = diag.db.getData() as LayoutData; + + // Create the root SVG - the element is the div containing the SVG element + const svg = getDiagramElement(id, securityLevel); + + data4Layout.type = diag.type; + data4Layout.layoutAlgorithm = getRegisteredLayoutAlgorithm(layout); + + data4Layout.nodeSpacing = conf?.nodeSpacing || 50; + data4Layout.rankSpacing = conf?.rankSpacing || 50; + data4Layout.markers = ['aggregation', 'extension', 'composition', 'dependency', 'lollipop']; + data4Layout.diagramId = id; + await render(data4Layout, svg); + const padding = 8; + utils.insertTitle( + svg, + 'classDiagramTitleText', + conf?.titleTopMargin ?? 25, + diag.db.getDiagramTitle() + ); + + setupViewPortForSVG(svg, padding, 'classDiagram', conf?.useMaxWidth ?? true); +}; + +export default { + getClasses, + draw, + getDir, +}; diff --git a/packages/mermaid/src/diagrams/class/classTypes.ts b/packages/mermaid/src/diagrams/class/classTypes.ts index f1955a2246..9d0d47569c 100644 --- a/packages/mermaid/src/diagrams/class/classTypes.ts +++ b/packages/mermaid/src/diagrams/class/classTypes.ts @@ -5,7 +5,9 @@ export interface ClassNode { id: string; type: string; label: string; - cssClasses: string[]; + shape: string; + text: string; + cssClasses: string; methods: ClassMember[]; members: ClassMember[]; annotations: string[]; @@ -16,6 +18,7 @@ export interface ClassNode { linkTarget?: string; haveCallback?: boolean; tooltip?: string; + look?: string; } export type Visibility = '#' | '+' | '~' | '-' | ''; @@ -30,6 +33,7 @@ export class ClassMember { cssStyle!: string; memberType!: 'method' | 'attribute'; visibility!: Visibility; + text: string; /** * denote if static or to determine which css class to apply to the node * @defaultValue '' @@ -50,6 +54,7 @@ export class ClassMember { this.memberType = memberType; this.visibility = ''; this.classifier = ''; + this.text = ''; const sanitizedInput = sanitizeText(input, getConfig()); this.parseMember(sanitizedInput); } @@ -85,7 +90,7 @@ export class ClassMember { this.visibility = detectedVisibility as Visibility; } - this.id = match[2].trim(); + this.id = match[2]; this.parameters = match[3] ? match[3].trim() : ''; potentialClassifier = match[4] ? match[4].trim() : ''; this.returnType = match[5] ? match[5].trim() : ''; @@ -118,6 +123,14 @@ export class ClassMember { } this.classifier = potentialClassifier; + // Preserve one space only + this.id = this.id.startsWith(' ') ? ' ' + this.id.trim() : this.id.trim(); + + const combinedText = `${this.visibility ? '\\' + this.visibility : ''}${parseGenericTypes(this.id)}${this.memberType === 'method' ? `(${parseGenericTypes(this.parameters)})${this.returnType ? ' : ' + parseGenericTypes(this.returnType) : ''}` : ''}`; + this.text = combinedText.replaceAll('<', '<').replaceAll('>', '>'); + if (this.text.startsWith('\\<')) { + this.text = this.text.replace('\\<', '~'); + } } parseClassifier() { @@ -154,6 +167,12 @@ export interface ClassRelation { }; } +export interface Interface { + id: string; + label: string; + classId: string; +} + export interface NamespaceNode { id: string; domId: string; @@ -161,5 +180,11 @@ export interface NamespaceNode { children: NamespaceMap; } +export interface StyleClass { + id: string; + styles: string[]; + textStyles: string[]; +} + export type ClassMap = Map; export type NamespaceMap = Map; diff --git a/packages/mermaid/src/diagrams/class/parser/classDiagram.jison b/packages/mermaid/src/diagrams/class/parser/classDiagram.jison index ed4e72027f..83d9bd48ed 100644 --- a/packages/mermaid/src/diagrams/class/parser/classDiagram.jison +++ b/packages/mermaid/src/diagrams/class/parser/classDiagram.jison @@ -61,6 +61,7 @@ Function arguments are optional: 'call ()' simply executes 'callb [^"]* return "STR"; <*>["] this.begin("string"); "style" return 'STYLE'; +"classDef" return 'CLASSDEF'; "namespace" { this.begin('namespace'); return 'NAMESPACE'; } \s*(\r?\n)+ { this.popState(); return 'NEWLINE'; } @@ -241,11 +242,13 @@ classLabel namespaceName : alphaNumToken { $$=$1; } + | alphaNumToken DOT namespaceName { $$=$1+'.'+$3; } | alphaNumToken namespaceName { $$=$1+$2; } ; className : alphaNumToken { $$=$1; } + | alphaNumToken DOT className { $$=$1+'.'+$3; } | classLiteralName { $$=$1; } | alphaNumToken className { $$=$1+$2; } | alphaNumToken GENERICTYPE { $$=$1+'~'+$2+'~'; } @@ -263,6 +266,7 @@ statement | styleStatement | cssClassStatement | noteStatement + | classDefStatement | direction | acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); } | acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); } @@ -270,12 +274,12 @@ statement ; namespaceStatement - : namespaceIdentifier STRUCT_START classStatements STRUCT_STOP {yy.addClassesToNamespace($1, $3);} - | namespaceIdentifier STRUCT_START NEWLINE classStatements STRUCT_STOP {yy.addClassesToNamespace($1, $4);} + : namespaceIdentifier STRUCT_START classStatements STRUCT_STOP { yy.addClassesToNamespace($1, $3); } + | namespaceIdentifier STRUCT_START NEWLINE classStatements STRUCT_STOP { yy.addClassesToNamespace($1, $4); } ; namespaceIdentifier - : NAMESPACE namespaceName {$$=$2; yy.addNamespace($2);} + : NAMESPACE namespaceName { $$=$2; yy.addNamespace($2); } ; classStatements @@ -324,6 +328,15 @@ noteStatement | NOTE noteText { yy.addNote($2); } ; +classDefStatement + : CLASSDEF classList stylesOpt {$$ = $CLASSDEF;yy.defineClass($classList,$stylesOpt);} + ; + +classList + : ALPHA { $$ = [$ALPHA]; } + | classList COMMA ALPHA = { $$ = $classList.concat([$ALPHA]); } + ; + direction : direction_tb { yy.setDirection('TB');} diff --git a/packages/mermaid/src/diagrams/class/shapeUtil.ts b/packages/mermaid/src/diagrams/class/shapeUtil.ts new file mode 100644 index 0000000000..94c8f817ae --- /dev/null +++ b/packages/mermaid/src/diagrams/class/shapeUtil.ts @@ -0,0 +1,223 @@ +import { select } from 'd3'; +import { getConfig } from '../../config.js'; +import { getNodeClasses } from '../../rendering-util/rendering-elements/shapes/util.js'; +import { calculateTextWidth, decodeEntities } from '../../utils.js'; +import type { ClassMember, ClassNode } from './classTypes.js'; +import { sanitizeText } from '../../diagram-api/diagramAPI.js'; +import { createText } from '../../rendering-util/createText.js'; +import { evaluate, hasKatex } from '../common/common.js'; +import type { Node } from '../../rendering-util/types.js'; +import type { MermaidConfig } from '../../config.type.js'; +import type { D3Selection } from '../../types.js'; + +// Creates the shapeSvg and inserts text +export async function textHelper( + parent: D3Selection, + node: any, + config: MermaidConfig, + useHtmlLabels: boolean, + GAP = config.class!.padding ?? 12 +) { + const TEXT_PADDING = !useHtmlLabels ? 3 : 0; + const shapeSvg = parent + // @ts-ignore: Ignore error for using .insert on SVGAElement + .insert('g') + .attr('class', getNodeClasses(node)) + .attr('id', node.domId || node.id); + + let annotationGroup = null; + let labelGroup = null; + let membersGroup = null; + let methodsGroup = null; + + let annotationGroupHeight = 0; + let labelGroupHeight = 0; + let membersGroupHeight = 0; + + annotationGroup = shapeSvg.insert('g').attr('class', 'annotation-group text'); + if (node.annotations.length > 0) { + const annotation = node.annotations[0]; + await addText(annotationGroup, { text: `ÂĢ${annotation}Âģ` } as unknown as ClassMember, 0); + + const annotationGroupBBox = annotationGroup.node()!.getBBox(); + annotationGroupHeight = annotationGroupBBox.height; + } + + labelGroup = shapeSvg.insert('g').attr('class', 'label-group text'); + await addText(labelGroup, node, 0, ['font-weight: bolder']); + const labelGroupBBox = labelGroup.node()!.getBBox(); + labelGroupHeight = labelGroupBBox.height; + + membersGroup = shapeSvg.insert('g').attr('class', 'members-group text'); + let yOffset = 0; + for (const member of node.members) { + const height = await addText(membersGroup, member, yOffset, [member.parseClassifier()]); + yOffset += height + TEXT_PADDING; + } + membersGroupHeight = membersGroup.node()!.getBBox().height; + if (membersGroupHeight <= 0) { + membersGroupHeight = GAP / 2; + } + + methodsGroup = shapeSvg.insert('g').attr('class', 'methods-group text'); + let methodsYOffset = 0; + for (const method of node.methods) { + const height = await addText(methodsGroup, method, methodsYOffset, [method.parseClassifier()]); + methodsYOffset += height + TEXT_PADDING; + } + + let bbox = shapeSvg.node()!.getBBox(); + + // Center annotation + if (annotationGroup !== null) { + const annotationGroupBBox = annotationGroup.node()!.getBBox(); + annotationGroup.attr('transform', `translate(${-annotationGroupBBox.width / 2})`); + } + + // Adjust label + labelGroup.attr('transform', `translate(${-labelGroupBBox.width / 2}, ${annotationGroupHeight})`); + + bbox = shapeSvg.node()!.getBBox(); + + membersGroup.attr( + 'transform', + `translate(${0}, ${annotationGroupHeight + labelGroupHeight + GAP * 2})` + ); + bbox = shapeSvg.node()!.getBBox(); + methodsGroup.attr( + 'transform', + `translate(${0}, ${annotationGroupHeight + labelGroupHeight + (membersGroupHeight ? membersGroupHeight + GAP * 4 : GAP * 2)})` + ); + + bbox = shapeSvg.node()!.getBBox(); + + return { shapeSvg, bbox }; +} + +// Modified version of labelHelper() to help create and place text for classes +async function addText( + parentGroup: D3Selection, + node: Node | ClassNode | ClassMember, + yOffset: number, + styles: string[] = [] +) { + const textEl = parentGroup.insert('g').attr('class', 'label').attr('style', styles.join('; ')); + const config = getConfig(); + let useHtmlLabels = + 'useHtmlLabels' in node ? node.useHtmlLabels : (evaluate(config.htmlLabels) ?? true); + + let textContent = ''; + // Support regular node type (.label) and classNodes (.text) + if ('text' in node) { + textContent = node.text; + } else { + textContent = node.label!; + } + + // createText() will cause unwanted behavior because of classDiagram syntax so workarounds are needed + + if (!useHtmlLabels && textContent.startsWith('\\')) { + textContent = textContent.substring(1); + } + + if (hasKatex(textContent)) { + useHtmlLabels = true; + } + + const text = await createText( + textEl, + sanitizeText(decodeEntities(textContent)), + { + width: calculateTextWidth(textContent, config) + 50, // Add room for error when splitting text into multiple lines + classes: 'markdown-node-label', + useHtmlLabels, + }, + config + ); + let bbox; + let numberOfLines = 1; + + if (!useHtmlLabels) { + // Undo font-weight normal + if (styles.includes('font-weight: bolder')) { + select(text).selectAll('tspan').attr('font-weight', ''); + } + + numberOfLines = text.children.length; + + const textChild = text.children[0]; + if (text.textContent === '' || text.textContent.includes('>')) { + textChild.textContent = + textContent[0] + + textContent.substring(1).replaceAll('>', '>').replaceAll('<', '<').trim(); + + // Text was improperly removed due to spaces (preserve one space if present) + const preserveSpace = textContent[1] === ' '; + if (preserveSpace) { + textChild.textContent = textChild.textContent[0] + ' ' + textChild.textContent.substring(1); + } + } + + // To support empty boxes + if (textChild.textContent === 'undefined') { + textChild.textContent = ''; + } + + // Get the bounding box after the text update + bbox = text.getBBox(); + } else { + const div = text.children[0]; + const dv = select(text); + + numberOfLines = div.innerHTML.split('
').length; + // Katex math support + if (div.innerHTML.includes('')) { + numberOfLines += div.innerHTML.split('').length - 1; + } + + // Support images + const images = div.getElementsByTagName('img'); + if (images) { + const noImgText = textContent.replace(/]*>/g, '').trim() === ''; + await Promise.all( + [...images].map( + (img) => + new Promise((res) => { + function setupImage() { + img.style.display = 'flex'; + img.style.flexDirection = 'column'; + + if (noImgText) { + // default size if no text + const bodyFontSize = + config.fontSize?.toString() ?? window.getComputedStyle(document.body).fontSize; + const enlargingFactor = 5; + const width = parseInt(bodyFontSize, 10) * enlargingFactor + 'px'; + img.style.minWidth = width; + img.style.maxWidth = width; + } else { + img.style.width = '100%'; + } + res(img); + } + setTimeout(() => { + if (img.complete) { + setupImage(); + } + }); + img.addEventListener('error', setupImage); + img.addEventListener('load', setupImage); + }) + ) + ); + } + + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + + // Center text and offset by yOffset + textEl.attr('transform', 'translate(0,' + (-bbox.height / (2 * numberOfLines) + yOffset) + ')'); + return bbox.height; +} diff --git a/packages/mermaid/src/diagrams/class/styles.js b/packages/mermaid/src/diagrams/class/styles.js index 9bad27f386..4a888a2658 100644 --- a/packages/mermaid/src/diagrams/class/styles.js +++ b/packages/mermaid/src/diagrams/class/styles.js @@ -20,6 +20,10 @@ const getStyles = (options) => .label text { fill: ${options.classText}; } + +.labelBkg { + background: ${options.mainBkg}; +} .edgeLabel .label span { background: ${options.mainBkg}; } diff --git a/packages/mermaid/src/diagrams/common/common.ts b/packages/mermaid/src/diagrams/common/common.ts index e24c8e85c8..fd76d0a45d 100644 --- a/packages/mermaid/src/diagrams/common/common.ts +++ b/packages/mermaid/src/diagrams/common/common.ts @@ -32,14 +32,14 @@ const setupDompurifyHooksIfNotSetup = (() => { function setupDompurifyHooks() { const TEMPORARY_ATTRIBUTE = 'data-temp-href-target'; - DOMPurify.addHook('beforeSanitizeAttributes', (node: Element) => { - if (node.tagName === 'A' && node.hasAttribute('target')) { + DOMPurify.addHook('beforeSanitizeAttributes', (node) => { + if (node instanceof Element && node.tagName === 'A' && node.hasAttribute('target')) { node.setAttribute(TEMPORARY_ATTRIBUTE, node.getAttribute('target') ?? ''); } }); - DOMPurify.addHook('afterSanitizeAttributes', (node: Element) => { - if (node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) { + DOMPurify.addHook('afterSanitizeAttributes', (node) => { + if (node instanceof Element && node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) { node.setAttribute('target', node.getAttribute(TEMPORARY_ATTRIBUTE) ?? ''); node.removeAttribute(TEMPORARY_ATTRIBUTE); if (node.getAttribute('target') === '_blank') { @@ -83,7 +83,6 @@ export const sanitizeText = (text: string, config: MermaidConfig): string => { return text; } if (config.dompurifyConfig) { - // eslint-disable-next-line @typescript-eslint/no-base-to-string text = DOMPurify.sanitize(sanitizeMore(text, config), config.dompurifyConfig).toString(); } else { text = DOMPurify.sanitize(sanitizeMore(text, config), { diff --git a/packages/mermaid/src/diagrams/common/svgDrawCommon.ts b/packages/mermaid/src/diagrams/common/svgDrawCommon.ts index 8d5ba3b7ca..59c6d43cfe 100644 --- a/packages/mermaid/src/diagrams/common/svgDrawCommon.ts +++ b/packages/mermaid/src/diagrams/common/svgDrawCommon.ts @@ -1,5 +1,6 @@ import { sanitizeUrl } from '@braintree/sanitize-url'; -import type { Group, SVG } from '../../diagram-api/types.js'; +import type { SVG, SVGGroup } from '../../diagram-api/types.js'; +import { lineBreakRegex } from './common.js'; import type { Bound, D3ImageElement, @@ -11,9 +12,8 @@ import type { TextData, TextObject, } from './commonTypes.js'; -import { lineBreakRegex } from './common.js'; -export const drawRect = (element: SVG | Group, rectData: RectData): D3RectElement => { +export const drawRect = (element: SVG | SVGGroup, rectData: RectData): D3RectElement => { const rectElement: D3RectElement = element.append('rect'); rectElement.attr('x', rectData.x); rectElement.attr('y', rectData.y); @@ -50,7 +50,7 @@ export const drawRect = (element: SVG | Group, rectData: RectData): D3RectElemen * @param element - Diagram (reference for bounds) * @param bounds - Shape of the rectangle */ -export const drawBackgroundRect = (element: SVG | Group, bounds: Bound): void => { +export const drawBackgroundRect = (element: SVG | SVGGroup, bounds: Bound): void => { const rectData: RectData = { x: bounds.startx, y: bounds.starty, @@ -64,7 +64,7 @@ export const drawBackgroundRect = (element: SVG | Group, bounds: Bound): void => rectElement.lower(); }; -export const drawText = (element: SVG | Group, textData: TextData): D3TextElement => { +export const drawText = (element: SVG | SVGGroup, textData: TextData): D3TextElement => { const nText: string = textData.text.replace(lineBreakRegex, ' '); const textElem: D3TextElement = element.append('text'); @@ -84,7 +84,7 @@ export const drawText = (element: SVG | Group, textData: TextData): D3TextElemen return textElem; }; -export const drawImage = (elem: SVG | Group, x: number, y: number, link: string): void => { +export const drawImage = (elem: SVG | SVGGroup, x: number, y: number, link: string): void => { const imageElement: D3ImageElement = elem.append('image'); imageElement.attr('x', x); imageElement.attr('y', y); @@ -93,7 +93,7 @@ export const drawImage = (elem: SVG | Group, x: number, y: number, link: string) }; export const drawEmbeddedImage = ( - element: SVG | Group, + element: SVG | SVGGroup, x: number, y: number, link: string diff --git a/packages/mermaid/src/diagrams/er/erRenderer.js b/packages/mermaid/src/diagrams/er/erRenderer.js index 33fb5bd4ae..0327bfc9d5 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/error/errorRenderer.ts b/packages/mermaid/src/diagrams/error/errorRenderer.ts index 1b3622c6dc..a5f10acefc 100644 --- a/packages/mermaid/src/diagrams/error/errorRenderer.ts +++ b/packages/mermaid/src/diagrams/error/errorRenderer.ts @@ -1,5 +1,5 @@ +import type { SVG, SVGGroup } from '../../diagram-api/types.js'; import { log } from '../../logger.js'; -import type { Group, SVG } from '../../diagram-api/types.js'; import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; import { configureSvgSize } from '../../setupGraphViewbox.js'; @@ -13,7 +13,7 @@ import { configureSvgSize } from '../../setupGraphViewbox.js'; export const draw = (_text: string, id: string, version: string) => { log.debug('rendering svg for syntax error\n'); const svg: SVG = selectSvgElement(id); - const g: Group = svg.append('g'); + const g: SVGGroup = svg.append('g'); svg.attr('viewBox', '0 0 2412 512'); configureSvgSize(svg, 100, 512, true); diff --git a/packages/mermaid/src/diagrams/flowchart/elk/detector.ts b/packages/mermaid/src/diagrams/flowchart/elk/detector.ts index b476ff11ba..6688ffd8c0 100644 --- a/packages/mermaid/src/diagrams/flowchart/elk/detector.ts +++ b/packages/mermaid/src/diagrams/flowchart/elk/detector.ts @@ -1,34 +1,26 @@ import type { - ExternalDiagramDefinition, DiagramDetector, DiagramLoader, + ExternalDiagramDefinition, } from '../../../diagram-api/types.js'; -import { log } from '../../../logger.js'; const id = 'flowchart-elk'; -const detector: DiagramDetector = (txt, config): boolean => { +const detector: DiagramDetector = (txt, config = {}): boolean => { if ( // If diagram explicitly states flowchart-elk /^\s*flowchart-elk/.test(txt) || // If a flowchart/graph diagram has their default renderer set to elk (/^\s*flowchart|graph/.test(txt) && config?.flowchart?.defaultRenderer === 'elk') ) { - // This will log at the end, hopefully. - setTimeout( - () => - log.warn( - 'flowchart-elk was moved to an external package in Mermaid v11. Please refer [release notes](link) for more details. This diagram will be rendered using `dagre` layout as a fallback.' - ), - 500 - ); + config.layout = 'elk'; return true; } return false; }; const loader: DiagramLoader = async () => { - const { diagram } = await import('../flowDiagram-v2.js'); + const { diagram } = await import('../flowDiagram.js'); return { id, diagram }; }; diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.ts b/packages/mermaid/src/diagrams/flowchart/flowDb.ts index d03f1d9890..1dbc789c92 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.ts @@ -1,8 +1,11 @@ import { select } from 'd3'; -import utils from '../../utils.js'; +import utils, { getEdgeId } from '../../utils.js'; import { getConfig, defaultConfig } from '../../diagram-api/diagramAPI.js'; import common from '../common/common.js'; +import { isValidShape, type ShapeID } from '../../rendering-util/rendering-elements/shapes.js'; +import type { Node, Edge } from '../../rendering-util/types.js'; import { log } from '../../logger.js'; +import * as yaml from 'js-yaml'; import { setAccTitle, getAccTitle, @@ -12,7 +15,16 @@ import { setDiagramTitle, getDiagramTitle, } from '../common/commonDb.js'; -import type { FlowVertex, FlowClass, FlowSubGraph, FlowText, FlowEdge, FlowLink } from './types.js'; +import type { + FlowVertex, + FlowClass, + FlowSubGraph, + FlowText, + FlowEdge, + FlowLink, + FlowVertexTypeParam, +} from './types.js'; +import type { NodeMetaData } from '../../types.js'; const MERMAID_DOM_ID_PREFIX = 'flowchart-'; let vertexCounter = 0; @@ -50,17 +62,18 @@ export const lookUpDomId = function (id: string) { /** * Function called by parser when a node definition has been found - * */ export const addVertex = function ( id: string, textObj: FlowText, - type: 'group', + type: FlowVertexTypeParam, style: string[], classes: string[], dir: string, - props = {} + props = {}, + shapeData: any ) { + // console.log('addVertex', id, shapeData); if (!id || id.trim().length === 0) { return; } @@ -114,6 +127,60 @@ export const addVertex = function ( } else if (props !== undefined) { Object.assign(vertex.props, props); } + + if (shapeData !== undefined) { + let yamlData; + // detect if shapeData contains a newline character + // console.log('shapeData', shapeData); + if (!shapeData.includes('\n')) { + // console.log('yamlData shapeData has no new lines', shapeData); + yamlData = '{\n' + shapeData + '\n}'; + } else { + // console.log('yamlData shapeData has new lines', shapeData); + yamlData = shapeData + '\n'; + } + // console.log('yamlData', yamlData); + const doc = yaml.load(yamlData, { schema: yaml.JSON_SCHEMA }) as NodeMetaData; + if (doc.shape) { + if (doc.shape !== doc.shape.toLowerCase() || doc.shape.includes('_')) { + throw new Error(`No such shape: ${doc.shape}. Shape names should be lowercase.`); + } else if (!isValidShape(doc.shape)) { + throw new Error(`No such shape: ${doc.shape}.`); + } + vertex.type = doc?.shape; + } + + if (doc?.label) { + vertex.text = doc?.label; + } + if (doc?.icon) { + vertex.icon = doc?.icon; + if (!doc.label?.trim() && vertex.text === id) { + vertex.text = ''; + } + } + if (doc?.form) { + vertex.form = doc?.form; + } + if (doc?.pos) { + vertex.pos = doc?.pos; + } + if (doc?.img) { + vertex.img = doc?.img; + if (!doc.label?.trim() && vertex.text === id) { + vertex.text = ''; + } + } + if (doc?.constraint) { + vertex.constraint = doc.constraint; + } + if (doc.w) { + vertex.assetWidth = Number(doc.w); + } + if (doc.h) { + vertex.assetHeight = Number(doc.h); + } + } }; /** @@ -200,10 +267,17 @@ export const updateLink = function (positions: ('default' | number)[], style: st if (pos === 'default') { edges.defaultStyle = style; } else { - if (utils.isSubstringInArray('fill', style) === -1) { - style.push('fill:none'); - } + // if (utils.isSubstringInArray('fill', style) === -1) { + // style.push('fill:none'); + // } edges[pos].style = style; + // if edges[pos].style does have fill not set, set it to none + if ( + (edges[pos]?.style?.length ?? 0) > 0 && + !edges[pos]?.style?.some((s) => s?.startsWith('fill')) + ) { + edges[pos]?.style?.push('fill:none'); + } } }); }; @@ -219,7 +293,7 @@ export const addClass = function (ids: string, style: string[]) { if (style !== undefined && style !== null) { style.forEach(function (s) { if (/color/.exec(s)) { - const newStyle = s.replace('fill', 'bgFill').replace('color', 'fill'); + const newStyle = s.replace('fill', 'bgFill'); // .replace('color', 'fill'); classNode.textStyles.push(newStyle); } classNode.styles.push(s); @@ -417,7 +491,6 @@ const setupToolTips = function (element: Element) { } const rect = (this as Element)?.getBoundingClientRect(); - // @ts-ignore TODO: fix this tooltipElem.transition().duration(200).style('opacity', '.9'); tooltipElem .text(el.attr('title')) @@ -427,7 +500,6 @@ const setupToolTips = function (element: Element) { el.classed('hover', true); }) .on('mouseout', function () { - // @ts-ignore TODO: fix this tooltipElem.transition().duration(500).style('opacity', 0); const el = select(this); el.classed('hover', false); @@ -728,14 +800,12 @@ export const destructLink = (_str: string, _startStr: string) => { // Todo optimizer this by caching existing nodes const exists = (allSgs: FlowSubGraph[], _id: string) => { - let res = false; - allSgs.forEach((sg) => { - const pos = sg.nodes.indexOf(_id); - if (pos >= 0) { - res = true; + for (const sg of allSgs) { + if (sg.nodes.includes(_id)) { + return true; } - }); - return res; + } + return false; }; /** * Deletes an id from all subgraphs @@ -755,11 +825,210 @@ export const lex = { firstGraph, }; +const getTypeFromVertex = (vertex: FlowVertex): ShapeID => { + if (vertex.img) { + return 'imageSquare'; + } + if (vertex.icon) { + if (vertex.form === 'circle') { + return 'iconCircle'; + } + if (vertex.form === 'square') { + return 'iconSquare'; + } + if (vertex.form === 'rounded') { + return 'iconRounded'; + } + return 'icon'; + } + switch (vertex.type) { + case 'square': + case undefined: + return 'squareRect'; + case 'round': + return 'roundedRect'; + case 'ellipse': + // @ts-expect-error -- Ellipses are broken, see https://github.com/mermaid-js/mermaid/issues/5976 + return 'ellipse'; + default: + return vertex.type; + } +}; + +const findNode = (nodes: Node[], id: string) => nodes.find((node) => node.id === id); +const destructEdgeType = (type: string | undefined) => { + let arrowTypeStart = 'none'; + let arrowTypeEnd = 'arrow_point'; + switch (type) { + case 'arrow_point': + case 'arrow_circle': + case 'arrow_cross': + arrowTypeEnd = type; + break; + + case 'double_arrow_point': + case 'double_arrow_circle': + case 'double_arrow_cross': + arrowTypeStart = type.replace('double_', ''); + arrowTypeEnd = arrowTypeStart; + break; + } + return { arrowTypeStart, arrowTypeEnd }; +}; + +const addNodeFromVertex = ( + vertex: FlowVertex, + nodes: Node[], + parentDB: Map, + subGraphDB: Map, + config: any, + look: string +) => { + const parentId = parentDB.get(vertex.id); + const isGroup = subGraphDB.get(vertex.id) ?? false; + + const node = findNode(nodes, vertex.id); + if (node) { + node.cssStyles = vertex.styles; + node.cssCompiledStyles = getCompiledStyles(vertex.classes); + node.cssClasses = vertex.classes.join(' '); + } else { + const baseNode = { + id: vertex.id, + label: vertex.text, + labelStyle: '', + parentId, + padding: config.flowchart?.padding || 8, + cssStyles: vertex.styles, + cssCompiledStyles: getCompiledStyles(['default', 'node', ...vertex.classes]), + cssClasses: 'default ' + vertex.classes.join(' '), + dir: vertex.dir, + domId: vertex.domId, + look, + link: vertex.link, + linkTarget: vertex.linkTarget, + tooltip: getTooltip(vertex.id), + icon: vertex.icon, + pos: vertex.pos, + img: vertex.img, + assetWidth: vertex.assetWidth, + assetHeight: vertex.assetHeight, + constraint: vertex.constraint, + }; + if (isGroup) { + nodes.push({ + ...baseNode, + isGroup: true, + shape: 'rect', + }); + } else { + nodes.push({ + ...baseNode, + isGroup: false, + shape: getTypeFromVertex(vertex), + }); + } + } +}; + +function getCompiledStyles(classDefs: string[]) { + let compiledStyles: string[] = []; + for (const customClass of classDefs) { + const cssClass = classes.get(customClass); + if (cssClass?.styles) { + compiledStyles = [...compiledStyles, ...(cssClass.styles ?? [])].map((s) => s.trim()); + } + if (cssClass?.textStyles) { + compiledStyles = [...compiledStyles, ...(cssClass.textStyles ?? [])].map((s) => s.trim()); + } + } + return compiledStyles; +} + +export const getData = () => { + const config = getConfig(); + const nodes: Node[] = []; + const edges: Edge[] = []; + + const subGraphs = getSubGraphs(); + const parentDB = new Map(); + const subGraphDB = new Map(); + + // Setup the subgraph data for adding nodes + for (let i = subGraphs.length - 1; i >= 0; i--) { + const subGraph = subGraphs[i]; + if (subGraph.nodes.length > 0) { + subGraphDB.set(subGraph.id, true); + } + for (const id of subGraph.nodes) { + parentDB.set(id, subGraph.id); + } + } + + // Data is setup, add the nodes + for (let i = subGraphs.length - 1; i >= 0; i--) { + const subGraph = subGraphs[i]; + nodes.push({ + id: subGraph.id, + label: subGraph.title, + labelStyle: '', + parentId: parentDB.get(subGraph.id), + padding: 8, + cssCompiledStyles: getCompiledStyles(subGraph.classes), + cssClasses: subGraph.classes.join(' '), + shape: 'rect', + dir: subGraph.dir, + isGroup: true, + look: config.look, + }); + } + + const n = getVertices(); + n.forEach((vertex) => { + addNodeFromVertex(vertex, nodes, parentDB, subGraphDB, config, config.look || 'classic'); + }); + + const e = getEdges(); + e.forEach((rawEdge, index) => { + const { arrowTypeStart, arrowTypeEnd } = destructEdgeType(rawEdge.type); + const styles = [...(e.defaultStyle ?? [])]; + + if (rawEdge.style) { + styles.push(...rawEdge.style); + } + const edge: Edge = { + id: getEdgeId(rawEdge.start, rawEdge.end, { counter: index, prefix: 'L' }), + start: rawEdge.start, + end: rawEdge.end, + type: rawEdge.type ?? 'normal', + label: rawEdge.text, + labelpos: 'c', + thickness: rawEdge.stroke, + minlen: rawEdge.length, + classes: + rawEdge?.stroke === 'invisible' + ? '' + : 'edge-thickness-normal edge-pattern-solid flowchart-link', + arrowTypeStart: rawEdge?.stroke === 'invisible' ? 'none' : arrowTypeStart, + arrowTypeEnd: rawEdge?.stroke === 'invisible' ? 'none' : arrowTypeEnd, + arrowheadStyle: 'fill: #333', + labelStyle: styles, + style: styles, + pattern: rawEdge.stroke, + look: config.look, + }; + edges.push(edge); + }); + + return { nodes, edges, other: {}, config }; +}; + export default { defaultConfig: () => defaultConfig.flowchart, setAccTitle, getAccTitle, getAccDescription, + getData, setAccDescription, addVertex, lookUpDomId, diff --git a/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts b/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts index dda5a67f0d..df3f57e47a 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts @@ -1,16 +1,20 @@ -import type { DiagramDetector, DiagramLoader } from '../../diagram-api/types.js'; -import type { ExternalDiagramDefinition } from '../../diagram-api/types.js'; +import type { + DiagramDetector, + DiagramLoader, + ExternalDiagramDefinition, +} from '../../diagram-api/types.js'; 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; @@ -19,7 +23,7 @@ const detector: DiagramDetector = (txt, config) => { }; const loader: DiagramLoader = async () => { - const { diagram } = await import('./flowDiagram-v2.js'); + const { diagram } = await import('./flowDiagram.js'); return { id, diagram }; }; diff --git a/packages/mermaid/src/diagrams/flowchart/flowDiagram-v2.ts b/packages/mermaid/src/diagrams/flowchart/flowDiagram-v2.ts deleted file mode 100644 index 368a98ccae..0000000000 --- a/packages/mermaid/src/diagrams/flowchart/flowDiagram-v2.ts +++ /dev/null @@ -1,25 +0,0 @@ -// @ts-ignore: JISON doesn't support types -import flowParser from './parser/flow.jison'; -import flowDb from './flowDb.js'; -import flowRendererV2 from './flowRenderer-v2.js'; -import flowStyles from './styles.js'; -import type { MermaidConfig } from '../../config.type.js'; -import { setConfig } from '../../diagram-api/diagramAPI.js'; - -export const diagram = { - parser: flowParser, - db: flowDb, - renderer: flowRendererV2, - styles: flowStyles, - init: (cnf: MermaidConfig) => { - if (!cnf.flowchart) { - cnf.flowchart = {}; - } - cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute; - // flowchart-v2 uses dagre-wrapper, which doesn't have access to flowchart cnf - setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } }); - flowRendererV2.setConf(cnf.flowchart); - flowDb.clear(); - flowDb.setGen('gen-2'); - }, -}; diff --git a/packages/mermaid/src/diagrams/flowchart/flowDiagram.ts b/packages/mermaid/src/diagrams/flowchart/flowDiagram.ts index ca4f8fba8a..67cdf918fe 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDiagram.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowDiagram.ts @@ -1,24 +1,26 @@ +import type { MermaidConfig } from '../../config.type.js'; +import { setConfig } from '../../diagram-api/diagramAPI.js'; +import flowDb from './flowDb.js'; +import renderer from './flowRenderer-v3-unified.js'; // @ts-ignore: JISON doesn't support types import flowParser from './parser/flow.jison'; -import flowDb from './flowDb.js'; -import flowRenderer from './flowRenderer.js'; -import flowRendererV2 from './flowRenderer-v2.js'; import flowStyles from './styles.js'; -import type { MermaidConfig } from '../../config.type.js'; export const diagram = { parser: flowParser, db: flowDb, - renderer: flowRendererV2, + renderer, styles: flowStyles, init: (cnf: MermaidConfig) => { if (!cnf.flowchart) { cnf.flowchart = {}; } - // TODO, broken as of 2022-09-14 (13809b50251845475e6dca65cc395761be38fbd2) + if (cnf.layout) { + setConfig({ layout: cnf.layout }); + } cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute; - flowRenderer.setConf(cnf.flowchart); + setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } }); flowDb.clear(); - flowDb.setGen('gen-1'); + flowDb.setGen('gen-2'); }, }; diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js deleted file mode 100644 index 1dce1391ef..0000000000 --- a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js +++ /dev/null @@ -1,515 +0,0 @@ -import * as graphlib from 'dagre-d3-es/src/graphlib/index.js'; -import { select, curveLinear, selectAll } from 'd3'; -import { getConfig } from '../../diagram-api/diagramAPI.js'; -import utils, { getEdgeId } from '../../utils.js'; -import { render } from '../../dagre-wrapper/index.js'; -import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js'; -import { log } from '../../logger.js'; -import common, { evaluate, renderKatex } from '../common/common.js'; -import { interpolateToCurve, getStylesFromArray } from '../../utils.js'; -import { setupGraphViewbox } from '../../setupGraphViewbox.js'; - -const conf = {}; -export const setConf = function (cnf) { - const keys = Object.keys(cnf); - for (const key of keys) { - conf[key] = cnf[key]; - } -}; - -/** - * Function that adds the vertices found during parsing to the graph to be rendered. - * - * @param vert Object containing the vertices. - * @param g The graph that is to be drawn. - * @param svgId - * @param root - * @param doc - * @param diagObj - */ -export const addVertices = async function (vert, g, svgId, root, doc, diagObj) { - const svg = root.select(`[id="${svgId}"]`); - const keys = vert.keys(); - - // Iterate through each item in the vertex object (containing all the vertices found) in the graph definition - for (const id of keys) { - const vertex = vert.get(id); - - /** - * Variable for storing the classes for the vertex - * - * @type {string} - */ - let classStr = 'default'; - if (vertex.classes.length > 0) { - classStr = vertex.classes.join(' '); - } - classStr = classStr + ' flowchart-label'; - const styles = getStylesFromArray(vertex.styles); - - // Use vertex id as text in the box if no text is provided by the graph definition - let vertexText = vertex.text !== undefined ? vertex.text : vertex.id; - - // We create a SVG label, either by delegating to addHtmlLabel or manually - let vertexNode; - log.info('vertex', vertex, vertex.labelType); - if (vertex.labelType === 'markdown') { - log.info('vertex', vertex, vertex.labelType); - } else { - if (evaluate(getConfig().flowchart.htmlLabels)) { - // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that? - const node = { - label: vertexText, - }; - vertexNode = addHtmlLabel(svg, node).node(); - vertexNode.parentNode.removeChild(vertexNode); - } else { - const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text'); - svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:')); - - const rows = vertexText.split(common.lineBreakRegex); - - for (const row of rows) { - const tspan = doc.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', '1'); - tspan.textContent = row; - svgLabel.appendChild(tspan); - } - vertexNode = svgLabel; - } - } - - let radius = 0; - let _shape = ''; - // Set the shape based parameters - switch (vertex.type) { - case 'round': - radius = 5; - _shape = 'rect'; - break; - case 'square': - _shape = 'rect'; - break; - case 'diamond': - _shape = 'question'; - break; - case 'hexagon': - _shape = 'hexagon'; - break; - case 'odd': - _shape = 'rect_left_inv_arrow'; - break; - case 'lean_right': - _shape = 'lean_right'; - break; - case 'lean_left': - _shape = 'lean_left'; - break; - case 'trapezoid': - _shape = 'trapezoid'; - break; - case 'inv_trapezoid': - _shape = 'inv_trapezoid'; - break; - case 'odd_right': - _shape = 'rect_left_inv_arrow'; - break; - case 'circle': - _shape = 'circle'; - break; - case 'ellipse': - _shape = 'ellipse'; - break; - case 'stadium': - _shape = 'stadium'; - break; - case 'subroutine': - _shape = 'subroutine'; - break; - case 'cylinder': - _shape = 'cylinder'; - break; - case 'group': - _shape = 'rect'; - break; - case 'doublecircle': - _shape = 'doublecircle'; - break; - default: - _shape = 'rect'; - } - const labelText = await renderKatex(vertexText, getConfig()); - - // Add the node - g.setNode(vertex.id, { - labelStyle: styles.labelStyle, - shape: _shape, - labelText, - labelType: vertex.labelType, - rx: radius, - ry: radius, - class: classStr, - style: styles.style, - id: vertex.id, - link: vertex.link, - linkTarget: vertex.linkTarget, - tooltip: diagObj.db.getTooltip(vertex.id) || '', - domId: diagObj.db.lookUpDomId(vertex.id), - haveCallback: vertex.haveCallback, - width: vertex.type === 'group' ? 500 : undefined, - dir: vertex.dir, - type: vertex.type, - props: vertex.props, - padding: getConfig().flowchart.padding, - }); - - log.info('setNode', { - labelStyle: styles.labelStyle, - labelType: vertex.labelType, - shape: _shape, - labelText, - rx: radius, - ry: radius, - class: classStr, - style: styles.style, - id: vertex.id, - domId: diagObj.db.lookUpDomId(vertex.id), - width: vertex.type === 'group' ? 500 : undefined, - type: vertex.type, - dir: vertex.dir, - props: vertex.props, - padding: getConfig().flowchart.padding, - }); - } -}; - -/** - * Add edges to graph based on parsed graph definition - * - * @param {object} edges The edges to add to the graph - * @param {object} g The graph object - * @param _diagObj - */ -export const addEdges = async function (edges, g, _diagObj) { - log.info('abc78 edges = ', edges); - let cnt = 0; - let linkIdCnt = {}; - - let defaultStyle; - let defaultLabelStyle; - - if (edges.defaultStyle !== undefined) { - const defaultStyles = getStylesFromArray(edges.defaultStyle); - defaultStyle = defaultStyles.style; - defaultLabelStyle = defaultStyles.labelStyle; - } - - for (const edge of edges) { - cnt++; - - // Identify Link - const linkIdBase = getEdgeId(edge.start, edge.end, { - counter: cnt, - prefix: 'L', - }); - - // count the links from+to the same node to give unique id - if (linkIdCnt[linkIdBase] === undefined) { - linkIdCnt[linkIdBase] = 0; - log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]); - } else { - linkIdCnt[linkIdBase]++; - log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]); - } - let linkId = `${linkIdBase}_${linkIdCnt[linkIdBase]}`; - - log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]); - const linkNameStart = 'LS-' + edge.start; - const linkNameEnd = 'LE-' + edge.end; - - const edgeData = { style: '', labelStyle: '' }; - edgeData.minlen = edge.length || 1; - //edgeData.id = 'id' + cnt; - - // Set link type for rendering - if (edge.type === 'arrow_open') { - edgeData.arrowhead = 'none'; - } else { - edgeData.arrowhead = 'normal'; - } - - // Check of arrow types, placed here in order not to break old rendering - edgeData.arrowTypeStart = 'arrow_open'; - edgeData.arrowTypeEnd = 'arrow_open'; - - /* eslint-disable no-fallthrough */ - switch (edge.type) { - case 'double_arrow_cross': - edgeData.arrowTypeStart = 'arrow_cross'; - case 'arrow_cross': - edgeData.arrowTypeEnd = 'arrow_cross'; - break; - case 'double_arrow_point': - edgeData.arrowTypeStart = 'arrow_point'; - case 'arrow_point': - edgeData.arrowTypeEnd = 'arrow_point'; - break; - case 'double_arrow_circle': - edgeData.arrowTypeStart = 'arrow_circle'; - case 'arrow_circle': - edgeData.arrowTypeEnd = 'arrow_circle'; - break; - } - - let style = ''; - let labelStyle = ''; - - switch (edge.stroke) { - case 'normal': - style = 'fill:none;'; - if (defaultStyle !== undefined) { - style = defaultStyle; - } - if (defaultLabelStyle !== undefined) { - labelStyle = defaultLabelStyle; - } - edgeData.thickness = 'normal'; - edgeData.pattern = 'solid'; - break; - case 'dotted': - edgeData.thickness = 'normal'; - edgeData.pattern = 'dotted'; - edgeData.style = 'fill:none;stroke-width:2px;stroke-dasharray:3;'; - break; - case 'thick': - edgeData.thickness = 'thick'; - edgeData.pattern = 'solid'; - edgeData.style = 'stroke-width: 3.5px;fill:none;'; - break; - case 'invisible': - edgeData.thickness = 'invisible'; - edgeData.pattern = 'solid'; - edgeData.style = 'stroke-width: 0;fill:none;'; - break; - } - if (edge.style !== undefined) { - const styles = getStylesFromArray(edge.style); - style = styles.style; - labelStyle = styles.labelStyle; - } - - edgeData.style = edgeData.style += style; - edgeData.labelStyle = edgeData.labelStyle += labelStyle; - - if (edge.interpolate !== undefined) { - edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear); - } else if (edges.defaultInterpolate !== undefined) { - edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear); - } else { - edgeData.curve = interpolateToCurve(conf.curve, curveLinear); - } - - if (edge.text === undefined) { - if (edge.style !== undefined) { - edgeData.arrowheadStyle = 'fill: #333'; - } - } else { - edgeData.arrowheadStyle = 'fill: #333'; - edgeData.labelpos = 'c'; - } - edgeData.labelType = edge.labelType; - edgeData.label = await renderKatex(edge.text.replace(common.lineBreakRegex, '\n'), getConfig()); - - if (edge.style === undefined) { - edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;'; - } - - edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:'); - - edgeData.id = linkId; - edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd; - - // Add the edge to the graph - g.setEdge(edge.start, edge.end, edgeData, cnt); - } -}; - -/** - * Returns the all the styles from classDef statements in the graph definition. - * - * @param text - * @param diagObj - * @returns {Map} ClassDef styles - */ -export const getClasses = function (text, diagObj) { - return diagObj.db.getClasses(); -}; - -/** - * Draws a flowchart in the tag with id: id based on the graph definition in text. - * - * @param text - * @param id - * @param _version - * @param diagObj - */ - -export const draw = async function (text, id, _version, diagObj) { - log.info('Drawing flowchart'); - - // Fetch the default direction, use TD if none was found - let dir = diagObj.db.getDirection(); - if (dir === undefined) { - dir = 'TD'; - } - - const { securityLevel, flowchart: conf } = getConfig(); - const nodeSpacing = conf.nodeSpacing ?? 50; - const rankSpacing = conf.rankSpacing ?? 50; - - // Handle root and document for when rendering in sandbox mode - let sandboxElement; - if (securityLevel === 'sandbox') { - sandboxElement = select('#i' + id); - } - const root = - securityLevel === 'sandbox' - ? select(sandboxElement.nodes()[0].contentDocument.body) - : select('body'); - const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document; - - // Create the input mermaid.graph - const g = new graphlib.Graph({ - multigraph: true, - compound: true, - }) - .setGraph({ - rankdir: dir, - nodesep: nodeSpacing, - ranksep: rankSpacing, - marginx: 0, - marginy: 0, - }) - .setDefaultEdgeLabel(function () { - return {}; - }); - - let subG; - const subGraphs = diagObj.db.getSubGraphs(); - log.info('Subgraphs - ', subGraphs); - for (let i = subGraphs.length - 1; i >= 0; i--) { - subG = subGraphs[i]; - log.info('Subgraph - ', subG); - diagObj.db.addVertex( - subG.id, - { text: subG.title, type: subG.labelType }, - 'group', - undefined, - subG.classes, - subG.dir - ); - } - - // Fetch the vertices/nodes and edges/links from the parsed graph definition - const vert = diagObj.db.getVertices(); - - const edges = diagObj.db.getEdges(); - - log.info('Edges', edges); - let i = 0; - for (i = subGraphs.length - 1; i >= 0; i--) { - subG = subGraphs[i]; - - selectAll('cluster').append('text'); - - for (const node of subG.nodes) { - log.info('Setting up subgraphs', node, subG.id); - g.setParent(node, subG.id); - } - } - await addVertices(vert, g, id, root, doc, diagObj); - await addEdges(edges, g, diagObj); - - // Add custom shapes - // flowChartShapes.addToRenderV2(addShape); - - // Set up an SVG group so that we can translate the final graph. - const svg = root.select(`[id="${id}"]`); - - // Run the renderer. This is what draws the final graph. - const element = root.select('#' + id + ' g'); - await render(element, g, ['point', 'circle', 'cross'], 'flowchart', id); - - utils.insertTitle(svg, 'flowchartTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle()); - - setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth); - - // Index nodes - diagObj.db.indexNodes('subGraph' + i); - - // Add label rects for non html labels - if (!conf.htmlLabels) { - const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label'); - for (const label of labels) { - // Get dimensions of label - const dim = label.getBBox(); - - const rect = doc.createElementNS('http://www.w3.org/2000/svg', 'rect'); - rect.setAttribute('rx', 0); - rect.setAttribute('ry', 0); - rect.setAttribute('width', dim.width); - rect.setAttribute('height', dim.height); - - label.insertBefore(rect, label.firstChild); - } - } - - // If node has a link, wrap it in an anchor SVG object. - const keys = [...vert.keys()]; - keys.forEach((key) => { - const vertex = vert.get(key); - - if (vertex.link) { - const node = select('#' + id + ' [id="' + key + '"]'); - if (node) { - const link = doc.createElementNS('http://www.w3.org/2000/svg', 'a'); - link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' ')); - link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link); - link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener'); - if (securityLevel === 'sandbox') { - link.setAttributeNS('http://www.w3.org/2000/svg', 'target', '_top'); - } else if (vertex.linkTarget) { - link.setAttributeNS('http://www.w3.org/2000/svg', 'target', vertex.linkTarget); - } - - const linkNode = node.insert(function () { - return link; - }, ':first-child'); - - const shape = node.select('.label-container'); - if (shape) { - linkNode.append(function () { - return shape.node(); - }); - } - - const label = node.select('.label'); - if (label) { - linkNode.append(function () { - return label.node(); - }); - } - } - } - }); -}; - -export default { - setConf, - addVertices, - addEdges, - getClasses, - draw, -}; diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts new file mode 100644 index 0000000000..6cc15258d4 --- /dev/null +++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts @@ -0,0 +1,104 @@ +import { select } from 'd3'; +import { getConfig } from '../../diagram-api/diagramAPI.js'; +import type { DiagramStyleClassDef } from '../../diagram-api/types.js'; +import { log } from '../../logger.js'; +import { getDiagramElement } from '../../rendering-util/insertElementsForSize.js'; +import { getRegisteredLayoutAlgorithm, render } from '../../rendering-util/render.js'; +import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js'; +import type { LayoutData } from '../../rendering-util/types.js'; +import utils from '../../utils.js'; +import { getDirection } from './flowDb.js'; + +export const getClasses = function ( + text: string, + diagramObj: any +): Map { + return diagramObj.db.getClasses(); +}; + +export const draw = async function (text: string, id: string, _version: string, diag: any) { + log.info('REF0:'); + log.info('Drawing state diagram (v2)', id); + const { securityLevel, flowchart: conf, layout } = getConfig(); + + // Handle root and document for when rendering in sandbox mode + let sandboxElement; + if (securityLevel === 'sandbox') { + sandboxElement = select('#i' + id); + } + + // @ts-ignore - document is always available + const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document; + + // The getData method provided in all supported diagrams is used to extract the data from the parsed structure + // into the Layout data format + log.debug('Before getData: '); + const data4Layout = diag.db.getData() as LayoutData; + log.debug('Data: ', data4Layout); + // Create the root SVG + const svg = getDiagramElement(id, securityLevel); + const direction = getDirection(); + + data4Layout.type = diag.type; + data4Layout.layoutAlgorithm = getRegisteredLayoutAlgorithm(layout); + if (data4Layout.layoutAlgorithm === 'dagre' && layout === 'elk') { + log.warn( + 'flowchart-elk was moved to an external package in Mermaid v11. Please refer [release notes](https://github.com/mermaid-js/mermaid/releases/tag/v11.0.0) for more details. This diagram will be rendered using `dagre` layout as a fallback.' + ); + } + data4Layout.direction = direction; + data4Layout.nodeSpacing = conf?.nodeSpacing || 50; + data4Layout.rankSpacing = conf?.rankSpacing || 50; + data4Layout.markers = ['point', 'circle', 'cross']; + + data4Layout.diagramId = id; + log.debug('REF1:', data4Layout); + await render(data4Layout, svg); + const padding = data4Layout.config.flowchart?.diagramPadding ?? 8; + utils.insertTitle( + svg, + 'flowchartTitleText', + conf?.titleTopMargin || 0, + diag.db.getDiagramTitle() + ); + setupViewPortForSVG(svg, padding, 'flowchart', conf?.useMaxWidth || false); + + // If node has a link, wrap it in an anchor SVG object. + for (const vertex of data4Layout.nodes) { + const node = select(`#${id} [id="${vertex.id}"]`); + if (!node || !vertex.link) { + continue; + } + const link = doc.createElementNS('http://www.w3.org/2000/svg', 'a'); + link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.cssClasses); + link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener'); + if (securityLevel === 'sandbox') { + link.setAttributeNS('http://www.w3.org/2000/svg', 'target', '_top'); + } else if (vertex.linkTarget) { + link.setAttributeNS('http://www.w3.org/2000/svg', 'target', vertex.linkTarget); + } + + const linkNode = node.insert(function () { + return link; + }, ':first-child'); + + const shape = node.select('.label-container'); + if (shape) { + linkNode.append(function () { + return shape.node(); + }); + } + + const label = node.select('.label'); + if (label) { + linkNode.append(function () { + return label.node(); + }); + } + } +}; + +export default { + getClasses, + draw, +}; diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer.addEdges.spec.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer.addEdges.spec.js deleted file mode 100644 index 00f9ef8512..0000000000 --- a/packages/mermaid/src/diagrams/flowchart/flowRenderer.addEdges.spec.js +++ /dev/null @@ -1,150 +0,0 @@ -import flowDb from './flowDb.js'; -import { parser } from './parser/flow.jison'; -import flowRenderer from './flowRenderer.js'; -import { addDiagrams } from '../../diagram-api/diagram-orchestration.js'; - -const diag = { - db: flowDb, -}; -addDiagrams(); - -describe('when using mermaid and ', function () { - describe('when calling addEdges ', function () { - beforeEach(function () { - parser.yy = flowDb; - flowDb.clear(); - flowDb.setGen('gen-2'); - }); - it('should handle edges with text', async () => { - parser.parse('graph TD;A-->|text ex|B;'); - flowDb.getVertices(); - const edges = flowDb.getEdges(); - - const mockG = { - setEdge: function (start, end, options) { - expect(start).toContain('flowchart-A-'); - expect(end).toContain('flowchart-B-'); - expect(options.arrowhead).toBe('normal'); - expect(options.label.match('text ex')).toBeTruthy(); - }, - }; - - await flowRenderer.addEdges(edges, mockG, diag); - }); - - it('should handle edges without text', async function () { - parser.parse('graph TD;A-->B;'); - flowDb.getVertices(); - const edges = flowDb.getEdges(); - - const mockG = { - setEdge: function (start, end, options) { - expect(start).toContain('flowchart-A-'); - expect(end).toContain('flowchart-B-'); - expect(options.arrowhead).toBe('normal'); - }, - }; - - await flowRenderer.addEdges(edges, mockG, diag); - }); - - it('should handle open-ended edges', async () => { - parser.parse('graph TD;A---B;'); - flowDb.getVertices(); - const edges = flowDb.getEdges(); - - const mockG = { - setEdge: function (start, end, options) { - expect(start).toContain('flowchart-A-'); - expect(end).toContain('flowchart-B-'); - expect(options.arrowhead).toBe('none'); - }, - }; - - await flowRenderer.addEdges(edges, mockG, diag); - }); - - it('should handle edges with styles defined', async () => { - parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;'); - flowDb.getVertices(); - const edges = flowDb.getEdges(); - - const mockG = { - setEdge: function (start, end, options) { - expect(start).toContain('flowchart-A-'); - expect(end).toContain('flowchart-B-'); - expect(options.arrowhead).toBe('none'); - expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;'); - }, - }; - - await flowRenderer.addEdges(edges, mockG, diag); - }); - it('should handle edges with interpolation defined', async () => { - parser.parse('graph TD;A---B; linkStyle 0 interpolate basis'); - flowDb.getVertices(); - const edges = flowDb.getEdges(); - - const mockG = { - setEdge: function (start, end, options) { - expect(start).toContain('flowchart-A-'); - expect(end).toContain('flowchart-B-'); - expect(options.arrowhead).toBe('none'); - expect(options.curve).toBe('basis'); // mocked as string - }, - }; - - await flowRenderer.addEdges(edges, mockG, diag); - }); - it('should handle edges with text and styles defined', async () => { - parser.parse('graph TD;A---|the text|B; linkStyle 0 stroke:val1,stroke-width:val2;'); - flowDb.getVertices(); - const edges = flowDb.getEdges(); - - const mockG = { - setEdge: function (start, end, options) { - expect(start).toContain('flowchart-A-'); - expect(end).toContain('flowchart-B-'); - expect(options.arrowhead).toBe('none'); - expect(options.label.match('the text')).toBeTruthy(); - expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;'); - }, - }; - - await flowRenderer.addEdges(edges, mockG, diag); - }); - - it('should set fill to "none" by default when handling edges', async () => { - parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;'); - flowDb.getVertices(); - const edges = flowDb.getEdges(); - - const mockG = { - setEdge: function (start, end, options) { - expect(start).toContain('flowchart-A-'); - expect(end).toContain('flowchart-B'); - expect(options.arrowhead).toBe('none'); - expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;'); - }, - }; - - await flowRenderer.addEdges(edges, mockG, diag); - }); - - it('should not set fill to none if fill is set in linkStyle', async () => { - parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2,fill:blue;'); - flowDb.getVertices(); - const edges = flowDb.getEdges(); - const mockG = { - setEdge: function (start, end, options) { - expect(start).toContain('flowchart-A-'); - expect(end).toContain('flowchart-B-'); - expect(options.arrowhead).toBe('none'); - expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:blue;'); - }, - }; - - await flowRenderer.addEdges(edges, mockG, diag); - }); - }); -}); diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer.js deleted file mode 100644 index 314c6aa52b..0000000000 --- a/packages/mermaid/src/diagrams/flowchart/flowRenderer.js +++ /dev/null @@ -1,503 +0,0 @@ -import * as graphlib from 'dagre-d3-es/src/graphlib/index.js'; -import { select, curveLinear, selectAll } from 'd3'; -import { getConfig } from '../../diagram-api/diagramAPI.js'; -import { render as Render } from 'dagre-d3-es'; -import { applyStyle } from 'dagre-d3-es/src/dagre-js/util.js'; -import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js'; -import { log } from '../../logger.js'; -import common, { evaluate, renderKatex } from '../common/common.js'; -import { interpolateToCurve, getStylesFromArray, getEdgeId } from '../../utils.js'; -import { setupGraphViewbox } from '../../setupGraphViewbox.js'; -import flowChartShapes from './flowChartShapes.js'; -import { replaceIconSubstring } from '../../rendering-util/createText.js'; - -const conf = {}; -export const setConf = function (cnf) { - const keys = Object.keys(cnf); - for (const key of keys) { - conf[key] = cnf[key]; - } -}; - -/** - * Function that adds the vertices found in the graph definition to the graph to be rendered. - * - * @param vert Object containing the vertices. - * @param g The graph that is to be drawn. - * @param svgId - * @param root - * @param _doc - * @param diagObj - */ -export const addVertices = async function (vert, g, svgId, root, _doc, diagObj) { - const svg = !root ? select(`[id="${svgId}"]`) : root.select(`[id="${svgId}"]`); - const doc = !_doc ? document : _doc; - const keys = Object.keys(vert); - - // Iterate through each item in the vertex object (containing all the vertices found) in the graph definition - for (const id of keys) { - const vertex = vert[id]; - - /** - * Variable for storing the classes for the vertex - * - * @type {string} - */ - let classStr = 'default'; - if (vertex.classes.length > 0) { - classStr = vertex.classes.join(' '); - } - - const styles = getStylesFromArray(vertex.styles); - - // Use vertex id as text in the box if no text is provided by the graph definition - let vertexText = vertex.text !== undefined ? vertex.text : vertex.id; - - // We create a SVG label, either by delegating to addHtmlLabel or manually - let vertexNode; - if (evaluate(getConfig().flowchart.htmlLabels)) { - // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that? - const replacedVertexText = replaceIconSubstring(vertexText); - const node = { - label: await renderKatex(replacedVertexText, getConfig()), - }; - vertexNode = addHtmlLabel(svg, node).node(); - vertexNode.parentNode.removeChild(vertexNode); - } else { - const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text'); - svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:')); - - const rows = vertexText.split(common.lineBreakRegex); - - for (const row of rows) { - const tspan = doc.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', '1'); - tspan.textContent = row; - svgLabel.appendChild(tspan); - } - vertexNode = svgLabel; - } - - let radius = 0; - let _shape = ''; - // Set the shape based parameters - switch (vertex.type) { - case 'round': - radius = 5; - _shape = 'rect'; - break; - case 'square': - _shape = 'rect'; - break; - case 'diamond': - _shape = 'question'; - break; - case 'hexagon': - _shape = 'hexagon'; - break; - case 'odd': - _shape = 'rect_left_inv_arrow'; - break; - case 'lean_right': - _shape = 'lean_right'; - break; - case 'lean_left': - _shape = 'lean_left'; - break; - case 'trapezoid': - _shape = 'trapezoid'; - break; - case 'inv_trapezoid': - _shape = 'inv_trapezoid'; - break; - case 'odd_right': - _shape = 'rect_left_inv_arrow'; - break; - case 'circle': - _shape = 'circle'; - break; - case 'ellipse': - _shape = 'ellipse'; - break; - case 'stadium': - _shape = 'stadium'; - break; - case 'subroutine': - _shape = 'subroutine'; - break; - case 'cylinder': - _shape = 'cylinder'; - break; - case 'group': - _shape = 'rect'; - break; - default: - _shape = 'rect'; - } - // Add the node - log.warn('Adding node', vertex.id, vertex.domId); - g.setNode(diagObj.db.lookUpDomId(vertex.id), { - labelType: 'svg', - labelStyle: styles.labelStyle, - shape: _shape, - label: vertexNode, - rx: radius, - ry: radius, - class: classStr, - style: styles.style, - id: diagObj.db.lookUpDomId(vertex.id), - }); - } -}; - -/** - * Add edges to graph based on parsed graph definition - * - * @param {object} edges The edges to add to the graph - * @param {object} g The graph object - * @param diagObj - */ -export const addEdges = async function (edges, g, diagObj) { - let cnt = 0; - - let defaultStyle; - let defaultLabelStyle; - - if (edges.defaultStyle !== undefined) { - const defaultStyles = getStylesFromArray(edges.defaultStyle); - defaultStyle = defaultStyles.style; - defaultLabelStyle = defaultStyles.labelStyle; - } - - for (const edge of edges) { - cnt++; - - // Identify Link - const linkId = getEdgeId(edge.start, edge.end, { - counter: cnt, - prefix: 'L', - }); - const linkNameStart = 'LS-' + edge.start; - const linkNameEnd = 'LE-' + edge.end; - - const edgeData = {}; - - // Set link type for rendering - if (edge.type === 'arrow_open') { - edgeData.arrowhead = 'none'; - } else { - edgeData.arrowhead = 'normal'; - } - - let style = ''; - let labelStyle = ''; - - if (edge.style !== undefined) { - const styles = getStylesFromArray(edge.style); - style = styles.style; - labelStyle = styles.labelStyle; - } else { - switch (edge.stroke) { - case 'normal': - style = 'fill:none'; - if (defaultStyle !== undefined) { - style = defaultStyle; - } - if (defaultLabelStyle !== undefined) { - labelStyle = defaultLabelStyle; - } - break; - case 'dotted': - style = 'fill:none;stroke-width:2px;stroke-dasharray:3;'; - break; - case 'thick': - style = ' stroke-width: 3.5px;fill:none'; - break; - } - } - - edgeData.style = style; - edgeData.labelStyle = labelStyle; - - if (edge.interpolate !== undefined) { - edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear); - } else if (edges.defaultInterpolate !== undefined) { - edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear); - } else { - edgeData.curve = interpolateToCurve(conf.curve, curveLinear); - } - - if (edge.text === undefined) { - if (edge.style !== undefined) { - edgeData.arrowheadStyle = 'fill: #333'; - } - } else { - edgeData.arrowheadStyle = 'fill: #333'; - edgeData.labelpos = 'c'; - - if (evaluate(getConfig().flowchart.htmlLabels)) { - edgeData.labelType = 'html'; - edgeData.label = `${await renderKatex(replaceIconSubstring(edge.text), getConfig())}`; - } else { - edgeData.labelType = 'text'; - edgeData.label = edge.text.replace(common.lineBreakRegex, '\n'); - - if (edge.style === undefined) { - edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none'; - } - - edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:'); - } - } - - edgeData.id = linkId; - edgeData.class = linkNameStart + ' ' + linkNameEnd; - edgeData.minlen = edge.length || 1; - - // Add the edge to the graph - g.setEdge(diagObj.db.lookUpDomId(edge.start), diagObj.db.lookUpDomId(edge.end), edgeData, cnt); - } -}; - -/** - * Returns the all the styles from classDef statements in the graph definition. - * - * @param text - * @param diagObj - * @returns {Map} ClassDef styles - */ -export const getClasses = function (text, diagObj) { - log.info('Extracting classes'); - return diagObj.db.getClasses(); -}; - -/** - * Draws a flowchart in the tag with id: id based on the graph definition in text. - * - * @param text - * @param id - * @param _version - * @param diagObj - */ -export const draw = async function (text, id, _version, diagObj) { - log.info('Drawing flowchart'); - const { securityLevel, flowchart: conf } = getConfig(); - let sandboxElement; - if (securityLevel === 'sandbox') { - sandboxElement = select('#i' + id); - } - const root = - securityLevel === 'sandbox' - ? select(sandboxElement.nodes()[0].contentDocument.body) - : select('body'); - const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document; - - // Fetch the default direction, use TD if none was found - let dir = diagObj.db.getDirection(); - if (dir === undefined) { - dir = 'TD'; - } - const nodeSpacing = conf.nodeSpacing ?? 50; - const rankSpacing = conf.rankSpacing ?? 50; - - // Create the input mermaid.graph - const g = new graphlib.Graph({ - multigraph: true, - compound: true, - }) - .setGraph({ - rankdir: dir, - nodesep: nodeSpacing, - ranksep: rankSpacing, - marginx: 8, - marginy: 8, - }) - .setDefaultEdgeLabel(function () { - return {}; - }); - - let subG; - const subGraphs = diagObj.db.getSubGraphs(); - for (let i = subGraphs.length - 1; i >= 0; i--) { - subG = subGraphs[i]; - diagObj.db.addVertex(subG.id, subG.title, 'group', undefined, subG.classes); - } - - // Fetch the vertices/nodes and edges/links from the parsed graph definition - const vert = diagObj.db.getVertices(); - log.warn('Get vertices', vert); - - const edges = diagObj.db.getEdges(); - - let i = 0; - for (i = subGraphs.length - 1; i >= 0; i--) { - subG = subGraphs[i]; - - selectAll('cluster').append('text'); - - for (const node of subG.nodes) { - log.warn( - 'Setting subgraph', - node, - diagObj.db.lookUpDomId(node), - diagObj.db.lookUpDomId(subG.id) - ); - g.setParent(diagObj.db.lookUpDomId(node), diagObj.db.lookUpDomId(subG.id)); - } - } - await addVertices(vert, g, id, root, doc, diagObj); - await addEdges(edges, g, diagObj); - - // Create the renderer - const render = new Render(); - - // Add custom shapes - flowChartShapes.addToRender(render); - - // Add our custom arrow - an empty arrowhead - render.arrows().none = function normal(parent, id, edge, type) { - const marker = parent - .append('marker') - .attr('id', id) - .attr('viewBox', '0 0 10 10') - .attr('refX', 9) - .attr('refY', 5) - .attr('markerUnits', 'strokeWidth') - .attr('markerWidth', 8) - .attr('markerHeight', 6) - .attr('orient', 'auto'); - - const path = marker.append('path').attr('d', 'M 0 0 L 0 0 L 0 0 z'); - applyStyle(path, edge[type + 'Style']); - }; - - // Override normal arrowhead defined in d3. Remove style & add class to allow css styling. - render.arrows().normal = function normal(parent, id) { - const marker = parent - .append('marker') - .attr('id', id) - .attr('viewBox', '0 0 10 10') - .attr('refX', 9) - .attr('refY', 5) - .attr('markerUnits', 'strokeWidth') - .attr('markerWidth', 8) - .attr('markerHeight', 6) - .attr('orient', 'auto'); - - marker - .append('path') - .attr('d', 'M 0 0 L 10 5 L 0 10 z') - .attr('class', 'arrowheadPath') - .style('stroke-width', 1) - .style('stroke-dasharray', '1,0'); - }; - - // Set up an SVG group so that we can translate the final graph. - const svg = root.select(`[id="${id}"]`); - - // Run the renderer. This is what draws the final graph. - const element = root.select('#' + id + ' g'); - render(element, g); - - element.selectAll('g.node').attr('title', function () { - return diagObj.db.getTooltip(this.id); - }); - - // Index nodes - diagObj.db.indexNodes('subGraph' + i); - - // reposition labels - for (i = 0; i < subGraphs.length; i++) { - subG = subGraphs[i]; - if (subG.title !== 'undefined') { - const clusterRects = doc.querySelectorAll( - '#' + id + ' [id="' + diagObj.db.lookUpDomId(subG.id) + '"] rect' - ); - const clusterEl = doc.querySelectorAll( - '#' + id + ' [id="' + diagObj.db.lookUpDomId(subG.id) + '"]' - ); - - const xPos = clusterRects[0].x.baseVal.value; - const yPos = clusterRects[0].y.baseVal.value; - const _width = clusterRects[0].width.baseVal.value; - const cluster = select(clusterEl[0]); - const te = cluster.select('.label'); - te.attr('transform', `translate(${xPos + _width / 2}, ${yPos + 14})`); - te.attr('id', id + 'Text'); - - for (const className of subG.classes) { - clusterEl[0].classList.add(className); - } - } - } - - // Add label rects for non html labels - if (!conf.htmlLabels) { - const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label'); - for (const label of labels) { - // Get dimensions of label - const dim = label.getBBox(); - - const rect = doc.createElementNS('http://www.w3.org/2000/svg', 'rect'); - rect.setAttribute('rx', 0); - rect.setAttribute('ry', 0); - rect.setAttribute('width', dim.width); - rect.setAttribute('height', dim.height); - // rect.setAttribute('style', 'fill:#e8e8e8;'); - - label.insertBefore(rect, label.firstChild); - } - } - setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth); - - // If node has a link, wrap it in an anchor SVG object. - const keys = [...vert.keys()]; - keys.forEach(function (key) { - const vertex = vert.get(key); - - if (vertex.link) { - const node = root.select('#' + id + ' [id="' + diagObj.db.lookUpDomId(key) + '"]'); - if (node) { - const link = doc.createElementNS('http://www.w3.org/2000/svg', 'a'); - link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' ')); - link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link); - link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener'); - if (securityLevel === 'sandbox') { - link.setAttributeNS('http://www.w3.org/2000/svg', 'target', '_top'); - } else if (vertex.linkTarget) { - link.setAttributeNS('http://www.w3.org/2000/svg', 'target', vertex.linkTarget); - } - - const linkNode = node.insert(function () { - return link; - }, ':first-child'); - - const shape = node.select('.label-container'); - if (shape) { - linkNode.append(function () { - return shape.node(); - }); - } - - const label = node.select('.label'); - if (label) { - linkNode.append(function () { - return label.node(); - }); - } - } - } - }); -}; - -export default { - setConf, - addVertices, - addEdges, - getClasses, - draw, -}; diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer.spec.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer.spec.js deleted file mode 100644 index 79bf75453c..0000000000 --- a/packages/mermaid/src/diagrams/flowchart/flowRenderer.spec.js +++ /dev/null @@ -1,277 +0,0 @@ -/* eslint-disable @typescript-eslint/restrict-template-expressions */ -import { addVertices, addEdges } from './flowRenderer.js'; -import { setConfig } from '../../diagram-api/diagramAPI.js'; - -setConfig({ - flowchart: { - htmlLabels: false, - }, -}); - -describe('the flowchart renderer', function () { - describe('when adding vertices to a graph', function () { - [ - ['round', 'rect', 5], - ['square', 'rect'], - ['diamond', 'question'], - ['hexagon', 'hexagon'], - ['odd', 'rect_left_inv_arrow'], - ['lean_right', 'lean_right'], - ['lean_left', 'lean_left'], - ['trapezoid', 'trapezoid'], - ['inv_trapezoid', 'inv_trapezoid'], - ['odd_right', 'rect_left_inv_arrow'], - ['circle', 'circle'], - ['ellipse', 'ellipse'], - ['stadium', 'stadium'], - ['subroutine', 'subroutine'], - ['cylinder', 'cylinder'], - ['group', 'rect'], - ].forEach(function ([type, expectedShape, expectedRadios = 0]) { - it(`should add the correct shaped node to the graph for vertex type ${type}`, async function () { - const fakeDiag = { - db: { - lookUpDomId: () => { - return 'my-node-id'; - }, - }, - }; - const addedNodes = []; - const mockG = { - setNode: function (id, object) { - addedNodes.push([id, object]); - }, - }; - await addVertices( - { - v1: { - type, - id: 'my-node-id', - classes: [], - styles: [], - text: 'my vertex text', - }, - }, - mockG, - 'svg-id', - undefined, - undefined, - fakeDiag - ); - expect(addedNodes).toHaveLength(1); - expect(addedNodes[0][0]).toEqual('my-node-id'); - expect(addedNodes[0][1]).toHaveProperty('id', 'my-node-id'); - expect(addedNodes[0][1]).toHaveProperty('labelType', 'svg'); - expect(addedNodes[0][1]).toHaveProperty('shape', expectedShape); - expect(addedNodes[0][1]).toHaveProperty('rx', expectedRadios); - expect(addedNodes[0][1]).toHaveProperty('ry', expectedRadios); - }); - }); - - ['Multi
Line', 'Multi
Line', 'Multi
Line', 'MultiLine'].forEach( - function (labelText) { - it('should handle multiline texts with different line breaks', async function () { - const addedNodes = []; - const fakeDiag = { - db: { - lookUpDomId: () => { - return 'my-node-id'; - }, - }, - }; - const mockG = { - setNode: function (id, object) { - addedNodes.push([id, object]); - }, - }; - await addVertices( - { - v1: { - type: 'rect', - id: 'my-node-id', - classes: [], - styles: [], - text: 'Multi
Line', - }, - }, - mockG, - 'svg-id', - false, - document, - fakeDiag - ); - expect(addedNodes).toHaveLength(1); - expect(addedNodes[0][0]).toEqual('my-node-id'); - expect(addedNodes[0][1]).toHaveProperty('id', 'my-node-id'); - expect(addedNodes[0][1]).toHaveProperty('labelType', 'svg'); - expect(addedNodes[0][1].label).toBeDefined(); - expect(addedNodes[0][1].label).toBeDefined(); // node - expect(addedNodes[0][1].label.firstChild.innerHTML).toEqual('Multi'); // node, line 1 - expect(addedNodes[0][1].label.lastChild.innerHTML).toEqual('Line'); // node, line 2 - }); - } - ); - - [ - [['fill:#fff'], 'fill:#fff;', ''], - [['color:#ccc'], '', 'color:#ccc;'], - [['fill:#fff', 'color:#ccc'], 'fill:#fff;', 'color:#ccc;'], - [ - ['fill:#fff', 'color:#ccc', 'text-align:center'], - 'fill:#fff;', - 'color:#ccc;text-align:center;', - ], - ].forEach(function ([style, expectedStyle, expectedLabelStyle]) { - it(`should add the styles to style and/or labelStyle for style ${style}`, async function () { - const addedNodes = []; - const fakeDiag = { - db: { - lookUpDomId: () => { - return 'my-node-id'; - }, - }, - }; - const mockG = { - setNode: function (id, object) { - addedNodes.push([id, object]); - }, - }; - await addVertices( - { - v1: { - type: 'rect', - id: 'my-node-id', - classes: [], - styles: style, - text: 'my vertex text', - }, - }, - mockG, - 'svg-id', - undefined, - undefined, - fakeDiag - ); - expect(addedNodes).toHaveLength(1); - expect(addedNodes[0][0]).toEqual('my-node-id'); - expect(addedNodes[0][1]).toHaveProperty('id', 'my-node-id'); - expect(addedNodes[0][1]).toHaveProperty('labelType', 'svg'); - expect(addedNodes[0][1]).toHaveProperty('style', expectedStyle); - expect(addedNodes[0][1]).toHaveProperty('labelStyle', expectedLabelStyle); - }); - }); - - it(`should add default class to all nodes which do not have another class assigned`, async function () { - const addedNodes = []; - const mockG = { - setNode: function (id, object) { - addedNodes.push([id, object]); - }, - }; - const fakeDiag = { - db: { - lookUpDomId: () => { - return 'my-node-id'; - }, - }, - }; - await addVertices( - { - v1: { - type: 'rect', - id: 'my-node-id', - classes: [], - styles: [], - text: 'my vertex text', - }, - v2: { - type: 'rect', - id: 'myNode', - classes: ['myClass'], - styles: [], - text: 'my vertex text', - }, - }, - mockG, - 'svg-id', - undefined, - undefined, - fakeDiag - ); - expect(addedNodes).toHaveLength(2); - expect(addedNodes[0][0]).toEqual('my-node-id'); - expect(addedNodes[0][1]).toHaveProperty('class', 'default'); - expect(addedNodes[1][0]).toEqual('my-node-id'); - expect(addedNodes[1][1]).toHaveProperty('class', 'myClass'); - }); - }); - - describe('when adding edges to a graph', function () { - it('should handle multiline texts and set centered label position', async function () { - const addedEdges = []; - const fakeDiag = { - db: { - lookUpDomId: () => { - return 'my-node-id'; - }, - }, - }; - const mockG = { - setEdge: function (s, e, data, c) { - addedEdges.push(data); - }, - }; - await addEdges( - [ - { text: 'Multi
Line' }, - { text: 'Multi
Line' }, - { text: 'Multi
Line' }, - { text: 'MultiLine' }, - { style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi
Line' }, - { style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi
Line' }, - { style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi
Line' }, - { style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'MultiLine' }, - ], - mockG, - fakeDiag - ); - - addedEdges.forEach(function (edge) { - expect(edge).toHaveProperty('label', 'Multi\nLine'); - expect(edge).toHaveProperty('labelpos', 'c'); - }); - }); - - [ - [['stroke:DarkGray'], 'stroke:DarkGray;', ''], - [['color:red'], '', 'fill:red;'], - [['stroke:DarkGray', 'color:red'], 'stroke:DarkGray;', 'fill:red;'], - [ - ['stroke:DarkGray', 'color:red', 'stroke-width:2px'], - 'stroke:DarkGray;stroke-width:2px;', - 'fill:red;', - ], - ].forEach(function ([style, expectedStyle, expectedLabelStyle]) { - it(`should add the styles to style and/or labelStyle for style ${style}`, async function () { - const addedEdges = []; - const fakeDiag = { - db: { - lookUpDomId: () => { - return 'my-node-id'; - }, - }, - }; - const mockG = { - setEdge: function (s, e, data, c) { - addedEdges.push(data); - }, - }; - await addEdges([{ style: style, text: 'styling' }], mockG, fakeDiag); - - expect(addedEdges).toHaveLength(1); - expect(addedEdges[0]).toHaveProperty('style', expectedStyle); - expect(addedEdges[0]).toHaveProperty('labelStyle', expectedLabelStyle); - }); - }); - }); -}); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-node-data.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-node-data.spec.js new file mode 100644 index 0000000000..1669cfada0 --- /dev/null +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-node-data.spec.js @@ -0,0 +1,293 @@ +import flowDb from '../flowDb.js'; +import flow from './flow.jison'; +import { setConfig } from '../../../config.js'; + +setConfig({ + securityLevel: 'strict', +}); + +describe('when parsing directions', function () { + beforeEach(function () { + flow.parser.yy = flowDb; + flow.parser.yy.clear(); + flow.parser.yy.setGen('gen-2'); + }); + + it('should handle basic shape data statements', function () { + const res = flow.parser.parse(`flowchart TB + D@{ shape: rounded}`); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('D'); + }); + it('should handle basic shape data statements', function () { + const res = flow.parser.parse(`flowchart TB + D@{ shape: rounded }`); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('D'); + }); + + it('should handle basic shape data statements with &', function () { + const res = flow.parser.parse(`flowchart TB + D@{ shape: rounded } & E`); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(2); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('D'); + expect(data4Layout.nodes[1].label).toEqual('E'); + }); + it('should handle shape data statements with edges', function () { + const res = flow.parser.parse(`flowchart TB + D@{ shape: rounded } --> E`); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(2); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('D'); + expect(data4Layout.nodes[1].label).toEqual('E'); + }); + it('should handle basic shape data statements with amp and edges 1', function () { + const res = flow.parser.parse(`flowchart TB + D@{ shape: rounded } & E --> F`); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(3); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('D'); + expect(data4Layout.nodes[1].label).toEqual('E'); + }); + it('should handle basic shape data statements with amp and edges 2', function () { + const res = flow.parser.parse(`flowchart TB + D@{ shape: rounded } & E@{ shape: rounded } --> F`); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(3); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('D'); + expect(data4Layout.nodes[1].label).toEqual('E'); + }); + it('should handle basic shape data statements with amp and edges 3', function () { + const res = flow.parser.parse(`flowchart TB + D@{ shape: rounded } & E@{ shape: rounded } --> F & G@{ shape: rounded }`); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(4); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('D'); + expect(data4Layout.nodes[1].label).toEqual('E'); + }); + it('should handle basic shape data statements with amp and edges 4', function () { + const res = flow.parser.parse(`flowchart TB + D@{ shape: rounded } & E@{ shape: rounded } --> F@{ shape: rounded } & G@{ shape: rounded }`); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(4); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('D'); + expect(data4Layout.nodes[1].label).toEqual('E'); + }); + it('should handle basic shape data statements with amp and edges 5, trailing space', function () { + const res = flow.parser.parse(`flowchart TB + D@{ shape: rounded } & E@{ shape: rounded } --> F{ shape: rounded } & G{ shape: rounded } `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(4); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('D'); + expect(data4Layout.nodes[1].label).toEqual('E'); + }); + it('should no matter of there are no leading spaces', function () { + const res = flow.parser.parse(`flowchart TB + D@{shape: rounded}`); + + const data4Layout = flow.parser.yy.getData(); + + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('D'); + }); + + it('should no matter of there are many leading spaces', function () { + const res = flow.parser.parse(`flowchart TB + D@{ shape: rounded}`); + + const data4Layout = flow.parser.yy.getData(); + + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('D'); + }); + + it('should be forgiving with many spaces before teh end', function () { + const res = flow.parser.parse(`flowchart TB + D@{ shape: rounded }`); + + const data4Layout = flow.parser.yy.getData(); + + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('D'); + }); + it('should be possible to add multiple properties on the same line', function () { + const res = flow.parser.parse(`flowchart TB + D@{ shape: rounded , label: "DD"}`); + + const data4Layout = flow.parser.yy.getData(); + + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('rounded'); + expect(data4Layout.nodes[0].label).toEqual('DD'); + }); + it('should be possible to link to a node with more data', function () { + const res = flow.parser.parse(`flowchart TB + A --> D@{ + shape: circle + other: "clock" + } + + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(2); + expect(data4Layout.nodes[0].shape).toEqual('squareRect'); + expect(data4Layout.nodes[0].label).toEqual('A'); + expect(data4Layout.nodes[1].label).toEqual('D'); + expect(data4Layout.nodes[1].shape).toEqual('circle'); + + expect(data4Layout.edges.length).toBe(1); + }); + it('should not disturb adding multiple nodes after each other', function () { + const res = flow.parser.parse(`flowchart TB + A[hello] + B@{ + shape: circle + other: "clock" + } + C[Hello]@{ + shape: circle + other: "clock" + } + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(3); + expect(data4Layout.nodes[0].shape).toEqual('squareRect'); + expect(data4Layout.nodes[0].label).toEqual('hello'); + expect(data4Layout.nodes[1].shape).toEqual('circle'); + expect(data4Layout.nodes[1].label).toEqual('B'); + expect(data4Layout.nodes[2].shape).toEqual('circle'); + expect(data4Layout.nodes[2].label).toEqual('Hello'); + }); + it('should use handle bracket end (}) character inside the shape data', function () { + const res = flow.parser.parse(`flowchart TB + A@{ + label: "This is }" + other: "clock" + } + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('squareRect'); + expect(data4Layout.nodes[0].label).toEqual('This is }'); + }); + it('should error on non-existent shape', function () { + expect(() => { + flow.parser.parse(`flowchart TB + A@{ shape: this-shape-does-not-exist } + `); + }).toThrow('No such shape: this-shape-does-not-exist.'); + }); + it('should error on internal-only shape', function () { + expect(() => { + // this shape does exist, but it's only supposed to be for internal/backwards compatibility use + flow.parser.parse(`flowchart TB + A@{ shape: rect_left_inv_arrow } + `); + }).toThrow('No such shape: rect_left_inv_arrow. Shape names should be lowercase.'); + }); + it('Diamond shapes should work as usual', function () { + const res = flow.parser.parse(`flowchart TB + A{This is a label} +`); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('diamond'); + expect(data4Layout.nodes[0].label).toEqual('This is a label'); + }); + it('Multi line strings should be supported', function () { + const res = flow.parser.parse(`flowchart TB + A@{ + label: | + This is a + multiline string + other: "clock" + } + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('squareRect'); + expect(data4Layout.nodes[0].label).toEqual('This is a\nmultiline string\n'); + }); + it('Multi line strings should be supported', function () { + const res = flow.parser.parse(`flowchart TB + A@{ + label: "This is a + multiline string" + other: "clock" + } + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('squareRect'); + expect(data4Layout.nodes[0].label).toEqual('This is a
multiline string'); + }); + it(' should be possible to use } in strings', function () { + const res = flow.parser.parse(`flowchart TB + A@{ + label: "This is a string with }" + other: "clock" + } + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('squareRect'); + expect(data4Layout.nodes[0].label).toEqual('This is a string with }'); + }); + it(' should be possible to use @ in strings', function () { + const res = flow.parser.parse(`flowchart TB + A@{ + label: "This is a string with @" + other: "clock" + } + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('squareRect'); + expect(data4Layout.nodes[0].label).toEqual('This is a string with @'); + }); + it(' should be possible to use @ in strings', function () { + const res = flow.parser.parse(`flowchart TB + A@{ + label: "This is a string with}" + other: "clock" + } + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('squareRect'); + expect(data4Layout.nodes[0].label).toEqual('This is a string with}'); + }); +}); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 54949bfae8..b3df82fa5a 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -23,6 +23,9 @@ %x href %x callbackname %x callbackargs +%x shapeData +%x shapeDataStr +%x shapeDataEndBracket %% accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; } @@ -34,6 +37,32 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multilin [^\}]* return "acc_descr_multiline_value"; // .*[^\n]* { return "acc_descr_line"} + +\@\{ { + // console.log('=> shapeData', yytext); + this.pushState("shapeData"); yytext=""; return 'SHAPE_DATA' } +["] { + // console.log('=> shapeDataStr', yytext); + this.pushState("shapeDataStr"); + return 'SHAPE_DATA'; + } +["] { + // console.log('shapeData <==', yytext); + this.popState(); return 'SHAPE_DATA'} +[^\"]+ { + // console.log('shapeData', yytext); + const re = /\n\s*/g; + yytext = yytext.replace(re,"
"); + return 'SHAPE_DATA'} +[^}^"]+ { + // console.log('shapeData', yytext); + return 'SHAPE_DATA'; + } +"}" { + // console.log('<== root', yytext) + this.popState(); + } + /* ---interactivity command--- 'call' adds a callback to the specified node. 'call' can only be specified when @@ -49,10 +78,11 @@ Function arguments are optional: 'call ()' simply executes 'callba \) this.popState(); [^)]* return 'CALLBACKARGS'; + [^`"]+ { return "MD_STR";} [`]["] { this.popState();} <*>["][`] { this.begin("md_string");} -[^"]+ return "STR"; +[^"]+ { return "STR"; } ["] this.popState(); <*>["] this.pushState("string"); "style" return 'STYLE'; @@ -62,6 +92,8 @@ Function arguments are optional: 'call ()' simply executes 'callba "classDef" return 'CLASSDEF'; "class" return 'CLASS'; + + /* ---interactivity command--- 'href' adds a link to the specified node. 'href' can only be specified when the @@ -356,23 +388,38 @@ statement separator: NEWLINE | SEMI | EOF ; +shapeData: + shapeData SHAPE_DATA + { $$ = $1 + $2; } + | SHAPE_DATA + { $$ = $1; } + ; -vertexStatement: vertexStatement link node - { /* console.warn('vs',$vertexStatement.stmt,$node); */ yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } } +vertexStatement: vertexStatement link node shapeData + { /* console.warn('vs shapeData',$vertexStatement.stmt,$node, $shapeData);*/ yy.addVertex($node[0],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } } + | vertexStatement link node + { /*console.warn('vs',$vertexStatement.stmt,$node);*/ yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } } | vertexStatement link node spaceList { /* console.warn('vs',$vertexStatement.stmt,$node); */ yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } } - |node spaceList {/*console.warn('noda', $node);*/ $$ = {stmt: $node, nodes:$node }} - |node { /*console.warn('noda', $node);*/ $$ = {stmt: $node, nodes:$node }} + |node spaceList { /*console.warn('vertexStatement: node spaceList', $node);*/ $$ = {stmt: $node, nodes:$node }} + |node shapeData { + /*console.warn('vertexStatement: node shapeData', $node[0], $shapeData);*/ + yy.addVertex($node[0],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); + $$ = {stmt: $node, nodes:$node, shapeData: $shapeData} + } + |node { /* console.warn('vertexStatement: single node', $node); */ $$ = {stmt: $node, nodes:$node }} ; node: styledVertex - { /* console.warn('nod', $styledVertex); */ $$ = [$styledVertex];} + { /*console.warn('nod', $styledVertex);*/ $$ = [$styledVertex];} + | node shapeData spaceList AMP spaceList styledVertex + { yy.addVertex($node[0],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); $$ = $node.concat($styledVertex); /*console.warn('pip2', $node[0], $styledVertex, $$);*/ } | node spaceList AMP spaceList styledVertex - { $$ = $node.concat($styledVertex); /* console.warn('pip', $node[0], $styledVertex, $$); */ } + { $$ = $node.concat($styledVertex); /*console.warn('pip', $node[0], $styledVertex, $$);*/ } ; styledVertex: vertex - { /* console.warn('nod', $vertex); */ $$ = $vertex;} + { /* console.warn('nodc', $vertex);*/ $$ = $vertex;} | vertex STYLE_SEPARATOR idString {$$ = $vertex;yy.setClass($vertex,$idString)} ; diff --git a/packages/mermaid/src/diagrams/flowchart/styles.ts b/packages/mermaid/src/diagrams/flowchart/styles.ts index b30c0b9bc7..ade9613fb4 100644 --- a/packages/mermaid/src/diagrams/flowchart/styles.ts +++ b/packages/mermaid/src/diagrams/flowchart/styles.ts @@ -38,11 +38,14 @@ const getStyles = (options: FlowChartStyleOptions) => .cluster-label text { fill: ${options.titleColor}; } - .cluster-label span,p { + .cluster-label span { color: ${options.titleColor}; } + .cluster-label span p { + background-color: transparent; + } - .label text,span,p { + .label text,span { fill: ${options.nodeTextColor || options.textColor}; color: ${options.nodeTextColor || options.textColor}; } @@ -56,7 +59,7 @@ const getStyles = (options: FlowChartStyleOptions) => stroke: ${options.nodeBorder}; stroke-width: 1px; } - .flowchart-label text { + .rough-node .label text , .node .label text, .image-shape .label, .icon-shape .label { text-anchor: middle; } // .flowchart-label .text-outer-tspan { @@ -72,13 +75,20 @@ const getStyles = (options: FlowChartStyleOptions) => stroke-width: 1px; } - .node .label { + .rough-node .label,.node .label, .image-shape .label, .icon-shape .label { text-align: center; } .node.clickable { cursor: pointer; } + + .root .anchor path { + fill: ${options.lineColor} !important; + stroke-width: 0; + stroke: ${options.lineColor}; + } + .arrowheadPath { fill: ${options.arrowheadColor}; } @@ -95,6 +105,9 @@ const getStyles = (options: FlowChartStyleOptions) => .edgeLabel { background-color: ${options.edgeLabelBackground}; + p { + background-color: ${options.edgeLabelBackground}; + } rect { opacity: 0.5; background-color: ${options.edgeLabelBackground}; @@ -106,7 +119,7 @@ const getStyles = (options: FlowChartStyleOptions) => /* For html labels only */ .labelBkg { background-color: ${fade(options.edgeLabelBackground, 0.5)}; - // background-color: + // background-color: } .cluster rect { @@ -119,7 +132,7 @@ const getStyles = (options: FlowChartStyleOptions) => fill: ${options.titleColor}; } - .cluster span,p { + .cluster span { color: ${options.titleColor}; } /* .cluster div { @@ -145,6 +158,25 @@ const getStyles = (options: FlowChartStyleOptions) => font-size: 18px; fill: ${options.textColor}; } + + rect.text { + fill: none; + stroke-width: 0; + } + + .icon-shape, .image-shape { + background-color: ${options.edgeLabelBackground}; + p { + background-color: ${options.edgeLabelBackground}; + padding: 2px; + } + rect { + opacity: 0.5; + background-color: ${options.edgeLabelBackground}; + fill: ${options.edgeLabelBackground}; + } + text-align: center; + } `; export default getStyles; diff --git a/packages/mermaid/src/diagrams/flowchart/types.ts b/packages/mermaid/src/diagrams/flowchart/types.ts index 954759f393..b2c5cf6202 100644 --- a/packages/mermaid/src/diagrams/flowchart/types.ts +++ b/packages/mermaid/src/diagrams/flowchart/types.ts @@ -1,3 +1,28 @@ +import type { ShapeID } from '../../rendering-util/rendering-elements/shapes.js'; + +/** + * Valid `type` args to `yy.addVertex` taken from + * `packages/mermaid/src/diagrams/flowchart/parser/flow.jison` + */ +export type FlowVertexTypeParam = + | undefined + | 'square' + | 'doublecircle' + | 'circle' + | 'ellipse' + | 'stadium' + | 'subroutine' + | 'rect' + | 'cylinder' + | 'round' + | 'diamond' + | 'hexagon' + | 'odd' + | 'trapezoid' + | 'inv_trapezoid' + | 'lean_right' + | 'lean_left'; + export interface FlowVertex { classes: string[]; dir?: string; @@ -10,7 +35,16 @@ export interface FlowVertex { props?: any; styles: string[]; text?: string; - type?: string; + type?: ShapeID | FlowVertexTypeParam; + icon?: string; + form?: string; + pos?: 't' | 'b'; + img?: string; + assetWidth?: number; + assetHeight?: number; + defaultWidth?: number; + imageAspectRatio?: number; + constraint?: 'on' | 'off'; } export interface FlowText { @@ -23,7 +57,7 @@ export interface FlowEdge { end: string; interpolate?: string; type?: string; - stroke?: string; + stroke?: 'normal' | 'thick' | 'invisible' | 'dotted'; style?: string[]; length?: number; text: string; diff --git a/packages/mermaid/src/diagrams/gantt/parser/gantt.spec.js b/packages/mermaid/src/diagrams/gantt/parser/gantt.spec.js index 281e9b6bd3..33f91a8825 100644 --- a/packages/mermaid/src/diagrams/gantt/parser/gantt.spec.js +++ b/packages/mermaid/src/diagrams/gantt/parser/gantt.spec.js @@ -1,7 +1,6 @@ import { parser } from './gantt.jison'; import ganttDb from '../ganttDb.js'; -import { convert } from '../../../tests/util.js'; -import { vi } from 'vitest'; +import { vi, it } from 'vitest'; const spyOn = vi.spyOn; const parserFnConstructor = (str) => { return () => { @@ -150,14 +149,14 @@ describe('when parsing a gantt diagram it', function () { expect(tasks[3].id).toEqual('d'); expect(tasks[3].task).toEqual('task D'); }); - it.each(convert` + it.each` tags | milestone | done | crit | active ${'milestone'} | ${true} | ${false} | ${false} | ${false} ${'done'} | ${false} | ${true} | ${false} | ${false} ${'crit'} | ${false} | ${false} | ${true} | ${false} ${'active'} | ${false} | ${false} | ${false} | ${true} ${'crit,milestone,done'} | ${true} | ${true} | ${true} | ${false} - `)('should handle a task with tags $tags', ({ tags, milestone, done, crit, active }) => { + `('should handle a task with tags $tags', ({ tags, milestone, done, crit, active }) => { const str = 'gantt\n' + 'dateFormat YYYY-MM-DD\n' + 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 0000000000..9b3236f907 --- /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 cebc4fc3ef..0000000000 --- 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 0000000000..44597e9d78 --- /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 2a9efdb59c..d6e8a06134 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 d498577fe0..0000000000 --- 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 0000000000..c56bc6f449 --- /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 1fb64a5c43..0000000000 --- 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 b8b13e0899..0000000000 --- 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 0000000000..39a64a623b --- /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 0000000000..32b951bcc4 --- /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 fa2c70586d..0000000000 --- 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/diagrams/info/infoRenderer.ts b/packages/mermaid/src/diagrams/info/infoRenderer.ts index 25ae72fce0..a8314eb728 100644 --- a/packages/mermaid/src/diagrams/info/infoRenderer.ts +++ b/packages/mermaid/src/diagrams/info/infoRenderer.ts @@ -1,7 +1,7 @@ +import type { DrawDefinition, SVG, SVGGroup } from '../../diagram-api/types.js'; import { log } from '../../logger.js'; -import { configureSvgSize } from '../../setupGraphViewbox.js'; -import type { DrawDefinition, Group, SVG } from '../../diagram-api/types.js'; import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; +import { configureSvgSize } from '../../setupGraphViewbox.js'; /** * Draws a an info picture in the tag with id: id based on the graph definition in text. @@ -16,7 +16,7 @@ const draw: DrawDefinition = (text, id, version) => { const svg: SVG = selectSvgElement(id); configureSvgSize(svg, 100, 400, true); - const group: Group = svg.append('g'); + const group: SVGGroup = svg.append('g'); group .append('text') .attr('x', 100) diff --git a/packages/mermaid/src/diagrams/kanban/detector.ts b/packages/mermaid/src/diagrams/kanban/detector.ts new file mode 100644 index 0000000000..3c07ca4dff --- /dev/null +++ b/packages/mermaid/src/diagrams/kanban/detector.ts @@ -0,0 +1,23 @@ +import type { + DiagramDetector, + DiagramLoader, + ExternalDiagramDefinition, +} from '../../diagram-api/types.js'; +const id = 'kanban'; + +const detector: DiagramDetector = (txt) => { + return /^\s*kanban/.test(txt); +}; + +const loader: DiagramLoader = async () => { + const { diagram } = await import('./kanban-definition.js'); + return { id, diagram }; +}; + +const plugin: ExternalDiagramDefinition = { + id, + detector, + loader, +}; + +export default plugin; diff --git a/packages/mermaid/src/diagrams/kanban/kanban-definition.ts b/packages/mermaid/src/diagrams/kanban/kanban-definition.ts new file mode 100644 index 0000000000..fbaed30c0f --- /dev/null +++ b/packages/mermaid/src/diagrams/kanban/kanban-definition.ts @@ -0,0 +1,13 @@ +// @ts-ignore: JISON doesn't support types +import parser from './parser/kanban.jison'; +import db from './kanbanDb.js'; +import renderer from './kanbanRenderer.js'; +import styles from './styles.js'; +import type { DiagramDefinition } from '../../diagram-api/types.js'; + +export const diagram: DiagramDefinition = { + db, + renderer, + parser, + styles, +}; diff --git a/packages/mermaid/src/diagrams/kanban/kanban.spec.ts b/packages/mermaid/src/diagrams/kanban/kanban.spec.ts new file mode 100644 index 0000000000..58fdab0e61 --- /dev/null +++ b/packages/mermaid/src/diagrams/kanban/kanban.spec.ts @@ -0,0 +1,494 @@ +// @ts-expect-error No types available for JISON +import { parser as kanban } from './parser/kanban.jison'; +import kanbanDB from './kanbanDb.js'; +import type { KanbanNode } from '../../rendering-util/types.js'; +// Todo fix utils functions for tests +import { setLogLevel } from '../../diagram-api/diagramAPI.js'; + +describe('when parsing a kanban ', function () { + beforeEach(function () { + kanban.yy = kanbanDB; + kanban.yy.clear(); + setLogLevel('trace'); + }); + describe('hiearchy', function () { + it('KNBN-1 should handle a simple root definition abc122', function () { + const str = `kanban + root`; + + kanban.parse(str); + const sections = kanban.yy.getSections(); + expect(sections.length).toEqual(1); + expect(sections[0].label).toEqual('root'); + }); + it('KNBN-2 should handle a hierachial kanban definition', function () { + const str = `kanban + root + child1 + child2 + `; + + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections.length).toEqual(1); + expect(sections[0].label).toEqual('root'); + expect(children.length).toEqual(2); + expect(children[0].label).toEqual('child1'); + expect(children[1].label).toEqual('child2'); + }); + + /** CATCH case when a lower level comes later, should throw + * a + * b + * c + */ + + it('3 should handle a simple root definition with a shape and without an id abc123', function () { + const str = `kanban + (root)`; + + kanban.parse(str); + const sections = kanban.yy.getSections(); + expect(sections[0].label).toEqual('root'); + }); + + it('KNBN-4 should not dsitinguis between deeper hierachial levels in thr kanban definition', function () { + const str = `kanban + root + child1 + leaf1 + child2`; + + // less picky is better + // expect(() => kanban.parse(str)).toThrow( + // 'There can be only one root. No parent could be found for ("fakeRoot")' + // ); + + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections.length).toBe(1); + expect(children.length).toBe(3); + }); + it('5 Multiple sections are ok', function () { + const str = `kanban + section1 + section2`; + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections.length).toBe(2); + expect(sections[0].label).toBe('section1'); + expect(sections[1].label).toBe('section2'); + + // expect(() => kanban.parse(str)).toThrow( + // 'There can be only one root. No parent could be found for ("fakeRoot")' + // ); + }); + it('KNBN-6 real root in wrong place', function () { + const str = `kanban + root + fakeRoot + realRootWrongPlace`; + expect(() => kanban.parse(str)).toThrow( + 'Items without section detected, found section ("fakeRoot")' + ); + }); + }); + describe('nodes', function () { + it('KNBN-7 should handle an id and type for a node definition', function () { + const str = `kanban + root[The root] + `; + + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(sections[0].label).toEqual('The root'); + }); + it('KNBN-8 should handle an id and type for a node definition', function () { + const str = `kanban + root + theId(child1)`; + + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].label).toEqual('root'); + expect(children.length).toEqual(1); + const child = children[0]; + expect(child.label).toEqual('child1'); + expect(child.id).toEqual('theId'); + }); + it('KNBN-9 should handle an id and type for a node definition', function () { + const str = `kanban +root + theId(child1)`; + + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].label).toEqual('root'); + expect(children.length).toEqual(1); + const child = children[0]; + expect(child.label).toEqual('child1'); + expect(child.id).toEqual('theId'); + }); + }); + describe('decorations', function () { + it('KNBN-13 should be possible to set an icon for the node', function () { + const str = `kanban + root[The root] + ::icon(bomb) + `; + // ::class1 class2 + + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(sections[0].label).toEqual('The root'); + + expect(sections[0].icon).toEqual('bomb'); + }); + it('KNBN-14 should be possible to set classes for the node', function () { + const str = `kanban + root[The root] + :::m-4 p-8 + `; + // ::class1 class2 + + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(sections[0].label).toEqual('The root'); + expect(sections[0].cssClasses).toEqual('m-4 p-8'); + }); + it('KNBN-15 should be possible to set both classes and icon for the node', function () { + const str = `kanban + root[The root] + :::m-4 p-8 + ::icon(bomb) + `; + // ::class1 class2 + + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(sections[0].label).toEqual('The root'); + expect(sections[0].cssClasses).toEqual('m-4 p-8'); + expect(sections[0].icon).toEqual('bomb'); + }); + it('KNBN-16 should be possible to set both classes and icon for the node', function () { + const str = `kanban + root[The root] + ::icon(bomb) + :::m-4 p-8 + `; + // ::class1 class2 + + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(sections[0].label).toEqual('The root'); + // expect(sections[0].type).toEqual('rect'); + expect(sections[0].cssClasses).toEqual('m-4 p-8'); + expect(sections[0].icon).toEqual('bomb'); + }); + }); + describe('descriptions', function () { + it('KNBN-17 should be possible to use node syntax in the descriptions', function () { + const str = `kanban + root["String containing []"] +`; + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(sections[0].label).toEqual('String containing []'); + }); + it('KNBN-18 should be possible to use node syntax in the descriptions in children', function () { + const str = `kanban + root["String containing []"] + child1["String containing ()"] +`; + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(sections[0].label).toEqual('String containing []'); + expect(children.length).toEqual(1); + expect(children[0].label).toEqual('String containing ()'); + }); + it('KNBN-19 should be possible to have a child after a class assignment', function () { + const str = `kanban + root(Root) + Child(Child) + :::hot + a(a) + b[New Stuff]`; + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(sections[0].label).toEqual('Root'); + expect(children.length).toEqual(3); + + const item1 = children[0]; + const item2 = children[1]; + const item3 = children[2]; + expect(item1.id).toEqual('Child'); + expect(item2.id).toEqual('a'); + expect(item3.id).toEqual('b'); + }); + }); + it('KNBN-20 should be possible to have meaningless empty rows in a kanban abc124', function () { + const str = `kanban + root(Root) + Child(Child) + a(a) + + b[New Stuff]`; + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(sections[0].label).toEqual('Root'); + expect(children.length).toEqual(3); + + const item1 = children[0]; + const item2 = children[1]; + const item3 = children[2]; + expect(item1.id).toEqual('Child'); + expect(item2.id).toEqual('a'); + expect(item3.id).toEqual('b'); + }); + it('KNBN-21 should be possible to have comments in a kanban', function () { + const str = `kanban + root(Root) + Child(Child) + a(a) + + %% This is a comment + b[New Stuff]`; + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(sections[0].label).toEqual('Root'); + + const child = children[0]; + expect(child.id).toEqual('Child'); + expect(children[1].id).toEqual('a'); + expect(children[2].id).toEqual('b'); + expect(children.length).toEqual(3); + }); + + it('KNBN-22 should be possible to have comments at the end of a line', function () { + const str = `kanban + root(Root) + Child(Child) + a(a) %% This is a comment + b[New Stuff]`; + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(sections[0].label).toEqual('Root'); + expect(children.length).toEqual(3); + + const child1 = children[0]; + expect(child1.id).toEqual('Child'); + const child2 = children[1]; + expect(child2.id).toEqual('a'); + const child3 = children[2]; + expect(child3.id).toEqual('b'); + }); + it('KNBN-23 Rows with only spaces should not interfere', function () { + const str = 'kanban\nroot\n A\n \n\n B'; + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(children.length).toEqual(2); + + const child = children[0]; + expect(child.id).toEqual('A'); + const child2 = children[1]; + expect(child2.id).toEqual('B'); + }); + it('KNBN-24 Handle rows above the kanban declarations', function () { + const str = '\n \nkanban\nroot\n A\n \n\n B'; + kanban.parse(str); + + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(children.length).toEqual(2); + + const child = children[0]; + expect(child.id).toEqual('A'); + const child2 = children[1]; + expect(child2.id).toEqual('B'); + }); + it('KNBN-25 Handle rows above the kanban declarations, no space', function () { + const str = '\n\n\nkanban\nroot\n A\n \n\n B'; + kanban.parse(str); + const data = kanban.yy.getData(); + const sections = kanban.yy.getSections(); + const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id); + + expect(sections[0].id).toEqual('root'); + expect(children.length).toEqual(2); + + const child = children[0]; + expect(child.id).toEqual('A'); + const child2 = children[1]; + expect(child2.id).toEqual('B'); + }); +}); +describe('item data data', function () { + beforeEach(function () { + kanban.yy = kanbanDB; + kanban.yy.clear(); + setLogLevel('trace'); + }); + it('KNBN-30 should be possible to set the priority', function () { + let str = `kanban + root + `; + str = `kanban + root@{ priority: high } + `; + kanban.parse(str); + const sections = kanban.yy.getSections(); + expect(sections[0].id).toEqual('root'); + expect(sections[0].priority).toEqual('high'); + }); + it('KNBN-31 should be possible to set the assignment', function () { + const str = `kanban + root@{ assigned: knsv } + `; + kanban.parse(str); + const sections = kanban.yy.getSections(); + expect(sections[0].id).toEqual('root'); + expect(sections[0].assigned).toEqual('knsv'); + }); + it('KNBN-32 should be possible to set the icon', function () { + const str = `kanban + root@{ icon: star } + `; + kanban.parse(str); + const sections = kanban.yy.getSections(); + expect(sections[0].id).toEqual('root'); + expect(sections[0].icon).toEqual('star'); + }); + it('KNBN-33 should be possible to set the icon', function () { + const str = `kanban + root@{ icon: star } + `; + kanban.parse(str); + const sections = kanban.yy.getSections(); + expect(sections[0].id).toEqual('root'); + expect(sections[0].icon).toEqual('star'); + }); + it('KNBN-34 should be possible to set the metadata using multiple lines', function () { + const str = `kanban + root@{ + icon: star + assigned: knsv + } + `; + kanban.parse(str); + const sections = kanban.yy.getSections(); + expect(sections[0].id).toEqual('root'); + expect(sections[0].icon).toEqual('star'); + expect(sections[0].assigned).toEqual('knsv'); + }); + it('KNBN-35 should be possible to set the metadata using one line', function () { + const str = `kanban + root@{ icon: star, assigned: knsv } + `; + kanban.parse(str); + const sections = kanban.yy.getSections(); + expect(sections[0].id).toEqual('root'); + expect(sections[0].icon).toEqual('star'); + expect(sections[0].assigned).toEqual('knsv'); + }); + it('KNBN-36 should be possible to set the label using the new syntax', function () { + const str = `kanban + root@{ icon: star, label: 'fix things' } + `; + kanban.parse(str); + const sections = kanban.yy.getSections(); + expect(sections[0].label).toEqual('fix things'); + }); + it('KNBN-37 should be possible to set the external id', function () { + const str = `kanban + root@{ ticket: MC-1234 } + `; + kanban.parse(str); + const sections = kanban.yy.getSections(); + const data = kanban.yy.getData(); + expect(sections[0].id).toEqual('root'); + expect(sections[0].ticket).toEqual('MC-1234'); + }); +}); diff --git a/packages/mermaid/src/diagrams/kanban/kanbanDb.ts b/packages/mermaid/src/diagrams/kanban/kanbanDb.ts new file mode 100644 index 0000000000..a9bcbb7b81 --- /dev/null +++ b/packages/mermaid/src/diagrams/kanban/kanbanDb.ts @@ -0,0 +1,253 @@ +import { getConfig } from '../../diagram-api/diagramAPI.js'; +import type { D3Element } from '../../types.js'; +import { sanitizeText } from '../../diagrams/common/common.js'; +import { log } from '../../logger.js'; +import type { Edge, KanbanNode } from '../../rendering-util/types.js'; +import defaultConfig from '../../defaultConfig.js'; +import type { NodeMetaData } from '../../types.js'; +import * as yaml from 'js-yaml'; + +let nodes: KanbanNode[] = []; +let sections: KanbanNode[] = []; +let cnt = 0; +let elements: Record = {}; + +const clear = () => { + nodes = []; + sections = []; + cnt = 0; + elements = {}; +}; +/* + * if your level is the section level return null - then you do not belong to a level + * otherwise return the current section + */ +const getSection = (level: number) => { + if (nodes.length === 0) { + // console.log('No nodes'); + return null; + } + const sectionLevel = nodes[0].level; + let lastSection = null; + for (let i = nodes.length - 1; i >= 0; i--) { + if (nodes[i].level === sectionLevel && !lastSection) { + lastSection = nodes[i]; + // console.log('lastSection found', lastSection); + } + // console.log('HERE', nodes[i].id, level, nodes[i].level, sectionLevel); + if (nodes[i].level < sectionLevel) { + throw new Error('Items without section detected, found section ("' + nodes[i].label + '")'); + } + } + if (level === lastSection?.level) { + return null; + } + + // No found + return lastSection; +}; + +const getSections = function () { + return sections; +}; + +const getData = function () { + const edges = [] as Edge[]; + const _nodes: KanbanNode[] = []; + + const sections = getSections(); + const conf = getConfig(); + + for (const section of sections) { + const node = { + id: section.id, + label: sanitizeText(section.label ?? '', conf), + isGroup: true, + ticket: section.ticket, + shape: 'kanbanSection', + level: section.level, + look: conf.look, + } satisfies KanbanNode; + _nodes.push(node); + const children = nodes.filter((n) => n.parentId === section.id); + + for (const item of children) { + const childNode = { + id: item.id, + parentId: section.id, + label: sanitizeText(item.label ?? '', conf), + isGroup: false, + ticket: item?.ticket, + priority: item?.priority, + assigned: item?.assigned, + icon: item?.icon, + shape: 'kanbanItem', + level: item.level, + rx: 5, + ry: 5, + cssStyles: ['text-align: left'], + } satisfies KanbanNode; + _nodes.push(childNode); + } + } + + return { nodes: _nodes, edges, other: {}, config: getConfig() }; +}; + +const addNode = (level: number, id: string, descr: string, type: number, shapeData: string) => { + const conf = getConfig(); + let padding: number = conf.mindmap?.padding ?? defaultConfig.mindmap.padding; + switch (type) { + case nodeType.ROUNDED_RECT: + case nodeType.RECT: + case nodeType.HEXAGON: + padding *= 2; + } + + const node: KanbanNode = { + id: sanitizeText(id, conf) || 'kbn' + cnt++, + level, + label: sanitizeText(descr, conf), + width: conf.mindmap?.maxNodeWidth ?? defaultConfig.mindmap.maxNodeWidth, + padding, + isGroup: false, + } satisfies KanbanNode; + + if (shapeData !== undefined) { + let yamlData; + // detect if shapeData contains a newline character + // console.log('shapeData', shapeData); + if (!shapeData.includes('\n')) { + // console.log('yamlData shapeData has no new lines', shapeData); + yamlData = '{\n' + shapeData + '\n}'; + } else { + // console.log('yamlData shapeData has new lines', shapeData); + yamlData = shapeData + '\n'; + } + const doc = yaml.load(yamlData, { schema: yaml.JSON_SCHEMA }) as NodeMetaData; + // console.log('yamlData', doc); + if (doc.shape && (doc.shape !== doc.shape.toLowerCase() || doc.shape.includes('_'))) { + throw new Error(`No such shape: ${doc.shape}. Shape names should be lowercase.`); + } + + // if shape is defined in the yaml data, use it if it is a valid shape kanbanItem + if (doc?.shape && doc.shape === 'kanbanItem') { + node.shape = doc?.shape; + } + if (doc?.label) { + node.label = doc?.label; + } + if (doc?.icon) { + node.icon = doc?.icon.toString(); + } + if (doc?.assigned) { + node.assigned = doc?.assigned.toString(); + } + if (doc?.ticket) { + node.ticket = doc?.ticket.toString(); + } + + if (doc?.priority) { + node.priority = doc?.priority; + } + } + + const section = getSection(level); + if (section) { + // @ts-ignore false positive for section.id + node.parentId = section.id || 'kbn' + cnt++; + } else { + sections.push(node); + } + nodes.push(node); +}; + +const nodeType = { + DEFAULT: 0, + NO_BORDER: 0, + ROUNDED_RECT: 1, + RECT: 2, + CIRCLE: 3, + CLOUD: 4, + BANG: 5, + HEXAGON: 6, +}; + +const getType = (startStr: string, endStr: string): number => { + log.debug('In get type', startStr, endStr); + switch (startStr) { + case '[': + return nodeType.RECT; + case '(': + return endStr === ')' ? nodeType.ROUNDED_RECT : nodeType.CLOUD; + case '((': + return nodeType.CIRCLE; + case ')': + return nodeType.CLOUD; + case '))': + return nodeType.BANG; + case '{{': + return nodeType.HEXAGON; + default: + return nodeType.DEFAULT; + } +}; + +const setElementForId = (id: number, element: D3Element) => { + elements[id] = element; +}; + +const decorateNode = (decoration?: { class?: string; icon?: string }) => { + if (!decoration) { + return; + } + const config = getConfig(); + const node = nodes[nodes.length - 1]; + if (decoration.icon) { + node.icon = sanitizeText(decoration.icon, config); + } + if (decoration.class) { + node.cssClasses = sanitizeText(decoration.class, config); + } +}; + +const type2Str = (type: number) => { + switch (type) { + case nodeType.DEFAULT: + return 'no-border'; + case nodeType.RECT: + return 'rect'; + case nodeType.ROUNDED_RECT: + return 'rounded-rect'; + case nodeType.CIRCLE: + return 'circle'; + case nodeType.CLOUD: + return 'cloud'; + case nodeType.BANG: + return 'bang'; + case nodeType.HEXAGON: + return 'hexgon'; // cspell: disable-line + default: + return 'no-border'; + } +}; + +// Expose logger to grammar +const getLogger = () => log; +const getElementById = (id: number) => elements[id]; + +const db = { + clear, + addNode, + getSections, + getData, + nodeType, + getType, + setElementForId, + decorateNode, + type2Str, + getLogger, + getElementById, +} as const; + +export default db; diff --git a/packages/mermaid/src/diagrams/kanban/kanbanRenderer.ts b/packages/mermaid/src/diagrams/kanban/kanbanRenderer.ts new file mode 100644 index 0000000000..dc57808a9c --- /dev/null +++ b/packages/mermaid/src/diagrams/kanban/kanbanRenderer.ts @@ -0,0 +1,96 @@ +import { getConfig } from '../../diagram-api/diagramAPI.js'; +import type { DrawDefinition } from '../../diagram-api/types.js'; +import { log } from '../../logger.js'; +import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; +import { setupGraphViewbox } from '../../setupGraphViewbox.js'; +import type { KanbanDB } from './kanbanTypes.js'; +import defaultConfig from '../../defaultConfig.js'; +import { insertCluster } from '../../rendering-util/rendering-elements/clusters.js'; +import { insertNode, positionNode } from '../../rendering-util/rendering-elements/nodes.js'; +import type { ClusterNode } from '../../rendering-util/types.js'; + +export const draw: DrawDefinition = async (text, id, _version, diagObj) => { + log.debug('Rendering kanban diagram\n' + text); + + const db = diagObj.db as KanbanDB; + const data4Layout = db.getData(); + + const conf = getConfig(); + conf.htmlLabels = false; + + const svg = selectSvgElement(id); + + // Draw the graph and start with drawing the nodes without proper position + // this gives us the size of the nodes and we can set the positions later + + const sectionsElem = svg.append('g'); + sectionsElem.attr('class', 'sections'); + const nodesElem = svg.append('g'); + nodesElem.attr('class', 'items'); + const sections = data4Layout.nodes.filter( + // TODO: TypeScript 5.5 will infer this predicate automatically + (node): node is typeof node & ClusterNode => node.isGroup + ); + let cnt = 0; + // TODO set padding + const padding = 10; + + const sectionObjects = []; + let maxLabelHeight = 25; + for (const section of sections) { + const WIDTH = conf?.kanban?.sectionWidth || 200; + // const top = (-WIDTH * 3) / 2 + 25; + // let y = top; + cnt = cnt + 1; + section.x = WIDTH * cnt + ((cnt - 1) * padding) / 2; + section.width = WIDTH; + section.y = 0; + section.height = WIDTH * 3; + section.rx = 5; + section.ry = 5; + + // Todo, use theme variable THEME_COLOR_LIMIT instead of 10 + section.cssClasses = section.cssClasses + ' section-' + cnt; + const sectionObj = await insertCluster(sectionsElem, section); + maxLabelHeight = Math.max(maxLabelHeight, sectionObj?.labelBBox?.height); + sectionObjects.push(sectionObj); + } + let i = 0; + for (const section of sections) { + const sectionObj = sectionObjects[i]; + i = i + 1; + const WIDTH = conf?.kanban?.sectionWidth || 200; + const top = (-WIDTH * 3) / 2 + maxLabelHeight; + let y = top; + const sectionItems = data4Layout.nodes.filter((node) => node.parentId === section.id); + for (const item of sectionItems) { + if (item.isGroup) { + // Kanban diagrams should not have groups within groups + // this should never happen + throw new Error('Groups within groups are not allowed in Kanban diagrams'); + } + item.x = section.x; + item.width = WIDTH - 1.5 * padding; + const nodeEl = await insertNode(nodesElem, item, { config: conf }); + const bbox = nodeEl.node()!.getBBox(); + item.y = y + bbox.height / 2; + await positionNode(item); + y = item.y + bbox.height / 2 + padding / 2; + } + const rect = sectionObj.cluster.select('rect'); + const height = Math.max(y - top + 3 * padding, 50) + (maxLabelHeight - 25); + rect.attr('height', height); + } + + // Setup the view box and size of the svg element + setupGraphViewbox( + undefined, + svg, + conf.mindmap?.padding ?? defaultConfig.kanban.padding, + conf.mindmap?.useMaxWidth ?? defaultConfig.kanban.useMaxWidth + ); +}; + +export default { + draw, +}; diff --git a/packages/mermaid/src/diagrams/kanban/kanbanTypes.ts b/packages/mermaid/src/diagrams/kanban/kanbanTypes.ts new file mode 100644 index 0000000000..efcb61f98e --- /dev/null +++ b/packages/mermaid/src/diagrams/kanban/kanbanTypes.ts @@ -0,0 +1,3 @@ +import type kanbanDb from './kanbanDb.js'; + +export type KanbanDB = typeof kanbanDb; diff --git a/packages/mermaid/src/diagrams/kanban/parser/kanban.jison b/packages/mermaid/src/diagrams/kanban/parser/kanban.jison new file mode 100644 index 0000000000..6fb31bf0bb --- /dev/null +++ b/packages/mermaid/src/diagrams/kanban/parser/kanban.jison @@ -0,0 +1,166 @@ +/** mermaid + * https://knsv.github.io/mermaid + * (c) 2015 Knut Sveidqvist + * MIT license. + */ +%lex + +%options case-insensitive + +%{ + // Pre-lexer code can go here +%} +%x NODE +%x NSTR +%x NSTR2 +%x ICON +%x CLASS +%x shapeData +%x shapeDataStr +%x shapeDataEndBracket + +%% + +\@\{ { + // console.log('=> shapeData', yytext); + this.pushState("shapeData"); yytext=""; return 'SHAPE_DATA' } +["] { + // console.log('=> shapeDataStr', yytext); + this.pushState("shapeDataStr"); + return 'SHAPE_DATA'; + } +["] { + // console.log('shapeData <==', yytext); + this.popState(); return 'SHAPE_DATA'} +[^\"]+ { + // console.log('shapeData', yytext); + const re = /\n\s*/g; + yytext = yytext.replace(re,"
"); + return 'SHAPE_DATA'} +[^}^"]+ { + // console.log('shapeData', yytext); + return 'SHAPE_DATA'; + } +"}" { + // console.log('<== root', yytext) + this.popState(); + } +\s*\%\%.* {yy.getLogger().trace('Found comment',yytext); return 'SPACELINE';} +// \%\%[^\n]*\n /* skip comments */ +"kanban" {return 'KANBAN';} +":::" { this.begin('CLASS'); } +.+ { this.popState();return 'CLASS'; } +\n { this.popState();} +// [\s]*"::icon(" { this.begin('ICON'); } +"::icon(" { yy.getLogger().trace('Begin icon');this.begin('ICON'); } +[\s]+[\n] {yy.getLogger().trace('SPACELINE');return 'SPACELINE' /* skip all whitespace */ ;} +[\n]+ return 'NL'; +[^\)]+ { return 'ICON'; } +\) {yy.getLogger().trace('end icon');this.popState();} +"-)" { yy.getLogger().trace('Exploding node'); this.begin('NODE');return 'NODE_DSTART'; } +"(-" { yy.getLogger().trace('Cloud'); this.begin('NODE');return 'NODE_DSTART'; } +"))" { yy.getLogger().trace('Explosion Bang'); this.begin('NODE');return 'NODE_DSTART'; } +")" { yy.getLogger().trace('Cloud Bang'); this.begin('NODE');return 'NODE_DSTART'; } +"((" { this.begin('NODE');return 'NODE_DSTART'; } +"{{" { this.begin('NODE');return 'NODE_DSTART'; } +"(" { this.begin('NODE');return 'NODE_DSTART'; } +"[" { this.begin('NODE');return 'NODE_DSTART'; } +[\s]+ return 'SPACELIST' /* skip all whitespace */ ; +// !(-\() return 'NODE_ID'; +[^\(\[\n\)\{\}@]+ {return 'NODE_ID';} +<> return 'EOF'; +["][`] { this.begin("NSTR2");} +[^`"]+ { return "NODE_DESCR";} +[`]["] { this.popState();} +["] { yy.getLogger().trace('Starting NSTR');this.begin("NSTR");} +[^"]+ { yy.getLogger().trace('description:', yytext); return "NODE_DESCR";} +["] {this.popState();} +[\)]\) {this.popState();yy.getLogger().trace('node end ))');return "NODE_DEND";} +[\)] {this.popState();yy.getLogger().trace('node end )');return "NODE_DEND";} +[\]] {this.popState();yy.getLogger().trace('node end ...',yytext);return "NODE_DEND";} +"}}" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";} +"(-" {this.popState();yy.getLogger().trace('node end (-');return "NODE_DEND";} +"-)" {this.popState();yy.getLogger().trace('node end (-');return "NODE_DEND";} +"((" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";} +"(" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";} +[^\)\]\(\}]+ { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';} +.+(?!\(\() { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';} +// [\[] return 'NODE_START'; +// .+ return 'TXT' ; + +/lex + +%start start + +%% /* language grammar */ + +start +// %{ : info document 'EOF' { return yy; } } + : mindMap + | spaceLines mindMap + ; + +spaceLines + : SPACELINE + | spaceLines SPACELINE + | spaceLines NL + ; + +mindMap + : KANBAN document { return yy; } + | KANBAN NL document { return yy; } + ; + +stop + : NL {yy.getLogger().trace('Stop NL ');} + | EOF {yy.getLogger().trace('Stop EOF ');} + | SPACELINE + | stop NL {yy.getLogger().trace('Stop NL2 ');} + | stop EOF {yy.getLogger().trace('Stop EOF2 ');} + ; +document + : document statement stop + | statement stop + ; + +statement + : SPACELIST node shapeData { yy.getLogger().info('Node: ',$2.id);yy.addNode($1.length, $2.id, $2.descr, $2.type, $3); } + | SPACELIST node { yy.getLogger().info('Node: ',$2.id);yy.addNode($1.length, $2.id, $2.descr, $2.type); } + | SPACELIST ICON { yy.getLogger().trace('Icon: ',$2);yy.decorateNode({icon: $2}); } + | SPACELIST CLASS { yy.decorateNode({class: $2}); } + | SPACELINE { yy.getLogger().trace('SPACELIST');} + | node shapeData { yy.getLogger().trace('Node: ',$1.id);yy.addNode(0, $1.id, $1.descr, $1.type, $2); } + | node { yy.getLogger().trace('Node: ',$1.id);yy.addNode(0, $1.id, $1.descr, $1.type); } + | ICON { yy.decorateNode({icon: $1}); } + | CLASS { yy.decorateNode({class: $1}); } + | SPACELIST + ; + + + +node + :nodeWithId + |nodeWithoutId + ; + +nodeWithoutId + : NODE_DSTART NODE_DESCR NODE_DEND + { yy.getLogger().trace("node found ..", $1); $$ = { id: $2, descr: $2, type: yy.getType($1, $3) }; } + ; + +nodeWithId + : NODE_ID { $$ = { id: $1, descr: $1, type: 0 }; } + | NODE_ID NODE_DSTART NODE_DESCR NODE_DEND + { yy.getLogger().trace("node found ..", $1); $$ = { id: $1, descr: $3, type: yy.getType($2, $4) }; } + ; + +shapeData: + shapeData SHAPE_DATA + { $$ = $1 + $2; } + | SHAPE_DATA + { $$ = $1; } + ; + + + +%% diff --git a/packages/mermaid/src/diagrams/kanban/samples.md b/packages/mermaid/src/diagrams/kanban/samples.md new file mode 100644 index 0000000000..a75cfeeeff --- /dev/null +++ b/packages/mermaid/src/diagrams/kanban/samples.md @@ -0,0 +1,105 @@ +```mermaid +kanban + New + Sometimes wrong Shape type is highlighted + In progress + + +``` + +```mermaid +kanban + Todo + Create JISON + Update DB function + Create parsing tests + define getData + Create renderer + In progress + Design grammar + +``` + +Adding ID + +```mermaid +kanban + id1[Todo] + id2[Create JISON] + id3[Update DB function] + id4[Create parsing tests] + id5[define getData] + id6[Create renderer] + id7[In progress] + id8[Design grammar] + +``` + +Background color for section + +```mermaid +kanban + id1[Todo] + id2[Create JISON] + id3[Update DB function] + id4[Create parsing tests] + id5[define getData] + id6[Create renderer] + id7[In progress] + id8[Design grammar] + + style n2 stroke:#AA00FF,fill:#E1BEE7 +``` + +Background color for section + +```mermaid +kanban + id1[Todo] + id2[Create JISON] + id3[Update DB function] + id4[Create parsing tests] + id5[define getData] + id6[Create renderer] + id7[In progress] + id8[Design grammar] + + id2@{ + assigned: knsv + icon: heart + priority: high + descr: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + } + style n1 stroke:#AA00FF,fill:#E1BEE7 +``` + +Background color for section + +```mermaid +--- +config: + kanban: + showIds: true + fields: [[title],[description][id, assigned]] +--- +kanban + id1[Todo] + id2[Create JISON] + id3[Update DB function] + id4[Create parsing tests] + id5[define getData] + id6[Create renderer] + id7[In progress] + id8[Design grammar] + + id2@{ + assigned: knsv + icon: heart + priority: high + descr: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + } + style n1 stroke:#AA00FF,fill:#E1BEE7 +``` + +priority - dedicated +link - dedicated diff --git a/packages/mermaid/src/diagrams/kanban/styles.ts b/packages/mermaid/src/diagrams/kanban/styles.ts new file mode 100644 index 0000000000..8b40224b24 --- /dev/null +++ b/packages/mermaid/src/diagrams/kanban/styles.ts @@ -0,0 +1,109 @@ +// @ts-expect-error Incorrect khroma types +import { darken, lighten, isDark } from 'khroma'; +import type { DiagramStylesProvider } from '../../diagram-api/types.js'; + +const genSections: DiagramStylesProvider = (options) => { + let sections = ''; + + for (let i = 0; i < options.THEME_COLOR_LIMIT; i++) { + options['lineColor' + i] = options['lineColor' + i] || options['cScaleInv' + i]; + if (isDark(options['lineColor' + i])) { + options['lineColor' + i] = lighten(options['lineColor' + i], 20); + } else { + options['lineColor' + i] = darken(options['lineColor' + i], 20); + } + } + + const adjuster = (color: string, level: number) => + options.darkMode ? darken(color, level) : lighten(color, level); + + for (let i = 0; i < options.THEME_COLOR_LIMIT; i++) { + const sw = '' + (17 - 3 * i); + sections += ` + .section-${i - 1} rect, .section-${i - 1} path, .section-${i - 1} circle, .section-${ + i - 1 + } polygon, .section-${i - 1} path { + fill: ${adjuster(options['cScale' + i], 10)}; + stroke: ${adjuster(options['cScale' + i], 10)}; + + } + .section-${i - 1} text { + fill: ${options['cScaleLabel' + i]}; + } + .node-icon-${i - 1} { + font-size: 40px; + color: ${options['cScaleLabel' + i]}; + } + .section-edge-${i - 1}{ + stroke: ${options['cScale' + i]}; + } + .edge-depth-${i - 1}{ + stroke-width: ${sw}; + } + .section-${i - 1} line { + stroke: ${options['cScaleInv' + i]} ; + stroke-width: 3; + } + + .disabled, .disabled circle, .disabled text { + fill: lightgray; + } + .disabled text { + fill: #efefef; + } + + .node rect, + .node circle, + .node ellipse, + .node polygon, + .node path { + fill: ${options.background}; + stroke: ${options.nodeBorder}; + stroke-width: 1px; + } + + .kanban-ticket-link { + fill: ${options.background}; + stroke: ${options.nodeBorder}; + text-decoration: underline; + } + `; + } + return sections; +}; + +// TODO: These options seem incorrect. +const getStyles: DiagramStylesProvider = (options) => + ` + .edge { + stroke-width: 3; + } + ${genSections(options)} + .section-root rect, .section-root path, .section-root circle, .section-root polygon { + fill: ${options.git0}; + } + .section-root text { + fill: ${options.gitBranchLabel0}; + } + .icon-container { + height:100%; + display: flex; + justify-content: center; + align-items: center; + } + .edge { + fill: none; + } + .cluster-label, .label { + color: ${options.textColor}; + fill: ${options.textColor}; + } + .kanban-label { + dy: 1em; + alignment-baseline: middle; + text-anchor: middle; + dominant-baseline: middle; + text-align: center; + } +`; +export default getStyles; diff --git a/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts b/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts index af49d0415e..5bfa203922 100644 --- a/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts +++ b/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts @@ -16,18 +16,20 @@ import defaultConfig from '../../defaultConfig.js'; // Inject the layout algorithm into cytoscape cytoscape.use(coseBilkent); -function drawNodes( +async function drawNodes( db: MindmapDB, svg: D3Element, mindmap: FilledMindMapNode, section: number, conf: MermaidConfig ) { - drawNode(db, svg, mindmap, section, conf); + await drawNode(db, svg, mindmap, section, conf); if (mindmap.children) { - mindmap.children.forEach((child, index) => { - drawNodes(db, svg, child, section < 0 ? index : section, conf); - }); + await Promise.all( + mindmap.children.map((child, index) => + drawNodes(db, svg, child, section < 0 ? index : section, conf) + ) + ); } } @@ -177,7 +179,7 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj) => { edgesElem.attr('class', 'mindmap-edges'); const nodesElem = svg.append('g'); nodesElem.attr('class', 'mindmap-nodes'); - drawNodes(db, nodesElem, mm as FilledMindMapNode, -1, conf); + await drawNodes(db, nodesElem, mm as FilledMindMapNode, -1, conf); // Next step is to layout the mindmap, giving each node a position diff --git a/packages/mermaid/src/diagrams/mindmap/svgDraw.ts b/packages/mermaid/src/diagrams/mindmap/svgDraw.ts index 4288cfca69..209a6a0e18 100644 --- a/packages/mermaid/src/diagrams/mindmap/svgDraw.ts +++ b/packages/mermaid/src/diagrams/mindmap/svgDraw.ts @@ -174,13 +174,13 @@ const roundedRectBkg: ShapeFunction = function (db, elem, node) { * @param conf - The configuration object * @returns The height nodes dom element */ -export const drawNode = function ( +export const drawNode = async function ( db: MindmapDB, elem: D3Element, node: FilledMindMapNode, fullSection: number, conf: MermaidConfig -): number { +): Promise { const htmlLabels = conf.htmlLabels; const section = fullSection % (MAX_SECTIONS - 1); const nodeElem = elem.append('g'); @@ -195,7 +195,7 @@ export const drawNode = function ( // Create the wrapped text element const textElem = nodeElem.append('g'); const description = node.descr.replace(/()/g, '\n'); - createText( + await createText( textElem, description, { diff --git a/packages/mermaid/src/diagrams/packet/renderer.ts b/packages/mermaid/src/diagrams/packet/renderer.ts index c89e055cce..25445a2284 100644 --- a/packages/mermaid/src/diagrams/packet/renderer.ts +++ b/packages/mermaid/src/diagrams/packet/renderer.ts @@ -1,6 +1,6 @@ import type { Diagram } from '../../Diagram.js'; import type { PacketDiagramConfig } from '../../config.type.js'; -import type { DiagramRenderer, DrawDefinition, Group, SVG } from '../../diagram-api/types.js'; +import type { DiagramRenderer, DrawDefinition, SVG, SVGGroup } from '../../diagram-api/types.js'; import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; import { configureSvgSize } from '../../setupGraphViewbox.js'; import type { PacketDB, PacketWord } from './types.js'; @@ -39,7 +39,7 @@ const drawWord = ( rowNumber: number, { rowHeight, paddingX, paddingY, bitWidth, bitsPerRow, showBits }: Required ) => { - const group: Group = svg.append('g'); + const group: SVGGroup = svg.append('g'); const wordY = rowNumber * (rowHeight + paddingY) + paddingY; for (const block of word) { const blockX = (block.start % bitsPerRow) * bitWidth + 1; diff --git a/packages/mermaid/src/diagrams/pie/pie.spec.ts b/packages/mermaid/src/diagrams/pie/pie.spec.ts index b7bbf65bfa..f00906cc51 100644 --- a/packages/mermaid/src/diagrams/pie/pie.spec.ts +++ b/packages/mermaid/src/diagrams/pie/pie.spec.ts @@ -130,8 +130,8 @@ describe('pie', () => { expect(sections.get('bat')).toBe(40); }); - it('should handle simple pie with negative decimal', () => { - expect(async () => { + it('should handle simple pie with negative decimal', async () => { + await expect(async () => { await parser.parse(`pie "ash" : -60.67 "bat" : 40.12 diff --git a/packages/mermaid/src/diagrams/pie/pieRenderer.ts b/packages/mermaid/src/diagrams/pie/pieRenderer.ts index 8f3b9cc5b3..a0cdce3df7 100644 --- a/packages/mermaid/src/diagrams/pie/pieRenderer.ts +++ b/packages/mermaid/src/diagrams/pie/pieRenderer.ts @@ -1,13 +1,13 @@ import type d3 from 'd3'; -import { scaleOrdinal, pie as d3pie, arc } from 'd3'; +import { arc, pie as d3pie, scaleOrdinal } from 'd3'; +import type { MermaidConfig, PieDiagramConfig } from '../../config.type.js'; +import { getConfig } from '../../diagram-api/diagramAPI.js'; +import type { DrawDefinition, SVG, SVGGroup } from '../../diagram-api/types.js'; import { log } from '../../logger.js'; +import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; import { configureSvgSize } from '../../setupGraphViewbox.js'; -import { getConfig } from '../../diagram-api/diagramAPI.js'; import { cleanAndMerge, parseFontSize } from '../../utils.js'; -import type { DrawDefinition, Group, SVG } from '../../diagram-api/types.js'; import type { D3Section, PieDB, Sections } from './pieTypes.js'; -import type { MermaidConfig, PieDiagramConfig } from '../../config.type.js'; -import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; const createPieArcs = (sections: Sections): d3.PieArcDatum[] => { // Compute the position of each group on the pie: @@ -46,7 +46,7 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => { const height = 450; const pieWidth: number = height; const svg: SVG = selectSvgElement(id); - const group: Group = svg.append('g'); + const group: SVGGroup = svg.append('g'); group.attr('transform', 'translate(' + pieWidth / 2 + ',' + height / 2 + ')'); const { themeVariables } = globalConfig; diff --git a/packages/mermaid/src/diagrams/requirement/requirementRenderer.js b/packages/mermaid/src/diagrams/requirement/requirementRenderer.js index 6b51437520..778dc36b1c 100644 --- a/packages/mermaid/src/diagrams/requirement/requirementRenderer.js +++ b/packages/mermaid/src/diagrams/requirement/requirementRenderer.js @@ -1,11 +1,11 @@ import { line, select } from 'd3'; import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js'; import * as graphlib from 'dagre-d3-es/src/graphlib/index.js'; +import { getConfig } from '../../diagram-api/diagramAPI.js'; import { log } from '../../logger.js'; import { configureSvgSize } from '../../setupGraphViewbox.js'; import common from '../common/common.js'; import markers from './requirementMarkers.js'; -import { getConfig } from '../../diagram-api/diagramAPI.js'; let conf = {}; let relCnt = 0; diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js index 7f6b80ca5b..fde813cef7 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js +++ b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js @@ -2063,8 +2063,8 @@ participant Alice`; }); }); - it.each(['__proto__', 'constructor'])('should allow %s as an actor name', function (prop) { - expect( + it.each(['__proto__', 'constructor'])('should allow %s as an actor name', async function (prop) { + await expect( mermaidAPI.parse(` sequenceDiagram ${prop}-->>A: Hello, how are you?`) diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index 5299f1b1e1..951d84b862 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -1088,6 +1088,19 @@ export const draw = async function (_text: string, id: string, _version: string, const { bounds: box } = bounds.getBounds(); + if (box.startx === undefined) { + box.startx = 0; + } + if (box.starty === undefined) { + box.starty = 0; + } + if (box.stopx === undefined) { + box.stopx = 0; + } + if (box.stopy === undefined) { + box.stopy = 0; + } + // Make sure the height of the diagram supports long menus. let boxHeight = box.stopy - box.starty; if (boxHeight < requiredBoxSize.maxHeight) { diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.js b/packages/mermaid/src/diagrams/sequence/svgDraw.js index 51968ef9fe..c681c94918 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.js @@ -329,7 +329,7 @@ export const fixLifeLineHeights = (diagram, actors, actorKeys, conf) => { const drawActorTypeParticipant = function (elem, actor, conf, isFooter) { const actorY = isFooter ? actor.stopy : actor.starty; const center = actor.x + actor.width / 2; - const centerY = actorY + 5; + const centerY = actorY + actor.height; const boxplusLineGroup = elem.append('g').lower(); var g = boxplusLineGroup; diff --git a/packages/mermaid/src/diagrams/state/dataFetcher.js b/packages/mermaid/src/diagrams/state/dataFetcher.js new file mode 100644 index 0000000000..921544ff27 --- /dev/null +++ b/packages/mermaid/src/diagrams/state/dataFetcher.js @@ -0,0 +1,379 @@ +import { getConfig } from '../../diagram-api/diagramAPI.js'; +import { log } from '../../logger.js'; +import common from '../common/common.js'; +import { + CSS_DIAGRAM_CLUSTER, + CSS_DIAGRAM_CLUSTER_ALT, + CSS_DIAGRAM_NOTE, + CSS_DIAGRAM_STATE, + CSS_EDGE, + CSS_EDGE_NOTE_EDGE, + DEFAULT_NESTED_DOC_DIR, + DEFAULT_STATE_TYPE, + DIVIDER_TYPE, + DOMID_STATE, + DOMID_TYPE_SPACER, + G_EDGE_ARROWHEADSTYLE, + G_EDGE_LABELPOS, + G_EDGE_LABELTYPE, + G_EDGE_STYLE, + G_EDGE_THICKNESS, + NOTE, + NOTE_ID, + PARENT, + PARENT_ID, + SHAPE_DIVIDER, + SHAPE_END, + SHAPE_GROUP, + SHAPE_NOTE, + SHAPE_NOTEGROUP, + SHAPE_START, + SHAPE_STATE, + SHAPE_STATE_WITH_DESC, + STMT_RELATION, + STMT_STATE, +} from './stateCommon.js'; + +// List of nodes created from the parsed diagram statement items +let nodeDb = new Map(); + +let graphItemCount = 0; // used to construct ids, etc. + +/** + * Create a standard string for the dom ID of an item. + * If a type is given, insert that before the counter, preceded by the type spacer + * + * @param itemId + * @param counter + * @param {string | null} type + * @param typeSpacer + * @returns {string} + */ +export function stateDomId(itemId = '', counter = 0, type = '', typeSpacer = DOMID_TYPE_SPACER) { + const typeStr = type !== null && type.length > 0 ? `${typeSpacer}${type}` : ''; + return `${DOMID_STATE}-${itemId}${typeStr}-${counter}`; +} + +const setupDoc = (parentParsedItem, doc, diagramStates, nodes, edges, altFlag, look, classes) => { + // graphItemCount = 0; + log.trace('items', doc); + doc.forEach((item) => { + switch (item.stmt) { + case STMT_STATE: + dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look, classes); + break; + case DEFAULT_STATE_TYPE: + dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look, classes); + break; + case STMT_RELATION: + { + dataFetcher( + parentParsedItem, + item.state1, + diagramStates, + nodes, + edges, + altFlag, + look, + classes + ); + dataFetcher( + parentParsedItem, + item.state2, + diagramStates, + nodes, + edges, + altFlag, + look, + classes + ); + const edgeData = { + id: 'edge' + graphItemCount, + start: item.state1.id, + end: item.state2.id, + arrowhead: 'normal', + arrowTypeEnd: 'arrow_barb', + style: G_EDGE_STYLE, + labelStyle: '', + label: common.sanitizeText(item.description, getConfig()), + arrowheadStyle: G_EDGE_ARROWHEADSTYLE, + labelpos: G_EDGE_LABELPOS, + labelType: G_EDGE_LABELTYPE, + thickness: G_EDGE_THICKNESS, + classes: CSS_EDGE, + look, + }; + edges.push(edgeData); + graphItemCount++; + } + break; + } + }); +}; + +/** + * Get the direction from the statement items. + * Look through all of the documents (docs) in the parsedItems + * Because is a _document_ direction, the default direction is not necessarily the same as the overall default _diagram_ direction. + * @param {object[]} parsedItem - the parsed statement item to look through + * @param [defaultDir] - the direction to use if none is found + * @returns {string} + */ +const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => { + let dir = defaultDir; + if (parsedItem.doc) { + for (const parsedItemDoc of parsedItem.doc) { + if (parsedItemDoc.stmt === 'dir') { + dir = parsedItemDoc.value; + } + } + } + return dir; +}; + +function insertOrUpdateNode(nodes, nodeData, classes) { + if (!nodeData.id || nodeData.id === '' || nodeData.id === '') { + return; + } + + //Populate node style attributes if nodeData has classes defined + if (nodeData.cssClasses) { + if (!Array.isArray(nodeData.cssCompiledStyles)) { + nodeData.cssCompiledStyles = []; + } + + nodeData.cssClasses.split(' ').forEach((cssClass) => { + if (classes.get(cssClass)) { + const classDef = classes.get(cssClass); + nodeData.cssCompiledStyles = [...nodeData.cssCompiledStyles, ...classDef.styles]; + } + }); + } + const existingNodeData = nodes.find((node) => node.id === nodeData.id); + if (existingNodeData) { + //update the existing nodeData + Object.assign(existingNodeData, nodeData); + } else { + nodes.push(nodeData); + } +} +/** + * Get classes from the db for the info item. + * If there aren't any or if dbInfoItem isn't defined, return an empty string. + * Else create 1 string from the list of classes found + * + * @param {undefined | null | object} dbInfoItem + * @returns {string} + */ +function getClassesFromDbInfo(dbInfoItem) { + return dbInfoItem?.classes?.join(' ') ?? ''; +} + +function getStylesFromDbInfo(dbInfoItem) { + return dbInfoItem?.styles ?? []; +} + +export const dataFetcher = ( + parent, + parsedItem, + diagramStates, + nodes, + edges, + altFlag, + look, + classes +) => { + const itemId = parsedItem.id; + const dbState = diagramStates.get(itemId); + const classStr = getClassesFromDbInfo(dbState); + const style = getStylesFromDbInfo(dbState); + + log.info('dataFetcher parsedItem', parsedItem, dbState, style); + + if (itemId !== 'root') { + let shape = SHAPE_STATE; + // The if === true / false can be removed if we can guarantee that the parsedItem.start is always a boolean + if (parsedItem.start === true) { + shape = SHAPE_START; + } else if (parsedItem.start === false) { + shape = SHAPE_END; + } + if (parsedItem.type !== DEFAULT_STATE_TYPE) { + shape = parsedItem.type; + } + + // Add the node to our list (nodeDb) + if (!nodeDb.get(itemId)) { + nodeDb.set(itemId, { + id: itemId, + shape, + description: common.sanitizeText(itemId, getConfig()), + cssClasses: `${classStr} ${CSS_DIAGRAM_STATE}`, + cssStyles: style, + }); + } + + const newNode = nodeDb.get(itemId); + + // Save data for description and group so that for instance a statement without description overwrites + // one with description @todo TODO What does this mean? If important, add a test for it + + // Build of the array of description strings + if (parsedItem.description) { + if (Array.isArray(newNode.description)) { + // There already is an array of strings,add to it + newNode.shape = SHAPE_STATE_WITH_DESC; + newNode.description.push(parsedItem.description); + } else { + if (newNode.description?.length > 0) { + // if there is a description already transform it to an array + newNode.shape = SHAPE_STATE_WITH_DESC; + if (newNode.description === itemId) { + // If the previous description was this, remove it + newNode.description = [parsedItem.description]; + } else { + newNode.description = [newNode.description, parsedItem.description]; + } + } else { + newNode.shape = SHAPE_STATE; + newNode.description = parsedItem.description; + } + } + newNode.description = common.sanitizeTextOrArray(newNode.description, getConfig()); + } + + // If there's only 1 description entry, just use a regular state shape + if (newNode.description?.length === 1 && newNode.shape === SHAPE_STATE_WITH_DESC) { + if (newNode.type === 'group') { + newNode.shape = SHAPE_GROUP; + } else { + newNode.shape = SHAPE_STATE; + } + } + + // group + if (!newNode.type && parsedItem.doc) { + log.info('Setting cluster for XCX', itemId, getDir(parsedItem)); + newNode.type = 'group'; + newNode.isGroup = true; + newNode.dir = getDir(parsedItem); + newNode.shape = parsedItem.type === DIVIDER_TYPE ? SHAPE_DIVIDER : SHAPE_GROUP; + newNode.cssClasses = `${newNode.cssClasses} ${CSS_DIAGRAM_CLUSTER} ${altFlag ? CSS_DIAGRAM_CLUSTER_ALT : ''}`; + } + + // This is what will be added to the graph + const nodeData = { + labelStyle: '', + shape: newNode.shape, + label: newNode.description, + cssClasses: newNode.cssClasses, + cssCompiledStyles: [], + cssStyles: newNode.cssStyles, + id: itemId, + dir: newNode.dir, + domId: stateDomId(itemId, graphItemCount), + type: newNode.type, + isGroup: newNode.type === 'group', + padding: 8, + rx: 10, + ry: 10, + look, + }; + + // Clear the label for dividers who have no description + if (nodeData.shape === SHAPE_DIVIDER) { + nodeData.label = ''; + } + + if (parent && parent.id !== 'root') { + log.trace('Setting node ', itemId, ' to be child of its parent ', parent.id); + nodeData.parentId = parent.id; + } + + nodeData.centerLabel = true; + + if (parsedItem.note) { + // Todo: set random id + const noteData = { + labelStyle: '', + shape: SHAPE_NOTE, + label: parsedItem.note.text, + cssClasses: CSS_DIAGRAM_NOTE, + // useHtmlLabels: false, + cssStyles: [], + cssCompilesStyles: [], + id: itemId + NOTE_ID + '-' + graphItemCount, + domId: stateDomId(itemId, graphItemCount, NOTE), + type: newNode.type, + isGroup: newNode.type === 'group', + padding: getConfig().flowchart.padding, + look, + position: parsedItem.note.position, + }; + const parentNodeId = itemId + PARENT_ID; + const groupData = { + labelStyle: '', + shape: SHAPE_NOTEGROUP, + label: parsedItem.note.text, + cssClasses: newNode.cssClasses, + cssStyles: [], + id: itemId + PARENT_ID, + domId: stateDomId(itemId, graphItemCount, PARENT), + type: 'group', + isGroup: true, + padding: 16, //getConfig().flowchart.padding + look, + position: parsedItem.note.position, + }; + graphItemCount++; + + //add parent id to groupData + groupData.id = parentNodeId; + //add parent id to noteData + noteData.parentId = parentNodeId; + //nodeData.parentId = parentNodeId; + + //insert groupData + insertOrUpdateNode(nodes, groupData, classes); + //insert noteData + insertOrUpdateNode(nodes, noteData, classes); + //insert nodeData + insertOrUpdateNode(nodes, nodeData, classes); + + let from = itemId; + let to = noteData.id; + + if (parsedItem.note.position === 'left of') { + from = noteData.id; + to = itemId; + } + + edges.push({ + id: from + '-' + to, + start: from, + end: to, + arrowhead: 'none', + arrowTypeEnd: '', + style: G_EDGE_STYLE, + labelStyle: '', + classes: CSS_EDGE_NOTE_EDGE, + arrowheadStyle: G_EDGE_ARROWHEADSTYLE, + labelpos: G_EDGE_LABELPOS, + labelType: G_EDGE_LABELTYPE, + thickness: G_EDGE_THICKNESS, + look, + }); + } else { + insertOrUpdateNode(nodes, nodeData, classes); + } + } + if (parsedItem.doc) { + log.trace('Adding nodes children '); + setupDoc(parsedItem, parsedItem.doc, diagramStates, nodes, edges, !altFlag, look, classes); + } +}; + +export const reset = () => { + nodeDb.clear(); + graphItemCount = 0; +}; diff --git a/packages/mermaid/src/diagrams/state/parser/state-style.spec.js b/packages/mermaid/src/diagrams/state/parser/state-style.spec.js index 1be9148b8d..fed63c4448 100644 --- a/packages/mermaid/src/diagrams/state/parser/state-style.spec.js +++ b/packages/mermaid/src/diagrams/state/parser/state-style.spec.js @@ -217,4 +217,50 @@ describe('ClassDefs and classes when parsing a State diagram', () => { }); }); }); + + describe('style statement for a state (style)', () => { + describe('defining (style)', () => { + it('has "style" as a keyword, an id, and can set a css style attribute', function () { + stateDiagram.parser.parse(`stateDiagram-v2 + id1 + style id1 background:#bbb`); + stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2()); + const data4Layout = stateDiagram.parser.yy.getData(); + + expect(data4Layout.nodes[0].cssStyles).toEqual(['background:#bbb']); + }); + it('has "style" as a keyword, an id, and can set a css style attribute', function () { + stateDiagram.parser.parse(`stateDiagram-v2 + id1 + id2 + style id1,id2 background:#bbb`); + stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2()); + const data4Layout = stateDiagram.parser.yy.getData(); + + expect(data4Layout.nodes[0].cssStyles).toEqual(['background:#bbb']); + expect(data4Layout.nodes[1].cssStyles).toEqual(['background:#bbb']); + }); + + it('can define multiple attributes separated by commas', function () { + stateDiagram.parser.parse(`stateDiagram-v2 + id1 + id2 + style id1,id2 background:#bbb, font-weight:bold, font-style:italic;`); + + stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2()); + const data4Layout = stateDiagram.parser.yy.getData(); + + expect(data4Layout.nodes[0].cssStyles).toEqual([ + 'background:#bbb', + 'font-weight:bold', + 'font-style:italic', + ]); + expect(data4Layout.nodes[1].cssStyles).toEqual([ + 'background:#bbb', + 'font-weight:bold', + 'font-style:italic', + ]); + }); + }); + }); }); diff --git a/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison b/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison index 44235ecd40..e3bc512352 100644 --- a/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison +++ b/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison @@ -27,6 +27,13 @@ %x CLASSDEFID %x CLASS %x CLASS_STYLE + +// Style statement states +%x STYLE +%x STYLE_IDS +%x STYLEDEF_STYLES +%x STYLEDEF_STYLEOPTS + %x NOTE %x NOTE_ID %x NOTE_TEXT @@ -75,6 +82,10 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili (\w+)+((","\s*\w+)*) { this.popState(); this.pushState('CLASS_STYLE'); return 'CLASSENTITY_IDS' } [^\n]* { this.popState(); return 'STYLECLASS' } +"style"\s+ { this.pushState('STYLE'); return 'style'; } + diff --git a/packages/mermaid/src/docs/.vitepress/config.ts b/packages/mermaid/src/docs/.vitepress/config.ts index 940fc6940a..8a0225c56b 100644 --- a/packages/mermaid/src/docs/.vitepress/config.ts +++ b/packages/mermaid/src/docs/.vitepress/config.ts @@ -157,6 +157,8 @@ function sidebarSyntax() { { text: 'XY Chart đŸ”Ĩ', link: '/syntax/xyChart' }, { text: 'Block Diagram đŸ”Ĩ', link: '/syntax/block' }, { text: 'Packet đŸ”Ĩ', link: '/syntax/packet' }, + { text: 'Kanban đŸ”Ĩ', link: '/syntax/kanban' }, + { text: 'Architecture đŸ”Ĩ', link: '/syntax/architecture' }, { text: 'Other Examples', link: '/syntax/examples' }, ], }, @@ -173,6 +175,7 @@ function sidebarConfig() { { text: 'API-Usage', link: '/config/usage' }, { text: 'Mermaid API Configuration', link: '/config/setup/README' }, { text: 'Mermaid Configuration Options', link: '/config/schema-docs/config' }, + { text: 'Registering icons', link: '/config/icons' }, { text: 'Directives', link: '/config/directives' }, { text: 'Theming', link: '/config/theming' }, { text: 'Math', link: '/config/math' }, diff --git a/packages/mermaid/src/docs/.vitepress/theme/index.ts b/packages/mermaid/src/docs/.vitepress/theme/index.ts index 3ce3aea238..3ec200937f 100644 --- a/packages/mermaid/src/docs/.vitepress/theme/index.ts +++ b/packages/mermaid/src/docs/.vitepress/theme/index.ts @@ -11,11 +11,11 @@ import HomePage from '../components/HomePage.vue'; import TopBar from '../components/TopBar.vue'; import { getRedirect } from './redirect.js'; // @ts-ignore Type not available -import { h } from 'vue'; -import Theme from 'vitepress/theme'; -import '../style/main.css'; import 'uno.css'; import type { EnhanceAppContext } from 'vitepress'; +import Theme from 'vitepress/theme'; +import { h } from 'vue'; +import '../style/main.css'; export default { ...DefaultTheme, @@ -24,6 +24,7 @@ export default { // Keeping this as comment as it took a lot of time to figure out how to add a component to the top bar. 'home-hero-before': () => h(TopBar), 'home-features-after': () => h(HomePage), + 'doc-before': () => h(TopBar), }); }, enhanceApp({ app, router }: EnhanceAppContext) { diff --git a/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts b/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts index 47e2386926..2357fe384b 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/adding-new-shape.md b/packages/mermaid/src/docs/adding-new-shape.md new file mode 100644 index 0000000000..8ba5d083ca --- /dev/null +++ b/packages/mermaid/src/docs/adding-new-shape.md @@ -0,0 +1,225 @@ +# Custom SVG Shapes Library + +This library provides a collection of custom SVG shapes, utilities, and helpers for generating diagram components. The shapes are designed to be used within an SVG container and include a variety of common and complex shapes. + +## Overview + +## Shape Helpers and Utilities + +Before starting with shape creation, it's essential to familiarize yourself with the utilities provided in the `utils.ts` file from `packages/mermaid/src/rendering-util/rendering-elements/shapes/util.js`. These utilities are designed to assist with various aspects of SVG shape manipulation and ensure consistent and accurate rendering. + +## Available Utilities + +### 1. `labelHelper` + +- **Purpose**: This function creates and inserts labels inside SVG shapes. +- **Features**: + - Handles both HTML labels and plain text. + - Calculates the bounding box dimensions of the label. + - Ensures proper positioning of labels within shapes. + +### 2. `updateNodeBounds` + +- **Purpose**: Updates the bounding box dimensions (width and height) of a node. +- **Usage**: + - Adjusts the size of the node to fit the content or shape. + - Useful for ensuring that shapes resize appropriately based on their content. + +### 3. `insertPolygonShape` + +- **Purpose**: Inserts a polygon shape into an SVG container. +- **Features**: + - Handles the creation and insertion of complex polygonal shapes. + - Configures the shape's appearance and positioning within the SVG container. + +### 4. `getNodeClasses` + +- **Purpose**: Returns the appropriate CSS classes for a node based on its configuration. +- **Usage**: + - Dynamically applies CSS classes to nodes for styling purposes. + - Ensures that nodes adhere to the desired design and theme. + +### 5. `createPathFromPoints` + +- **Purpose**: Generates an SVG path string from an array of points. +- **Usage**: + - Converts a list of points into a smooth path. + - Useful for creating custom shapes or paths within the SVG. + +### 6. `generateFullSineWavePoints` + +- **Purpose**: Generates points for a sine wave, useful for creating wavy-edged shapes. +- **Usage**: + - Facilitates the creation of shapes with wavy or sine-wave edges. + - Can be used to add decorative or dynamic edges to shapes. + +## Getting Started + +To utilize these utilities, simply import them from the `utils.ts` file into your shape creation script. These helpers will streamline the process of building and customizing SVG shapes, ensuring consistent results across your projects. + +```typescript +import { + labelHelper, + updateNodeBounds, + insertPolygonShape, + getNodeClasses, + createPathFromPoints, + generateFullSineWavePoints, +} from './utils.ts'; +``` + +## Example Usage + +Here’s a basic example of how you might use some of these utilities: + +```typescript +import { labelHelper, insertPolygonShape } from './utils.ts'; + +const svgContainer = document.getElementById('svgContainer'); + +// Insert a polygon shape +insertPolygonShape(svgContainer /* shape-specific parameters */); + +// Create and insert a label inside the shape +labelHelper(svgContainer /* label-specific parameters */); +``` + +## Adding New Shapes + +### 1. Create the Shape Function + +To add a new shape: + +- **Create the shape function**: Create a new file of name of the shape and export a function in the `shapes` directory that generates your shape. The file and function should follow the pattern used in existing shapes and return an SVG element. + +- **Example**: + + ```typescript + import { Node, RenderOptions } from '../../types.ts'; + + export const myNewShape = async ( + parent: SVGAElement, + node: Node, + renderOptions: RenderOptions + ) => { + // Create your shape here + const shape = parent.insert('g').attr('class', 'my-new-shape'); + // Add other elements or styles as needed + return shape; + }; + ``` + +### 2. Register the Shape + +- **Register the shape**: Add your shape to the `shapes` object in the [main shapes module](../rendering-util/rendering-elements/shapes.ts). This allows your shape to be recognized and used within the system. + +- **Example**: + + ```typescript + import { myNewShape } from './shapes/myNewShape'; + + const shapes = { + ..., + { + semanticName: 'My Shape', + name: 'Shape Name', + shortName: '', + description: '', + aliases: ['', '', '', ''], + handler: myNewShape, + }, + }; + ``` + +# Shape Intersection Algorithms + +This contains algorithms and utilities for calculating intersection points for various shapes in SVG. Arrow intersection points are crucial for accurately determining where arrows connect with shapes. Ensuring precise intersection points enhances the clarity and accuracy of flowcharts and diagrams. + +## Shape Intersection Functions + +### 1. `Ellipse` + +Calculates the intersection points for an ellipse. + +**Usage**: + +```javascript +import intersectEllipse from './intersect-ellipse.js'; + +const intersection = intersectEllipse(node, rx, ry, point); +``` + +- **Parameters**: + - `node`: The SVG node element. + - `rx`: The x-radius of the ellipse. + - `ry`: The y-radius of the ellipse. + - `point`: The point from which the intersection is calculated. + +### 2. `intersectRect` + +Calculates the intersection points for a rectangle. + +**Usage**: + +```javascript +import intersectRect from './intersect-rect.js'; + +const intersection = intersectRect(node, point); +``` + +- **Parameters**: + - `node`: The SVG node element. + - `point`: The point from which the intersection is calculated. + +### 3. `intersectPolygon` + +Calculates the intersection points for a polygon. + +**Usage**: + +```javascript +import intersectPolygon from './intersect-polygon.js'; + +const intersection = intersectPolygon(node, polyPoints, point); +``` + +- **Parameters**: + - `node`: The SVG node element. + - `polyPoints`: Array of points defining the polygon. + - `point`: The point from which the intersection is calculated. + +## Cypress Tests + +To ensure the robustness of the flowchart shapes, there are implementation of comprehensive Cypress test cases in `newShapes.spec.ts` file. These tests cover various aspects such as: + +- **Shapes**: Testing new shapes like `bowTieRect`, `waveRectangle`, `trapezoidalPentagon`, etc. +- **Looks**: Verifying shapes under different visual styles (`classic` and `handDrawn`). +- **Directions**: Ensuring correct rendering in all flow directions of arrows : + - `TB` `(Top -> Bottom)` + - `BT` `(Bottom -> Top)` + - `LR` `(Left -> Right)` + - `RL` `(Right -> Left)` +- **Labels**: Testing shapes with different labels, including: + - No labels + - Short labels + - Very long labels + - Markdown with `htmlLabels:true` and `htmlLabels:false` +- **Styles**: Applying custom styles to shapes and verifying correct rendering. +- **Class Definitions**: Using `classDef` to apply custom classes and testing their impact. + +### Running the Tests + +To run the Cypress tests, follow these steps: + +1. Ensure you have all dependencies installed by running: + ```bash + pnpm install + ``` +2. Start the Cypress test runner: + + ```bash + cypress open --env updateSnapshots=true + + ``` + +3. Select the test suite from the Cypress interface to run all the flowchart shape tests. diff --git a/packages/mermaid/src/docs/community/contributing.md b/packages/mermaid/src/docs/community/contributing.md index 71048d0952..4cd6495634 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/config/icons.md b/packages/mermaid/src/docs/config/icons.md new file mode 100644 index 0000000000..db079f841d --- /dev/null +++ b/packages/mermaid/src/docs/config/icons.md @@ -0,0 +1,49 @@ +# Registering icon pack in mermaid + +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@1/icons.json').then((res) => res.json()), + }, +]); +``` + +Using packages and a bundler: + +```bash +npm install @iconify-json/logos@1 +``` + +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, + }, +]); +``` diff --git a/packages/mermaid/src/docs/ecosystem/integrations-community.md b/packages/mermaid/src/docs/ecosystem/integrations-community.md index d77a82b448..6cff12aac2 100644 --- a/packages/mermaid/src/docs/ecosystem/integrations-community.md +++ b/packages/mermaid/src/docs/ecosystem/integrations-community.md @@ -51,13 +51,15 @@ To add an integration to this list, see the [Integrations - create page](./integ - [SVG diagram generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator) - [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) ✅ - [Mermaid Plugin for JetBrains IDEs](https://plugins.jetbrains.com/plugin/20146-mermaid) +- [MonsterWriter](https://www.monsterwriter.com/) ✅ - [Joplin](https://joplinapp.org) ✅ - [LiveBook](https://livebook.dev) ✅ +- [Slidev](https://sli.dev) ✅ - [Tuleap](https://docs.tuleap.org/user-guide/writing-in-tuleap.html#graphs) ✅ - [Mermaid Flow Visual Editor](https://www.mermaidflow.app) ✅ - [Mermerd](https://github.com/KarnerTh/mermerd) - [Slab](https://slab.com) ✅ -- [Swimm](https://docs.swimm.io/features/diagrams-and-charts/#mermaid--swimm--up-to-date-diagrams-) ✅ +- [Swimm](https://docs.swimm.io/features/diagrams-and-charts) ✅ - [NotesHub](https://noteshub.app) ✅ - [Notion](https://notion.so) ✅ - [Observable](https://observablehq.com/@observablehq/mermaid) ✅ @@ -67,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 @@ -128,7 +131,7 @@ Communication tools and platforms ### Wikis - [DokuWiki](https://dokuwiki.org) - - [ComboStrap](https://combostrap.com/mermaid) + - [ComboStrap](https://combostrap.com/utility/create-diagram-with-mermaid-vh3ab9yj) - [Mermaid Plugin](https://www.dokuwiki.org/plugin:mermaid) - [Foswiki](https://foswiki.org) - [Mermaid Plugin](https://foswiki.org/Extensions/MermaidPlugin) @@ -137,7 +140,7 @@ Communication tools and platforms - [Mermaid Extension](https://www.mediawiki.org/wiki/Extension:Mermaid) - [PmWiki](https://www.pmwiki.org) - [MermaidJs Cookbook recipe](https://www.pmwiki.org/wiki/Cookbook/MermaidJs) -- [Semantic Media Wiki](https://semantic-mediawiki.org) +- [Semantic Media Wiki](https://www.semantic-mediawiki.org) - [Mermaid Plugin](https://github.com/SemanticMediaWiki/Mermaid) - [TiddlyWiki](https://tiddlywiki.com/) - [mermaid-tw5: wrapper for Mermaid Live](https://github.com/efurlanm/mermaid-tw5) @@ -177,8 +180,6 @@ Communication tools and platforms - [=Diagram block](https://github.com/zag/podlite/tree/main/packages/podlite-diagrams) - [Standard Notes](https://standardnotes.com/) - [Mermaid Extension](https://github.com/nienow/sn-mermaid) -- [Sublime Text 3](https://sublimetext.com) - - [Mermaid Package](https://packagecontrol.io/packages/Mermaid) - [VS Code](https://code.visualstudio.com/) - [Mermaid Editor](https://marketplace.visualstudio.com/items?itemName=tomoyukim.vscode-mermaid-editor) - [Mermaid Export](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.mermaid-export) @@ -192,15 +193,22 @@ Communication tools and platforms - [Vim](https://www.vim.org) - [Vim Diagram Syntax](https://github.com/zhaozg/vim-diagram) - [Official Vim Syntax and ft plugin](https://github.com/craigmac/vim-mermaid) +- [Zed](https://zed.dev) + - [zed-mermaid](https://github.com/gabeidx/zed-mermaid) ### Document Generation +- [Astro](https://astro.build/) + - [Adding diagrams to your Astro site with MermaidJS and Playwright](https://agramont.net/blog/diagraming-with-mermaidjs-astro/) - [Codedoc](https://codedoc.cc/) - [codedoc-mermaid-plugin](https://www.npmjs.com/package/codedoc-mermaid-plugin) - [Docsy Hugo Theme](https://www.docsy.dev/docs/adding-content/lookandfeel/#diagrams-with-mermaid) ✅ - [Docusaurus](https://docusaurus.io/docs/markdown-features/diagrams) ✅ - [Gatsby](https://www.gatsbyjs.com/) - [gatsby-remark-mermaid](https://github.com/remcohaszing/gatsby-remark-mermaid) +- [Jekyll](https://jekyllrb.com/) + - [jekyll-mermaid](https://rubygems.org/gems/jekyll-mermaid) + - [jekyll-mermaid-diagrams](https://github.com/fuzhibo/jekyll-mermaid-diagrams) - [JSDoc](https://jsdoc.app/) - [jsdoc-mermaid](https://github.com/Jellyvision/jsdoc-mermaid) - [Madness](https://madness.dannyb.co/) @@ -209,7 +217,7 @@ Communication tools and platforms - [MkDocs](https://www.mkdocs.org) - [mkdocs-mermaid2-plugin](https://github.com/fralau/mkdocs-mermaid2-plugin) - [mkdocs-material](https://github.com/squidfunk/mkdocs-material), check the [docs](https://squidfunk.github.io/mkdocs-material/reference/diagrams/) - - [Quarto](https://quarto.org/) +- [Quarto](https://quarto.org/) ✅ - [rehype](https://github.com/rehypejs/rehype) - [rehype-mermaid](https://github.com/remcohaszing/rehype-mermaid) - [remark](https://remark.js.org/) @@ -238,17 +246,12 @@ Communication tools and platforms ### Other -- [Astro](https://astro.build/) - - [Adding diagrams to your Astro site with MermaidJS and Playwright](https://agramont.net/blog/diagraming-with-mermaidjs-astro/) - [Bisheng](https://www.npmjs.com/package/bisheng) - [bisheng-plugin-mermaid](https://github.com/yct21/bisheng-plugin-mermaid) - [Blazorade Mermaid: Render Mermaid diagrams in Blazor applications](https://github.com/Blazorade/Blazorade-Mermaid/wiki) - [Codemia: A tool to practice system design problems](https://codemia.io) ✅ - [ExDoc](https://github.com/elixir-lang/ex_doc) - [Rendering Mermaid graphs](https://github.com/elixir-lang/ex_doc#rendering-mermaid-graphs) -- [Jekyll](https://jekyllrb.com/) - - [jekyll-mermaid](https://rubygems.org/gems/jekyll-mermaid) - - [jekyll-mermaid-diagrams](https://github.com/fuzhibo/jekyll-mermaid-diagrams) - [MarkChart: Preview Mermaid diagrams on macOS](https://markchart.app/) - [mermaid-isomorphic](https://github.com/remcohaszing/mermaid-isomorphic) - [mermaid-server: Generate diagrams using a HTTP request](https://github.com/TomWright/mermaid-server) diff --git a/packages/mermaid/src/docs/ecosystem/mermaid-chart.md b/packages/mermaid/src/docs/ecosystem/mermaid-chart.md index 732b9b68cd..77a7020b77 100644 --- a/packages/mermaid/src/docs/ecosystem/mermaid-chart.md +++ b/packages/mermaid/src/docs/ecosystem/mermaid-chart.md @@ -6,7 +6,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
-Mermaid Chart - A smarter way to create diagrams | Product Hunt +Mermaid Chart - A smarter way to create diagrams | Product Hunt ## About @@ -16,17 +16,20 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun - **Editor** - A web based editor for creating and editing Mermaid diagrams. -- **Visual Editor** - The Visual Editor enables users of all skill levels to create diagrams easily and efficiently, with both GUI and code-based editing options. +- **Mermaid AI** - Use our embedded AI Chat to generate diagrams from natural language descriptions. -- **AI Chat** - Use our embedded AI Chat to generate diagrams from natural language descriptions. +- **Whiteboard** - A virtual whiteboard for creating and editing Mermaid diagrams. - **Plugins** - A plugin system for extending the functionality of Mermaid. Official Mermaid Chart plugins: - [Mermaid Chart GPT](https://chat.openai.com/g/g-1IRFKwq4G-mermaid-chart) + - [Confluence](https://marketplace.atlassian.com/apps/1234056/mermaid-chart-for-confluence?hosting=cloud&tab=overview) + - [Jira](https://marketplace.atlassian.com/apps/1234810/mermaid-chart-for-jira?tab=overview&hosting=cloud) - [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=MermaidChart.vscode-mermaid-chart) - [JetBrains IDE](https://plugins.jetbrains.com/plugin/23043-mermaid-chart) + - [Google Docs](https://gsuite.google.com/marketplace/app/mermaidchart/947683068472) - [Microsoft PowerPoint and Word](https://appsource.microsoft.com/en-us/product/office/WA200006214?tab=Overview) Visit our [Plugins](https://www.mermaidchart.com/plugins) page for more information. diff --git a/packages/mermaid/src/docs/ecosystem/tutorials.md b/packages/mermaid/src/docs/ecosystem/tutorials.md index d5bf9330db..7258361bfb 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 ed2df32dc4..627efdaa1b 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 d4ee1067f1..14c56370aa 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 4ada1e05cd..d15f79cdc3 100644 --- a/packages/mermaid/src/docs/news/blog.md +++ b/packages/mermaid/src/docs/news/blog.md @@ -1,5 +1,71 @@ # Blog +## [Mermaid 11.4 is out: New Features and Kanban Diagramming](https://www.mermaidchart.com/blog/posts/mermaid-11-4-is-out-new-features-and-kanban-diagramming) + +Mermaid 11.4 brings enhanced functionality with the introduction of Kanban diagrams, allowing users to create visual workflows with status columns and task details. + +October 31, 2024 · 2 mins + +## [How To Build an ER Diagram with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-build-an-er-diagram-with-mermaid-chart) + +An entity relationship (ER) diagram acts like a blueprint for your database. This makes ER diagrams effective tools for anyone dealing with complex databases, data modeling, and AI model training. + +October 24, 2024 · 4 mins + +## [Expanding the Horizons of Mermaid Flowcharts: Introducing 30 New Shapes!](https://www.mermaidchart.com/blog/posts/new-mermaid-flowchart-shapes/) + +24 September 2024 · 5 mins + +Discover 30 new shapes in Mermaid flowcharts, offering enhanced clarity, customization, and versatility for more dynamic and expressive visualizations. + +## [Introducing Architecture Diagrams in Mermaid](https://www.mermaidchart.com/blog/posts/mermaid-supports-architecture-diagrams/) + +2 September 2024 · 2 mins + +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 + +The Mermaid AI chat interface + +## [How to Make a Sequence Diagram with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-sequence-diagram-in-mermaid-chart-step-by-step-guide/) + +8 July 2024 · 6 mins + +Sequence diagrams are important for communicating complex systems in a clear and concise manner. + +## [How to Use the New “Comments” Feature in Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-use-the-new-comments-feature-in-mermaid-chart/) + +2 July 2024 · 3 mins + +How to Use the New Comments Feature in Mermaid Chart + +## [How to Use the official Mermaid Chart for Confluence app](https://www.mermaidchart.com/blog/posts/how-to-use-the-official-mermaid-chart-for-confluence-app/) + +21 May 2024 · 4 mins + +It doesn’t matter if you’re a data enthusiast, software engineer, or visual storyteller; our Confluence app can allow you to embed Mermaid Chart diagrams — and dynamically edit them — within your Confluence pages. + ## [How to Choose the Right Documentation Software](https://www.mermaidchart.com/blog/posts/how-to-choose-the-right-documentation-software/) 7 May 2024 · 5 mins diff --git a/packages/mermaid/src/docs/package.json b/packages/mermaid/src/docs/package.json index 146c4d2d88..7cfb8ab6cb 100644 --- a/packages/mermaid/src/docs/package.json +++ b/packages/mermaid/src/docs/package.json @@ -1,6 +1,7 @@ { - "name": "docs", + "name": "@mermaid-js/docs", "private": true, + "version": "0.0.1", "type": "module", "scripts": { "dev": "vitepress --port 3333 --open", diff --git a/packages/mermaid/src/docs/syntax/architecture.md b/packages/mermaid/src/docs/syntax/architecture.md new file mode 100644 index 0000000000..3fc5629f4d --- /dev/null +++ b/packages/mermaid/src/docs/syntax/architecture.md @@ -0,0 +1,175 @@ +# 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 database1(database)[My Database] +``` + +creates the service identified as `database1`, using the icon `database`, with the label `My Database`. + +If the service belongs to a group, it can be placed inside it through the optional `in` keyword + +``` +service database1(database)[My 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 [here](../config/icons.md). + +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/block.md b/packages/mermaid/src/docs/syntax/block.md index 7c5907dc7e..5b8aa1c999 100644 --- a/packages/mermaid/src/docs/syntax/block.md +++ b/packages/mermaid/src/docs/syntax/block.md @@ -106,7 +106,7 @@ block-beta a["A label"] b:2 c:2 d ``` -In this example, the block labeled "A wide one" spans two columns, while blocks 'b', 'c', and 'd' are allocated their own columns. This flexibility in block sizing is crucial for accurately representing systems with components of varying significance or size. +In this example, the block labeled "A labels" spans one column, while blocks 'b', 'c' span 2 columns, and 'd' is again allocated its own column. This flexibility in block sizing is crucial for accurately representing systems with components of varying significance or size. ### Creating Composite Blocks @@ -153,6 +153,19 @@ block-beta This example demonstrates how Mermaid dynamically adjusts the width of the columns to accommodate the widest block, in this case, 'a' and the composite block 'e'. This dynamic adjustment is essential for creating visually balanced and easy-to-understand diagrams. +**Merging Blocks Horizontally:** +In scenarios where you need to stack blocks horizontally, you can use column width to accomplish the task. Blocks can be arranged vertically by putting them in a single column. Here is how you can create a block diagram in which 4 blocks are stacked on top of each other: + +```mermaid-example +block-beta + block + columns 1 + a["A label"] b c d + end +``` + +In this example, the width of the merged block dynamically adjusts to the width of the largest child block. + With these advanced configuration options, Mermaid's block diagrams can be tailored to represent a wide array of complex systems and structures. The flexibility offered by these features enables users to create diagrams that are both informative and visually appealing. In the following sections, we will explore further capabilities, including different block shapes and linking options. ## 4. Block Varieties and Shapes diff --git a/packages/mermaid/src/docs/syntax/classDiagram.md b/packages/mermaid/src/docs/syntax/classDiagram.md index 029d11b540..552670d3f8 100644 --- a/packages/mermaid/src/docs/syntax/classDiagram.md +++ b/packages/mermaid/src/docs/syntax/classDiagram.md @@ -277,6 +277,34 @@ And `Link` can be one of: | -- | Solid | | .. | Dashed | +### Lollipop Interfaces + +Classes can also be given a special relation type that defines a lollipop interface on the class. A lollipop interface is defined using the following syntax: + +- `bar ()-- foo` +- `foo --() bar` + +The interface (bar) with the lollipop connects to the class (foo). + +Note: Each interface that is defined is unique and is meant to not be shared between classes / have multiple edges connecting to it. + +```mermaid-example +classDiagram + bar ()-- foo +``` + +```mermaid-example +classDiagram + class Class01 { + int amount + draw() + } + Class01 --() bar + Class02 --() bar + + foo ()-- Class01 +``` + ## Define Namespace A namespace groups classes. @@ -518,10 +546,12 @@ Beginner's tip—a full example using interactive links in an HTML page: ## Styling -### Styling a node (v10.7.0+) +### Styling a node It is possible to apply specific styles such as a thicker border or a different background color to an individual node using the `style` keyword. +Note that notes and namespaces cannot be styled individually but do support themes. + ```mermaid-example classDiagram class Animal @@ -533,159 +563,108 @@ classDiagram #### Classes More convenient than defining the style every time is to define a class of styles and attach this class to the nodes that -should have a different look. This is done by predefining classes in css styles that can be applied from the graph definition using the `cssClass` statement or the `:::` short hand. +should have a different look. -```html - +A class definition looks like the example below: + +``` +classDef className fill:#f9f,stroke:#333,stroke-width:4px; +``` + +Also, it is possible to define style to multiple classes in one statement: + +``` +classDef firstClassName,secondClassName font-size:12pt; ``` -Then attaching that class to a specific node: +Attachment of a class to a node is done as per below: ``` - cssClass "nodeId1" styleClass; +cssClass "nodeId1" className; ``` It is also possible to attach a class to a list of nodes in one statement: ``` - cssClass "nodeId1,nodeId2" styleClass; +cssClass "nodeId1,nodeId2" className; ``` A shorter form of adding a class is to attach the classname to the node using the `:::` operator: ```mermaid-example classDiagram - class Animal:::styleClass + class Animal:::someclass + classDef someclass fill:#f96 ``` Or: ```mermaid-example classDiagram - class Animal:::styleClass { + class Animal:::someclass { -int sizeInFeet -canEat() } + classDef someclass fill:#f96 ``` -?> cssClasses cannot be added using this shorthand method at the same time as a relation statement. +### Default class -?> Due to limitations with existing markup for class diagrams, it is not currently possible to define css classes within the diagram itself. **_Coming soon!_** +If a class is named default it will be applied to all nodes. Specific styles and classes should be defined afterwards to override the applied default styling. -### Default Styles +``` +classDef default fill:#f9f,stroke:#333,stroke-width:4px; +``` -The main styling of the class diagram is done with a preset number of css classes. During rendering these classes are extracted from the file located at src/themes/class.scss. The classes used here are described below: +```mermaid-example +classDiagram + class Animal:::pink + class Mineral -| Class | Description | -| ------------------ | ----------------------------------------------------------------- | -| g.classGroup text | Styles for general class text | -| classGroup .title | Styles for general class title | -| g.classGroup rect | Styles for class diagram rectangle | -| g.classGroup line | Styles for class diagram line | -| .classLabel .box | Styles for class label box | -| .classLabel .label | Styles for class label text | -| composition | Styles for composition arrow head and arrow line | -| aggregation | Styles for aggregation arrow head and arrow line(dashed or solid) | -| dependency | Styles for dependency arrow head and arrow line | + classDef default fill:#f96,color:red + classDef pink color:#f9f +``` -#### Sample stylesheet +### CSS Classes -```scss -body { - background: white; -} +It is also possible to predefine classes in CSS styles that can be applied from the graph definition as in the example +below: -g.classGroup text { - fill: $nodeBorder; - stroke: none; - font-family: 'trebuchet ms', verdana, arial; - font-family: var(--mermaid-font-family); - font-size: 10px; +**Example style** - .title { - font-weight: bolder; +```html + +``` -#compositionEnd { - @include composition; -} +**Example definition** -@mixin aggregation { - fill: $nodeBkg; - stroke: $nodeBorder; - stroke-width: 1; -} +```mermaid-example +classDiagram + class Animal:::styleClass +``` -#aggregationStart { - @include aggregation; -} +> cssClasses cannot be added using this shorthand method at the same time as a relation statement. -#aggregationEnd { - @include aggregation; -} +## Configuration -#dependencyStart { - @include composition; -} +### Members Box -#dependencyEnd { - @include composition; -} +It is possible to hide the empty members box of a class node. -#extensionStart { - @include composition; -} +This is done by changing the **hideEmptyMembersBox** value of the class diagram configuration. For more information on how to edit the Mermaid configuration see the [configuration page.](https://mermaid.js.org/config/configuration.html) -#extensionEnd { - @include composition; -} +```mermaid-example +--- + config: + class: + hideEmptyMembersBox: true +--- +classDiagram + class Duck ``` - -## Configuration - -`Coming soon!` diff --git a/packages/mermaid/src/docs/syntax/entityRelationshipDiagram.md b/packages/mermaid/src/docs/syntax/entityRelationshipDiagram.md index 3b874f6893..8c83d22320 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/docs/syntax/flowchart.md b/packages/mermaid/src/docs/syntax/flowchart.md index acffbc6931..829b71c2dc 100644 --- a/packages/mermaid/src/docs/syntax/flowchart.md +++ b/packages/mermaid/src/docs/syntax/flowchart.md @@ -194,6 +194,404 @@ flowchart TD id1(((This is the text in the circle))) ``` +## Expanded Node Shapes in Mermaid Flowcharts (v11.3.0+) + +Mermaid introduces 30 new shapes to enhance the flexibility and precision of flowchart creation. These new shapes provide more options to represent processes, decisions, events, data storage visually, and other elements within your flowcharts, improving clarity and semantic meaning. + +New Syntax for Shape Definition + +Mermaid now supports a general syntax for defining shape types to accommodate the growing number of shapes. This syntax allows you to assign specific shapes to nodes using a clear and flexible format: + +``` +A@{ shape: rect } +``` + +This syntax creates a node A as a rectangle. It renders in the same way as `A["A"]`, or `A`. + +### Complete List of New Shapes + +Below is a comprehensive list of the newly introduced shapes and their corresponding semantic meanings, short names, and aliases: + + + +### Example Flowchart with New Shapes + +Here’s an example flowchart that utilizes some of the newly introduced shapes: + +```mermaid-example +flowchart RL + A@{ shape: manual-file, label: "File Handling"} + B@{ shape: manual-input, label: "User Input"} + C@{ shape: docs, label: "Multiple Documents"} + D@{ shape: procs, label: "Process Automation"} + E@{ shape: paper-tape, label: "Paper Records"} +``` + +### Process + +```mermaid-example +flowchart TD + A@{ shape: rect, label: "This is a process" } +``` + +### Event + +```mermaid-example +flowchart TD + A@{ shape: rounded, label: "This is an event" } +``` + +### Terminal Point (Stadium) + +```mermaid-example +flowchart TD + A@{ shape: stadium, label: "Terminal point" } +``` + +### Subprocess + +```mermaid-example +flowchart TD + A@{ shape: subproc, label: "This is a subprocess" } +``` + +### Database (Cylinder) + +```mermaid-example +flowchart TD + A@{ shape: cyl, label: "Database" } +``` + +### Start (Circle) + +```mermaid-example +flowchart TD + A@{ shape: circle, label: "Start" } +``` + +### Odd + +```mermaid-example +flowchart TD + A@{ shape: odd, label: "Odd shape" } +``` + +### Decision (Diamond) + +```mermaid-example +flowchart TD + A@{ shape: diamond, label: "Decision" } +``` + +### Prepare Conditional (Hexagon) + +```mermaid-example +flowchart TD + A@{ shape: hex, label: "Prepare conditional" } +``` + +### Data Input/Output (Lean Right) + +```mermaid-example +flowchart TD + A@{ shape: lean-r, label: "Input/Output" } +``` + +### Data Input/Output (Lean Left) + +```mermaid-example +flowchart TD + A@{ shape: lean-l, label: "Output/Input" } +``` + +### Priority Action (Trapezoid Base Bottom) + +```mermaid-example +flowchart TD + A@{ shape: trap-b, label: "Priority action" } +``` + +### Manual Operation (Trapezoid Base Top) + +```mermaid-example +flowchart TD + A@{ shape: trap-t, label: "Manual operation" } +``` + +### Stop (Double Circle) + +```mermaid-example +flowchart TD + A@{ shape: dbl-circ, label: "Stop" } +``` + +### Text Block + +```mermaid-example +flowchart TD + A@{ shape: text, label: "This is a text block" } +``` + +### Card (Notched Rectangle) + +```mermaid-example +flowchart TD + A@{ shape: notch-rect, label: "Card" } +``` + +### Lined/Shaded Process + +```mermaid-example +flowchart TD + A@{ shape: lin-rect, label: "Lined process" } +``` + +### Start (Small Circle) + +```mermaid-example +flowchart TD + A@{ shape: sm-circ, label: "Small start" } +``` + +### Stop (Framed Circle) + +```mermaid-example +flowchart TD + A@{ shape: framed-circle, label: "Stop" } +``` + +### Fork/Join (Long Rectangle) + +```mermaid-example +flowchart TD + A@{ shape: fork, label: "Fork or Join" } +``` + +### Collate (Hourglass) + +```mermaid-example +flowchart TD + A@{ shape: hourglass, label: "Collate" } +``` + +### Comment (Curly Brace) + +```mermaid-example +flowchart TD + A@{ shape: comment, label: "Comment" } +``` + +### Comment Right (Curly Brace Right) + +```mermaid-example +flowchart TD + A@{ shape: brace-r, label: "Comment" } +``` + +### Comment with braces on both sides + +```mermaid-example +flowchart TD + A@{ shape: braces, label: "Comment" } +``` + +### Com Link (Lightning Bolt) + +```mermaid-example +flowchart TD + A@{ shape: bolt, label: "Communication link" } +``` + +### Document + +```mermaid-example +flowchart TD + A@{ shape: doc, label: "Document" } +``` + +### Delay (Half-Rounded Rectangle) + +```mermaid-example +flowchart TD + A@{ shape: delay, label: "Delay" } +``` + +### Direct Access Storage (Horizontal Cylinder) + +```mermaid-example +flowchart TD + A@{ shape: das, label: "Direct access storage" } +``` + +### Disk Storage (Lined Cylinder) + +```mermaid-example +flowchart TD + A@{ shape: lin-cyl, label: "Disk storage" } +``` + +### Display (Curved Trapezoid) + +```mermaid-example +flowchart TD + A@{ shape: curv-trap, label: "Display" } +``` + +### Divided Process (Divided Rectangle) + +```mermaid-example +flowchart TD + A@{ shape: div-rect, label: "Divided process" } +``` + +### Extract (Small Triangle) + +```mermaid-example +flowchart TD + A@{ shape: tri, label: "Extract" } +``` + +### Internal Storage (Window Pane) + +```mermaid-example +flowchart TD + A@{ shape: win-pane, label: "Internal storage" } +``` + +### Junction (Filled Circle) + +```mermaid-example +flowchart TD + A@{ shape: f-circ, label: "Junction" } +``` + +### Lined Document + +```mermaid-example +flowchart TD + A@{ shape: lin-doc, label: "Lined document" } +``` + +### Loop Limit (Notched Pentagon) + +```mermaid-example +flowchart TD + A@{ shape: notch-pent, label: "Loop limit" } +``` + +### Manual File (Flipped Triangle) + +```mermaid-example +flowchart TD + A@{ shape: flip-tri, label: "Manual file" } +``` + +### Manual Input (Sloped Rectangle) + +```mermaid-example +flowchart TD + A@{ shape: sl-rect, label: "Manual input" } +``` + +### Multi-Document (Stacked Document) + +```mermaid-example +flowchart TD + A@{ shape: docs, label: "Multiple documents" } +``` + +### Multi-Process (Stacked Rectangle) + +```mermaid-example +flowchart TD + A@{ shape: processes, label: "Multiple processes" } +``` + +### Paper Tape (Flag) + +```mermaid-example +flowchart TD + A@{ shape: flag, label: "Paper tape" } +``` + +### Stored Data (Bow Tie Rectangle) + +```mermaid-example +flowchart TD + A@{ shape: bow-rect, label: "Stored data" } +``` + +### Summary (Crossed Circle) + +```mermaid-example +flowchart TD + A@{ shape: cross-circ, label: "Summary" } +``` + +### Tagged Document + +```mermaid-example +flowchart TD + A@{ shape: tag-doc, label: "Tagged document" } +``` + +### Tagged Process (Tagged Rectangle) + +```mermaid-example +flowchart TD + A@{ shape: tag-rect, label: "Tagged process" } +``` + +## Special shapes in Mermaid Flowcharts (v11.3.0+) + +Mermaid also introduces 2 special shapes to enhance your flowcharts: **icon** and **image**. These shapes allow you to include icons and images directly within your flowcharts, providing more visual context and clarity. + +### Icon Shape + +You can use the `icon` shape to include an icon in your flowchart. To use icons, you need to register the icon pack first. Follow the instructions provided [here](../config/icons.md). The syntax for defining an icon shape is as follows: + +```mermaid-example +flowchart TD + A@{ icon: "fa:user", form: "square", label: "User Icon", pos: "t", h: 60 } +``` + +### Parameters + +- **icon**: The name of the icon from the registered icon pack. +- **form**: Specifies the background shape of the icon. If not defined there will be no background to icon. Options include: + - `square` + - `circle` + - `rounded` +- **label**: The text label associated with the icon. This can be any string. If not defined, no label will be displayed. +- **pos**: The position of the label. If not defined label will default to bottom of icon. Possible values are: + - `t` + - `b` +- **h**: The height of the icon. If not defined this will default to 48 which is minimum. + +### Image Shape + +You can use the `image` shape to include an image in your flowchart. The syntax for defining an image shape is as follows: + +```mermaid-example +flowchart TD + A@{ img: "https://example.com/image.png", label: "Image Label", pos: "t", w: 60, h: 60, constraint: "off" } +``` + +### Parameters + +- **img**: The URL of the image to be displayed. +- **label**: The text label associated with the image. This can be any string. If not defined, no label will be displayed. +- **pos**: The position of the label. If not defined, the label will default to the bottom of the image. Possible values are: + - `t` + - `b` +- **w**: The width of the image. If not defined, this will default to the natural width of the image. +- **h**: The height of the image. If not defined, this will default to the natural height of the image. +- **constraint**: Determines if the image should constrain the node size. This setting also ensures the image maintains its original aspect ratio, adjusting the height (`h`) accordingly to the width (`w`). If not defined, this will default to `off` Possible values are: + - `on` + - `off` + +These new shapes provide additional flexibility and visual appeal to your flowcharts, making them more informative and engaging. + ## Links between nodes Nodes can be connected with links/edges. It is possible to have different types of links or attach a text string to a link. diff --git a/packages/mermaid/src/docs/syntax/gantt.md b/packages/mermaid/src/docs/syntax/gantt.md index 8497b96a13..eab35d09f2 100644 --- a/packages/mermaid/src/docs/syntax/gantt.md +++ b/packages/mermaid/src/docs/syntax/gantt.md @@ -114,7 +114,7 @@ The `title` is an _optional_ string to be displayed at the top of the Gantt char The `excludes` is an _optional_ attribute that accepts specific dates in YYYY-MM-DD format, days of the week ("sunday") or "weekends", but not the word "weekdays". These date will be marked on the graph, and be excluded from the duration calculation of tasks. Meaning that if there are excluded dates during a task interval, the number of 'skipped' days will be added to the end of the task to ensure the duration is as specified in the code. -#### Weekend (v\+) +#### Weekend (v\11.0.0+) When excluding weekends, it is possible to configure the weekends to be either Friday and Saturday or Saturday and Sunday. By default weekends are Saturday and Sunday. To define the weekend start day, there is an _optional_ attribute `weekend` that can be added in a new line followed by either `friday` or `saturday`. @@ -390,7 +390,7 @@ mermaid.ganttConfig = { sectionFontSize: 24, // Font size for sections numberSectionStyles: 1, // The number of alternating section styles axisFormat: '%d/%m', // Date/time format of the axis - tickInterval: '1 week', // Axis ticks + tickInterval: '1week', // Axis ticks topAxis: true, // When this flag is set, date labels will be added to the top of the chart displayMode: 'compact', // Turns compact mode on weekday: 'sunday', // On which day a week-based interval should start diff --git a/packages/mermaid/src/docs/syntax/gitgraph.md b/packages/mermaid/src/docs/syntax/gitgraph.md index d0791718b8..2b3f1a88b0 100644 --- a/packages/mermaid/src/docs/syntax/gitgraph.md +++ b/packages/mermaid/src/docs/syntax/gitgraph.md @@ -571,7 +571,7 @@ Usage example: commit ``` -### Bottom to Top (`BT:`) (v+) +### Bottom to Top (`BT:`) (v11.0.0+) In `BT` (**Bottom-to-Top**) orientation, the commits run from bottom to top of the graph and branches are arranged side-by-side. diff --git a/packages/mermaid/src/docs/syntax/kanban.md b/packages/mermaid/src/docs/syntax/kanban.md new file mode 100644 index 0000000000..c50eed7d8c --- /dev/null +++ b/packages/mermaid/src/docs/syntax/kanban.md @@ -0,0 +1,113 @@ +# Mermaid Kanban Diagram Documentation + +Mermaid’s Kanban diagram allows you to create visual representations of tasks moving through different stages of a workflow. This guide explains how to use the Kanban diagram syntax, based on the provided example. + +## Overview + +A Kanban diagram in Mermaid starts with the kanban keyword, followed by the definition of columns (stages) and tasks within those columns. + +```mermaid-example +kanban + column1[Column Title] + task1[Task Description] +``` + +## Defining Columns + +Columns represent the different stages in your workflow, such as “Todo,” “In Progress,” “Done,” etc. Each column is defined using a unique identifier and a title enclosed in square brackets. + +**Syntax:** + +``` +columnId[Column Title] +``` + +- columnId: A unique identifier for the column. +- [Column Title]: The title displayed on the column header. + +Like this `id1[Todo]` + +## Adding Tasks to Columns + +Tasks are listed under their respective columns with an indentation. Each task also has a unique identifier and a description enclosed in square brackets. + +**Syntax:** + +``` +taskId[Task Description] +``` + + â€ĸ taskId: A unique identifier for the task. + â€ĸ [Task Description]: The description of the task. + +**Example:** + +``` +docs[Create Documentation] +``` + +## Adding Metadata to Tasks + +You can include additional metadata for each task using the @{ ... } syntax. Metadata can contain key-value pairs like assigned, ticket, priority, etc. This will be rendered added to the rendering of the node. + +## Supported Metadata Keys + + â€ĸ assigned: Specifies who is responsible for the task. + â€ĸ ticket: Links the task to a ticket or issue number. + â€ĸ priority: Indicates the urgency of the task. Allowed values: 'Very High', 'High', 'Low' and 'Very Low' + +```mermaid-example +kanban +todo[Todo] + id3[Update Database Function]@{ ticket: MC-2037, assigned: 'knsv', priority: 'High' } +``` + +## Configuration Options + +You can customize the Kanban diagram using a configuration block at the beginning of your markdown file. This is useful for setting global settings like a base URL for tickets. Currently there is one configuration option for kanban diagrams `ticketBaseUrl`. This can be set as in the the following example: + +```yaml +--- +config: + kanban: + ticketBaseUrl: 'https://yourproject.atlassian.net/browse/#TICKET#' +--- +``` + +When the kanban item has an assigned ticket number the ticket number in the diagram will have a link to an external system where the ticket is defined. The `ticketBaseUrl` sets the base URL to the external system and #TICKET# is replaced with the ticket value from task metadata to create a valid link. + +## Full Example + +Below is the full Kanban diagram based on the provided example: + +```mermaid-example +--- +config: + kanban: + ticketBaseUrl: 'https://mermaidchart.atlassian.net/browse/#TICKET#' +--- +kanban + Todo + [Create Documentation] + docs[Create Blog about the new diagram] + [In progress] + id6[Create renderer so that it works in all cases. We also add som extra text here for testing purposes. And some more just for the extra flare.] + id9[Ready for deploy] + id8[Design grammar]@{ assigned: 'knsv' } + id10[Ready for test] + id4[Create parsing tests]@{ ticket: MC-2038, assigned: 'K.Sveidqvist', priority: 'High' } + id66[last item]@{ priority: 'Very Low', assigned: 'knsv' } + id11[Done] + id5[define getData] + id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char]@{ ticket: MC-2036, priority: 'Very High'} + id3[Update DB function]@{ ticket: MC-2037, assigned: knsv, priority: 'High' } + + id12[Can't reproduce] + id3[Weird flickering in Firefox] +``` + +In conclusion, creating a Kanban diagram in Mermaid is a straightforward process that effectively visualizes your workflow. Start by using the kanban keyword to initiate the diagram. Define your columns with unique identifiers and titles to represent different stages of your project. Under each column, list your tasks—also with unique identifiers—and provide detailed descriptions as needed. Remember that proper indentation is crucial; tasks must be indented under their parent columns to maintain the correct structure. + +You can enhance your diagram by adding optional metadata to tasks using the @{ ... } syntax, which allows you to include additional context such as assignee, ticket numbers, and priority levels. For further customization, utilize the configuration block at the top of your file to set global options like ticketBaseUrl for linking tickets directly from your diagram. + +By adhering to these guidelines—ensuring unique identifiers, proper indentation, and utilizing metadata and configuration options—you can create a comprehensive and customized Kanban board that effectively maps out your project’s workflow using Mermaid. diff --git a/packages/mermaid/src/docs/syntax/packet.md b/packages/mermaid/src/docs/syntax/packet.md index 52a0de8873..c7b6cb71b4 100644 --- a/packages/mermaid/src/docs/syntax/packet.md +++ b/packages/mermaid/src/docs/syntax/packet.md @@ -1,4 +1,4 @@ -# Packet Diagram (v+) +# Packet Diagram (v11.0.0+) ## Introduction diff --git a/packages/mermaid/src/docs/syntax/sequenceDiagram.md b/packages/mermaid/src/docs/syntax/sequenceDiagram.md index 249f7bde03..2357b9bf43 100644 --- a/packages/mermaid/src/docs/syntax/sequenceDiagram.md +++ b/packages/mermaid/src/docs/syntax/sequenceDiagram.md @@ -105,6 +105,9 @@ end box rgb(33,66,99) ... actors ... end +box rgba(33,66,99,0.5) +... actors ... +end ``` ```note @@ -143,18 +146,18 @@ Messages can be of two displayed either solid or with a dotted line. There are ten types of arrows currently supported: -| Type | Description | -| -------- | ----------------------------------------------------------------------- | -| `->` | Solid line without arrow | -| `-->` | Dotted line without arrow | -| `->>` | Solid line with arrowhead | -| `-->>` | Dotted line with arrowhead | -| `<<->>` | Solid line with bidirectional arrowheads (v+) | -| `<<-->>` | Dotted line with bidirectional arrowheads (v+) | -| `-x` | Solid line with a cross at the end | -| `--x` | Dotted line with a cross at the end. | -| `-)` | Solid line with an open arrow at the end (async) | -| `--)` | Dotted line with a open arrow at the end (async) | +| Type | Description | +| -------- | ---------------------------------------------------- | +| `->` | Solid line without arrow | +| `-->` | Dotted line without arrow | +| `->>` | Solid line with arrowhead | +| `-->>` | Dotted line with arrowhead | +| `<<->>` | Solid line with bidirectional arrowheads (v11.0.0+) | +| `<<-->>` | Dotted line with bidirectional arrowheads (v11.0.0+) | +| `-x` | Solid line with a cross at the end | +| `--x` | Dotted line with a cross at the end | +| `-)` | Solid line with an open arrow at the end (async) | +| `--)` | Dotted line with a open arrow at the end (async) | ## Activations @@ -394,6 +397,12 @@ sequenceDiagram It is possible to highlight flows by providing colored background rects. This is done by the notation +``` +rect COLOR +... content ... +end +``` + The colors are defined using rgb and rgba syntax. ``` diff --git a/packages/mermaid/src/internals.ts b/packages/mermaid/src/internals.ts new file mode 100644 index 0000000000..7cc058cb3a --- /dev/null +++ b/packages/mermaid/src/internals.ts @@ -0,0 +1,33 @@ +import { getConfig } from './config.js'; +import common from './diagrams/common/common.js'; +import { log } from './logger.js'; +import { insertCluster } from './rendering-util/rendering-elements/clusters.js'; +import { + insertEdge, + insertEdgeLabel, + positionEdgeLabel, +} from './rendering-util/rendering-elements/edges.js'; +import insertMarkers from './rendering-util/rendering-elements/markers.js'; +import { insertNode } from './rendering-util/rendering-elements/nodes.js'; +import { labelHelper } from './rendering-util/rendering-elements/shapes/util.js'; +import { interpolateToCurve } from './utils.js'; + +/** + * Internal helpers for mermaid + * @deprecated - This should not be used by external packages, as the definitions will change without notice. + */ +export const internalHelpers = { + common, + getConfig, + insertCluster, + insertEdge, + insertEdgeLabel, + insertMarkers, + insertNode, + interpolateToCurve, + labelHelper, + log, + positionEdgeLabel, +}; + +export type InternalHelpers = typeof internalHelpers; diff --git a/packages/mermaid/src/mermaid.ts b/packages/mermaid/src/mermaid.ts index eb9007f9cd..e9fc9196aa 100644 --- a/packages/mermaid/src/mermaid.ts +++ b/packages/mermaid/src/mermaid.ts @@ -2,29 +2,39 @@ * 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 './rendering-util/icons.js'; import { dedent } from 'ts-dedent'; import type { MermaidConfig } from './config.type.js'; -import { log } from './logger.js'; -import utils from './utils.js'; -import type { ParseOptions, ParseResult, RenderResult } from './types.js'; -import { mermaidAPI } from './mermaidAPI.js'; -import { registerLazyLoadedDiagrams, detectType } from './diagram-api/detectType.js'; +import { detectType, registerLazyLoadedDiagrams } from './diagram-api/detectType.js'; +import { addDiagrams } from './diagram-api/diagram-orchestration.js'; import { loadRegisteredDiagrams } from './diagram-api/loadDiagram.js'; +import type { ExternalDiagramDefinition, SVG, SVGGroup } from './diagram-api/types.js'; import type { ParseErrorFunction } from './Diagram.js'; -import { isDetailedError } from './utils.js'; -import type { DetailedError } from './utils.js'; -import type { ExternalDiagramDefinition } from './diagram-api/types.js'; import type { UnknownDiagramError } from './errors.js'; -import { addDiagrams } from './diagram-api/diagram-orchestration.js'; +import type { InternalHelpers } from './internals.js'; +import { log } from './logger.js'; +import { mermaidAPI } from './mermaidAPI.js'; +import type { LayoutLoaderDefinition, RenderOptions } from './rendering-util/render.js'; +import { registerLayoutLoaders } from './rendering-util/render.js'; +import type { LayoutData } from './rendering-util/types.js'; +import type { ParseOptions, ParseResult, RenderResult } from './types.js'; +import type { DetailedError } from './utils.js'; +import utils, { isDetailedError } from './utils.js'; export type { - MermaidConfig, DetailedError, ExternalDiagramDefinition, + InternalHelpers, + LayoutData, + LayoutLoaderDefinition, + MermaidConfig, ParseErrorFunction, - RenderResult, ParseOptions, ParseResult, + RenderOptions, + RenderResult, + SVG, + SVGGroup, UnknownDiagramError, }; @@ -420,11 +430,13 @@ export interface Mermaid { */ init: typeof init; run: typeof run; + registerLayoutLoaders: typeof registerLayoutLoaders; registerExternalDiagrams: typeof registerExternalDiagrams; initialize: typeof initialize; contentLoaded: typeof contentLoaded; setParseErrorHandler: typeof setParseErrorHandler; detectType: typeof detectType; + registerIconPacks: typeof registerIconPacks; } const mermaid: Mermaid = { @@ -435,11 +447,13 @@ const mermaid: Mermaid = { init, run, registerExternalDiagrams, + registerLayoutLoaders, initialize, parseError: undefined, contentLoaded, setParseErrorHandler, detectType, + registerIconPacks, }; export default mermaid; diff --git a/packages/mermaid/src/mermaidAPI.spec.ts b/packages/mermaid/src/mermaidAPI.spec.ts index 7958f397e0..5bd1b1dfcf 100644 --- a/packages/mermaid/src/mermaidAPI.spec.ts +++ b/packages/mermaid/src/mermaidAPI.spec.ts @@ -1,4 +1,4 @@ -import { vi, it, expect, describe, beforeEach } from 'vitest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; // ------------------------------------- // Mocks and mocking @@ -30,7 +30,6 @@ vi.mock('./diagrams/packet/renderer.js'); vi.mock('./diagrams/xychart/xychartRenderer.js'); vi.mock('./diagrams/requirement/requirementRenderer.js'); vi.mock('./diagrams/sequence/sequenceRenderer.js'); -vi.mock('./diagrams/state/stateRenderer-v2.js'); // ------------------------------------- @@ -67,8 +66,8 @@ vi.mock('stylis', () => { }); import { compile, serialize } from 'stylis'; -import { decodeEntities, encodeEntities } from './utils.js'; import { Diagram } from './Diagram.js'; +import { decodeEntities, encodeEntities } from './utils.js'; import { toBase64 } from './utils/base64.js'; /** @@ -694,18 +693,79 @@ describe('mermaidAPI', () => { await expect(mermaidAPI.parse('graph TD;A--x|text including URL space|B;')).resolves .toMatchInlineSnapshot(` { + "config": {}, "diagramType": "flowchart-v2", } `); }); + it('returns config when defined in frontmatter', async () => { + await expect( + mermaidAPI.parse(`--- +config: + theme: base + flowchart: + htmlLabels: true +--- +graph TD;A--x|text including URL space|B;`) + ).resolves.toMatchInlineSnapshot(` + { + "config": { + "flowchart": { + "htmlLabels": true, + }, + "theme": "base", + }, + "diagramType": "flowchart-v2", + } +`); + }); + + it('returns config when defined in directive', async () => { + await expect( + mermaidAPI.parse(`%%{init: { 'theme': 'base' } }%% +graph TD;A--x|text including URL space|B;`) + ).resolves.toMatchInlineSnapshot(` + { + "config": { + "theme": "base", + }, + "diagramType": "flowchart-v2", + } +`); + }); + + it('returns merged config when defined in frontmatter and directive', async () => { + await expect( + mermaidAPI.parse(`--- +config: + theme: forest + flowchart: + htmlLabels: true +--- +%%{init: { 'theme': 'base' } }%% +graph TD;A--x|text including URL space|B;`) + ).resolves.toMatchInlineSnapshot(` + { + "config": { + "flowchart": { + "htmlLabels": true, + }, + "theme": "base", + }, + "diagramType": "flowchart-v2", + } +`); + }); + it('returns true for valid definition with silent option', async () => { await expect( mermaidAPI.parse('graph TD;A--x|text including URL space|B;', { suppressErrors: true }) ).resolves.toMatchInlineSnapshot(` - { - "diagramType": "flowchart-v2", - } - `); + { + "config": {}, + "diagramType": "flowchart-v2", + } + `); }); }); @@ -719,7 +779,7 @@ describe('mermaidAPI', () => { // We have to have both the specific textDiagramType and the expected type name because the expected type may be slightly different than was is put in the diagram text (ex: in -v2 diagrams) const diagramTypesAndExpectations = [ { textDiagramType: 'C4Context', expectedType: 'c4' }, - { textDiagramType: 'classDiagram', expectedType: 'classDiagram' }, + { textDiagramType: 'classDiagram', expectedType: 'class' }, { textDiagramType: 'classDiagram-v2', expectedType: 'classDiagram' }, { textDiagramType: 'erDiagram', expectedType: 'er' }, { textDiagramType: 'graph', expectedType: 'flowchart-v2' }, diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index 7bca4d995e..910ecb5e88 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -6,25 +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'; const MAX_TEXTLENGTH = 50_000; const MAX_TEXTLENGTH_EXCEEDED_MSG = @@ -72,9 +73,9 @@ async function parse(text: string, parseOptions?: ParseOptions): Promise { addDiagrams(); try { - const { code } = processAndSetConfigs(text); + const { code, config } = processAndSetConfigs(text); const diagram = await getDiagramFromText(code); - return { diagramType: diagram.type }; + return { diagramType: diagram.type, config }; } catch (error) { if (parseOptions?.suppressErrors) { return false; @@ -145,7 +146,11 @@ export const createCssStyles = ( } // create the css styles for the tspan element and the text styles (only if there are textStyles) if (!isEmpty(styleClassDef.textStyles)) { - cssStyles += cssImportantStyles(styleClassDef.id, 'tspan', styleClassDef.textStyles); + cssStyles += cssImportantStyles( + styleClassDef.id, + 'tspan', + (styleClassDef?.textStyles || []).map((s) => s.replace('color', 'fill')) + ); } }); } @@ -450,6 +455,7 @@ const render = async function ( svgCode = DOMPurify.sanitize(svgCode, { ADD_TAGS: DOMPURIFY_TAGS, ADD_ATTR: DOMPURIFY_ATTR, + HTML_INTEGRATION_POINTS: { foreignobject: true }, }); } @@ -469,9 +475,10 @@ const render = async function ( }; /** - * @param options - Initial Mermaid options + * @param userOptions - Initial Mermaid options */ -function initialize(options: MermaidConfig = {}) { +function initialize(userOptions: MermaidConfig = {}) { + 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/preprocess.ts b/packages/mermaid/src/preprocess.ts index 10bc0adea6..a623260703 100644 --- a/packages/mermaid/src/preprocess.ts +++ b/packages/mermaid/src/preprocess.ts @@ -49,7 +49,7 @@ const processDirectives = (code: string) => { * @param code - The code to preprocess. * @returns The object containing the preprocessed code, title, and configuration. */ -export function preprocessDiagram(code: string): DiagramMetadata & { code: string } { +export function preprocessDiagram(code: string) { const cleanedCode = cleanupText(code); const frontMatterResult = processFrontmatter(cleanedCode); const directiveResult = processDirectives(frontMatterResult.text); @@ -59,5 +59,5 @@ export function preprocessDiagram(code: string): DiagramMetadata & { code: strin code, title: frontMatterResult.title, config, - }; + } satisfies DiagramMetadata & { code: string }; } diff --git a/packages/mermaid/src/rendering-util/createText.ts b/packages/mermaid/src/rendering-util/createText.ts index 462a4834f1..cc189e46e7 100644 --- a/packages/mermaid/src/rendering-util/createText.ts +++ b/packages/mermaid/src/rendering-util/createText.ts @@ -1,7 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ // @ts-nocheck TODO: Fix types +import { getConfig } from '../diagram-api/diagramAPI.js'; +import common, { hasKatex, renderKatex } from '../diagrams/common/common.js'; +import { select } from 'd3'; import type { MermaidConfig } from '../config.type.js'; -import type { Group } from '../diagram-api/types.js'; +import type { SVGGroup } from '../diagram-api/types.js'; import type { D3TSpanElement, D3TextElement } from '../diagrams/common/commonTypes.js'; import { log } from '../logger.js'; import { markdownToHTML, markdownToLines } from '../rendering-util/handle-markdown-text.js'; @@ -15,11 +18,18 @@ function applyStyle(dom, styleFn) { } } -function addHtmlSpan(element, node, width, classes, addBackground = false) { +async function addHtmlSpan(element, node, width, classes, addBackground = false) { const fo = element.append('foreignObject'); - const div = fo.append('xhtml:div'); + // This is not the final width but used in order to make sure the foreign + // object in firefox gets a width at all. The final width is fetched from the div + fo.attr('width', `${10 * width}px`); + fo.attr('height', `${10 * width}px`); - const label = node.label; + const div = fo.append('xhtml:div'); + let label = node.label; + if (node.label && hasKatex(node.label)) { + label = await renderKatex(node.label.replace(common.lineBreakRegex, '\n'), getConfig()); + } const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel'; const span = div.append('span'); span.html(label); @@ -29,7 +39,9 @@ function addHtmlSpan(element, node, width, classes, addBackground = false) { applyStyle(div, node.labelStyle); div.style('display', 'table-cell'); div.style('white-space', 'nowrap'); + div.style('line-height', '1.5'); div.style('max-width', width + 'px'); + div.style('text-align', 'center'); div.attr('xmlns', 'http://www.w3.org/1999/xhtml'); if (addBackground) { div.attr('class', 'labelBkg'); @@ -43,8 +55,8 @@ function addHtmlSpan(element, node, width, classes, addBackground = false) { bbox = div.node().getBoundingClientRect(); } - fo.style('width', bbox.width); - fo.style('height', bbox.height); + // fo.style('width', bbox.width); + // fo.style('height', bbox.height); return fo.node(); } @@ -76,7 +88,7 @@ function computeWidthOfText(parentNode: any, lineHeight: number, line: MarkdownL } export function computeDimensionOfText( - parentNode: Group, + parentNode: SVGGroup, lineHeight: number, text: string ): DOMRect | undefined { @@ -107,7 +119,7 @@ function createFormattedText( ) { const lineHeight = 1.1; const labelGroup = g.append('g'); - const bkg = labelGroup.insert('rect').attr('class', 'background'); + const bkg = labelGroup.insert('rect').attr('class', 'background').attr('style', 'stroke: none'); const textElement = labelGroup.append('text').attr('y', '-10.1'); let lineIndex = 0; for (const line of structuredText) { @@ -129,8 +141,8 @@ function createFormattedText( const bbox = textElement.node().getBBox(); const padding = 2; bkg - .attr('x', -padding) - .attr('y', -padding) + .attr('x', bbox.x - padding) + .attr('y', bbox.y - padding) .attr('width', bbox.width + 2 * padding) .attr('height', bbox.height + 2 * padding); @@ -180,7 +192,7 @@ export function replaceIconSubstring(text: string) { // Note when using from flowcharts converting the API isNode means classes should be set accordingly. When using htmlLabels => to sett classes to'nodeLabel' when isNode=true otherwise 'edgeLabel' // When not using htmlLabels => to set classes to 'title-row' when isTitle=true otherwise 'title-row' -export const createText = ( +export const createText = async ( el, text = '', { @@ -192,24 +204,79 @@ export const createText = ( width = 200, addSvgBackground = false, } = {}, - config: MermaidConfig + config?: MermaidConfig ) => { - log.info('createText', text, style, isTitle, classes, useHtmlLabels, isNode, addSvgBackground); + log.debug( + 'XYZ createText', + text, + style, + isTitle, + classes, + useHtmlLabels, + isNode, + 'addSvgBackground: ', + addSvgBackground + ); if (useHtmlLabels) { // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that? const htmlText = markdownToHTML(text, config); const decodedReplacedText = replaceIconSubstring(decodeEntities(htmlText)); + + //for Katex the text could contain escaped characters, \\relax that should be transformed to \relax + const inputForKatex = text.replace(/\\\\/g, '\\'); + const node = { isNode, - label: decodedReplacedText, + label: hasKatex(text) ? inputForKatex : decodedReplacedText, labelStyle: style.replace('fill:', 'color:'), }; - const vertexNode = addHtmlSpan(el, node, width, classes, addSvgBackground); + const vertexNode = await addHtmlSpan(el, node, width, classes, addSvgBackground); return vertexNode; } else { - const structuredText = markdownToLines(text, config); - const svgLabel = createFormattedText(width, el, structuredText, addSvgBackground); + //sometimes the user might add br tags with 1 or more spaces in between, so we need to replace them with
+ const sanitizeBR = text.replace(//g, '
'); + const structuredText = markdownToLines(sanitizeBR.replace('
', '
'), config); + const svgLabel = createFormattedText( + width, + el, + structuredText, + text ? addSvgBackground : false + ); + if (isNode) { + if (/stroke:/.exec(style)) { + style = style.replace('stroke:', 'lineColor:'); + } + + const nodeLabelTextStyle = style + .replace(/stroke:[^;]+;?/g, '') + .replace(/stroke-width:[^;]+;?/g, '') + .replace(/fill:[^;]+;?/g, '') + .replace(/color:/g, 'fill:'); + select(svgLabel).attr('style', nodeLabelTextStyle); + // svgLabel.setAttribute('style', style); + } else { + //On style, assume `stroke`, `stroke-width` are used for edge path, so remove them + // remove `fill` + // use `background` as `fill` for label rect, + + const edgeLabelRectStyle = style + .replace(/stroke:[^;]+;?/g, '') + .replace(/stroke-width:[^;]+;?/g, '') + .replace(/fill:[^;]+;?/g, '') + .replace(/background:/g, 'fill:'); + select(svgLabel) + .select('rect') + .attr('style', edgeLabelRectStyle.replace(/background:/g, 'fill:')); + + // for text, update fill color with `color` + const edgeLabelTextStyle = style + .replace(/stroke:[^;]+;?/g, '') + .replace(/stroke-width:[^;]+;?/g, '') + .replace(/fill:[^;]+;?/g, '') + .replace(/color:/g, 'fill:'); + select(svgLabel).select('text').attr('style', edgeLabelTextStyle); + } return svgLabel; } }; diff --git a/packages/mermaid/src/rendering-util/handle-markdown-text.ts b/packages/mermaid/src/rendering-util/handle-markdown-text.ts index 3846e7f37e..f898875cf0 100644 --- a/packages/mermaid/src/rendering-util/handle-markdown-text.ts +++ b/packages/mermaid/src/rendering-util/handle-markdown-text.ts @@ -9,8 +9,10 @@ import type { MermaidConfig } from '../config.type.js'; * @returns processed markdown */ function preprocessMarkdown(markdown: string, { markdownAutoWrap }: MermaidConfig): string { + //Replace
with \n + const withoutBR = markdown.replace(//g, '\n'); // Replace multiple newlines with a single newline - const withoutMultipleNewlines = markdown.replace(/\n{2,}/g, '\n'); + const withoutMultipleNewlines = withoutBR.replace(/\n{2,}/g, '\n'); // Remove extra spaces at the beginning of each line const withoutExtraSpaces = dedent(withoutMultipleNewlines); if (markdownAutoWrap === false) { @@ -37,6 +39,7 @@ export function markdownToLines(markdown: string, config: MermaidConfig = {}): M lines.push([]); } textLine.split(' ').forEach((word) => { + word = word.replace(/'/g, `'`); if (word) { lines[currentLine].push({ content: word, type: parentType }); } @@ -46,6 +49,8 @@ export function markdownToLines(markdown: string, config: MermaidConfig = {}): M node.tokens.forEach((contentNode) => { processNode(contentNode as MarkedToken, node.type); }); + } else if (node.type === 'html') { + lines[currentLine].push({ content: node.text, type: 'normal' }); } } @@ -54,6 +59,8 @@ export function markdownToLines(markdown: string, config: MermaidConfig = {}): M treeNode.tokens?.forEach((contentNode) => { processNode(contentNode as MarkedToken); }); + } else if (treeNode.type === 'html') { + lines[currentLine].push({ content: treeNode.text, type: 'normal' }); } }); @@ -75,6 +82,12 @@ export function markdownToHTML(markdown: string, { markdownAutoWrap }: MermaidCo return `${node.tokens?.map(output).join('')}`; } else if (node.type === 'paragraph') { return `

${node.tokens?.map(output).join('')}

`; + } else if (node.type === 'space') { + return ''; + } else if (node.type === 'html') { + return `${node.text}`; + } else if (node.type === 'escape') { + return node.text; } return `Unsupported markdown: ${node.type}`; } diff --git a/packages/mermaid/src/rendering-util/icons.ts b/packages/mermaid/src/rendering-util/icons.ts new file mode 100644 index 0000000000..5eef3f7eb3 --- /dev/null +++ b/packages/mermaid/src/rendering-util/icons.ts @@ -0,0 +1,100 @@ +import { log } from '../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/insertElementsForSize.js b/packages/mermaid/src/rendering-util/insertElementsForSize.js new file mode 100644 index 0000000000..b71286351a --- /dev/null +++ b/packages/mermaid/src/rendering-util/insertElementsForSize.js @@ -0,0 +1,18 @@ +import { select } from 'd3'; + +export const getDiagramElement = (id, securityLevel) => { + let sandboxElement; + if (securityLevel === 'sandbox') { + sandboxElement = select('#i' + id); + } + const root = + securityLevel === 'sandbox' + ? select(sandboxElement.nodes()[0].contentDocument.body) + : select('body'); + + const svg = root.select(`[id="${id}"]`); + + // Run the renderer. This is what draws the final graph. + + 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 new file mode 100644 index 0000000000..6f1fa7d3bf --- /dev/null +++ b/packages/mermaid/src/rendering-util/layout-algorithms/dagre/index.js @@ -0,0 +1,377 @@ +import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js'; +import * as graphlibJson from 'dagre-d3-es/src/graphlib/json.js'; +import * as graphlib from 'dagre-d3-es/src/graphlib/index.js'; +import insertMarkers from '../../rendering-elements/markers.js'; +import { updateNodeBounds } from '../../rendering-elements/shapes/util.js'; +import { + clear as clearGraphlib, + clusterDb, + adjustClustersAndEdges, + findNonClusterChild, + sortNodesByHierarchy, +} from './mermaid-graphlib.js'; +import { + insertNode, + positionNode, + clear as clearNodes, + setNodeElem, +} from '../../rendering-elements/nodes.js'; +import { insertCluster, clear as clearClusters } from '../../rendering-elements/clusters.js'; +import { + insertEdgeLabel, + positionEdgeLabel, + insertEdge, + clear as clearEdges, +} from '../../rendering-elements/edges.js'; +import { log } from '../../../logger.js'; +import { getSubGraphTitleMargins } from '../../../utils/subGraphTitleMargins.js'; +import { getConfig } from '../../../diagram-api/diagramAPI.js'; + +const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, siteConfig) => { + log.warn('Graph in recursive render:XAX', graphlibJson.write(graph), parentCluster); + const dir = graph.graph().rankdir; + log.trace('Dir in recursive render - dir:', dir); + + const elem = _elem.insert('g').attr('class', 'root'); + if (!graph.nodes()) { + log.info('No nodes found for', graph); + } else { + log.info('Recursive render XXX', graph.nodes()); + } + if (graph.edges().length > 0) { + log.info('Recursive edges', graph.edge(graph.edges()[0])); + } + const clusters = elem.insert('g').attr('class', 'clusters'); + const edgePaths = elem.insert('g').attr('class', 'edgePaths'); + const edgeLabels = elem.insert('g').attr('class', 'edgeLabels'); + const nodes = elem.insert('g').attr('class', 'nodes'); + + // Insert nodes, this will insert them into the dom and each node will get a size. The size is updated + // to the abstract node and is later used by dagre for the layout + await Promise.all( + graph.nodes().map(async function (v) { + const node = graph.node(v); + if (parentCluster !== undefined) { + const data = JSON.parse(JSON.stringify(parentCluster.clusterData)); + // data.clusterPositioning = true; + log.trace( + 'Setting data for parent cluster XXX\n Node.id = ', + v, + '\n data=', + data.height, + '\nParent cluster', + parentCluster.height + ); + graph.setNode(parentCluster.id, data); + if (!graph.parent(v)) { + log.trace('Setting parent', v, parentCluster.id); + graph.setParent(v, parentCluster.id, data); + } + } + log.info('(Insert) Node XXX' + v + ': ' + JSON.stringify(graph.node(v))); + if (node?.clusterNode) { + // const children = graph.children(v); + log.info('Cluster identified XBX', v, node.width, graph.node(v)); + + // `node.graph.setGraph` applies the graph configurations such as nodeSpacing to subgraphs as without this the default values would be used + // We override only the `ranksep` and `nodesep` configurations to allow for setting subgraph spacing while avoiding overriding other properties + const { ranksep, nodesep } = graph.graph(); + node.graph.setGraph({ + ...node.graph.graph(), + ranksep: ranksep + 25, + nodesep, + }); + + // "o" will contain the full cluster not just the children + const o = await recursiveRender( + nodes, + node.graph, + diagramType, + id, + graph.node(v), + siteConfig + ); + const newEl = o.elem; + updateNodeBounds(node, newEl); + // node.height = o.diff; + node.diff = o.diff || 0; + log.info( + 'New compound node after recursive render XAX', + v, + 'width', + // node, + node.width, + 'height', + node.height + // node.x, + // node.y + ); + setNodeElem(newEl, node); + } else { + if (graph.children(v).length > 0) { + // This is a cluster but not to be rendered recursively + // Render as before + log.trace( + 'Cluster - the non recursive path XBX', + v, + node.id, + node, + node.width, + 'Graph:', + 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, nodes, graph.node(v), dir); + await insertNode(nodes, graph.node(v), { config: siteConfig, dir }); + } + } + }) + ); + + const processEdges = async () => { + const edgePromises = graph.edges().map(async function (e) { + const edge = graph.edge(e.v, e.w, e.name); + log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e)); + log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e))); + + // Check if link is either from or to a cluster + log.info( + 'Fix', + clusterDb, + 'ids:', + e.v, + e.w, + 'Translating: ', + clusterDb.get(e.v), + clusterDb.get(e.w) + ); + await insertEdgeLabel(edgeLabels, edge); + }); + + await Promise.all(edgePromises); + }; + + await processEdges(); + + log.info('Graph before layout:', JSON.stringify(graphlibJson.write(graph))); + + log.info('############################################# XXX'); + log.info('### Layout ### XXX'); + log.info('############################################# XXX'); + + dagreLayout(graph); + + log.info('Graph after layout:', JSON.stringify(graphlibJson.write(graph))); + // Move the nodes to the correct place + let diff = 0; + let { subGraphTitleTotalMargin } = getSubGraphTitleMargins(siteConfig); + await Promise.all( + sortNodesByHierarchy(graph).map(async function (v) { + const node = graph.node(v); + log.info( + 'Position XBX => ' + v + ': (' + node.x, + ',' + node.y, + ') width: ', + node.width, + ' height: ', + node.height + ); + if (node?.clusterNode) { + // Adjust for padding when on root level + node.y += subGraphTitleTotalMargin; + + log.info( + 'A tainted cluster node XBX1', + v, + node.id, + node.width, + node.height, + node.x, + node.y, + graph.parent(v) + ); + clusterDb.get(node.id).node = node; + positionNode(node); + } else { + // A tainted cluster node + if (graph.children(v).length > 0) { + log.info( + 'A pure cluster node XBX1', + v, + node.id, + node.x, + node.y, + node.width, + node.height, + graph.parent(v) + ); + node.height += subGraphTitleTotalMargin; + graph.node(node.parentId); + const halfPadding = node?.padding / 2 || 0; + const labelHeight = node?.labelBBox?.height || 0; + const offsetY = labelHeight - halfPadding || 0; + log.debug('OffsetY', offsetY, 'labelHeight', labelHeight, 'halfPadding', halfPadding); + await insertCluster(clusters, node); + + // A cluster in the non-recursive way + clusterDb.get(node.id).node = node; + } else { + // Regular node + const parent = graph.node(node.parentId); + node.y += subGraphTitleTotalMargin / 2; + log.info( + 'A regular node XBX1 - using the padding', + node.id, + 'parent', + node.parentId, + node.width, + node.height, + node.x, + node.y, + 'offsetY', + node.offsetY, + 'parent', + parent, + parent?.offsetY, + node + ); + + positionNode(node); + } + } + }) + ); + + // Move the edge labels to the correct place after layout + graph.edges().forEach(function (e) { + const edge = graph.edge(e); + log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge); + + edge.points.forEach((point) => (point.y += subGraphTitleTotalMargin / 2)); + const startNode = graph.node(e.v); + var endNode = graph.node(e.w); + const paths = insertEdge(edgePaths, edge, clusterDb, diagramType, startNode, endNode, id); + positionEdgeLabel(edge, paths); + }); + + graph.nodes().forEach(function (v) { + const n = graph.node(v); + log.info(v, n.type, n.diff); + if (n.isGroup) { + diff = n.diff; + } + }); + log.warn('Returning from recursive render XAX', elem, diff); + return { elem, diff }; +}; + +export const render = async (data4Layout, svg) => { + const graph = new graphlib.Graph({ + multigraph: true, + compound: true, + }) + .setGraph({ + rankdir: data4Layout.direction, + nodesep: + data4Layout.config?.nodeSpacing || + data4Layout.config?.flowchart?.nodeSpacing || + data4Layout.nodeSpacing, + ranksep: + data4Layout.config?.rankSpacing || + data4Layout.config?.flowchart?.rankSpacing || + data4Layout.rankSpacing, + marginx: 8, + marginy: 8, + }) + .setDefaultEdgeLabel(function () { + return {}; + }); + const element = svg.select('g'); + insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId); + clearNodes(); + clearEdges(); + clearClusters(); + clearGraphlib(); + + data4Layout.nodes.forEach((node) => { + graph.setNode(node.id, { ...node }); + if (node.parentId) { + graph.setParent(node.id, node.parentId); + } + }); + + log.debug('Edges:', data4Layout.edges); + data4Layout.edges.forEach((edge) => { + // 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 = ''; + if (node.isGroup) { + 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 { + descendants.clear(); + parents.clear(); + clusterDb.clear(); +}; + +const isDescendant = (id, ancestorId) => { + const ancestorDescendants = descendants.get(ancestorId) || []; + log.trace('In isDescendant', ancestorId, ' ', id, ' = ', ancestorDescendants.includes(id)); + return ancestorDescendants.includes(id); +}; + +const edgeInCluster = (edge, clusterId) => { + const clusterDescendants = descendants.get(clusterId) || []; + log.info('Descendants of ', clusterId, ' is ', clusterDescendants); + log.info('Edge is ', edge); + if (edge.v === clusterId || edge.w === clusterId) { + return false; + } + + if (!clusterDescendants) { + log.debug('Tilt, ', clusterId, ',not in descendants'); + return false; + } + + return ( + clusterDescendants.includes(edge.v) || + isDescendant(edge.v, clusterId) || + isDescendant(edge.w, clusterId) || + clusterDescendants.includes(edge.w) + ); +}; + +const copy = (clusterId, graph, newGraph, rootId) => { + log.warn( + 'Copying children of ', + clusterId, + 'root', + rootId, + 'data', + graph.node(clusterId), + rootId + ); + const nodes = graph.children(clusterId) || []; + + if (clusterId !== rootId) { + nodes.push(clusterId); + } + + log.warn('Copying (nodes) clusterId', clusterId, 'nodes', nodes); + + nodes.forEach((node) => { + if (graph.children(node).length > 0) { + copy(node, graph, newGraph, rootId); + } else { + const data = graph.node(node); + log.info('cp ', node, ' to ', rootId, ' with parent ', clusterId); + newGraph.setNode(node, data); + if (rootId !== graph.parent(node)) { + log.warn('Setting parent', node, graph.parent(node)); + newGraph.setParent(node, graph.parent(node)); + } + + if (clusterId !== rootId && node !== clusterId) { + log.debug('Setting parent', node, clusterId); + newGraph.setParent(node, clusterId); + } else { + log.info('In copy ', clusterId, 'root', rootId, 'data', graph.node(clusterId), rootId); + log.debug( + 'Not Setting parent for node=', + node, + 'cluster!==rootId', + clusterId !== rootId, + 'node!==clusterId', + node !== clusterId + ); + } + const edges = graph.edges(node); + log.debug('Copying Edges', edges); + edges.forEach((edge) => { + log.info('Edge', edge); + const data = graph.edge(edge.v, edge.w, edge.name); + log.info('Edge data', data, rootId); + try { + if (edgeInCluster(edge, rootId)) { + log.info('Copying as ', edge.v, edge.w, data, edge.name); + newGraph.setEdge(edge.v, edge.w, data, edge.name); + log.info('newGraph edges ', newGraph.edges(), newGraph.edge(newGraph.edges()[0])); + } else { + log.info( + 'Skipping copy of edge ', + edge.v, + '-->', + edge.w, + ' rootId: ', + rootId, + ' clusterId:', + clusterId + ); + } + } catch (e) { + log.error(e); + } + }); + } + log.debug('Removing node', node); + graph.removeNode(node); + }); +}; + +export const extractDescendants = (id, graph) => { + const children = graph.children(id); + let res = [...children]; + + for (const child of children) { + parents.set(child, id); + res = [...res, ...extractDescendants(child, graph)]; + } + + return res; +}; + +export const validate = (graph) => { + const edges = graph.edges(); + log.trace('Edges: ', edges); + for (const edge of edges) { + if (graph.children(edge.v).length > 0) { + log.trace('The node ', edge.v, ' is part of and edge even though it has children'); + return false; + } + if (graph.children(edge.w).length > 0) { + log.trace('The node ', edge.w, ' is part of and edge even though it has children'); + return false; + } + } + return true; +}; + +const findCommonEdges = (graph, id1, id2) => { + const edges1 = graph.edges().filter((edge) => edge.v === id1 || edge.w === id1); + const edges2 = graph.edges().filter((edge) => edge.v === id2 || edge.w === id2); + const edges1Prim = edges1.map((edge) => { + return { v: edge.v === id1 ? id2 : edge.v, w: edge.w === id1 ? id1 : edge.w }; + }); + const edges2Prim = edges2.map((edge) => { + return { v: edge.v, w: edge.w }; + }); + const result = edges1Prim.filter((edgeIn1) => { + return edges2Prim.some((edge) => edgeIn1.v === edge.v && edgeIn1.w === edge.w); + }); + + return result; +}; + +export const findNonClusterChild = (id, graph, clusterId) => { + const children = graph.children(id); + log.trace('Searching children of id ', id, children); + if (children.length < 1) { + return id; + } + let reserve; + for (const child of children) { + const _id = findNonClusterChild(child, graph, clusterId); + + const commonEdges = findCommonEdges(graph, clusterId, _id); + + if (_id) { + if (commonEdges.length > 0) { + reserve = _id; + } else { + return _id; + } + } + } + return reserve; +}; + +const getAnchorId = (id) => { + if (!clusterDb.has(id)) { + return id; + } + if (!clusterDb.get(id).externalConnections) { + return id; + } + + if (clusterDb.has(id)) { + return clusterDb.get(id).id; + } + return id; +}; + +export const adjustClustersAndEdges = (graph, depth) => { + if (!graph || depth > 10) { + log.debug('Opting out, no graph '); + return; + } else { + log.debug('Opting in, graph '); + } + + graph.nodes().forEach(function (id) { + const children = graph.children(id); + if (children.length > 0) { + log.warn( + 'Cluster identified', + id, + ' Replacement id in edges: ', + findNonClusterChild(id, graph, id) + ); + descendants.set(id, extractDescendants(id, graph)); + clusterDb.set(id, { id: findNonClusterChild(id, graph, id), clusterData: graph.node(id) }); + } + }); + + graph.nodes().forEach(function (id) { + const children = graph.children(id); + const edges = graph.edges(); + if (children.length > 0) { + log.debug('Cluster identified', id, descendants); + edges.forEach((edge) => { + const d1 = isDescendant(edge.v, id); + const d2 = isDescendant(edge.w, id); + + if (d1 ^ d2) { + log.warn('Edge: ', edge, ' leaves cluster ', id); + log.warn('Descendants of XXX ', id, ': ', descendants.get(id)); + clusterDb.get(id).externalConnections = true; + } + }); + } else { + log.debug('Not a cluster ', id, descendants); + } + }); + + for (let id of clusterDb.keys()) { + const nonClusterChild = clusterDb.get(id).id; + const parent = graph.parent(nonClusterChild); + + if (parent !== id && clusterDb.has(parent) && !clusterDb.get(parent).externalConnections) { + clusterDb.get(id).id = parent; + } + } + + graph.edges().forEach(function (e) { + const edge = graph.edge(e); + log.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e)); + log.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(graph.edge(e))); + + let v = e.v; + let w = e.w; + log.warn( + 'Fix XXX', + clusterDb, + 'ids:', + e.v, + e.w, + 'Translating: ', + 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); + graph.removeEdge(e.v, e.w, e.name); + if (v !== e.v) { + const parent = graph.parent(v); + clusterDb.get(parent).externalConnections = true; + edge.fromCluster = e.v; + } + if (w !== e.w) { + const parent = graph.parent(w); + clusterDb.get(parent).externalConnections = true; + edge.toCluster = e.w; + } + log.warn('Fix Replacing with XXX', v, w, e.name); + graph.setEdge(v, w, edge, e.name); + } + }); + log.warn('Adjusted Graph', graphlibJson.write(graph)); + extractor(graph, 0); + + log.trace(clusterDb); +}; + +export const extractor = (graph, depth) => { + log.warn('extractor - ', depth, graphlibJson.write(graph), graph.children('D')); + if (depth > 10) { + log.error('Bailing out'); + return; + } + let nodes = graph.nodes(); + let hasChildren = false; + for (const node of nodes) { + const children = graph.children(node); + hasChildren = hasChildren || children.length > 0; + } + + if (!hasChildren) { + log.debug('Done, no node has children', graph.nodes()); + return; + } + log.debug('Nodes = ', nodes, depth); + for (const node of nodes) { + log.debug( + 'Extracting node', + node, + clusterDb, + clusterDb.has(node) && !clusterDb.get(node).externalConnections, + !graph.parent(node), + graph.node(node), + graph.children('D'), + ' Depth ', + depth + ); + if (!clusterDb.has(node)) { + log.debug('Not a cluster', node, depth); + } else if ( + !clusterDb.get(node).externalConnections && + graph.children(node) && + graph.children(node).length > 0 + ) { + log.warn( + 'Cluster without external connections, without a parent and with children', + node, + depth + ); + + const graphSettings = graph.graph(); + let dir = graphSettings.rankdir === 'TB' ? 'LR' : 'TB'; + if (clusterDb.get(node)?.clusterData?.dir) { + dir = clusterDb.get(node).clusterData.dir; + log.warn('Fixing dir', clusterDb.get(node).clusterData.dir, dir); + } + + const clusterGraph = new graphlib.Graph({ + multigraph: true, + compound: true, + }) + .setGraph({ + rankdir: dir, + nodesep: 50, + ranksep: 50, + marginx: 8, + marginy: 8, + }) + .setDefaultEdgeLabel(function () { + return {}; + }); + + log.warn('Old graph before copy', graphlibJson.write(graph)); + copy(node, graph, clusterGraph, node); + graph.setNode(node, { + clusterNode: true, + id: node, + clusterData: clusterDb.get(node).clusterData, + label: clusterDb.get(node).label, + graph: clusterGraph, + }); + log.warn('New graph after copy node: (', node, ')', graphlibJson.write(clusterGraph)); + log.debug('Old graph after copy', graphlibJson.write(graph)); + } else { + log.warn( + 'Cluster ** ', + node, + ' **not meeting the criteria !externalConnections:', + !clusterDb.get(node).externalConnections, + ' no parent: ', + !graph.parent(node), + ' children ', + graph.children(node) && graph.children(node).length > 0, + graph.children('D'), + depth + ); + log.debug(clusterDb); + } + } + + nodes = graph.nodes(); + log.warn('New list of nodes', nodes); + for (const node of nodes) { + const data = graph.node(node); + log.warn(' Now next level', node, data); + if (data?.clusterNode) { + extractor(data.graph, depth + 1); + } + } +}; + +const sorter = (graph, nodes) => { + if (nodes.length === 0) { + return []; + } + let result = Object.assign([], nodes); + nodes.forEach((node) => { + const children = graph.children(node); + const sorted = sorter(graph, children); + result = [...result, ...sorted]; + }); + + return result; +}; + +export const sortNodesByHierarchy = (graph) => sorter(graph, graph.children()); diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/dagre/mermaid-graphlib.spec.js b/packages/mermaid/src/rendering-util/layout-algorithms/dagre/mermaid-graphlib.spec.js new file mode 100644 index 0000000000..11acd44eb1 --- /dev/null +++ b/packages/mermaid/src/rendering-util/layout-algorithms/dagre/mermaid-graphlib.spec.js @@ -0,0 +1,508 @@ +import * as graphlibJson from 'dagre-d3-es/src/graphlib/json.js'; +import * as graphlib from 'dagre-d3-es/src/graphlib/index.js'; +import { + validate, + adjustClustersAndEdges, + extractDescendants, + sortNodesByHierarchy, +} from './mermaid-graphlib.js'; +import { setLogLevel, log } from '../../../logger.js'; + +describe('Graphlib decorations', () => { + let g; + beforeEach(function () { + setLogLevel(1); + g = new graphlib.Graph({ + multigraph: true, + compound: true, + }); + g.setGraph({ + rankdir: 'TB', + nodesep: 10, + ranksep: 10, + marginx: 8, + marginy: 8, + }); + g.setDefaultEdgeLabel(function () { + return {}; + }); + }); + + describe('validate', function () { + it('Validate should detect edges between clusters', function () { + /* + subgraph C1 + a --> b + end + subgraph C2 + c + end + C1 --> C2 + */ + g.setNode('a', { data: 1 }); + g.setNode('b', { data: 2 }); + g.setNode('c', { data: 3 }); + g.setParent('a', 'C1'); + g.setParent('b', 'C1'); + g.setParent('c', 'C2'); + g.setEdge('a', 'b'); + g.setEdge('C1', 'C2'); + + expect(validate(g)).toBe(false); + }); + it('Validate should not detect edges between clusters after adjustment', function () { + /* + subgraph C1 + a --> b + end + subgraph C2 + c + end + C1 --> C2 + */ + g.setNode('a', {}); + g.setNode('b', {}); + g.setNode('c', {}); + g.setParent('a', 'C1'); + g.setParent('b', 'C1'); + g.setParent('c', 'C2'); + g.setEdge('a', 'b'); + g.setEdge('C1', 'C2'); + + adjustClustersAndEdges(g); + log.info(g.edges()); + expect(validate(g)).toBe(true); + }); + + it('Validate should detect edges between clusters and transform clusters GLB4', function () { + /* + a --> b + subgraph C1 + subgraph C2 + a + end + b + end + C1 --> c + */ + g.setNode('a', { data: 1 }); + g.setNode('b', { data: 2 }); + g.setNode('c', { data: 3 }); + g.setNode('C1', { data: 4 }); + g.setNode('C2', { data: 5 }); + g.setParent('a', 'C2'); + g.setParent('b', 'C1'); + g.setParent('C2', 'C1'); + g.setEdge('a', 'b', { name: 'C1-internal-link' }); + g.setEdge('C1', 'c', { name: 'C1-external-link' }); + + adjustClustersAndEdges(g); + log.info(g.nodes()); + expect(g.nodes().length).toBe(2); + expect(validate(g)).toBe(true); + }); + it('Validate should detect edges between clusters and transform clusters GLB5', function () { + /* + a --> b + subgraph C1 + a + end + subgraph C2 + b + end + C1 --> + */ + g.setNode('a', { data: 1 }); + g.setNode('b', { data: 2 }); + g.setParent('a', 'C1'); + g.setParent('b', 'C2'); + // g.setEdge('a', 'b', { name: 'C1-internal-link' }); + g.setEdge('C1', 'C2', { name: 'C1-external-link' }); + + log.info(g.nodes()); + adjustClustersAndEdges(g); + log.info(g.nodes()); + expect(g.nodes().length).toBe(2); + expect(validate(g)).toBe(true); + }); + it('adjustClustersAndEdges GLB6', function () { + /* + subgraph C1 + a + end + C1 --> b + */ + g.setNode('a', { data: 1 }); + g.setNode('b', { data: 2 }); + g.setNode('C1', { data: 3 }); + g.setParent('a', 'C1'); + g.setEdge('C1', 'b', { data: 'link1' }, '1'); + + // log.info(g.edges()) + adjustClustersAndEdges(g); + log.info(g.edges()); + expect(g.nodes()).toEqual(['b', 'C1']); + expect(g.edges().length).toBe(1); + expect(validate(g)).toBe(true); + expect(g.node('C1').clusterNode).toBe(true); + + const C1Graph = g.node('C1').graph; + expect(C1Graph.nodes()).toEqual(['a']); + }); + it('adjustClustersAndEdges GLB7', function () { + /* + subgraph C1 + a + end + C1 --> b + C1 --> c + */ + g.setNode('a', { data: 1 }); + g.setNode('b', { data: 2 }); + g.setNode('c', { data: 3 }); + g.setParent('a', 'C1'); + g.setNode('C1', { data: 4 }); + g.setEdge('C1', 'b', { data: 'link1' }, '1'); + g.setEdge('C1', 'c', { data: 'link2' }, '2'); + + log.info(g.node('C1')); + adjustClustersAndEdges(g); + log.info(g.edges()); + expect(g.nodes()).toEqual(['b', 'c', 'C1']); + expect(g.nodes().length).toBe(3); + expect(g.edges().length).toBe(2); + + expect(g.edges().length).toBe(2); + const edgeData = g.edge(g.edges()[1]); + expect(edgeData.data).toBe('link2'); + expect(validate(g)).toBe(true); + + const C1Graph = g.node('C1').graph; + expect(C1Graph.nodes()).toEqual(['a']); + }); + it('adjustClustersAndEdges GLB8', function () { + /* + subgraph A + a + end + subgraph B + b + end + subgraph C + c + end + A --> B + A --> C + */ + g.setNode('a', { data: 1 }); + g.setNode('b', { data: 2 }); + g.setNode('c', { data: 3 }); + g.setParent('a', 'A'); + g.setParent('b', 'B'); + g.setParent('c', 'C'); + g.setEdge('A', 'B', { data: 'link1' }, '1'); + g.setEdge('A', 'C', { data: 'link2' }, '2'); + + // log.info(g.edges()) + adjustClustersAndEdges(g); + expect(g.nodes()).toEqual(['A', 'B', 'C']); + expect(g.edges().length).toBe(2); + + expect(g.edges().length).toBe(2); + const edgeData = g.edge(g.edges()[1]); + expect(edgeData.data).toBe('link2'); + expect(validate(g)).toBe(true); + + const CGraph = g.node('C').graph; + expect(CGraph.nodes()).toEqual(['c']); + }); + + it('adjustClustersAndEdges the extracted graphs shall contain the correct data GLB10', function () { + /* + subgraph C + subgraph D + d + end + end + */ + + g.setNode('C', { data: 1 }); + g.setNode('D', { data: 2 }); + g.setNode('d', { data: 3 }); + g.setParent('d', 'D'); + g.setParent('D', 'C'); + + // log.info('Graph before', g.node('D')) + // log.info('Graph before', graphlibJson.write(g)) + adjustClustersAndEdges(g); + // log.info('Graph after', graphlibJson.write(g), g.node('C').graph) + + const CGraph = g.node('C').graph; + const DGraph = CGraph.node('D').graph; + + expect(CGraph.nodes()).toEqual(['D']); + expect(DGraph.nodes()).toEqual(['d']); + + expect(g.nodes()).toEqual(['C']); + expect(g.nodes().length).toBe(1); + }); + + it('adjustClustersAndEdges the extracted graphs shall contain the correct data GLB11', function () { + /* + subgraph A + a + end + subgraph B + b + end + subgraph C + subgraph D + d + end + end + A --> B + A --> C + */ + + g.setNode('C', { data: 1 }); + g.setNode('D', { data: 2 }); + g.setNode('d', { data: 3 }); + g.setNode('B', { data: 4 }); + g.setNode('b', { data: 5 }); + g.setNode('A', { data: 6 }); + g.setNode('a', { data: 7 }); + g.setParent('a', 'A'); + g.setParent('b', 'B'); + g.setParent('d', 'D'); + g.setParent('D', 'C'); + g.setEdge('A', 'B', { data: 'link1' }, '1'); + g.setEdge('A', 'C', { data: 'link2' }, '2'); + + log.info('Graph before', g.node('D')); + log.info('Graph before', graphlibJson.write(g)); + adjustClustersAndEdges(g); + log.trace('Graph after', graphlibJson.write(g)); + expect(g.nodes()).toEqual(['C', 'B', 'A']); + expect(g.nodes().length).toBe(3); + expect(g.edges().length).toBe(2); + + const AGraph = g.node('A').graph; + const BGraph = g.node('B').graph; + const CGraph = g.node('C').graph; + // log.info(CGraph.nodes()); + const DGraph = CGraph.node('D').graph; + // log.info('DG', CGraph.children('D')); + + log.info('A', AGraph.nodes()); + expect(AGraph.nodes().length).toBe(1); + expect(AGraph.nodes()).toEqual(['a']); + log.trace('Nodes', BGraph.nodes()); + expect(BGraph.nodes().length).toBe(1); + expect(BGraph.nodes()).toEqual(['b']); + expect(CGraph.nodes()).toEqual(['D']); + expect(CGraph.nodes().length).toEqual(1); + + expect(AGraph.edges().length).toBe(0); + expect(BGraph.edges().length).toBe(0); + expect(CGraph.edges().length).toBe(0); + expect(DGraph.nodes()).toEqual(['d']); + expect(DGraph.edges().length).toBe(0); + // expect(CGraph.node('D')).toEqual({ data: 2 }); + expect(g.edges().length).toBe(2); + + // expect(g.edges().length).toBe(2); + // const edgeData = g.edge(g.edges()[1]); + // expect(edgeData.data).toBe('link2'); + // expect(validate(g)).toBe(true); + }); + it('adjustClustersAndEdges the extracted graphs shall contain the correct links GLB20', function () { + /* + a --> b + subgraph b [Test] + c --> d -->e + end + */ + g.setNode('a', { data: 1 }); + g.setNode('b', { data: 2 }); + g.setNode('c', { data: 3 }); + g.setNode('d', { data: 3 }); + g.setNode('e', { data: 3 }); + g.setParent('c', 'b'); + g.setParent('d', 'b'); + g.setParent('e', 'b'); + g.setEdge('a', 'b', { data: 'link1' }, '1'); + g.setEdge('c', 'd', { data: 'link2' }, '2'); + g.setEdge('d', 'e', { data: 'link2' }, '2'); + + log.info('Graph before', graphlibJson.write(g)); + adjustClustersAndEdges(g); + const bGraph = g.node('b').graph; + // log.trace('Graph after', graphlibJson.write(g)) + log.info('Graph after', graphlibJson.write(bGraph)); + expect(bGraph.nodes().length).toBe(3); + expect(bGraph.edges().length).toBe(2); + }); + it('adjustClustersAndEdges the extracted graphs shall contain the correct links GLB21', function () { + /* + state a { + state b { + state c { + e + } + } + } + */ + g.setNode('a', { data: 1 }); + g.setNode('b', { data: 2 }); + g.setNode('c', { data: 3 }); + g.setNode('e', { data: 3 }); + g.setParent('b', 'a'); + g.setParent('c', 'b'); + g.setParent('e', 'c'); + + log.info('Graph before', graphlibJson.write(g)); + adjustClustersAndEdges(g); + const aGraph = g.node('a').graph; + const bGraph = aGraph.node('b').graph; + log.info('Graph after', graphlibJson.write(aGraph)); + const cGraph = bGraph.node('c').graph; + // log.trace('Graph after', graphlibJson.write(g)) + expect(aGraph.nodes().length).toBe(1); + expect(bGraph.nodes().length).toBe(1); + expect(cGraph.nodes().length).toBe(1); + expect(bGraph.edges().length).toBe(0); + }); + }); + it('adjustClustersAndEdges should handle nesting GLB77', function () { + /* +flowchart TB + subgraph A + b-->B + a-->c + end + subgraph B + c + end + */ + + const exportedGraph = JSON.parse( + '{"options":{"directed":true,"multigraph":true,"compound":true},"nodes":[{"v":"A","value":{"labelStyle":"","shape":"rect","labelText":"A","rx":0,"ry":0,"cssClass":"default","style":"","id":"A","width":500,"type":"group","padding":15}},{"v":"B","value":{"labelStyle":"","shape":"rect","labelText":"B","rx":0,"ry":0,"class":"default","style":"","id":"B","width":500,"type":"group","padding":15},"parent":"A"},{"v":"b","value":{"labelStyle":"","shape":"rect","labelText":"b","rx":0,"ry":0,"class":"default","style":"","id":"b","padding":15},"parent":"A"},{"v":"c","value":{"labelStyle":"","shape":"rect","labelText":"c","rx":0,"ry":0,"class":"default","style":"","id":"c","padding":15},"parent":"B"},{"v":"a","value":{"labelStyle":"","shape":"rect","labelText":"a","rx":0,"ry":0,"class":"default","style":"","id":"a","padding":15},"parent":"A"}],"edges":[{"v":"b","w":"B","name":"1","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-b-B","cssClasses":"flowchart-link LS-b LE-B"}},{"v":"a","w":"c","name":"2","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-a-c","cssClasses":"flowchart-link LS-a LE-c"}}],"value":{"rankdir":"TB","nodesep":50,"ranksep":50,"marginx":8,"marginy":8}}' + ); + const gr = graphlibJson.read(exportedGraph); + + log.info('Graph before', graphlibJson.write(gr)); + adjustClustersAndEdges(gr); + const aGraph = gr.node('A').graph; + const bGraph = aGraph.node('B').graph; + log.info('Graph after', graphlibJson.write(aGraph)); + // log.trace('Graph after', graphlibJson.write(g)) + expect(aGraph.parent('c')).toBe('B'); + expect(aGraph.parent('B')).toBe(undefined); + }); +}); +describe('extractDescendants', function () { + let g; + beforeEach(function () { + setLogLevel(1); + g = new graphlib.Graph({ + multigraph: true, + compound: true, + }); + g.setGraph({ + rankdir: 'TB', + nodesep: 10, + ranksep: 10, + marginx: 8, + marginy: 8, + }); + g.setDefaultEdgeLabel(function () { + return {}; + }); + }); + it('Simple case of one level descendants GLB9', function () { + /* + subgraph A + a + end + subgraph B + b + end + subgraph C + c + end + A --> B + A --> C + */ + g.setNode('a', { data: 1 }); + g.setNode('b', { data: 2 }); + g.setNode('c', { data: 3 }); + g.setParent('a', 'A'); + g.setParent('b', 'B'); + g.setParent('c', 'C'); + g.setEdge('A', 'B', { data: 'link1' }, '1'); + g.setEdge('A', 'C', { data: 'link2' }, '2'); + + // log.info(g.edges()) + const d1 = extractDescendants('A', g); + const d2 = extractDescendants('B', g); + const d3 = extractDescendants('C', g); + + expect(d1).toEqual(['a']); + expect(d2).toEqual(['b']); + expect(d3).toEqual(['c']); + }); +}); +describe('sortNodesByHierarchy', function () { + let g; + beforeEach(function () { + setLogLevel(1); + g = new graphlib.Graph({ + multigraph: true, + compound: true, + }); + g.setGraph({ + rankdir: 'TB', + nodesep: 10, + ranksep: 10, + marginx: 8, + marginy: 8, + }); + g.setDefaultEdgeLabel(function () { + return {}; + }); + }); + it('should sort proper en nodes are in reverse order', function () { + /* + a -->b + subgraph B + b + end + subgraph A + B + end + */ + g.setNode('a', { data: 1 }); + g.setNode('b', { data: 2 }); + g.setParent('b', 'B'); + g.setParent('B', 'A'); + g.setEdge('a', 'b', '1'); + expect(sortNodesByHierarchy(g)).toEqual(['a', 'A', 'B', 'b']); + }); + it('should sort proper en nodes are in correct order', function () { + /* + a -->b + subgraph B + b + end + subgraph A + B + end + */ + g.setNode('a', { data: 1 }); + g.setParent('B', 'A'); + g.setParent('b', 'B'); + g.setNode('b', { data: 2 }); + g.setEdge('a', 'b', '1'); + expect(sortNodesByHierarchy(g)).toEqual(['a', 'A', 'B', 'b']); + }); +}); diff --git a/packages/mermaid/src/rendering-util/render.ts b/packages/mermaid/src/rendering-util/render.ts new file mode 100644 index 0000000000..b975e7bf97 --- /dev/null +++ b/packages/mermaid/src/rendering-util/render.ts @@ -0,0 +1,71 @@ +import type { SVG } from '../diagram-api/types.js'; +import type { InternalHelpers } from '../internals.js'; +import { internalHelpers } from '../internals.js'; +import { log } from '../logger.js'; +import type { LayoutData } from './types.js'; + +export interface RenderOptions { + algorithm?: string; +} + +export interface LayoutAlgorithm { + render( + layoutData: LayoutData, + svg: SVG, + helpers: InternalHelpers, + options?: RenderOptions + ): Promise; +} + +export type LayoutLoader = () => Promise; +export interface LayoutLoaderDefinition { + name: string; + loader: LayoutLoader; + algorithm?: string; +} + +const layoutAlgorithms: Record = {}; + +export const registerLayoutLoaders = (loaders: LayoutLoaderDefinition[]) => { + for (const loader of loaders) { + layoutAlgorithms[loader.name] = loader; + } +}; + +// TODO: Should we load dagre without lazy loading? +const registerDefaultLayoutLoaders = () => { + registerLayoutLoaders([ + { + name: 'dagre', + loader: async () => await import('./layout-algorithms/dagre/index.js'), + }, + ]); +}; + +registerDefaultLayoutLoaders(); + +export const render = async (data4Layout: LayoutData, svg: SVG) => { + if (!(data4Layout.layoutAlgorithm in layoutAlgorithms)) { + throw new Error(`Unknown layout algorithm: ${data4Layout.layoutAlgorithm}`); + } + + const layoutDefinition = layoutAlgorithms[data4Layout.layoutAlgorithm]; + const layoutRenderer = await layoutDefinition.loader(); + return layoutRenderer.render(data4Layout, svg, internalHelpers, { + algorithm: layoutDefinition.algorithm, + }); +}; + +/** + * Get the registered layout algorithm. If the algorithm is not registered, use the fallback algorithm. + */ +export const getRegisteredLayoutAlgorithm = (algorithm = '', { fallback = 'dagre' } = {}) => { + if (algorithm in layoutAlgorithms) { + return algorithm; + } + if (fallback in layoutAlgorithms) { + log.warn(`Layout algorithm ${algorithm} is not registered. Using ${fallback} as fallback.`); + return fallback; + } + throw new Error(`Both layout algorithms ${algorithm} and ${fallback} are not registered.`); +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/clusters.js b/packages/mermaid/src/rendering-util/rendering-elements/clusters.js new file mode 100644 index 0000000000..1dd87d4388 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/clusters.js @@ -0,0 +1,517 @@ +import { getConfig } from '../../diagram-api/diagramAPI.js'; +import { evaluate } from '../../diagrams/common/common.js'; +import { log } from '../../logger.js'; +import { getSubGraphTitleMargins } from '../../utils/subGraphTitleMargins.js'; +import { select } from 'd3'; +import rough from 'roughjs'; +import { createText } from '../createText.ts'; +import intersectRect from '../rendering-elements/intersect/intersect-rect.js'; +import createLabel from './createLabel.js'; +import { createRoundedRectPathD } from './shapes/roundedRectPath.ts'; +import { styles2String, userNodeOverrides } from './shapes/handDrawnShapeStyles.js'; + +const rect = async (parent, node) => { + log.info('Creating subgraph rect for ', node.id, node); + const siteConfig = getConfig(); + const { themeVariables, handDrawnSeed } = siteConfig; + const { clusterBkg, clusterBorder } = themeVariables; + + const { labelStyles, nodeStyles, borderStyles, backgroundStyles } = styles2String(node); + + // Add outer g element + const shapeSvg = parent + .insert('g') + .attr('class', 'cluster ' + node.cssClasses) + .attr('id', node.id) + .attr('data-look', node.look); + + const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels); + + // Create the label and insert it after the rect + const labelEl = shapeSvg.insert('g').attr('class', 'cluster-label '); + + const text = await createText(labelEl, node.label, { + style: node.labelStyle, + useHtmlLabels, + isNode: true, + }); + + // Get the size of the label + let bbox = text.getBBox(); + + if (evaluate(siteConfig.flowchart.htmlLabels)) { + const div = text.children[0]; + const dv = select(text); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + + const width = node.width <= bbox.width + node.padding ? bbox.width + node.padding : node.width; + if (node.width <= bbox.width + node.padding) { + node.diff = (width - node.width) / 2 - node.padding; + } else { + node.diff = -node.padding; + } + + const height = node.height; + const x = node.x - width / 2; + const y = node.y - height / 2; + + log.trace('Data ', node, JSON.stringify(node)); + let rect; + if (node.look === 'handDrawn') { + // @ts-ignore TODO: Fix rough typings + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.7, + fill: clusterBkg, + // fill: 'red', + stroke: clusterBorder, + fillWeight: 3, + seed: handDrawnSeed, + }); + const roughNode = rc.path(createRoundedRectPathD(x, y, width, height, 0), options); + rect = shapeSvg.insert(() => { + log.debug('Rough node insert CXC', roughNode); + return roughNode; + }, ':first-child'); + // Should we affect the options instead of doing this? + rect.select('path:nth-child(2)').attr('style', borderStyles.join(';')); + rect.select('path').attr('style', backgroundStyles.join(';').replace('fill', 'stroke')); + } else { + // add the rect + rect = shapeSvg.insert('rect', ':first-child'); + // center the rect around its coordinate + rect + .attr('style', nodeStyles) + .attr('rx', node.rx) + .attr('ry', node.ry) + .attr('x', x) + .attr('y', y) + .attr('width', width) + .attr('height', height); + } + const { subGraphTitleTopMargin } = getSubGraphTitleMargins(siteConfig); + labelEl.attr( + 'transform', + // This puts the label on top of the box instead of inside it + `translate(${node.x - bbox.width / 2}, ${node.y - node.height / 2 + subGraphTitleTopMargin})` + ); + + if (labelStyles) { + const span = labelEl.select('span'); + if (span) { + span.attr('style', labelStyles); + } + } + // Center the label + + const rectBox = rect.node().getBBox(); + node.offsetX = 0; + node.width = rectBox.width; + node.height = rectBox.height; + // Used by layout engine to position subgraph in parent + node.offsetY = bbox.height - node.padding / 2; + + node.intersect = function (point) { + return intersectRect(node, point); + }; + + return { cluster: shapeSvg, labelBBox: bbox }; +}; + +/** + * Non visible cluster where the note is group with its + * + * @param {any} parent + * @param {any} node + * @returns {any} ShapeSvg + */ +const noteGroup = (parent, node) => { + // Add outer g element + const shapeSvg = parent.insert('g').attr('class', 'note-cluster').attr('id', node.id); + + // add the rect + const rect = shapeSvg.insert('rect', ':first-child'); + + const padding = 0 * node.padding; + const halfPadding = padding / 2; + + // center the rect around its coordinate + rect + .attr('rx', node.rx) + .attr('ry', node.ry) + .attr('x', node.x - node.width / 2 - halfPadding) + .attr('y', node.y - node.height / 2 - halfPadding) + .attr('width', node.width + padding) + .attr('height', node.height + padding) + .attr('fill', 'none'); + + const rectBox = rect.node().getBBox(); + node.width = rectBox.width; + node.height = rectBox.height; + + node.intersect = function (point) { + return intersectRect(node, point); + }; + + return { cluster: shapeSvg, labelBBox: { width: 0, height: 0 } }; +}; + +const roundedWithTitle = async (parent, node) => { + const siteConfig = getConfig(); + + const { themeVariables, handDrawnSeed } = siteConfig; + const { altBackground, compositeBackground, compositeTitleBackground, nodeBorder } = + themeVariables; + + // Add outer g element + const shapeSvg = parent + .insert('g') + .attr('class', node.cssClasses) + .attr('id', node.id) + .attr('data-id', node.id) + .attr('data-look', node.look); + + // add the rect + const outerRectG = shapeSvg.insert('g', ':first-child'); + + // Create the label and insert it after the rect + const label = shapeSvg.insert('g').attr('class', 'cluster-label'); + let innerRect = shapeSvg.append('rect'); + + const text = label + .node() + .appendChild(await createLabel(node.label, node.labelStyle, undefined, true)); + + // Get the size of the label + let bbox = text.getBBox(); + + if (evaluate(siteConfig.flowchart.htmlLabels)) { + const div = text.children[0]; + const dv = select(text); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + + // Rounded With Title + const padding = 0 * node.padding; + const halfPadding = padding / 2; + + const width = + (node.width <= bbox.width + node.padding ? bbox.width + node.padding : node.width) + padding; + if (node.width <= bbox.width + node.padding) { + node.diff = (width - node.width) / 2 - node.padding; + } else { + node.diff = -node.padding; + } + + const height = node.height + padding; + // const height = node.height + padding; + const innerHeight = node.height + padding - bbox.height - 6; + const x = node.x - width / 2; + const y = node.y - height / 2; + node.width = width; + const innerY = node.y - node.height / 2 - halfPadding + bbox.height + 2; + + // add the rect + let rect; + if (node.look === 'handDrawn') { + const isAlt = node.cssClasses.includes('statediagram-cluster-alt'); + const rc = rough.svg(shapeSvg); + const roughOuterNode = + node.rx || node.ry + ? rc.path(createRoundedRectPathD(x, y, width, height, 10), { + roughness: 0.7, + fill: compositeTitleBackground, + fillStyle: 'solid', + stroke: nodeBorder, + seed: handDrawnSeed, + }) + : rc.rectangle(x, y, width, height, { seed: handDrawnSeed }); + + rect = shapeSvg.insert(() => roughOuterNode, ':first-child'); + const roughInnerNode = rc.rectangle(x, innerY, width, innerHeight, { + fill: isAlt ? altBackground : compositeBackground, + fillStyle: isAlt ? 'hachure' : 'solid', + stroke: nodeBorder, + seed: handDrawnSeed, + }); + + rect = shapeSvg.insert(() => roughOuterNode, ':first-child'); + innerRect = shapeSvg.insert(() => roughInnerNode); + } else { + rect = outerRectG.insert('rect', ':first-child'); + const outerRectClass = 'outer'; + + // center the rect around its coordinate + rect + .attr('class', outerRectClass) + .attr('x', x) + .attr('y', y) + .attr('width', width) + .attr('height', height) + .attr('data-look', node.look); + innerRect + .attr('class', 'inner') + .attr('x', x) + .attr('y', innerY) + .attr('width', width) + .attr('height', innerHeight); + } + + label.attr( + 'transform', + `translate(${node.x - bbox.width / 2}, ${y + 1 - (evaluate(siteConfig.flowchart.htmlLabels) ? 0 : 3)})` + ); + + const rectBox = rect.node().getBBox(); + node.height = rectBox.height; + node.offsetX = 0; + // Used by layout engine to position subgraph in parent + node.offsetY = bbox.height - node.padding / 2; + node.labelBBox = bbox; + + node.intersect = function (point) { + return intersectRect(node, point); + }; + + return { cluster: shapeSvg, labelBBox: bbox }; +}; +const kanbanSection = async (parent, node) => { + log.info('Creating subgraph rect for ', node.id, node); + const siteConfig = getConfig(); + const { themeVariables, handDrawnSeed } = siteConfig; + const { clusterBkg, clusterBorder } = themeVariables; + + const { labelStyles, nodeStyles, borderStyles, backgroundStyles } = styles2String(node); + + // Add outer g element + const shapeSvg = parent + .insert('g') + .attr('class', 'cluster ' + node.cssClasses) + .attr('id', node.id) + .attr('data-look', node.look); + + const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels); + + // Create the label and insert it after the rect + const labelEl = shapeSvg.insert('g').attr('class', 'cluster-label '); + + const text = await createText(labelEl, node.label, { + style: node.labelStyle, + useHtmlLabels, + isNode: true, + width: node.width, + }); + + // Get the size of the label + let bbox = text.getBBox(); + + if (evaluate(siteConfig.flowchart.htmlLabels)) { + const div = text.children[0]; + const dv = select(text); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + + const width = node.width <= bbox.width + node.padding ? bbox.width + node.padding : node.width; + if (node.width <= bbox.width + node.padding) { + node.diff = (width - node.width) / 2 - node.padding; + } else { + node.diff = -node.padding; + } + + const height = node.height; + const x = node.x - width / 2; + const y = node.y - height / 2; + + log.trace('Data ', node, JSON.stringify(node)); + let rect; + if (node.look === 'handDrawn') { + // @ts-ignore TODO: Fix rough typings + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.7, + fill: clusterBkg, + // fill: 'red', + stroke: clusterBorder, + fillWeight: 4, + seed: handDrawnSeed, + }); + const roughNode = rc.path(createRoundedRectPathD(x, y, width, height, node.rx), options); + rect = shapeSvg.insert(() => { + log.debug('Rough node insert CXC', roughNode); + return roughNode; + }, ':first-child'); + // Should we affect the options instead of doing this? + rect.select('path:nth-child(2)').attr('style', borderStyles.join(';')); + rect.select('path').attr('style', backgroundStyles.join(';').replace('fill', 'stroke')); + } else { + // add the rect + rect = shapeSvg.insert('rect', ':first-child'); + // center the rect around its coordinate + rect + .attr('style', nodeStyles) + .attr('rx', node.rx) + .attr('ry', node.ry) + .attr('x', x) + .attr('y', y) + .attr('width', width) + .attr('height', height); + } + const { subGraphTitleTopMargin } = getSubGraphTitleMargins(siteConfig); + labelEl.attr( + 'transform', + // This puts the label on top of the box instead of inside it + `translate(${node.x - bbox.width / 2}, ${node.y - node.height / 2 + subGraphTitleTopMargin})` + ); + + if (labelStyles) { + const span = labelEl.select('span'); + if (span) { + span.attr('style', labelStyles); + } + } + // Center the label + + const rectBox = rect.node().getBBox(); + node.offsetX = 0; + node.width = rectBox.width; + node.height = rectBox.height; + // Used by layout engine to position subgraph in parent + node.offsetY = bbox.height - node.padding / 2; + + node.intersect = function (point) { + return intersectRect(node, point); + }; + + return { cluster: shapeSvg, labelBBox: bbox }; +}; +const divider = (parent, node) => { + const siteConfig = getConfig(); + + const { themeVariables, handDrawnSeed } = siteConfig; + const { nodeBorder } = themeVariables; + + // Add outer g element + const shapeSvg = parent + .insert('g') + .attr('class', node.cssClasses) + .attr('id', node.id) + .attr('data-look', node.look); + + // add the rect + const outerRectG = shapeSvg.insert('g', ':first-child'); + + const padding = 0 * node.padding; + + const width = node.width + padding; + + node.diff = -node.padding; + + const height = node.height + padding; + // const height = node.height + padding; + const x = node.x - width / 2; + const y = node.y - height / 2; + node.width = width; + + // add the rect + let rect; + if (node.look === 'handDrawn') { + const rc = rough.svg(shapeSvg); + const roughOuterNode = rc.rectangle(x, y, width, height, { + fill: 'lightgrey', + roughness: 0.5, + strokeLineDash: [5], + stroke: nodeBorder, + seed: handDrawnSeed, + }); + + rect = shapeSvg.insert(() => roughOuterNode, ':first-child'); + } else { + rect = outerRectG.insert('rect', ':first-child'); + const outerRectClass = 'divider'; + + // center the rect around its coordinate + rect + .attr('class', outerRectClass) + .attr('x', x) + .attr('y', y) + .attr('width', width) + .attr('height', height) + .attr('data-look', node.look); + } + + const rectBox = rect.node().getBBox(); + node.height = rectBox.height; + node.offsetX = 0; + // Used by layout engine to position subgraph in parent + node.offsetY = 0; + + node.intersect = function (point) { + return intersectRect(node, point); + }; + + return { cluster: shapeSvg, labelBBox: {} }; +}; + +const squareRect = rect; +const shapes = { + rect, + squareRect, + roundedWithTitle, + noteGroup, + divider, + kanbanSection, +}; + +let clusterElems = new Map(); + +/** + * @typedef {keyof typeof shapes} ClusterShapeID + */ + +/** + * @param {import('../types.js').ClusterNode} node - Shape defaults to 'rect' + */ +export const insertCluster = async (elem, node) => { + const shape = node.shape || 'rect'; + const cluster = await shapes[shape](elem, node); + clusterElems.set(node.id, cluster); + return cluster; +}; + +export const getClusterTitleWidth = (elem, node) => { + const label = createLabel(node.label, node.labelStyle, undefined, true); + elem.node().appendChild(label); + const width = label.getBBox().width; + elem.node().removeChild(label); + return width; +}; + +export const clear = () => { + clusterElems = new Map(); +}; + +export const positionCluster = (node) => { + log.info( + 'Position cluster (' + + node.id + + ', ' + + node.x + + ', ' + + node.y + + ') (' + + node?.width + + ', ' + + node?.height + + ')', + clusterElems.get(node.id) + ); + const el = clusterElems.get(node.id); + el.cluster.attr('transform', 'translate(' + node.x + ', ' + node.y + ')'); +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/createLabel.js b/packages/mermaid/src/rendering-util/rendering-elements/createLabel.js new file mode 100644 index 0000000000..482dbb9f13 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/createLabel.js @@ -0,0 +1,105 @@ +import { select } from 'd3'; +import { log } from '../../logger.js'; +import { getConfig } from '../../diagram-api/diagramAPI.js'; +import common, { evaluate, renderKatex, hasKatex } from '../../diagrams/common/common.js'; +import { decodeEntities } from '../../utils.js'; + +/** + * @param dom + * @param styleFn + */ +function applyStyle(dom, styleFn) { + if (styleFn) { + dom.attr('style', styleFn); + } +} + +/** + * @param {any} node + * @returns {Promise} Node + */ +async function addHtmlLabel(node) { + const fo = select(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject')); + const div = fo.append('xhtml:div'); + + let label = node.label; + if (node.label && hasKatex(node.label)) { + label = await renderKatex(node.label.replace(common.lineBreakRegex, '\n'), getConfig()); + } + const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel'; + div.html( + '' + + label + + '' + ); + + applyStyle(div, node.labelStyle); + div.style('display', 'inline-block'); + div.style('padding-right', '1px'); + // Fix for firefox + div.style('white-space', 'nowrap'); + div.attr('xmlns', 'http://www.w3.org/1999/xhtml'); + return fo.node(); +} +/** + * @param _vertexText + * @param style + * @param isTitle + * @param isNode + * @deprecated svg-util/createText instead + */ +const createLabel = async (_vertexText, style, isTitle, isNode) => { + let vertexText = _vertexText || ''; + if (typeof vertexText === 'object') { + vertexText = vertexText[0]; + } + + if (evaluate(getConfig().flowchart.htmlLabels)) { + // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that? + vertexText = vertexText.replace(/\\n|\n/g, '
'); + log.info('vertexText' + vertexText); + const node = { + isNode, + label: decodeEntities(vertexText).replace( + /fa[blrs]?:fa-[\w-]+/g, + (s) => `` + ), + labelStyle: style ? style.replace('fill:', 'color:') : style, + }; + let vertexNode = await addHtmlLabel(node); + // vertexNode.parentNode.removeChild(vertexNode); + return vertexNode; + } else { + const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + svgLabel.setAttribute('style', style.replace('color:', 'fill:')); + let rows = []; + if (typeof vertexText === 'string') { + rows = vertexText.split(/\\n|\n|/gi); + } else if (Array.isArray(vertexText)) { + rows = vertexText; + } 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'); + if (isTitle) { + tspan.setAttribute('class', 'title-row'); + } else { + tspan.setAttribute('class', 'row'); + } + tspan.textContent = row.trim(); + svgLabel.appendChild(tspan); + } + return svgLabel; + } +}; + +export default createLabel; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/edgeMarker.spec.ts b/packages/mermaid/src/rendering-util/rendering-elements/edgeMarker.spec.ts new file mode 100644 index 0000000000..7702782c21 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/edgeMarker.spec.ts @@ -0,0 +1,80 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +import type { SVG } from '../../diagram-api/types.js'; +import type { Mocked } from 'vitest'; +import { addEdgeMarkers } from './edgeMarker.js'; + +describe('addEdgeMarker', () => { + const svgPath = { + attr: vitest.fn(), + } as unknown as Mocked; + const url = 'http://example.com'; + const id = 'test'; + const diagramType = 'test'; + + beforeEach(() => { + svgPath.attr.mockReset(); + }); + + it('should add markers for arrow_cross:arrow_point', () => { + const arrowTypeStart = 'arrow_cross'; + const arrowTypeEnd = 'arrow_point'; + addEdgeMarkers(svgPath, { arrowTypeStart, arrowTypeEnd }, url, id, diagramType); + expect(svgPath.attr).toHaveBeenCalledWith( + 'marker-start', + `url(${url}#${id}_${diagramType}-crossStart)` + ); + expect(svgPath.attr).toHaveBeenCalledWith( + 'marker-end', + `url(${url}#${id}_${diagramType}-pointEnd)` + ); + }); + + it('should add markers for aggregation:arrow_point', () => { + const arrowTypeStart = 'aggregation'; + const arrowTypeEnd = 'arrow_point'; + addEdgeMarkers(svgPath, { arrowTypeStart, arrowTypeEnd }, url, id, diagramType); + expect(svgPath.attr).toHaveBeenCalledWith( + 'marker-start', + `url(${url}#${id}_${diagramType}-aggregationStart)` + ); + expect(svgPath.attr).toHaveBeenCalledWith( + 'marker-end', + `url(${url}#${id}_${diagramType}-pointEnd)` + ); + }); + + it('should add markers for arrow_point:aggregation', () => { + const arrowTypeStart = 'arrow_point'; + const arrowTypeEnd = 'aggregation'; + addEdgeMarkers(svgPath, { arrowTypeStart, arrowTypeEnd }, url, id, diagramType); + expect(svgPath.attr).toHaveBeenCalledWith( + 'marker-start', + `url(${url}#${id}_${diagramType}-pointStart)` + ); + expect(svgPath.attr).toHaveBeenCalledWith( + 'marker-end', + `url(${url}#${id}_${diagramType}-aggregationEnd)` + ); + }); + + it('should add markers for aggregation:composition', () => { + const arrowTypeStart = 'aggregation'; + const arrowTypeEnd = 'composition'; + addEdgeMarkers(svgPath, { arrowTypeStart, arrowTypeEnd }, url, id, diagramType); + expect(svgPath.attr).toHaveBeenCalledWith( + 'marker-start', + `url(${url}#${id}_${diagramType}-aggregationStart)` + ); + expect(svgPath.attr).toHaveBeenCalledWith( + 'marker-end', + `url(${url}#${id}_${diagramType}-compositionEnd)` + ); + }); + + it('should not add invalid markers', () => { + const arrowTypeStart = 'this is an invalid marker'; + const arrowTypeEnd = ') url(https://my-malicious-site.example)'; + addEdgeMarkers(svgPath, { arrowTypeStart, arrowTypeEnd }, url, id, diagramType); + expect(svgPath.attr).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/edgeMarker.ts b/packages/mermaid/src/rendering-util/rendering-elements/edgeMarker.ts new file mode 100644 index 0000000000..5371ac32d1 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/edgeMarker.ts @@ -0,0 +1,57 @@ +import type { SVG } from '../../diagram-api/types.js'; +import { log } from '../../logger.js'; +import type { EdgeData } from '../../types.js'; +/** + * Adds SVG markers to a path element based on the arrow types specified in the edge. + * + * @param svgPath - The SVG path element to add markers to. + * @param edge - The edge data object containing the arrow types. + * @param url - The URL of the SVG marker definitions. + * @param id - The ID prefix for the SVG marker definitions. + * @param diagramType - The type of diagram being rendered. + */ +export const addEdgeMarkers = ( + svgPath: SVG, + edge: Pick, + url: string, + id: string, + diagramType: string +) => { + if (edge.arrowTypeStart) { + addEdgeMarker(svgPath, 'start', edge.arrowTypeStart, url, id, diagramType); + } + if (edge.arrowTypeEnd) { + addEdgeMarker(svgPath, 'end', edge.arrowTypeEnd, url, id, diagramType); + } +}; + +const arrowTypesMap = { + arrow_cross: 'cross', + arrow_point: 'point', + arrow_barb: 'barb', + arrow_circle: 'circle', + aggregation: 'aggregation', + extension: 'extension', + composition: 'composition', + dependency: 'dependency', + lollipop: 'lollipop', +} as const; + +const addEdgeMarker = ( + svgPath: SVG, + position: 'start' | 'end', + arrowType: string, + url: string, + id: string, + diagramType: string +) => { + const endMarkerType = arrowTypesMap[arrowType as keyof typeof arrowTypesMap]; + + if (!endMarkerType) { + log.warn(`Unknown arrow type: ${arrowType}`); + return; // unknown arrow type, ignore + } + + const suffix = position === 'start' ? 'Start' : 'End'; + svgPath.attr(`marker-${position}`, `url(${url}#${id}_${diagramType}-${endMarkerType}${suffix})`); +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/edges.js b/packages/mermaid/src/rendering-util/rendering-elements/edges.js new file mode 100644 index 0000000000..a6a7a55f77 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/edges.js @@ -0,0 +1,574 @@ +import { getConfig } from '../../diagram-api/diagramAPI.js'; +import { evaluate } from '../../diagrams/common/common.js'; +import { log } from '../../logger.js'; +import { createText } from '../createText.js'; +import utils from '../../utils.js'; +import { getLineFunctionsWithOffset } from '../../utils/lineWithOffset.js'; +import { getSubGraphTitleMargins } from '../../utils/subGraphTitleMargins.js'; +import { curveBasis, line, select } from 'd3'; +import rough from 'roughjs'; +import createLabel from './createLabel.js'; +import { addEdgeMarkers } from './edgeMarker.ts'; + +const edgeLabels = new Map(); +const terminalLabels = new Map(); + +export const clear = () => { + edgeLabels.clear(); + terminalLabels.clear(); +}; + +export const getLabelStyles = (styleArray) => { + let styles = styleArray ? styleArray.reduce((acc, style) => acc + ';' + style, '') : ''; + return styles; +}; + +export const insertEdgeLabel = async (elem, edge) => { + let useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels); + + const labelElement = await createText(elem, edge.label, { + style: getLabelStyles(edge.labelStyle), + useHtmlLabels, + addSvgBackground: true, + isNode: false, + }); + log.info('abc82', edge, edge.labelType); + + // Create outer g, edgeLabel, this will be positioned after graph layout + const edgeLabel = elem.insert('g').attr('class', 'edgeLabel'); + + // Create inner g, label, this will be positioned now for centering the text + const label = edgeLabel.insert('g').attr('class', 'label'); + label.node().appendChild(labelElement); + + // Center the label + let bbox = labelElement.getBBox(); + if (useHtmlLabels) { + const div = labelElement.children[0]; + const dv = select(labelElement); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')'); + + // Make element accessible by id for positioning + edgeLabels.set(edge.id, edgeLabel); + + // Update the abstract data of the edge with the new information about its width and height + edge.width = bbox.width; + edge.height = bbox.height; + + let fo; + if (edge.startLabelLeft) { + // Create the actual text element + const startLabelElement = await createLabel( + edge.startLabelLeft, + getLabelStyles(edge.labelStyle) + ); + const startEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals'); + const inner = startEdgeLabelLeft.insert('g').attr('class', 'inner'); + fo = inner.node().appendChild(startLabelElement); + const slBox = startLabelElement.getBBox(); + inner.attr('transform', 'translate(' + -slBox.width / 2 + ', ' + -slBox.height / 2 + ')'); + if (!terminalLabels.get(edge.id)) { + terminalLabels.set(edge.id, {}); + } + terminalLabels.get(edge.id).startLeft = startEdgeLabelLeft; + setTerminalWidth(fo, edge.startLabelLeft); + } + if (edge.startLabelRight) { + // Create the actual text element + const startLabelElement = await createLabel( + edge.startLabelRight, + getLabelStyles(edge.labelStyle) + ); + const startEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals'); + const inner = startEdgeLabelRight.insert('g').attr('class', 'inner'); + fo = startEdgeLabelRight.node().appendChild(startLabelElement); + inner.node().appendChild(startLabelElement); + const slBox = startLabelElement.getBBox(); + inner.attr('transform', 'translate(' + -slBox.width / 2 + ', ' + -slBox.height / 2 + ')'); + + if (!terminalLabels.get(edge.id)) { + terminalLabels.set(edge.id, {}); + } + terminalLabels.get(edge.id).startRight = startEdgeLabelRight; + setTerminalWidth(fo, edge.startLabelRight); + } + if (edge.endLabelLeft) { + // Create the actual text element + const endLabelElement = await createLabel(edge.endLabelLeft, getLabelStyles(edge.labelStyle)); + const endEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals'); + const inner = endEdgeLabelLeft.insert('g').attr('class', 'inner'); + fo = inner.node().appendChild(endLabelElement); + const slBox = endLabelElement.getBBox(); + inner.attr('transform', 'translate(' + -slBox.width / 2 + ', ' + -slBox.height / 2 + ')'); + + endEdgeLabelLeft.node().appendChild(endLabelElement); + + if (!terminalLabels.get(edge.id)) { + terminalLabels.set(edge.id, {}); + } + terminalLabels.get(edge.id).endLeft = endEdgeLabelLeft; + setTerminalWidth(fo, edge.endLabelLeft); + } + if (edge.endLabelRight) { + // Create the actual text element + const endLabelElement = await createLabel(edge.endLabelRight, getLabelStyles(edge.labelStyle)); + const endEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals'); + const inner = endEdgeLabelRight.insert('g').attr('class', 'inner'); + + fo = inner.node().appendChild(endLabelElement); + const slBox = endLabelElement.getBBox(); + inner.attr('transform', 'translate(' + -slBox.width / 2 + ', ' + -slBox.height / 2 + ')'); + + endEdgeLabelRight.node().appendChild(endLabelElement); + if (!terminalLabels.get(edge.id)) { + terminalLabels.set(edge.id, {}); + } + terminalLabels.get(edge.id).endRight = endEdgeLabelRight; + setTerminalWidth(fo, edge.endLabelRight); + } + return labelElement; +}; + +/** + * @param {any} fo + * @param {any} value + */ +function setTerminalWidth(fo, value) { + if (getConfig().flowchart.htmlLabels && fo) { + fo.style.width = value.length * 9 + 'px'; + fo.style.height = '12px'; + } +} + +export const positionEdgeLabel = (edge, paths) => { + log.debug('Moving label abc88 ', edge.id, edge.label, edgeLabels.get(edge.id), paths); + let path = paths.updatedPath ? paths.updatedPath : paths.originalPath; + const siteConfig = getConfig(); + const { subGraphTitleTotalMargin } = getSubGraphTitleMargins(siteConfig); + if (edge.label) { + const el = edgeLabels.get(edge.id); + let x = edge.x; + let y = edge.y; + if (path) { + const pos = utils.calcLabelPosition(path); + log.debug( + 'Moving label ' + edge.label + ' from (', + x, + ',', + y, + ') to (', + pos.x, + ',', + pos.y, + ') abc88' + ); + if (paths.updatedPath) { + x = pos.x; + y = pos.y; + } + } + el.attr('transform', `translate(${x}, ${y + subGraphTitleTotalMargin / 2})`); + } + + if (edge.startLabelLeft) { + const el = terminalLabels.get(edge.id).startLeft; + let x = edge.x; + let y = edge.y; + if (path) { + const pos = utils.calcTerminalLabelPosition(edge.arrowTypeStart ? 10 : 0, 'start_left', path); + x = pos.x; + y = pos.y; + } + el.attr('transform', `translate(${x}, ${y})`); + } + if (edge.startLabelRight) { + const el = terminalLabels.get(edge.id).startRight; + let x = edge.x; + let y = edge.y; + if (path) { + const pos = utils.calcTerminalLabelPosition( + edge.arrowTypeStart ? 10 : 0, + 'start_right', + path + ); + x = pos.x; + y = pos.y; + } + el.attr('transform', `translate(${x}, ${y})`); + } + if (edge.endLabelLeft) { + const el = terminalLabels.get(edge.id).endLeft; + let x = edge.x; + let y = edge.y; + if (path) { + const pos = utils.calcTerminalLabelPosition(edge.arrowTypeEnd ? 10 : 0, 'end_left', path); + x = pos.x; + y = pos.y; + } + el.attr('transform', `translate(${x}, ${y})`); + } + if (edge.endLabelRight) { + const el = terminalLabels.get(edge.id).endRight; + let x = edge.x; + let y = edge.y; + if (path) { + const pos = utils.calcTerminalLabelPosition(edge.arrowTypeEnd ? 10 : 0, 'end_right', path); + x = pos.x; + y = pos.y; + } + el.attr('transform', `translate(${x}, ${y})`); + } +}; + +const outsideNode = (node, point) => { + const x = node.x; + const y = node.y; + const dx = Math.abs(point.x - x); + const dy = Math.abs(point.y - y); + const w = node.width / 2; + const h = node.height / 2; + return dx >= w || dy >= h; +}; + +export const intersection = (node, outsidePoint, insidePoint) => { + log.debug(`intersection calc abc89: + outsidePoint: ${JSON.stringify(outsidePoint)} + insidePoint : ${JSON.stringify(insidePoint)} + node : x:${node.x} y:${node.y} w:${node.width} h:${node.height}`); + const x = node.x; + const y = node.y; + + const dx = Math.abs(x - insidePoint.x); + const w = node.width / 2; + let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx; + const h = node.height / 2; + + const Q = Math.abs(outsidePoint.y - insidePoint.y); + const R = Math.abs(outsidePoint.x - insidePoint.x); + + if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) { + // Intersection is top or bottom of rect. + let q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y; + r = (R * q) / Q; + const res = { + x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r, + y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q, + }; + + if (r === 0) { + res.x = outsidePoint.x; + res.y = outsidePoint.y; + } + if (R === 0) { + res.x = outsidePoint.x; + } + if (Q === 0) { + res.y = outsidePoint.y; + } + + log.debug(`abc89 top/bottom calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res); + + return res; + } else { + // Intersection on sides of rect + if (insidePoint.x < outsidePoint.x) { + r = outsidePoint.x - w - x; + } else { + r = x - w - outsidePoint.x; + } + let q = (Q * r) / R; + let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r; + let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q; + log.debug(`sides calc abc89, Q ${Q}, q ${q}, R ${R}, r ${r}`, { _x, _y }); + if (r === 0) { + _x = outsidePoint.x; + _y = outsidePoint.y; + } + if (R === 0) { + _x = outsidePoint.x; + } + if (Q === 0) { + _y = outsidePoint.y; + } + + return { x: _x, y: _y }; + } +}; + +const cutPathAtIntersect = (_points, boundaryNode) => { + log.warn('abc88 cutPathAtIntersect', _points, boundaryNode); + let points = []; + let lastPointOutside = _points[0]; + let isInside = false; + _points.forEach((point) => { + log.info('abc88 checking point', point, boundaryNode); + + if (!outsideNode(boundaryNode, point) && !isInside) { + const inter = intersection(boundaryNode, lastPointOutside, point); + log.debug('abc88 inside', point, lastPointOutside, inter); + log.debug('abc88 intersection', inter, boundaryNode); + + let pointPresent = false; + points.forEach((p) => { + pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y); + }); + + if (!points.some((e) => e.x === inter.x && e.y === inter.y)) { + points.push(inter); + } else { + log.warn('abc88 no intersect', inter, points); + } + isInside = true; + } else { + log.warn('abc88 outside', point, lastPointOutside); + lastPointOutside = point; + if (!isInside) { + points.push(point); + } + } + }); + log.debug('returning points', points); + return points; +}; + +function extractCornerPoints(points) { + const cornerPoints = []; + const cornerPointPositions = []; + for (let i = 1; i < points.length - 1; i++) { + const prev = points[i - 1]; + const curr = points[i]; + const next = points[i + 1]; + if ( + prev.x === curr.x && + curr.y === next.y && + Math.abs(curr.x - next.x) > 5 && + Math.abs(curr.y - prev.y) > 5 + ) { + cornerPoints.push(curr); + cornerPointPositions.push(i); + } else if ( + prev.y === curr.y && + curr.x === next.x && + Math.abs(curr.x - prev.x) > 5 && + Math.abs(curr.y - next.y) > 5 + ) { + cornerPoints.push(curr); + cornerPointPositions.push(i); + } + } + return { cornerPoints, cornerPointPositions }; +} + +const findAdjacentPoint = function (pointA, pointB, distance) { + const xDiff = pointB.x - pointA.x; + const yDiff = pointB.y - pointA.y; + const length = Math.sqrt(xDiff * xDiff + yDiff * yDiff); + const ratio = distance / length; + return { x: pointB.x - ratio * xDiff, y: pointB.y - ratio * yDiff }; +}; + +const fixCorners = function (lineData) { + const { cornerPointPositions } = extractCornerPoints(lineData); + const newLineData = []; + for (let i = 0; i < lineData.length; i++) { + if (cornerPointPositions.includes(i)) { + const prevPoint = lineData[i - 1]; + const nextPoint = lineData[i + 1]; + const cornerPoint = lineData[i]; + + const newPrevPoint = findAdjacentPoint(prevPoint, cornerPoint, 5); + const newNextPoint = findAdjacentPoint(nextPoint, cornerPoint, 5); + + const xDiff = newNextPoint.x - newPrevPoint.x; + const yDiff = newNextPoint.y - newPrevPoint.y; + newLineData.push(newPrevPoint); + + const a = Math.sqrt(2) * 2; + let newCornerPoint = { x: cornerPoint.x, y: cornerPoint.y }; + if (Math.abs(nextPoint.x - prevPoint.x) > 10 && Math.abs(nextPoint.y - prevPoint.y) >= 10) { + log.debug( + 'Corner point fixing', + Math.abs(nextPoint.x - prevPoint.x), + Math.abs(nextPoint.y - prevPoint.y) + ); + const r = 5; + if (cornerPoint.x === newPrevPoint.x) { + newCornerPoint = { + x: xDiff < 0 ? newPrevPoint.x - r + a : newPrevPoint.x + r - a, + y: yDiff < 0 ? newPrevPoint.y - a : newPrevPoint.y + a, + }; + } else { + newCornerPoint = { + x: xDiff < 0 ? newPrevPoint.x - a : newPrevPoint.x + a, + y: yDiff < 0 ? newPrevPoint.y - r + a : newPrevPoint.y + r - a, + }; + } + } else { + log.debug( + 'Corner point skipping fixing', + Math.abs(nextPoint.x - prevPoint.x), + Math.abs(nextPoint.y - prevPoint.y) + ); + } + newLineData.push(newCornerPoint, newNextPoint); + } else { + newLineData.push(lineData[i]); + } + } + return newLineData; +}; + +export const insertEdge = function (elem, edge, clusterDb, diagramType, startNode, endNode, id) { + const { handDrawnSeed } = getConfig(); + let points = edge.points; + let pointsHasChanged = false; + const tail = startNode; + var head = endNode; + + if (head.intersect && tail.intersect) { + points = points.slice(1, edge.points.length - 1); + points.unshift(tail.intersect(points[0])); + log.debug( + 'Last point APA12', + edge.start, + '-->', + edge.end, + points[points.length - 1], + head, + head.intersect(points[points.length - 1]) + ); + points.push(head.intersect(points[points.length - 1])); + } + if (edge.toCluster) { + log.info('to cluster abc88', clusterDb.get(edge.toCluster)); + points = cutPathAtIntersect(edge.points, clusterDb.get(edge.toCluster).node); + + pointsHasChanged = true; + } + + if (edge.fromCluster) { + log.debug( + 'from cluster abc88', + clusterDb.get(edge.fromCluster), + JSON.stringify(points, null, 2) + ); + points = cutPathAtIntersect(points.reverse(), clusterDb.get(edge.fromCluster).node).reverse(); + + pointsHasChanged = true; + } + + let lineData = points.filter((p) => !Number.isNaN(p.y)); + lineData = fixCorners(lineData); + let curve = curveBasis; + if (edge.curve) { + curve = edge.curve; + } + + const { x, y } = getLineFunctionsWithOffset(edge); + const lineFunction = line().x(x).y(y).curve(curve); + + let strokeClasses; + switch (edge.thickness) { + case 'normal': + strokeClasses = 'edge-thickness-normal'; + break; + case 'thick': + strokeClasses = 'edge-thickness-thick'; + break; + case 'invisible': + strokeClasses = 'edge-thickness-invisible'; + break; + default: + strokeClasses = 'edge-thickness-normal'; + } + switch (edge.pattern) { + case 'solid': + strokeClasses += ' edge-pattern-solid'; + break; + case 'dotted': + strokeClasses += ' edge-pattern-dotted'; + break; + case 'dashed': + strokeClasses += ' edge-pattern-dashed'; + break; + default: + strokeClasses += ' edge-pattern-solid'; + } + let svgPath; + let linePath = lineFunction(lineData); + const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style]; + if (edge.look === 'handDrawn') { + const rc = rough.svg(elem); + Object.assign([], lineData); + + const svgPathNode = rc.path(linePath, { + roughness: 0.3, + seed: handDrawnSeed, + }); + + strokeClasses += ' transition'; + + svgPath = select(svgPathNode) + .select('path') + .attr('id', edge.id) + .attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '')) + .attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : ''); + let d = svgPath.attr('d'); + svgPath.attr('d', d); + elem.node().appendChild(svgPath.node()); + } else { + svgPath = elem + .append('path') + .attr('d', linePath) + .attr('id', edge.id) + .attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '')) + .attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : ''); + } + + // DEBUG code, DO NOT REMOVE + // adds a red circle at each edge coordinate + // cornerPoints.forEach((point) => { + // elem + // .append('circle') + // .style('stroke', 'blue') + // .style('fill', 'blue') + // .attr('r', 3) + // .attr('cx', point.x) + // .attr('cy', point.y); + // }); + // lineData.forEach((point) => { + // elem + // .append('circle') + // .style('stroke', 'blue') + // .style('fill', 'blue') + // .attr('r', 3) + // .attr('cx', point.x) + // .attr('cy', point.y); + // }); + + let url = ''; + if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) { + url = + window.location.protocol + + '//' + + window.location.host + + window.location.pathname + + window.location.search; + url = url.replace(/\(/g, '\\(').replace(/\)/g, '\\)'); + } + log.info('arrowTypeStart', edge.arrowTypeStart); + log.info('arrowTypeEnd', edge.arrowTypeEnd); + + addEdgeMarkers(svgPath, edge, url, id, diagramType); + + let paths = {}; + if (pointsHasChanged) { + paths.updatedPath = points; + } + paths.originalPath = edge.points; + return paths; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/intersect/index.js b/packages/mermaid/src/rendering-util/rendering-elements/intersect/index.js new file mode 100644 index 0000000000..e33b6dd510 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/intersect/index.js @@ -0,0 +1,17 @@ +/* + * Borrowed with love from from dagre-d3. Many thanks to cpettitt! + */ + +import node from './intersect-node.js'; +import circle from './intersect-circle.js'; +import ellipse from './intersect-ellipse.js'; +import polygon from './intersect-polygon.js'; +import rect from './intersect-rect.js'; + +export default { + node, + circle, + ellipse, + polygon, + rect, +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-circle.js b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-circle.js new file mode 100644 index 0000000000..b71d297eda --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-circle.js @@ -0,0 +1,7 @@ +import intersectEllipse from './intersect-ellipse.js'; + +function intersectCircle(node, rx, point) { + return intersectEllipse(node, rx, rx, point); +} + +export default intersectCircle; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-ellipse.js b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-ellipse.js new file mode 100644 index 0000000000..def637b2c5 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-ellipse.js @@ -0,0 +1,24 @@ +function intersectEllipse(node, rx, ry, point) { + // Formulae from: https://mathworld.wolfram.com/Ellipse-LineIntersection.html + + var cx = node.x; + var cy = node.y; + + var px = cx - point.x; + var py = cy - point.y; + + var det = Math.sqrt(rx * rx * py * py + ry * ry * px * px); + + var dx = Math.abs((rx * ry * px) / det); + if (point.x < cx) { + dx = -dx; + } + var dy = Math.abs((rx * ry * py) / det); + if (point.y < cy) { + dy = -dy; + } + + return { x: cx + dx, y: cy + dy }; +} + +export default intersectEllipse; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-line.js b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-line.js new file mode 100644 index 0000000000..bd3eb497f9 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-line.js @@ -0,0 +1,69 @@ +/** + * Returns the point at which two lines, p and q, intersect or returns undefined if they do not intersect. + */ +function intersectLine(p1, p2, q1, q2) { + // Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994, + // p7 and p473. + + var a1, a2, b1, b2, c1, c2; + var r1, r2, r3, r4; + var denom, offset, num; + var x, y; + + // Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x + + // b1 y + c1 = 0. + a1 = p2.y - p1.y; + b1 = p1.x - p2.x; + c1 = p2.x * p1.y - p1.x * p2.y; + + // Compute r3 and r4. + r3 = a1 * q1.x + b1 * q1.y + c1; + r4 = a1 * q2.x + b1 * q2.y + c1; + + // Check signs of r3 and r4. If both point 3 and point 4 lie on + // same side of line 1, the line segments do not intersect. + if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) { + return /*DON'T_INTERSECT*/; + } + + // Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0 + a2 = q2.y - q1.y; + b2 = q1.x - q2.x; + c2 = q2.x * q1.y - q1.x * q2.y; + + // Compute r1 and r2 + r1 = a2 * p1.x + b2 * p1.y + c2; + r2 = a2 * p2.x + b2 * p2.y + c2; + + // Check signs of r1 and r2. If both point 1 and point 2 lie + // on same side of second line segment, the line segments do + // not intersect. + if (r1 !== 0 && r2 !== 0 && sameSign(r1, r2)) { + return /*DON'T_INTERSECT*/; + } + + // Line segments intersect: compute intersection point. + denom = a1 * b2 - a2 * b1; + if (denom === 0) { + return /*COLLINEAR*/; + } + + offset = Math.abs(denom / 2); + + // The denom/2 is to get rounding instead of truncating. It + // is added or subtracted to the numerator, depending upon the + // sign of the numerator. + num = b1 * c2 - b2 * c1; + x = num < 0 ? (num - offset) / denom : (num + offset) / denom; + + num = a2 * c1 - a1 * c2; + y = num < 0 ? (num - offset) / denom : (num + offset) / denom; + + return { x: x, y: y }; +} + +function sameSign(r1, r2) { + return r1 * r2 > 0; +} + +export default intersectLine; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-node.js b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-node.js new file mode 100644 index 0000000000..08ed7b4c85 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-node.js @@ -0,0 +1,5 @@ +function intersectNode(node, point) { + return node.intersect(point); +} + +export default intersectNode; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-polygon.js b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-polygon.js new file mode 100644 index 0000000000..2e48ba8d01 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-polygon.js @@ -0,0 +1,63 @@ +import intersectLine from './intersect-line.js'; + +/** + * Returns the point ({x, y}) at which the point argument intersects with the node argument assuming + * that it has the shape specified by polygon. + */ +function intersectPolygon(node, polyPoints, point) { + let x1 = node.x; + let y1 = node.y; + + let intersections = []; + + let minX = Number.POSITIVE_INFINITY; + let minY = Number.POSITIVE_INFINITY; + if (typeof polyPoints.forEach === 'function') { + polyPoints.forEach(function (entry) { + minX = Math.min(minX, entry.x); + minY = Math.min(minY, entry.y); + }); + } else { + minX = Math.min(minX, polyPoints.x); + minY = Math.min(minY, polyPoints.y); + } + + let left = x1 - node.width / 2 - minX; + let top = y1 - node.height / 2 - minY; + + for (let i = 0; i < polyPoints.length; i++) { + let p1 = polyPoints[i]; + let p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0]; + let intersect = intersectLine( + node, + point, + { x: left + p1.x, y: top + p1.y }, + { x: left + p2.x, y: top + p2.y } + ); + if (intersect) { + intersections.push(intersect); + } + } + + if (!intersections.length) { + return node; + } + + if (intersections.length > 1) { + // More intersections, find the one nearest to edge end point + intersections.sort(function (p, q) { + let pdx = p.x - point.x; + let pdy = p.y - point.y; + let distp = Math.sqrt(pdx * pdx + pdy * pdy); + + let qdx = q.x - point.x; + let qdy = q.y - point.y; + let distq = Math.sqrt(qdx * qdx + qdy * qdy); + + return distp < distq ? -1 : distp === distq ? 0 : 1; + }); + } + return intersections[0]; +} + +export default intersectPolygon; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-rect.js b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-rect.js new file mode 100644 index 0000000000..daf6b5eea0 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-rect.js @@ -0,0 +1,32 @@ +const intersectRect = (node, point) => { + var x = node.x; + var y = node.y; + + // Rectangle intersection algorithm from: + // https://math.stackexchange.com/questions/108113/find-edge-between-two-boxes + var dx = point.x - x; + var dy = point.y - y; + var w = node.width / 2; + var h = node.height / 2; + + var sx, sy; + if (Math.abs(dy) * w > Math.abs(dx) * h) { + // Intersection is top or bottom of rect. + if (dy < 0) { + h = -h; + } + sx = dy === 0 ? 0 : (h * dx) / dy; + sy = h; + } else { + // Intersection is left or right of rect. + if (dx < 0) { + w = -w; + } + sx = w; + sy = dx === 0 ? 0 : (w * dy) / dx; + } + + return { x: x + sx, y: y + sy }; +}; + +export default intersectRect; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/markers.js b/packages/mermaid/src/rendering-util/rendering-elements/markers.js new file mode 100644 index 0000000000..b2592e20ec --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/markers.js @@ -0,0 +1,293 @@ +/** Setup arrow head and define the marker. The result is appended to the svg. */ +import { log } from '../../logger.js'; + +// Only add the number of markers that the diagram needs +const insertMarkers = (elem, markerArray, type, id) => { + markerArray.forEach((markerName) => { + markers[markerName](elem, type, id); + }); +}; + +const extension = (elem, type, id) => { + log.trace('Making markers for ', id); + elem + .append('defs') + .append('marker') + .attr('id', id + '_' + type + '-extensionStart') + .attr('class', 'marker extension ' + type) + .attr('refX', 18) + .attr('refY', 7) + .attr('markerWidth', 190) + .attr('markerHeight', 240) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 1,7 L18,13 V 1 Z'); + + elem + .append('defs') + .append('marker') + .attr('id', id + '_' + type + '-extensionEnd') + .attr('class', 'marker extension ' + type) + .attr('refX', 1) + .attr('refY', 7) + .attr('markerWidth', 20) + .attr('markerHeight', 28) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 1,1 V 13 L18,7 Z'); // this is actual shape for arrowhead +}; + +const composition = (elem, type, id) => { + elem + .append('defs') + .append('marker') + .attr('id', id + '_' + type + '-compositionStart') + .attr('class', 'marker composition ' + type) + .attr('refX', 18) + .attr('refY', 7) + .attr('markerWidth', 190) + .attr('markerHeight', 240) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z'); + + elem + .append('defs') + .append('marker') + .attr('id', id + '_' + type + '-compositionEnd') + .attr('class', 'marker composition ' + type) + .attr('refX', 1) + .attr('refY', 7) + .attr('markerWidth', 20) + .attr('markerHeight', 28) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z'); +}; +const aggregation = (elem, type, id) => { + elem + .append('defs') + .append('marker') + .attr('id', id + '_' + type + '-aggregationStart') + .attr('class', 'marker aggregation ' + type) + .attr('refX', 18) + .attr('refY', 7) + .attr('markerWidth', 190) + .attr('markerHeight', 240) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z'); + + elem + .append('defs') + .append('marker') + .attr('id', id + '_' + type + '-aggregationEnd') + .attr('class', 'marker aggregation ' + type) + .attr('refX', 1) + .attr('refY', 7) + .attr('markerWidth', 20) + .attr('markerHeight', 28) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z'); +}; +const dependency = (elem, type, id) => { + elem + .append('defs') + .append('marker') + .attr('id', id + '_' + type + '-dependencyStart') + .attr('class', 'marker dependency ' + type) + .attr('refX', 6) + .attr('refY', 7) + .attr('markerWidth', 190) + .attr('markerHeight', 240) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 5,7 L9,13 L1,7 L9,1 Z'); + + elem + .append('defs') + .append('marker') + .attr('id', id + '_' + type + '-dependencyEnd') + .attr('class', 'marker dependency ' + type) + .attr('refX', 13) + .attr('refY', 7) + .attr('markerWidth', 20) + .attr('markerHeight', 28) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z'); +}; +const lollipop = (elem, type, id) => { + elem + .append('defs') + .append('marker') + .attr('id', id + '_' + type + '-lollipopStart') + .attr('class', 'marker lollipop ' + type) + .attr('refX', 13) + .attr('refY', 7) + .attr('markerWidth', 190) + .attr('markerHeight', 240) + .attr('orient', 'auto') + .append('circle') + .attr('stroke', 'black') + .attr('fill', 'transparent') + .attr('cx', 7) + .attr('cy', 7) + .attr('r', 6); + + elem + .append('defs') + .append('marker') + .attr('id', id + '_' + type + '-lollipopEnd') + .attr('class', 'marker lollipop ' + type) + .attr('refX', 1) + .attr('refY', 7) + .attr('markerWidth', 190) + .attr('markerHeight', 240) + .attr('orient', 'auto') + .append('circle') + .attr('stroke', 'black') + .attr('fill', 'transparent') + .attr('cx', 7) + .attr('cy', 7) + .attr('r', 6); +}; +const point = (elem, type, id) => { + elem + .append('marker') + .attr('id', id + '_' + type + '-pointEnd') + .attr('class', 'marker ' + type) + .attr('viewBox', '0 0 10 10') + .attr('refX', 5) + .attr('refY', 5) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 8) + .attr('markerHeight', 8) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 0 0 L 10 5 L 0 10 z') + .attr('class', 'arrowMarkerPath') + .style('stroke-width', 1) + .style('stroke-dasharray', '1,0'); + elem + .append('marker') + .attr('id', id + '_' + type + '-pointStart') + .attr('class', 'marker ' + type) + .attr('viewBox', '0 0 10 10') + .attr('refX', 4.5) + .attr('refY', 5) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 8) + .attr('markerHeight', 8) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 0 5 L 10 10 L 10 0 z') + .attr('class', 'arrowMarkerPath') + .style('stroke-width', 1) + .style('stroke-dasharray', '1,0'); +}; +const circle = (elem, type, id) => { + elem + .append('marker') + .attr('id', id + '_' + type + '-circleEnd') + .attr('class', 'marker ' + type) + .attr('viewBox', '0 0 10 10') + .attr('refX', 11) + .attr('refY', 5) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 11) + .attr('markerHeight', 11) + .attr('orient', 'auto') + .append('circle') + .attr('cx', '5') + .attr('cy', '5') + .attr('r', '5') + .attr('class', 'arrowMarkerPath') + .style('stroke-width', 1) + .style('stroke-dasharray', '1,0'); + + elem + .append('marker') + .attr('id', id + '_' + type + '-circleStart') + .attr('class', 'marker ' + type) + .attr('viewBox', '0 0 10 10') + .attr('refX', -1) + .attr('refY', 5) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 11) + .attr('markerHeight', 11) + .attr('orient', 'auto') + .append('circle') + .attr('cx', '5') + .attr('cy', '5') + .attr('r', '5') + .attr('class', 'arrowMarkerPath') + .style('stroke-width', 1) + .style('stroke-dasharray', '1,0'); +}; +const cross = (elem, type, id) => { + elem + .append('marker') + .attr('id', id + '_' + type + '-crossEnd') + .attr('class', 'marker cross ' + type) + .attr('viewBox', '0 0 11 11') + .attr('refX', 12) + .attr('refY', 5.2) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 11) + .attr('markerHeight', 11) + .attr('orient', 'auto') + .append('path') + // .attr('stroke', 'black') + .attr('d', 'M 1,1 l 9,9 M 10,1 l -9,9') + .attr('class', 'arrowMarkerPath') + .style('stroke-width', 2) + .style('stroke-dasharray', '1,0'); + + elem + .append('marker') + .attr('id', id + '_' + type + '-crossStart') + .attr('class', 'marker cross ' + type) + .attr('viewBox', '0 0 11 11') + .attr('refX', -1) + .attr('refY', 5.2) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 11) + .attr('markerHeight', 11) + .attr('orient', 'auto') + .append('path') + // .attr('stroke', 'black') + .attr('d', 'M 1,1 l 9,9 M 10,1 l -9,9') + .attr('class', 'arrowMarkerPath') + .style('stroke-width', 2) + .style('stroke-dasharray', '1,0'); +}; +const barb = (elem, type, id) => { + elem + .append('defs') + .append('marker') + .attr('id', id + '_' + type + '-barbEnd') + .attr('refX', 19) + .attr('refY', 7) + .attr('markerWidth', 20) + .attr('markerHeight', 14) + .attr('markerUnits', 'userSpaceOnUse') + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 19,7 L9,13 L14,7 L9,1 Z'); +}; + +// TODO rename the class diagram markers to something shape descriptive and semantic free +const markers = { + extension, + composition, + aggregation, + dependency, + lollipop, + point, + circle, + cross, + barb, +}; +export default insertMarkers; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/nodes.spec.ts b/packages/mermaid/src/rendering-util/rendering-elements/nodes.spec.ts new file mode 100644 index 0000000000..c1f0e14371 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/nodes.spec.ts @@ -0,0 +1,236 @@ +import { shapes } from './shapes.js'; +import { describe, it, expect } from 'vitest'; + +describe('Test Alias for shapes', function () { + // for each shape in docs/syntax/flowchart.md, along with its semantic name, short name, and alias name, add a test case + // Process | rect | proc | rectangle + it('should support alias for rectangle shape ', function () { + expect(shapes.process).toBe(shapes.rect); + expect(shapes.proc).toBe(shapes.rect); + expect(shapes.rectangle).toBe(shapes.rect); + }); + + // event | rounded + it('should support alias for rounded shape ', function () { + expect(shapes.event).toBe(shapes.rounded); + }); + + // stadium | pill | term + it('should support alias for stadium shape ', function () { + expect(shapes.pill).toBe(shapes.stadium); + expect(shapes.terminal).toBe(shapes.stadium); + }); + + // fr-rect | subproc | framed-rectangle | subroutine + it('should support alias for subroutine shape ', function () { + expect(shapes['framed-rectangle']).toBe(shapes['fr-rect']); + expect(shapes.subproc).toBe(shapes['fr-rect']); + expect(shapes.subroutine).toBe(shapes['fr-rect']); + }); + + // cyl | db | cylinder + it('should support alias for cylinder shape ', function () { + expect(shapes.db).toBe(shapes.cylinder); + expect(shapes.cyl).toBe(shapes.cylinder); + }); + + // diam | decision | diamond + it('should support alias for diamond shape ', function () { + expect(shapes.diam).toBe(shapes.decision); + expect(shapes.diamond).toBe(shapes.decision); + }); + + // hex | hexagon | prepare + it('should support alias for hexagon shape ', function () { + expect(shapes.hex).toBe(shapes.hexagon); + expect(shapes.prepare).toBe(shapes.hexagon); + }); + + // l-r | lean-right | in-out + it('should support alias for lean-right shape ', function () { + expect(shapes['lean-r']).toBe(shapes['lean-right']); + expect(shapes['in-out']).toBe(shapes['lean-right']); + }); + + // l-l | lean-left | out-in + it('should support alias for lean-left shape ', function () { + expect(shapes['lean-l']).toBe(shapes['lean-left']); + expect(shapes['out-in']).toBe(shapes['lean-left']); + }); + + // trap-b | trapezoid-bottom | priority | trapezoid + it('should support alias for trapezoid shape ', function () { + expect(shapes['trapezoid-bottom']).toBe(shapes['trap-b']); + expect(shapes.priority).toBe(shapes['trap-b']); + expect(shapes.trapezoid).toBe(shapes['trap-b']); + }); + + // trap-t | trapezoid-top | manual | inv-trapezoid + it('should support alias for inv_trapezoid shape ', function () { + expect(shapes['trapezoid-top']).toBe(shapes['trap-t']); + expect(shapes.manual).toBe(shapes['trap-t']); + expect(shapes['inv-trapezoid']).toBe(shapes['trap-t']); + }); + + // dbl-circ| double-circle + it('should support alias for doublecircle shape ', function () { + expect(shapes['double-circle']).toBe(shapes['dbl-circ']); + }); + + // notched-rectangle | card | notch-rect + it('should support alias for notched-rectangle shape ', function () { + expect(shapes.card).toBe(shapes['notched-rectangle']); + expect(shapes['notch-rect']).toBe(shapes['notched-rectangle']); + }); + + it('should support alias for shadedProcess shape ', function () { + const aliases = ['lined-process', 'lined-rectangle', 'lin-proc', 'lin-rect'] as const; + for (const alias of aliases) { + expect(shapes[alias]).toBe(shapes['shaded-process']); + } + }); + + // sm-circ | small-circle | start + it('should support alias for smallCircle shape ', function () { + expect(shapes['small-circle']).toBe(shapes['sm-circ']); + expect(shapes.start).toBe(shapes['sm-circ']); + }); + + // framed-circle | stop + it('should support alias for framed circle shape ', function () { + expect(shapes.stop).toBe(shapes['framed-circle']); + }); + + // fork | join + it('should support alias for fork shape ', function () { + expect(shapes.join).toBe(shapes.fork); + }); + + // brace | comment | brace-l + it('should support alias for brace shape ', function () { + expect(shapes.comment).toBe(shapes.brace); + expect(shapes['brace-l']).toBe(shapes.brace); + }); + + // bolt | com-link | lightning-bolt + it('should support alias for bolt shape ', function () { + expect(shapes['com-link']).toBe(shapes.bolt); + expect(shapes['lightning-bolt']).toBe(shapes.bolt); + }); + + // document | doc + it('should support alias for waveEdgedRectangle shape ', function () { + expect(shapes.doc).toBe(shapes.document); + }); + + // delay | half-rounded-rectangle + it('should support alias for halfRoundedRectangle shape ', function () { + expect(shapes.delay).toBe(shapes['half-rounded-rectangle']); + }); + + // h-cyl | das | horizontal-cylinder + it('should support alias for horizontal-cylinder shape ', function () { + expect(shapes.das).toBe(shapes['h-cyl']); + expect(shapes['horizontal-cylinder']).toBe(shapes['h-cyl']); + }); + + // lin-cyl | disk | lined-cylinder + it('should support alias for linedCylinder shape ', function () { + expect(shapes.disk).toBe(shapes['lin-cyl']); + expect(shapes['lined-cylinder']).toBe(shapes['lin-cyl']); + }); + + // curv-trap | display | curved-trapezoid + it('should support alias for curvedTrapezoid shape ', function () { + expect(shapes.display).toBe(shapes['curv-trap']); + expect(shapes['curved-trapezoid']).toBe(shapes['curv-trap']); + }); + + // div-rect | div-proc | divided-rectangle + it('should support alias for dividedRectangle shape ', function () { + expect(shapes['div-proc']).toBe(shapes['div-rect']); + expect(shapes['divided-rectangle']).toBe(shapes['div-rect']); + }); + + // sm-tri | extract | small-triangle | triangle + it('should support alias for smallTriangle shape ', function () { + expect(shapes.extract).toBe(shapes.tri); + expect(shapes.triangle).toBe(shapes.tri); + }); + + // win-pane | internal-storage | window-pane + it('should support alias for windowPane shape ', function () { + expect(shapes['internal-storage']).toBe(shapes['win-pane']); + expect(shapes['window-pane']).toBe(shapes['win-pane']); + }); + + // fc | junction | filled-circle + it('should support alias for filledCircle shape ', function () { + expect(shapes.junction).toBe(shapes['f-circ']); + expect(shapes['filled-circle']).toBe(shapes['f-circ']); + }); + + // | lin-doc | lined-document + it('should support alias for linedWaveEdgedRectangle shape ', function () { + expect(shapes['lin-doc']).toBe(shapes['lined-document']); + }); + + // notch-pent | loop-limit | notched-pentagon + it('should support alias for notchedPentagon shape ', function () { + expect(shapes['loop-limit']).toBe(shapes['notch-pent']); + expect(shapes['notched-pentagon']).toBe(shapes['notch-pent']); + }); + + // flip-tri | manual-file | flipped-triangle + it('should support alias for flippedTriangle shape ', function () { + expect(shapes['manual-file']).toBe(shapes['flip-tri']); + expect(shapes['flipped-triangle']).toBe(shapes['flip-tri']); + }); + + //sl-rect | manual-input | sloped-rectangle + it('should support alias for slopedRectangle shape ', function () { + expect(shapes['manual-input']).toBe(shapes['sl-rect']); + expect(shapes['sloped-rectangle']).toBe(shapes['sl-rect']); + }); + + // docs | documents | st-doc | stacked-document + it('should support alias for multiWaveEdgedRectangle shape ', function () { + expect(shapes.docs).toBe(shapes.documents); + expect(shapes['st-doc']).toBe(shapes['stacked-document']); + }); + + // procs | processes | st-rect | stacked-rectangle + it('should support alias for multiRect shape ', function () { + expect(shapes.procs).toBe(shapes.processes); + expect(shapes['st-rect']).toBe(shapes['stacked-rectangle']); + }); + + // flag | paper-tape + it('should support alias for paperTape shape ', function () { + expect(shapes['paper-tape']).toBe(shapes.flag); + }); + + // bow-rect| stored-data | bow-tie-rectangle + it('should support alias for bowTieRect shape ', function () { + expect(shapes['stored-data']).toBe(shapes['bow-rect']); + expect(shapes['bow-tie-rectangle']).toBe(shapes['bow-rect']); + }); + + // cross-circ | summary | crossed-circle + it('should support alias for crossedCircle shape ', function () { + expect(shapes.summary).toBe(shapes['cross-circ']); + expect(shapes['crossed-circle']).toBe(shapes['cross-circ']); + }); + + // tag-doc| tag-document + it('should support alias for taggedDocument shape ', function () { + expect(shapes['tag-doc']).toBe(shapes['tagged-document']); + }); + + // tag-rect | tag-proc | tagged-rectangle | tagged-process + it('should support alias for taggedRect shape ', function () { + expect(shapes['tag-proc']).toBe(shapes['tag-rect']); + expect(shapes['tagged-rectangle']).toBe(shapes['tag-rect']); + expect(shapes['tagged-process']).toBe(shapes['tag-rect']); + }); +}); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/nodes.ts b/packages/mermaid/src/rendering-util/rendering-elements/nodes.ts new file mode 100644 index 0000000000..5af6cd17a0 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/nodes.ts @@ -0,0 +1,96 @@ +import { log } from '../../logger.js'; +import { shapes } from './shapes.js'; +import type { Node, NonClusterNode, ShapeRenderOptions } from '../types.js'; +import type { SVGGroup } from '../../mermaid.js'; +import type { D3Selection } from '../../types.js'; +import type { graphlib } from 'dagre-d3-es'; + +type ShapeHandler = (typeof shapes)[keyof typeof shapes]; +type NodeElement = D3Selection | Awaited>; + +const nodeElems = new Map(); + +export async function insertNode( + elem: SVGGroup, + node: NonClusterNode, + renderOptions: ShapeRenderOptions +) { + let newEl: NodeElement | undefined; + let el; + + //special check for rect shape (with or without rounded corners) + if (node.shape === 'rect') { + if (node.rx && node.ry) { + node.shape = 'roundedRect'; + } else { + node.shape = 'squareRect'; + } + } + + const shapeHandler = node.shape ? shapes[node.shape] : undefined; + + if (!shapeHandler) { + throw new Error(`No such shape: ${node.shape}. Please check your syntax.`); + } + + if (node.link) { + // Add link when appropriate + let target; + if (renderOptions.config.securityLevel === 'sandbox') { + target = '_top'; + } else if (node.linkTarget) { + target = node.linkTarget || '_blank'; + } + newEl = elem + .insert('svg:a') + .attr('xlink:href', node.link) + .attr('target', target ?? null); + el = await shapeHandler(newEl, node, renderOptions); + } else { + el = await shapeHandler(elem, node, renderOptions); + newEl = el; + } + if (node.tooltip) { + el.attr('title', node.tooltip); + } + + nodeElems.set(node.id, newEl); + + if (node.haveCallback) { + newEl.attr('class', newEl.attr('class') + ' clickable'); + } + return newEl; +} + +export const setNodeElem = (elem: NodeElement, node: Pick) => { + nodeElems.set(node.id, elem); +}; + +export const clear = () => { + nodeElems.clear(); +}; + +export const positionNode = (node: ReturnType) => { + const el = nodeElems.get(node.id)!; + log.trace( + 'Transforming node', + node.diff, + node, + 'translate(' + (node.x - node.width / 2 - 5) + ', ' + node.width / 2 + ')' + ); + const padding = 8; + const diff = node.diff || 0; + if (node.clusterNode) { + el.attr( + 'transform', + 'translate(' + + (node.x + diff - node.width / 2) + + ', ' + + (node.y - node.height / 2 - padding) + + ')' + ); + } else { + el.attr('transform', 'translate(' + node.x + ', ' + node.y + ')'); + } + return diff; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts new file mode 100644 index 0000000000..dbfc93677f --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts @@ -0,0 +1,504 @@ +import type { Entries } from 'type-fest'; +import type { D3Selection, MaybePromise } from '../../types.js'; +import type { Node, ShapeRenderOptions } from '../types.js'; +import { anchor } from './shapes/anchor.js'; +import { bowTieRect } from './shapes/bowTieRect.js'; +import { card } from './shapes/card.js'; +import { choice } from './shapes/choice.js'; +import { circle } from './shapes/circle.js'; +import { crossedCircle } from './shapes/crossedCircle.js'; +import { curlyBraceLeft } from './shapes/curlyBraceLeft.js'; +import { curlyBraceRight } from './shapes/curlyBraceRight.js'; +import { curlyBraces } from './shapes/curlyBraces.js'; +import { curvedTrapezoid } from './shapes/curvedTrapezoid.js'; +import { cylinder } from './shapes/cylinder.js'; +import { dividedRectangle } from './shapes/dividedRect.js'; +import { doublecircle } from './shapes/doubleCircle.js'; +import { filledCircle } from './shapes/filledCircle.js'; +import { flippedTriangle } from './shapes/flippedTriangle.js'; +import { forkJoin } from './shapes/forkJoin.js'; +import { halfRoundedRectangle } from './shapes/halfRoundedRectangle.js'; +import { hexagon } from './shapes/hexagon.js'; +import { hourglass } from './shapes/hourglass.js'; +import { icon } from './shapes/icon.js'; +import { iconCircle } from './shapes/iconCircle.js'; +import { iconRounded } from './shapes/iconRounded.js'; +import { iconSquare } from './shapes/iconSquare.js'; +import { imageSquare } from './shapes/imageSquare.js'; +import { inv_trapezoid } from './shapes/invertedTrapezoid.js'; +import { labelRect } from './shapes/labelRect.js'; +import { lean_left } from './shapes/leanLeft.js'; +import { lean_right } from './shapes/leanRight.js'; +import { lightningBolt } from './shapes/lightningBolt.js'; +import { linedCylinder } from './shapes/linedCylinder.js'; +import { linedWaveEdgedRect } from './shapes/linedWaveEdgedRect.js'; +import { multiRect } from './shapes/multiRect.js'; +import { multiWaveEdgedRectangle } from './shapes/multiWaveEdgedRectangle.js'; +import { note } from './shapes/note.js'; +import { question } from './shapes/question.js'; +import { rect_left_inv_arrow } from './shapes/rectLeftInvArrow.js'; +import { rectWithTitle } from './shapes/rectWithTitle.js'; +import { roundedRect } from './shapes/roundedRect.js'; +import { shadedProcess } from './shapes/shadedProcess.js'; +import { slopedRect } from './shapes/slopedRect.js'; +import { squareRect } from './shapes/squareRect.js'; +import { stadium } from './shapes/stadium.js'; +import { state } from './shapes/state.js'; +import { stateEnd } from './shapes/stateEnd.js'; +import { stateStart } from './shapes/stateStart.js'; +import { subroutine } from './shapes/subroutine.js'; +import { taggedRect } from './shapes/taggedRect.js'; +import { taggedWaveEdgedRectangle } from './shapes/taggedWaveEdgedRectangle.js'; +import { text } from './shapes/text.js'; +import { tiltedCylinder } from './shapes/tiltedCylinder.js'; +import { trapezoid } from './shapes/trapezoid.js'; +import { trapezoidalPentagon } from './shapes/trapezoidalPentagon.js'; +import { triangle } from './shapes/triangle.js'; +import { waveEdgedRectangle } from './shapes/waveEdgedRectangle.js'; +import { waveRectangle } from './shapes/waveRectangle.js'; +import { windowPane } from './shapes/windowPane.js'; +import { classBox } from './shapes/classBox.js'; +import { kanbanItem } from './shapes/kanbanItem.js'; + +type ShapeHandler = ( + parent: D3Selection, + node: Node, + options: ShapeRenderOptions +) => MaybePromise>; + +export interface ShapeDefinition { + semanticName: string; + name: string; + shortName: string; + description: string; + /** + * Aliases can include descriptive names, other short names, etc. + */ + aliases?: string[]; + /** + * These are names used by mermaid before the introduction of new shapes. These will not be in standard formats, and shouldn't be used by the users + */ + internalAliases?: string[]; + handler: ShapeHandler; +} + +export const shapesDefs = [ + { + semanticName: 'Process', + name: 'Rectangle', + shortName: 'rect', + description: 'Standard process shape', + aliases: ['proc', 'process', 'rectangle'], + internalAliases: ['squareRect'], + handler: squareRect, + }, + { + semanticName: 'Event', + name: 'Rounded Rectangle', + shortName: 'rounded', + description: 'Represents an event', + aliases: ['event'], + internalAliases: ['roundedRect'], + handler: roundedRect, + }, + { + semanticName: 'Terminal Point', + name: 'Stadium', + shortName: 'stadium', + description: 'Terminal point', + aliases: ['terminal', 'pill'], + handler: stadium, + }, + { + semanticName: 'Subprocess', + name: 'Framed Rectangle', + shortName: 'fr-rect', + description: 'Subprocess', + aliases: ['subprocess', 'subproc', 'framed-rectangle', 'subroutine'], + handler: subroutine, + }, + { + semanticName: 'Database', + name: 'Cylinder', + shortName: 'cyl', + description: 'Database storage', + aliases: ['db', 'database', 'cylinder'], + handler: cylinder, + }, + { + semanticName: 'Start', + name: 'Circle', + shortName: 'circle', + description: 'Starting point', + aliases: ['circ'], + handler: circle, + }, + { + semanticName: 'Decision', + name: 'Diamond', + shortName: 'diam', + description: 'Decision-making step', + aliases: ['decision', 'diamond', 'question'], + handler: question, + }, + { + semanticName: 'Prepare Conditional', + name: 'Hexagon', + shortName: 'hex', + description: 'Preparation or condition step', + aliases: ['hexagon', 'prepare'], + handler: hexagon, + }, + { + semanticName: 'Data Input/Output', + name: 'Lean Right', + shortName: 'lean-r', + description: 'Represents input or output', + aliases: ['lean-right', 'in-out'], + internalAliases: ['lean_right'], + handler: lean_right, + }, + { + semanticName: 'Data Input/Output', + name: 'Lean Left', + shortName: 'lean-l', + description: 'Represents output or input', + aliases: ['lean-left', 'out-in'], + internalAliases: ['lean_left'], + handler: lean_left, + }, + { + semanticName: 'Priority Action', + name: 'Trapezoid Base Bottom', + shortName: 'trap-b', + description: 'Priority action', + aliases: ['priority', 'trapezoid-bottom', 'trapezoid'], + handler: trapezoid, + }, + { + semanticName: 'Manual Operation', + name: 'Trapezoid Base Top', + shortName: 'trap-t', + description: 'Represents a manual task', + aliases: ['manual', 'trapezoid-top', 'inv-trapezoid'], + internalAliases: ['inv_trapezoid'], + handler: inv_trapezoid, + }, + { + semanticName: 'Stop', + name: 'Double Circle', + shortName: 'dbl-circ', + description: 'Represents a stop point', + aliases: ['double-circle'], + internalAliases: ['doublecircle'], + handler: doublecircle, + }, + { + semanticName: 'Text Block', + name: 'Text Block', + shortName: 'text', + description: 'Text block', + handler: text, + }, + { + semanticName: 'Card', + name: 'Notched Rectangle', + shortName: 'notch-rect', + description: 'Represents a card', + aliases: ['card', 'notched-rectangle'], + handler: card, + }, + { + semanticName: 'Lined/Shaded Process', + name: 'Lined Rectangle', + shortName: 'lin-rect', + description: 'Lined process shape', + aliases: ['lined-rectangle', 'lined-process', 'lin-proc', 'shaded-process'], + handler: shadedProcess, + }, + { + semanticName: 'Start', + name: 'Small Circle', + shortName: 'sm-circ', + description: 'Small starting point', + aliases: ['start', 'small-circle'], + internalAliases: ['stateStart'], + handler: stateStart, + }, + { + semanticName: 'Stop', + name: 'Framed Circle', + shortName: 'fr-circ', + description: 'Stop point', + aliases: ['stop', 'framed-circle'], + internalAliases: ['stateEnd'], + handler: stateEnd, + }, + { + semanticName: 'Fork/Join', + name: 'Filled Rectangle', + shortName: 'fork', + description: 'Fork or join in process flow', + aliases: ['join'], + internalAliases: ['forkJoin'], + handler: forkJoin, + }, + { + semanticName: 'Collate', + name: 'Hourglass', + shortName: 'hourglass', + description: 'Represents a collate operation', + aliases: ['hourglass', 'collate'], + handler: hourglass, + }, + { + semanticName: 'Comment', + name: 'Curly Brace', + shortName: 'brace', + description: 'Adds a comment', + aliases: ['comment', 'brace-l'], + handler: curlyBraceLeft, + }, + { + semanticName: 'Comment Right', + name: 'Curly Brace', + shortName: 'brace-r', + description: 'Adds a comment', + handler: curlyBraceRight, + }, + { + semanticName: 'Comment with braces on both sides', + name: 'Curly Braces', + shortName: 'braces', + description: 'Adds a comment', + handler: curlyBraces, + }, + { + semanticName: 'Com Link', + name: 'Lightning Bolt', + shortName: 'bolt', + description: 'Communication link', + aliases: ['com-link', 'lightning-bolt'], + handler: lightningBolt, + }, + { + semanticName: 'Document', + name: 'Document', + shortName: 'doc', + description: 'Represents a document', + aliases: ['doc', 'document'], + handler: waveEdgedRectangle, + }, + { + semanticName: 'Delay', + name: 'Half-Rounded Rectangle', + shortName: 'delay', + description: 'Represents a delay', + aliases: ['half-rounded-rectangle'], + handler: halfRoundedRectangle, + }, + { + semanticName: 'Direct Access Storage', + name: 'Horizontal Cylinder', + shortName: 'h-cyl', + description: 'Direct access storage', + aliases: ['das', 'horizontal-cylinder'], + handler: tiltedCylinder, + }, + { + semanticName: 'Disk Storage', + name: 'Lined Cylinder', + shortName: 'lin-cyl', + description: 'Disk storage', + aliases: ['disk', 'lined-cylinder'], + handler: linedCylinder, + }, + { + semanticName: 'Display', + name: 'Curved Trapezoid', + shortName: 'curv-trap', + description: 'Represents a display', + aliases: ['curved-trapezoid', 'display'], + handler: curvedTrapezoid, + }, + { + semanticName: 'Divided Process', + name: 'Divided Rectangle', + shortName: 'div-rect', + description: 'Divided process shape', + aliases: ['div-proc', 'divided-rectangle', 'divided-process'], + handler: dividedRectangle, + }, + { + semanticName: 'Extract', + name: 'Triangle', + shortName: 'tri', + description: 'Extraction process', + aliases: ['extract', 'triangle'], + handler: triangle, + }, + { + semanticName: 'Internal Storage', + name: 'Window Pane', + shortName: 'win-pane', + description: 'Internal storage', + aliases: ['internal-storage', 'window-pane'], + handler: windowPane, + }, + { + semanticName: 'Junction', + name: 'Filled Circle', + shortName: 'f-circ', + description: 'Junction point', + aliases: ['junction', 'filled-circle'], + handler: filledCircle, + }, + { + semanticName: 'Loop Limit', + name: 'Trapezoidal Pentagon', + shortName: 'notch-pent', + description: 'Loop limit step', + aliases: ['loop-limit', 'notched-pentagon'], + handler: trapezoidalPentagon, + }, + { + semanticName: 'Manual File', + name: 'Flipped Triangle', + shortName: 'flip-tri', + description: 'Manual file operation', + aliases: ['manual-file', 'flipped-triangle'], + handler: flippedTriangle, + }, + { + semanticName: 'Manual Input', + name: 'Sloped Rectangle', + shortName: 'sl-rect', + description: 'Manual input step', + aliases: ['manual-input', 'sloped-rectangle'], + handler: slopedRect, + }, + { + semanticName: 'Multi-Document', + name: 'Stacked Document', + shortName: 'docs', + description: 'Multiple documents', + aliases: ['documents', 'st-doc', 'stacked-document'], + handler: multiWaveEdgedRectangle, + }, + { + semanticName: 'Multi-Process', + name: 'Stacked Rectangle', + shortName: 'st-rect', + description: 'Multiple processes', + aliases: ['procs', 'processes', 'stacked-rectangle'], + handler: multiRect, + }, + { + semanticName: 'Stored Data', + name: 'Bow Tie Rectangle', + shortName: 'bow-rect', + description: 'Stored data', + aliases: ['stored-data', 'bow-tie-rectangle'], + handler: bowTieRect, + }, + { + semanticName: 'Summary', + name: 'Crossed Circle', + shortName: 'cross-circ', + description: 'Summary', + aliases: ['summary', 'crossed-circle'], + handler: crossedCircle, + }, + { + semanticName: 'Tagged Document', + name: 'Tagged Document', + shortName: 'tag-doc', + description: 'Tagged document', + aliases: ['tag-doc', 'tagged-document'], + handler: taggedWaveEdgedRectangle, + }, + { + semanticName: 'Tagged Process', + name: 'Tagged Rectangle', + shortName: 'tag-rect', + description: 'Tagged process', + aliases: ['tagged-rectangle', 'tag-proc', 'tagged-process'], + handler: taggedRect, + }, + { + semanticName: 'Paper Tape', + name: 'Flag', + shortName: 'flag', + description: 'Paper tape', + aliases: ['paper-tape'], + handler: waveRectangle, + }, + { + semanticName: 'Odd', + name: 'Odd', + shortName: 'odd', + description: 'Odd shape', + internalAliases: ['rect_left_inv_arrow'], + handler: rect_left_inv_arrow, + }, + { + semanticName: 'Lined Document', + name: 'Lined Document', + shortName: 'lin-doc', + description: 'Lined document', + aliases: ['lined-document'], + handler: linedWaveEdgedRect, + }, +] as const satisfies ShapeDefinition[]; + +const generateShapeMap = () => { + // These are the shapes that didn't have documentation present + const undocumentedShapes = { + // States + state, + choice, + note, + + // Rectangles + rectWithTitle, + labelRect, + + // Icons + iconSquare, + iconCircle, + icon, + iconRounded, + imageSquare, + anchor, + + // Kanban diagram + kanbanItem, + + // class diagram + classBox, + } as const; + + const entries = [ + ...(Object.entries(undocumentedShapes) as Entries), + ...shapesDefs.flatMap((shape) => { + const aliases = [ + shape.shortName, + ...('aliases' in shape ? shape.aliases : []), + ...('internalAliases' in shape ? shape.internalAliases : []), + ]; + return aliases.map((alias) => [alias, shape.handler] as const); + }), + ]; + return Object.fromEntries(entries) as Record< + (typeof entries)[number][0], + (typeof entries)[number][1] + > satisfies Record; +}; + +export const shapes = generateShapeMap(); + +export function isValidShape(shape: string): shape is ShapeID { + return shape in shapes; +} + +export type ShapeID = keyof typeof shapes; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/anchor.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/anchor.ts new file mode 100644 index 0000000000..11821a11b6 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/anchor.ts @@ -0,0 +1,46 @@ +import { log } from '../../../logger.js'; +import { updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { handleUndefinedAttr } from '../../../utils.js'; +import type { D3Selection } from '../../../types.js'; + +export function anchor(parent: D3Selection, node: Node) { + const { labelStyles } = styles2String(node); + node.labelStyle = labelStyles; + const classes = getNodeClasses(node); + let cssClasses = classes; + if (!classes) { + cssClasses = 'anchor'; + } + const shapeSvg = parent + .insert('g') + .attr('class', cssClasses) + .attr('id', node.domId || node.id); + + const radius = 1; + + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { fill: 'black', stroke: 'none', fillStyle: 'solid' }); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + } + const roughNode = rc.circle(0, 0, radius * 2, options); + const circleElem = shapeSvg.insert(() => roughNode, ':first-child'); + circleElem.attr('class', 'anchor').attr('style', handleUndefinedAttr(cssStyles)); + + updateNodeBounds(node, circleElem); + + node.intersect = function (point) { + log.info('Circle intersect', node, radius, point); + return intersect.circle(node, radius, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/bowTieRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/bowTieRect.ts new file mode 100644 index 0000000000..d4b41103f8 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/bowTieRect.ts @@ -0,0 +1,127 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +function generateArcPoints( + x1: number, + y1: number, + x2: number, + y2: number, + rx: number, + ry: number, + clockwise: boolean +) { + const numPoints = 20; + // Calculate midpoint + const midX = (x1 + x2) / 2; + const midY = (y1 + y2) / 2; + + // Calculate the angle of the line connecting the points + const angle = Math.atan2(y2 - y1, x2 - x1); + + // Calculate transformed coordinates for the ellipse + const dx = (x2 - x1) / 2; + const dy = (y2 - y1) / 2; + + // Scale to unit circle + const transformedX = dx / rx; + const transformedY = dy / ry; + + // Calculate the distance between points on the unit circle + const distance = Math.sqrt(transformedX ** 2 + transformedY ** 2); + + // Check if the ellipse can be drawn with the given radii + if (distance > 1) { + throw new Error('The given radii are too small to create an arc between the points.'); + } + + // Calculate the distance from the midpoint to the center of the ellipse + const scaledCenterDistance = Math.sqrt(1 - distance ** 2); + + // Calculate the center of the ellipse + const centerX = midX + scaledCenterDistance * ry * Math.sin(angle) * (clockwise ? -1 : 1); + const centerY = midY - scaledCenterDistance * rx * Math.cos(angle) * (clockwise ? -1 : 1); + + // Calculate the start and end angles on the ellipse + const startAngle = Math.atan2((y1 - centerY) / ry, (x1 - centerX) / rx); + const endAngle = Math.atan2((y2 - centerY) / ry, (x2 - centerX) / rx); + + // Adjust angles for clockwise/counterclockwise + let angleRange = endAngle - startAngle; + if (clockwise && angleRange < 0) { + angleRange += 2 * Math.PI; + } + if (!clockwise && angleRange > 0) { + angleRange -= 2 * Math.PI; + } + + // Generate points + const points = []; + for (let i = 0; i < numPoints; i++) { + const t = i / (numPoints - 1); + const angle = startAngle + t * angleRange; + const x = centerX + rx * Math.cos(angle); + const y = centerY + ry * Math.sin(angle); + points.push({ x, y }); + } + + return points; +} + +export async function bowTieRect(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const w = bbox.width + node.padding + 20; + const h = bbox.height + node.padding; + + const ry = h / 2; + const rx = ry / (2.5 + h / 50); + + // let shape: d3.Selection; + const { cssStyles } = node; + + const points = [ + { x: w / 2, y: -h / 2 }, + { x: -w / 2, y: -h / 2 }, + ...generateArcPoints(-w / 2, -h / 2, -w / 2, h / 2, rx, ry, false), + { x: w / 2, y: h / 2 }, + ...generateArcPoints(w / 2, h / 2, w / 2, -h / 2, rx, ry, true), + ]; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + const bowTieRectPath = createPathFromPoints(points); + const bowTieRectShapePath = rc.path(bowTieRectPath, options); + const bowTieRectShape = shapeSvg.insert(() => bowTieRectShapePath, ':first-child'); + + bowTieRectShape.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + bowTieRectShape.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + bowTieRectShape.selectAll('path').attr('style', nodeStyles); + } + + bowTieRectShape.attr('transform', `translate(${rx / 2}, 0)`); + + updateNodeBounds(node, bowTieRectShape); + + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/card.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/card.ts new file mode 100644 index 0000000000..4aaf9222a7 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/card.ts @@ -0,0 +1,70 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +import { insertPolygonShape } from './insertPolygonShape.js'; +import { createPathFromPoints } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +// const createPathFromPoints = (points: { x: number; y: number }[]): string => { +// const pointStrings = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`); +// pointStrings.push('Z'); +// return pointStrings.join(' '); +// }; + +export async function card(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + + const h = bbox.height + node.padding; + const padding = 12; + const w = bbox.width + node.padding + padding; + const left = 0; + const right = w; + const top = -h; + const bottom = 0; + const points = [ + { x: left + padding, y: top }, + { x: right, y: top }, + { x: right, y: bottom }, + { x: left, y: bottom }, + { x: left, y: top + padding }, + { x: left + padding, y: top }, + ]; + + let polygon: D3Selection | Awaited>; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const pathData = createPathFromPoints(points); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (nodeStyles) { + polygon.attr('style', nodeStyles); + } + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/choice.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/choice.ts new file mode 100644 index 0000000000..4edd685875 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/choice.ts @@ -0,0 +1,55 @@ +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import rough from 'roughjs'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import { createPathFromPoints, getNodeClasses } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +export function choice(parent: D3Selection, node: Node) { + const { nodeStyles } = styles2String(node); + node.label = ''; + const shapeSvg = parent + .insert('g') + .attr('class', getNodeClasses(node)) + .attr('id', node.domId ?? node.id); + const { cssStyles } = node; + + const s = Math.max(28, node.width ?? 0); + + const points = [ + { x: 0, y: s / 2 }, + { x: s / 2, y: 0 }, + { x: 0, y: -s / 2 }, + { x: -s / 2, y: 0 }, + ]; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const choicePath = createPathFromPoints(points); + const roughNode = rc.path(choicePath, options); + const choiceShape = shapeSvg.insert(() => roughNode, ':first-child'); + + if (cssStyles && node.look !== 'handDrawn') { + choiceShape.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + choiceShape.selectAll('path').attr('style', nodeStyles); + } + + node.width = 28; + node.height = 28; + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts new file mode 100644 index 0000000000..6b3be6765d --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts @@ -0,0 +1,45 @@ +import { log } from '../../../logger.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; + +export async function circle(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, getNodeClasses(node)); + + const radius = bbox.width / 2 + halfPadding; + let circleElem; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const roughNode = rc.circle(0, 0, radius * 2, options); + + circleElem = shapeSvg.insert(() => roughNode, ':first-child'); + circleElem.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles)); + } else { + circleElem = shapeSvg + .insert('circle', ':first-child') + .attr('class', 'basic label-container') + .attr('style', nodeStyles) + .attr('r', radius) + .attr('cx', 0) + .attr('cy', 0); + } + + updateNodeBounds(node, circleElem); + + node.intersect = function (point) { + log.info('Circle intersect', node, radius, point); + return intersect.circle(node, radius, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/classBox.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/classBox.ts new file mode 100644 index 0000000000..e35ee94abb --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/classBox.ts @@ -0,0 +1,207 @@ +import { updateNodeBounds } from './util.js'; +import { getConfig } from '../../../diagram-api/diagramAPI.js'; +import { select } from 'd3'; +import type { Node } from '../../types.js'; +import type { ClassNode } from '../../../diagrams/class/classTypes.js'; +import rough from 'roughjs'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import intersect from '../intersect/index.js'; +import { textHelper } from '../../../diagrams/class/shapeUtil.js'; +import { evaluate } from '../../../diagrams/common/common.js'; +import type { D3Selection } from '../../../types.js'; + +export async function classBox(parent: D3Selection, node: Node) { + const config = getConfig(); + const PADDING = config.class!.padding ?? 12; + const GAP = PADDING; + const useHtmlLabels = node.useHtmlLabels ?? evaluate(config.htmlLabels) ?? true; + // Treat node as classNode + const classNode = node as unknown as ClassNode; + classNode.annotations = classNode.annotations ?? []; + classNode.members = classNode.members ?? []; + classNode.methods = classNode.methods ?? []; + + const { shapeSvg, bbox } = await textHelper(parent, node, config, useHtmlLabels, GAP); + + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + + node.cssStyles = classNode.styles || ''; + + const styles = classNode.styles?.join(';') || nodeStyles || ''; + + if (!node.cssStyles) { + node.cssStyles = styles.replaceAll('!important', '').split(';'); + } + + const renderExtraBox = + classNode.members.length === 0 && + classNode.methods.length === 0 && + !config.class?.hideEmptyMembersBox; + + // Setup roughjs + // @ts-ignore TODO: Fix rough typings + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const w = bbox.width; + let h = bbox.height; + if (classNode.members.length === 0 && classNode.methods.length === 0) { + h += GAP; + } else if (classNode.members.length > 0 && classNode.methods.length === 0) { + h += GAP * 2; + } + const x = -w / 2; + const y = -h / 2; + + // Create and center rectangle + const roughRect = rc.rectangle( + x - PADDING, + y - + PADDING - + (renderExtraBox + ? PADDING + : classNode.members.length === 0 && classNode.methods.length === 0 + ? -PADDING / 2 + : 0), + w + 2 * PADDING, + h + + 2 * PADDING + + (renderExtraBox + ? PADDING * 2 + : classNode.members.length === 0 && classNode.methods.length === 0 + ? -PADDING + : 0), + options + ); + + const rect = shapeSvg.insert(() => roughRect, ':first-child'); + rect.attr('class', 'basic label-container'); + const rectBBox = rect.node()!.getBBox(); + + // Rect is centered so now adjust labels. + // TODO: Fix types + shapeSvg.selectAll('.text').each((_: any, i: number, nodes: any) => { + const text = select(nodes[i]); + // Get the current transform attribute + const transform = text.attr('transform'); + // Initialize variables for the translation values + let translateY = 0; + // Check if the transform attribute exists + if (transform) { + const regex = RegExp(/translate\(([^,]+),([^)]+)\)/); + const translate = regex.exec(transform); + if (translate) { + translateY = parseFloat(translate[2]); + } + } + // Add to the y value + let newTranslateY = + translateY + + y + + PADDING - + (renderExtraBox + ? PADDING + : classNode.members.length === 0 && classNode.methods.length === 0 + ? -PADDING / 2 + : 0); + if (!useHtmlLabels) { + // Fix so non html labels are better centered. + // BBox of text seems to be slightly different when calculated so we offset + newTranslateY -= 4; + } + let newTranslateX = x; + if ( + text.attr('class').includes('label-group') || + text.attr('class').includes('annotation-group') + ) { + newTranslateX = -text.node()?.getBBox().width / 2 || 0; + shapeSvg.selectAll('text').each(function (_: any, i: number, nodes: any) { + if (window.getComputedStyle(nodes[i]).textAnchor === 'middle') { + newTranslateX = 0; + } + }); + } + // Set the updated transform attribute + text.attr('transform', `translate(${newTranslateX}, ${newTranslateY})`); + }); + + // Render divider lines. + const annotationGroupHeight = + (shapeSvg.select('.annotation-group').node() as SVGGraphicsElement).getBBox().height - + (renderExtraBox ? PADDING / 2 : 0) || 0; + const labelGroupHeight = + (shapeSvg.select('.label-group').node() as SVGGraphicsElement).getBBox().height - + (renderExtraBox ? PADDING / 2 : 0) || 0; + const membersGroupHeight = + (shapeSvg.select('.members-group').node() as SVGGraphicsElement).getBBox().height - + (renderExtraBox ? PADDING / 2 : 0) || 0; + // First line (under label) + if (classNode.members.length > 0 || classNode.methods.length > 0 || renderExtraBox) { + const roughLine = rc.line( + rectBBox.x, + annotationGroupHeight + labelGroupHeight + y + PADDING, + rectBBox.x + rectBBox.width, + annotationGroupHeight + labelGroupHeight + y + PADDING, + options + ); + const line = shapeSvg.insert(() => roughLine); + line.attr('class', 'divider').attr('style', styles); + } + + // Second line (under members) + if (renderExtraBox || classNode.members.length > 0 || classNode.methods.length > 0) { + const roughLine = rc.line( + rectBBox.x, + annotationGroupHeight + labelGroupHeight + membersGroupHeight + y + GAP * 2 + PADDING, + rectBBox.x + rectBBox.width, + annotationGroupHeight + labelGroupHeight + membersGroupHeight + y + PADDING + GAP * 2, + options + ); + const line = shapeSvg.insert(() => roughLine); + line.attr('class', 'divider').attr('style', styles); + } + + /// Apply styles /// + if (classNode.look !== 'handDrawn') { + shapeSvg.selectAll('path').attr('style', styles); + } + // Apply other styles like stroke-width and stroke-dasharray to border (not background of shape) + rect.select(':nth-child(2)').attr('style', styles); + // Divider lines + shapeSvg.selectAll('.divider').select('path').attr('style', styles); + // Text elements + if (node.labelStyle) { + shapeSvg.selectAll('span').attr('style', node.labelStyle); + } else { + shapeSvg.selectAll('span').attr('style', styles); + } + // SVG text uses fill not color + if (!useHtmlLabels) { + // We just want to apply color to the text + const colorRegex = RegExp(/color\s*:\s*([^;]*)/); + const match = colorRegex.exec(styles); + if (match) { + const colorStyle = match[0].replace('color', 'fill'); + shapeSvg.selectAll('tspan').attr('style', colorStyle); + } else if (labelStyles) { + const match = colorRegex.exec(labelStyles); + if (match) { + const colorStyle = match[0].replace('color', 'fill'); + shapeSvg.selectAll('tspan').attr('style', colorStyle); + } + } + } + + updateNodeBounds(node, rect); + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/crossedCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/crossedCircle.ts new file mode 100644 index 0000000000..eaacb9f34a --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/crossedCircle.ts @@ -0,0 +1,67 @@ +import { log } from '../../../logger.js'; +import { getNodeClasses, updateNodeBounds } from './util.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; +import type { D3Selection } from '../../../types.js'; + +function createLine(r: number) { + const xAxis45 = Math.cos(Math.PI / 4); // cosine of 45 degrees + const yAxis45 = Math.sin(Math.PI / 4); // sine of 45 degrees + const lineLength = r * 2; + + const pointQ1 = { x: (lineLength / 2) * xAxis45, y: (lineLength / 2) * yAxis45 }; // Quadrant I + const pointQ2 = { x: -(lineLength / 2) * xAxis45, y: (lineLength / 2) * yAxis45 }; // Quadrant II + const pointQ3 = { x: -(lineLength / 2) * xAxis45, y: -(lineLength / 2) * yAxis45 }; // Quadrant III + const pointQ4 = { x: (lineLength / 2) * xAxis45, y: -(lineLength / 2) * yAxis45 }; // Quadrant IV + + return `M ${pointQ2.x},${pointQ2.y} L ${pointQ4.x},${pointQ4.y} + M ${pointQ1.x},${pointQ1.y} L ${pointQ3.x},${pointQ3.y}`; +} + +export function crossedCircle(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + node.label = ''; + const shapeSvg = parent + .insert('g') + .attr('class', getNodeClasses(node)) + .attr('id', node.domId ?? node.id); + const radius = Math.max(30, node?.width ?? 0); + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const circleNode = rc.circle(0, 0, radius * 2, options); + const linePath = createLine(radius); + const lineNode = rc.path(linePath, options); + + const crossedCircle = shapeSvg.insert(() => circleNode, ':first-child'); + crossedCircle.insert(() => lineNode); + + if (cssStyles && node.look !== 'handDrawn') { + crossedCircle.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + crossedCircle.selectAll('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, crossedCircle); + + node.intersect = function (point) { + log.info('crossedCircle intersect', node, { radius, point }); + const pos = intersect.circle(node, radius, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceLeft.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceLeft.ts new file mode 100644 index 0000000000..00113ae4f9 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceLeft.ts @@ -0,0 +1,115 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +function generateCirclePoints( + centerX: number, + centerY: number, + radius: number, + numPoints = 100, + startAngle = 0, + endAngle = 180 +) { + const points = []; + + // Convert angles to radians + const startAngleRad = (startAngle * Math.PI) / 180; + const endAngleRad = (endAngle * Math.PI) / 180; + + // Calculate the angle range in radians + const angleRange = endAngleRad - startAngleRad; + + // Calculate the angle step + const angleStep = angleRange / (numPoints - 1); + + for (let i = 0; i < numPoints; i++) { + const angle = startAngleRad + i * angleStep; + const x = centerX + radius * Math.cos(angle); + const y = centerY + radius * Math.sin(angle); + points.push({ x: -x, y: -y }); + } + + return points; +} + +export async function curlyBraceLeft( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = bbox.width + (node.padding ?? 0); + const h = bbox.height + (node.padding ?? 0); + const radius = Math.max(5, h * 0.1); + + const { cssStyles } = node; + + const points = [ + ...generateCirclePoints(w / 2, -h / 2, radius, 30, -90, 0), + { x: -w / 2 - radius, y: radius }, + ...generateCirclePoints(w / 2 + radius * 2, -radius, radius, 20, -180, -270), + ...generateCirclePoints(w / 2 + radius * 2, radius, radius, 20, -90, -180), + { x: -w / 2 - radius, y: -h / 2 }, + ...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90), + ]; + + const rectPoints = [ + { x: w / 2, y: -h / 2 - radius }, + { x: -w / 2, y: -h / 2 - radius }, + ...generateCirclePoints(w / 2, -h / 2, radius, 20, -90, 0), + { x: -w / 2 - radius, y: -radius }, + ...generateCirclePoints(w / 2 + w * 0.1, -radius, radius, 20, -180, -270), + ...generateCirclePoints(w / 2 + w * 0.1, radius, radius, 20, -90, -180), + { x: -w / 2 - radius, y: h / 2 }, + ...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90), + { x: -w / 2, y: h / 2 + radius }, + { x: w / 2, y: h / 2 + radius }, + ]; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { fill: 'none' }); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + const curlyBraceLeftPath = createPathFromPoints(points); + const newCurlyBracePath = curlyBraceLeftPath.replace('Z', ''); + const curlyBraceLeftNode = rc.path(newCurlyBracePath, options); + const rectPath = createPathFromPoints(rectPoints); + const rectShape = rc.path(rectPath, { ...options }); + const curlyBraceLeftShape = shapeSvg.insert('g', ':first-child'); + curlyBraceLeftShape.insert(() => rectShape, ':first-child').attr('stroke-opacity', 0); + curlyBraceLeftShape.insert(() => curlyBraceLeftNode, ':first-child'); + curlyBraceLeftShape.attr('class', 'text'); + + if (cssStyles && node.look !== 'handDrawn') { + curlyBraceLeftShape.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + curlyBraceLeftShape.selectAll('path').attr('style', nodeStyles); + } + + curlyBraceLeftShape.attr('transform', `translate(${radius}, 0)`); + + label.attr( + 'transform', + `translate(${-w / 2 + radius - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, curlyBraceLeftShape); + + node.intersect = function (point) { + const pos = intersect.polygon(node, rectPoints, point); + + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceRight.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceRight.ts new file mode 100644 index 0000000000..d208efc975 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceRight.ts @@ -0,0 +1,115 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +function generateCirclePoints( + centerX: number, + centerY: number, + radius: number, + numPoints = 100, + startAngle = 0, + endAngle = 180 +) { + const points = []; + + // Convert angles to radians + const startAngleRad = (startAngle * Math.PI) / 180; + const endAngleRad = (endAngle * Math.PI) / 180; + + // Calculate the angle range in radians + const angleRange = endAngleRad - startAngleRad; + + // Calculate the angle step + const angleStep = angleRange / (numPoints - 1); + + for (let i = 0; i < numPoints; i++) { + const angle = startAngleRad + i * angleStep; + const x = centerX + radius * Math.cos(angle); + const y = centerY + radius * Math.sin(angle); + points.push({ x, y }); + } + + return points; +} + +export async function curlyBraceRight( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = bbox.width + (node.padding ?? 0); + const h = bbox.height + (node.padding ?? 0); + const radius = Math.max(5, h * 0.1); + + const { cssStyles } = node; + + const points = [ + ...generateCirclePoints(w / 2, -h / 2, radius, 20, -90, 0), + { x: w / 2 + radius, y: -radius }, + ...generateCirclePoints(w / 2 + radius * 2, -radius, radius, 20, -180, -270), + ...generateCirclePoints(w / 2 + radius * 2, radius, radius, 20, -90, -180), + { x: w / 2 + radius, y: h / 2 }, + ...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90), + ]; + + const rectPoints = [ + { x: -w / 2, y: -h / 2 - radius }, + { x: w / 2, y: -h / 2 - radius }, + ...generateCirclePoints(w / 2, -h / 2, radius, 20, -90, 0), + { x: w / 2 + radius, y: -radius }, + ...generateCirclePoints(w / 2 + radius * 2, -radius, radius, 20, -180, -270), + ...generateCirclePoints(w / 2 + radius * 2, radius, radius, 20, -90, -180), + { x: w / 2 + radius, y: h / 2 }, + ...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90), + { x: w / 2, y: h / 2 + radius }, + { x: -w / 2, y: h / 2 + radius }, + ]; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { fill: 'none' }); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + const curlyBraceRightPath = createPathFromPoints(points); + const newCurlyBracePath = curlyBraceRightPath.replace('Z', ''); + const curlyBraceRightNode = rc.path(newCurlyBracePath, options); + const rectPath = createPathFromPoints(rectPoints); + const rectShape = rc.path(rectPath, { ...options }); + const curlyBraceRightShape = shapeSvg.insert('g', ':first-child'); + curlyBraceRightShape.insert(() => rectShape, ':first-child').attr('stroke-opacity', 0); + curlyBraceRightShape.insert(() => curlyBraceRightNode, ':first-child'); + curlyBraceRightShape.attr('class', 'text'); + + if (cssStyles && node.look !== 'handDrawn') { + curlyBraceRightShape.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + curlyBraceRightShape.selectAll('path').attr('style', nodeStyles); + } + + curlyBraceRightShape.attr('transform', `translate(${-radius}, 0)`); + + label.attr( + 'transform', + `translate(${-w / 2 + (node.padding ?? 0) / 2 - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, curlyBraceRightShape); + + node.intersect = function (point) { + const pos = intersect.polygon(node, rectPoints, point); + + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraces.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraces.ts new file mode 100644 index 0000000000..1fd9b6f055 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraces.ts @@ -0,0 +1,134 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +function generateCirclePoints( + centerX: number, + centerY: number, + radius: number, + numPoints = 100, + startAngle = 0, + endAngle = 180 +) { + const points = []; + + // Convert angles to radians + const startAngleRad = (startAngle * Math.PI) / 180; + const endAngleRad = (endAngle * Math.PI) / 180; + + // Calculate the angle range in radians + const angleRange = endAngleRad - startAngleRad; + + // Calculate the angle step + const angleStep = angleRange / (numPoints - 1); + + for (let i = 0; i < numPoints; i++) { + const angle = startAngleRad + i * angleStep; + const x = centerX + radius * Math.cos(angle); + const y = centerY + radius * Math.sin(angle); + points.push({ x: -x, y: -y }); + } + + return points; +} + +export async function curlyBraces( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = bbox.width + (node.padding ?? 0); + const h = bbox.height + (node.padding ?? 0); + const radius = Math.max(5, h * 0.1); + + const { cssStyles } = node; + + const leftCurlyBracePoints = [ + ...generateCirclePoints(w / 2, -h / 2, radius, 30, -90, 0), + { x: -w / 2 - radius, y: radius }, + ...generateCirclePoints(w / 2 + radius * 2, -radius, radius, 20, -180, -270), + ...generateCirclePoints(w / 2 + radius * 2, radius, radius, 20, -90, -180), + { x: -w / 2 - radius, y: -h / 2 }, + ...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90), + ]; + + const rightCurlyBracePoints = [ + ...generateCirclePoints(-w / 2 + radius + radius / 2, -h / 2, radius, 20, -90, -180), + { x: w / 2 - radius / 2, y: radius }, + ...generateCirclePoints(-w / 2 - radius / 2, -radius, radius, 20, 0, 90), + ...generateCirclePoints(-w / 2 - radius / 2, radius, radius, 20, -90, 0), + { x: w / 2 - radius / 2, y: -radius }, + ...generateCirclePoints(-w / 2 + radius + radius / 2, h / 2, radius, 30, -180, -270), + ]; + + const rectPoints = [ + { x: w / 2, y: -h / 2 - radius }, + { x: -w / 2, y: -h / 2 - radius }, + ...generateCirclePoints(w / 2, -h / 2, radius, 20, -90, 0), + { x: -w / 2 - radius, y: -radius }, + ...generateCirclePoints(w / 2 + radius * 2, -radius, radius, 20, -180, -270), + ...generateCirclePoints(w / 2 + radius * 2, radius, radius, 20, -90, -180), + { x: -w / 2 - radius, y: h / 2 }, + ...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90), + { x: -w / 2, y: h / 2 + radius }, + { x: w / 2 - radius - radius / 2, y: h / 2 + radius }, + ...generateCirclePoints(-w / 2 + radius + radius / 2, -h / 2, radius, 20, -90, -180), + { x: w / 2 - radius / 2, y: radius }, + ...generateCirclePoints(-w / 2 - radius / 2, -radius, radius, 20, 0, 90), + ...generateCirclePoints(-w / 2 - radius / 2, radius, radius, 20, -90, 0), + { x: w / 2 - radius / 2, y: -radius }, + ...generateCirclePoints(-w / 2 + radius + radius / 2, h / 2, radius, 30, -180, -270), + ]; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { fill: 'none' }); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + const leftCurlyBracePath = createPathFromPoints(leftCurlyBracePoints); + const newLeftCurlyBracePath = leftCurlyBracePath.replace('Z', ''); + const leftCurlyBraceNode = rc.path(newLeftCurlyBracePath, options); + const rightCurlyBracePath = createPathFromPoints(rightCurlyBracePoints); + const newRightCurlyBracePath = rightCurlyBracePath.replace('Z', ''); + const rightCurlyBraceNode = rc.path(newRightCurlyBracePath, options); + const rectPath = createPathFromPoints(rectPoints); + const rectShape = rc.path(rectPath, { ...options }); + const curlyBracesShape = shapeSvg.insert('g', ':first-child'); + curlyBracesShape.insert(() => rectShape, ':first-child').attr('stroke-opacity', 0); + curlyBracesShape.insert(() => leftCurlyBraceNode, ':first-child'); + curlyBracesShape.insert(() => rightCurlyBraceNode, ':first-child'); + curlyBracesShape.attr('class', 'text'); + + if (cssStyles && node.look !== 'handDrawn') { + curlyBracesShape.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + curlyBracesShape.selectAll('path').attr('style', nodeStyles); + } + + curlyBracesShape.attr('transform', `translate(${radius - radius / 4}, 0)`); + + label.attr( + 'transform', + `translate(${-w / 2 + (node.padding ?? 0) / 2 - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, curlyBracesShape); + + node.intersect = function (point) { + const pos = intersect.polygon(node, rectPoints, point); + + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curvedTrapezoid.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curvedTrapezoid.ts new file mode 100644 index 0000000000..26339b65ce --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curvedTrapezoid.ts @@ -0,0 +1,75 @@ +import { + labelHelper, + updateNodeBounds, + getNodeClasses, + createPathFromPoints, + generateCirclePoints, +} from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +export async function curvedTrapezoid( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const minWidth = 80, + minHeight = 20; + const w = Math.max(minWidth, (bbox.width + (node.padding ?? 0) * 2) * 1.25, node?.width ?? 0); + const h = Math.max(minHeight, bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const radius = h / 2; + + const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const totalWidth = w, + totalHeight = h; + const rw = totalWidth - radius; + const tw = totalHeight / 4; + + const points = [ + { x: rw, y: 0 }, + { x: tw, y: 0 }, + { x: 0, y: totalHeight / 2 }, + { x: tw, y: totalHeight }, + { x: rw, y: totalHeight }, + ...generateCirclePoints(-rw, -totalHeight / 2, radius, 50, 270, 90), + ]; + + const pathData = createPathFromPoints(points); + const shapeNode = rc.path(pathData, options); + + const polygon = shapeSvg.insert(() => shapeNode, ':first-child'); + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', nodeStyles); + } + + polygon.attr('transform', `translate(${-w / 2}, ${-h / 2})`); + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts new file mode 100644 index 0000000000..9d2cd63f6d --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts @@ -0,0 +1,125 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; + +export const createCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [ + `M${x},${y + ry}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `a${rx},${ry} 0,0,0 ${-width},0`, + `l0,${height}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `l0,${-height}`, + ].join(' '); +}; +export const createOuterCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [ + `M${x},${y + ry}`, + `M${x + width},${y + ry}`, + `a${rx},${ry} 0,0,0 ${-width},0`, + `l0,${height}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `l0,${-height}`, + ].join(' '); +}; +export const createInnerCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [`M${x - width / 2},${-height / 2}`, `a${rx},${ry} 0,0,0 ${width},0`].join(' '); +}; +export async function cylinder(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + node.padding, node.width ?? 0); + const rx = w / 2; + const ry = rx / (2.5 + w / 50); + const h = Math.max(bbox.height + ry + node.padding, node.height ?? 0); + + let cylinder: D3Selection | D3Selection; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const outerPathData = createOuterCylinderPathD(0, 0, w, h, rx, ry); + const innerPathData = createInnerCylinderPathD(0, ry, w, h, rx, ry); + const outerNode = rc.path(outerPathData, userNodeOverrides(node, {})); + const innerLine = rc.path(innerPathData, userNodeOverrides(node, { fill: 'none' })); + + cylinder = shapeSvg.insert(() => innerLine, ':first-child'); + cylinder = shapeSvg.insert(() => outerNode, ':first-child'); + cylinder.attr('class', 'basic label-container'); + if (cssStyles) { + cylinder.attr('style', cssStyles); + } + } else { + const pathData = createCylinderPathD(0, 0, w, h, rx, ry); + cylinder = shapeSvg + .insert('path', ':first-child') + .attr('d', pathData) + .attr('class', 'basic label-container') + .attr('style', handleUndefinedAttr(cssStyles)) + .attr('style', nodeStyles); + } + + cylinder.attr('label-offset-y', ry); + cylinder.attr('transform', `translate(${-w / 2}, ${-(h / 2 + ry)})`); + + updateNodeBounds(node, cylinder); + + label.attr( + 'transform', + `translate(${-(bbox.width / 2) - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) + (node.padding ?? 0) / 1.5 - (bbox.y - (bbox.top ?? 0))})` + ); + + node.intersect = function (point) { + const pos = intersect.rect(node, point); + const x = pos.x - (node.x ?? 0); + + if ( + rx != 0 && + (Math.abs(x) < (node.width ?? 0) / 2 || + (Math.abs(x) == (node.width ?? 0) / 2 && + Math.abs(pos.y - (node.y ?? 0)) > (node.height ?? 0) / 2 - ry)) + ) { + let y = ry * ry * (1 - (x * x) / (rx * rx)); + if (y > 0) { + y = Math.sqrt(y); + } + y = ry - y; + if (point.y - (node.y ?? 0) > 0) { + y = -y; + } + + pos.y += y; + } + + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/dividedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/dividedRect.ts new file mode 100644 index 0000000000..77df581554 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/dividedRect.ts @@ -0,0 +1,71 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +export async function dividedRectangle( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const rectOffset = h * 0.2; + + const x = -w / 2; + const y = -h / 2 - rectOffset / 2; + + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const pts = [ + { x, y: y + rectOffset }, + { x: -x, y: y + rectOffset }, + { x: -x, y: -y }, + { x, y: -y }, + { x, y }, + { x: -x, y }, + { x: -x, y: y + rectOffset }, + ]; + + const poly = rc.polygon( + pts.map((p) => [p.x, p.y]), + options + ); + + const polygon = shapeSvg.insert(() => poly, ':first-child'); + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectAll('path').attr('style', nodeStyles); + } + + label.attr( + 'transform', + `translate(${x + (node.padding ?? 0) / 2 - (bbox.x - (bbox.left ?? 0))}, ${y + rectOffset + (node.padding ?? 0) / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + const pos = intersect.rect(node, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/document.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/document.ts new file mode 100644 index 0000000000..d5ea3aa762 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/document.ts @@ -0,0 +1,120 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; + +export const createCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [ + `M${x},${y + ry}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `a${rx},${ry} 0,0,0 ${-width},0`, + `l0,${height}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `l0,${-height}`, + ].join(' '); +}; +export const createOuterCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [ + `M${x},${y + ry}`, + `M${x + width},${y + ry}`, + `a${rx},${ry} 0,0,0 ${-width},0`, + `l0,${height}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `l0,${-height}`, + ].join(' '); +}; +export const createInnerCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [`M${x - width / 2},${-height / 2}`, `a${rx},${ry} 0,0,0 ${width},0`].join(' '); +}; +export async function cylinder(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const w = bbox.width + node.padding; + const rx = w / 2; + const ry = rx / (2.5 + w / 50); + const h = bbox.height + ry + node.padding; + + let cylinder: D3Selection | D3Selection; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const outerPathData = createOuterCylinderPathD(0, 0, w, h, rx, ry); + const innerPathData = createInnerCylinderPathD(0, ry, w, h, rx, ry); + const outerNode = rc.path(outerPathData, userNodeOverrides(node, {})); + const innerLine = rc.path(innerPathData, userNodeOverrides(node, { fill: 'none' })); + + cylinder = shapeSvg.insert(() => innerLine, ':first-child'); + cylinder = shapeSvg.insert(() => outerNode, ':first-child'); + cylinder.attr('class', 'basic label-container'); + if (cssStyles) { + cylinder.attr('style', cssStyles); + } + } else { + const pathData = createCylinderPathD(0, 0, w, h, rx, ry); + cylinder = shapeSvg + .insert('path', ':first-child') + .attr('d', pathData) + .attr('class', 'basic label-container') + .attr('style', handleUndefinedAttr(cssStyles)) + .attr('style', nodeStyles); + } + + cylinder.attr('label-offset-y', ry); + cylinder.attr('transform', `translate(${-w / 2}, ${-(h / 2 + ry)})`); + + updateNodeBounds(node, cylinder); + + node.intersect = function (point) { + const pos = intersect.rect(node, point); + const x = pos.x - (node.x ?? 0); + + if ( + rx != 0 && + (Math.abs(x) < (node.width ?? 0) / 2 || + (Math.abs(x) == (node.width ?? 0) / 2 && + Math.abs(pos.y - (node.y ?? 0)) > (node.height ?? 0) / 2 - ry)) + ) { + let y = ry * ry * (1 - (x * x) / (rx * rx)); + if (y != 0) { + y = Math.sqrt(y); + } + y = ry - y; + if (point.y - (node.y ?? 0) > 0) { + y = -y; + } + + pos.y += y; + } + + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts new file mode 100644 index 0000000000..bc0d844daf --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts @@ -0,0 +1,71 @@ +import { log } from '../../../logger.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; + +export async function doublecircle( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, getNodeClasses(node)); + const gap = 5; + const outerRadius = bbox.width / 2 + halfPadding + gap; + const innerRadius = bbox.width / 2 + halfPadding; + + let circleGroup; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const outerOptions = userNodeOverrides(node, { roughness: 0.2, strokeWidth: 2.5 }); + + const innerOptions = userNodeOverrides(node, { roughness: 0.2, strokeWidth: 1.5 }); + const outerRoughNode = rc.circle(0, 0, outerRadius * 2, outerOptions); + const innerRoughNode = rc.circle(0, 0, innerRadius * 2, innerOptions); + + circleGroup = shapeSvg.insert('g', ':first-child'); + // circleGroup = circleGroup.insert(() => outerRoughNode, ':first-child'); + circleGroup + .attr('class', handleUndefinedAttr(node.cssClasses)) + .attr('style', handleUndefinedAttr(cssStyles)); + + circleGroup.node()?.appendChild(outerRoughNode); + circleGroup.node()?.appendChild(innerRoughNode); + } else { + circleGroup = shapeSvg.insert('g', ':first-child'); + + const outerCircle = circleGroup.insert('circle', ':first-child'); + const innerCircle = circleGroup.insert('circle'); + circleGroup.attr('class', 'basic label-container').attr('style', nodeStyles); + + outerCircle + .attr('class', 'outer-circle') + .attr('style', nodeStyles) + .attr('r', outerRadius) + .attr('cx', 0) + .attr('cy', 0); + + innerCircle + .attr('class', 'inner-circle') + .attr('style', nodeStyles) + .attr('r', innerRadius) + .attr('cx', 0) + .attr('cy', 0); + } + + updateNodeBounds(node, circleGroup); + + node.intersect = function (point) { + log.info('DoubleCircle intersect', node, outerRadius, point); + return intersect.circle(node, outerRadius, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts new file mode 100644 index 0000000000..707aed2c7b --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts @@ -0,0 +1,70 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node, RectOptions } from '../../types.js'; +import { createRoundedRectPathD } from './roundedRectPath.js'; +import { userNodeOverrides, styles2String } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; + +export async function drawRect( + parent: D3Selection, + node: Node, + options: RectOptions +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + // console.log('IPI labelStyles:', labelStyles); + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + + const totalWidth = Math.max(bbox.width + options.labelPaddingX * 2, node?.width || 0); + const totalHeight = Math.max(bbox.height + options.labelPaddingY * 2, node?.height || 0); + const x = -totalWidth / 2; + const y = -totalHeight / 2; + + // log.info('IPI node = ', node); + + let rect; + let { rx, ry } = node; + const { cssStyles } = node; + + //use options rx, ry overrides if present + if (options?.rx && options.ry) { + rx = options.rx; + ry = options.ry; + } + + if (node.look === 'handDrawn') { + // @ts-ignore TODO: Fix rough typings + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + const roughNode = + rx || ry + ? rc.path(createRoundedRectPathD(x, y, totalWidth, totalHeight, rx || 0), options) + : rc.rectangle(x, y, totalWidth, totalHeight, options); + + rect = shapeSvg.insert(() => roughNode, ':first-child'); + rect.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles)); + } else { + rect = shapeSvg.insert('rect', ':first-child'); + + rect + .attr('class', 'basic label-container') + .attr('style', nodeStyles) + .attr('rx', handleUndefinedAttr(rx)) + .attr('ry', handleUndefinedAttr(ry)) + .attr('x', x) + .attr('y', y) + .attr('width', totalWidth) + .attr('height', totalHeight); + } + + updateNodeBounds(node, rect); + + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/filledCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/filledCircle.ts new file mode 100644 index 0000000000..3469697c77 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/filledCircle.ts @@ -0,0 +1,56 @@ +import rough from 'roughjs'; +import { log } from '../../../logger.js'; +import type { Node, ShapeRenderOptions } from '../../types.js'; +import intersect from '../intersect/index.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import { getNodeClasses, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +export function filledCircle( + parent: D3Selection, + node: Node, + { config: { themeVariables } }: ShapeRenderOptions +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.label = ''; + node.labelStyle = labelStyles; + const shapeSvg = parent + .insert('g') + .attr('class', getNodeClasses(node)) + .attr('id', node.domId ?? node.id); + const radius = 7; + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const { nodeBorder } = themeVariables; + const options = userNodeOverrides(node, { fillStyle: 'solid' }); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + } + + const circleNode = rc.circle(0, 0, radius * 2, options); + + const filledCircle = shapeSvg.insert(() => circleNode, ':first-child'); + + filledCircle.selectAll('path').attr('style', `fill: ${nodeBorder} !important;`); + + if (cssStyles && cssStyles.length > 0 && node.look !== 'handDrawn') { + filledCircle.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + filledCircle.selectAll('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, filledCircle); + + node.intersect = function (point) { + log.info('filledCircle intersect', node, { radius, point }); + const pos = intersect.circle(node, radius, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/flippedTriangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/flippedTriangle.ts new file mode 100644 index 0000000000..33f65b3268 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/flippedTriangle.ts @@ -0,0 +1,68 @@ +import { log } from '../../../logger.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { createPathFromPoints } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +export async function flippedTriangle( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + + const w = bbox.width + (node.padding ?? 0); + const h = w + bbox.height; + + const tw = w + bbox.height; + const points = [ + { x: 0, y: -h }, + { x: tw, y: -h }, + { x: tw / 2, y: 0 }, + ]; + + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + const pathData = createPathFromPoints(points); + const roughNode = rc.path(pathData, options); + + const flippedTriangle = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-h / 2}, ${h / 2})`); + + if (cssStyles && node.look !== 'handDrawn') { + flippedTriangle.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + flippedTriangle.selectChildren('path').attr('style', nodeStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, flippedTriangle); + + label.attr( + 'transform', + `translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))}, ${-h / 2 + (node.padding ?? 0) / 2 + (bbox.y - (bbox.top ?? 0))})` + ); + + node.intersect = function (point) { + log.info('Triangle intersect', node, points, point); + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/forkJoin.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/forkJoin.ts new file mode 100644 index 0000000000..9ae587618e --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/forkJoin.ts @@ -0,0 +1,66 @@ +import rough from 'roughjs'; +import type { Node, ShapeRenderOptions } from '../../types.js'; +import intersect from '../intersect/index.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import { getNodeClasses, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +export function forkJoin( + parent: D3Selection, + node: Node, + { dir, config: { state, themeVariables } }: ShapeRenderOptions +) { + const { nodeStyles } = styles2String(node); + node.label = ''; + const shapeSvg = parent + .insert('g') + .attr('class', getNodeClasses(node)) + .attr('id', node.domId ?? node.id); + + const { cssStyles } = node; + let width = Math.max(70, node?.width ?? 0); + let height = Math.max(10, node?.height ?? 0); + + if (dir === 'LR') { + width = Math.max(10, node?.width ?? 0); + height = Math.max(70, node?.height ?? 0); + } + + const x = (-1 * width) / 2; + const y = (-1 * height) / 2; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + stroke: themeVariables.lineColor, + fill: themeVariables.lineColor, + }); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const roughNode = rc.rectangle(x, y, width, height, options); + + const shape = shapeSvg.insert(() => roughNode, ':first-child'); + + if (cssStyles && node.look !== 'handDrawn') { + shape.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + shape.selectAll('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, shape); + const padding = state?.padding ?? 0; + if (node.width && node.height) { + node.width += padding / 2 || 0; + node.height += padding / 2 || 0; + } + node.intersect = function (point) { + return intersect.rect(node, point); + }; + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/halfRoundedRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/halfRoundedRectangle.ts new file mode 100644 index 0000000000..330420cf04 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/halfRoundedRectangle.ts @@ -0,0 +1,72 @@ +import { log } from '../../../logger.js'; +import { + labelHelper, + updateNodeBounds, + getNodeClasses, + createPathFromPoints, + generateCirclePoints, +} from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +export async function halfRoundedRectangle( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const minWidth = 80, + minHeight = 50; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(minWidth, bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(minHeight, bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const radius = h / 2; + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x: -w / 2, y: -h / 2 }, + { x: w / 2 - radius, y: -h / 2 }, + ...generateCirclePoints(-w / 2 + radius, 0, radius, 50, 90, 270), + { x: w / 2 - radius, y: h / 2 }, + { x: -w / 2, y: h / 2 }, + ]; + + const pathData = createPathFromPoints(points); + const shapeNode = rc.path(pathData, options); + const polygon = shapeSvg.insert(() => shapeNode, ':first-child'); + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', nodeStyles); + } + + // label.attr( + // 'transform', + // `translate(${-w / 2 + (node.padding ?? 0) - (bbox.x - (bbox.left ?? 0))}, ${-h / 2 + (node.padding ?? 0) - (bbox.y - (bbox.top ?? 0))})` + // ); + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + log.info('Pill intersect', node, { radius, point }); + const pos = intersect.polygon(node, points, point); + return pos; + }; + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.ts new file mode 100644 index 0000000000..80e2a4423e --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.ts @@ -0,0 +1,109 @@ +import { getConfig } from '../../../diagram-api/diagramAPI.js'; +import type { Node } from '../../types.js'; + +// Striped fill like start or fork nodes in state diagrams +export const solidStateFill = (color: string) => { + const { handDrawnSeed } = getConfig(); + return { + fill: color, + hachureAngle: 120, // angle of hachure, + hachureGap: 4, + fillWeight: 2, + roughness: 0.7, + stroke: color, + seed: handDrawnSeed, + }; +}; + +export const compileStyles = (node: Node) => { + // node.cssCompiledStyles is an array of strings in the form of 'key: value' where jey is the css property and value is the value + // the array is the styles of node node from the classes it is using + // node.cssStyles is an array of styles directly set on the node + // concat the arrays and remove duplicates such that the values from node.cssStyles are used if there are duplicates + const stylesMap = styles2Map([...(node.cssCompiledStyles || []), ...(node.cssStyles || [])]); + return { stylesMap, stylesArray: [...stylesMap] }; +}; + +export const styles2Map = (styles: string[]) => { + const styleMap = new Map(); + styles.forEach((style) => { + const [key, value] = style.split(':'); + styleMap.set(key.trim(), value?.trim()); + }); + return styleMap; +}; + +export const styles2String = (node: Node) => { + const { stylesArray } = compileStyles(node); + const labelStyles: string[] = []; + const nodeStyles: string[] = []; + const borderStyles: string[] = []; + const backgroundStyles: string[] = []; + + stylesArray.forEach((style) => { + const key = style[0]; + if ( + key === 'color' || + key === 'font-size' || + key === 'font-family' || + key === 'font-weight' || + key === 'font-style' || + key === 'text-decoration' || + key === 'text-align' || + key === 'text-transform' || + key === 'line-height' || + key === 'letter-spacing' || + key === 'word-spacing' || + key === 'text-shadow' || + key === 'text-overflow' || + key === 'white-space' || + key === 'word-wrap' || + key === 'word-break' || + key === 'overflow-wrap' || + key === 'hyphens' + ) { + labelStyles.push(style.join(':') + ' !important'); + } else { + nodeStyles.push(style.join(':') + ' !important'); + if (key.includes('stroke')) { + borderStyles.push(style.join(':') + ' !important'); + } + if (key === 'fill') { + backgroundStyles.push(style.join(':') + ' !important'); + } + } + }); + + return { + labelStyles: labelStyles.join(';'), + nodeStyles: nodeStyles.join(';'), + stylesArray, + borderStyles, + backgroundStyles, + }; +}; + +// Striped fill like start or fork nodes in state diagrams +// TODO remove any +export const userNodeOverrides = (node: Node, options: any) => { + const { themeVariables, handDrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + const { stylesMap } = compileStyles(node); + + // index the style array to a map object + const result = Object.assign( + { + roughness: 0.7, + fill: stylesMap.get('fill') || mainBkg, + fillStyle: 'hachure', // solid fill + fillWeight: 4, + hachureGap: 5.2, + stroke: stylesMap.get('stroke') || nodeBorder, + seed: handDrawnSeed, + strokeWidth: stylesMap.get('stroke-width')?.replace('px', '') || 1.3, + fillLineDash: [0, 0], + }, + options + ); + return result; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts new file mode 100644 index 0000000000..52a4397a2f --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts @@ -0,0 +1,80 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; +import type { D3Selection } from '../../../types.js'; + +export const createHexagonPathD = ( + x: number, + y: number, + width: number, + height: number, + m: number +): string => { + return [ + `M${x + m},${y}`, + `L${x + width - m},${y}`, + `L${x + width},${y - height / 2}`, + `L${x + width - m},${y - height}`, + `L${x + m},${y - height}`, + `L${x},${y - height / 2}`, + 'Z', + ].join(' '); +}; + +export async function hexagon(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + + const f = 4; + const h = bbox.height + node.padding; + const m = h / f; + const w = bbox.width + 2 * m + node.padding; + const points = [ + { x: m, y: 0 }, + { x: w - m, y: 0 }, + { x: w, y: -h / 2 }, + { x: w - m, y: -h }, + { x: m, y: -h }, + { x: 0, y: -h / 2 }, + ]; + + let polygon: D3Selection | Awaited>; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const pathData = createHexagonPathD(0, 0, w, h, m); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (nodeStyles) { + polygon.attr('style', nodeStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/hourglass.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/hourglass.ts new file mode 100644 index 0000000000..0a1f55a741 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/hourglass.ts @@ -0,0 +1,62 @@ +import { log } from '../../../logger.js'; +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +export async function hourglass(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.label = ''; + node.labelStyle = labelStyles; + const { shapeSvg } = await labelHelper(parent, node, getNodeClasses(node)); + + const w = Math.max(30, node?.width ?? 0); + const h = Math.max(30, node?.height ?? 0); + + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x: 0, y: 0 }, + { x: w, y: 0 }, + { x: 0, y: h }, + { x: w, y: h }, + ]; + + const pathData = createPathFromPoints(points); + const shapeNode = rc.path(pathData, options); + const polygon = shapeSvg.insert(() => shapeNode, ':first-child'); + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', nodeStyles); + } + + polygon.attr('transform', `translate(${-w / 2}, ${-h / 2})`); + + updateNodeBounds(node, polygon); + + // label.attr('transform', `translate(${-bbox.width / 2}, ${(h/2)})`); // To transform text below hourglass shape + + node.intersect = function (point) { + log.info('Pill intersect', node, { points }); + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/icon.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/icon.ts new file mode 100644 index 0000000000..e0735f3d3c --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/icon.ts @@ -0,0 +1,138 @@ +import rough from 'roughjs'; +import { log } from '../../../logger.js'; +import { getIconSVG } from '../../icons.js'; +import type { Node, ShapeRenderOptions } from '../../types.js'; +import intersect from '../intersect/index.js'; +import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +export async function icon( + parent: D3Selection, + node: Node, + { config: { themeVariables, flowchart } }: ShapeRenderOptions +) { + const { labelStyles } = styles2String(node); + node.labelStyle = labelStyles; + const assetHeight = node.assetHeight ?? 48; + const assetWidth = node.assetWidth ?? 48; + const iconSize = Math.max(assetHeight, assetWidth); + const defaultWidth = flowchart?.wrappingWidth; + node.width = Math.max(iconSize, defaultWidth ?? 0); + const { shapeSvg, bbox, label } = await labelHelper(parent, node, 'icon-shape default'); + + const topLabel = node.pos === 't'; + + const height = iconSize; + const width = iconSize; + const { nodeBorder } = themeVariables; + const { stylesMap } = compileStyles(node); + + const x = -width / 2; + const y = -height / 2; + + const labelPadding = node.label ? 8 : 0; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { stroke: 'none', fill: 'none' }); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const iconNode = rc.rectangle(x, y, width, height, options); + + const outerWidth = Math.max(width, bbox.width); + const outerHeight = height + bbox.height + labelPadding; + + const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, { + ...options, + fill: 'transparent', + stroke: 'none', + }); + + const iconShape = shapeSvg.insert(() => iconNode, ':first-child'); + const outerShape = shapeSvg.insert(() => outerNode); + + if (node.icon) { + const iconElem = shapeSvg.append('g'); + iconElem.html( + `${await getIconSVG(node.icon, { + height: iconSize, + width: iconSize, + fallbackPrefix: '', + })}` + ); + const iconBBox = iconElem.node()!.getBBox(); + const iconWidth = iconBBox.width; + const iconHeight = iconBBox.height; + const iconX = iconBBox.x; + const iconY = iconBBox.y; + iconElem.attr( + 'transform', + `translate(${-iconWidth / 2 - iconX},${ + topLabel + ? bbox.height / 2 + labelPadding / 2 - iconHeight / 2 - iconY + : -bbox.height / 2 - labelPadding / 2 - iconHeight / 2 - iconY + })` + ); + iconElem.attr('style', `color: ${stylesMap.get('stroke') ?? nodeBorder};`); + } + + label.attr( + 'transform', + `translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))},${ + topLabel ? -outerHeight / 2 : outerHeight / 2 - bbox.height + })` + ); + + iconShape.attr( + 'transform', + `translate(${0},${ + topLabel ? bbox.height / 2 + labelPadding / 2 : -bbox.height / 2 - labelPadding / 2 + })` + ); + + updateNodeBounds(node, outerShape); + + node.intersect = function (point) { + log.info('iconSquare intersect', node, point); + if (!node.label) { + return intersect.rect(node, point); + } + const dx = node.x ?? 0; + const dy = node.y ?? 0; + const nodeHeight = node.height ?? 0; + let points = []; + if (topLabel) { + points = [ + { x: dx - bbox.width / 2, y: dy - nodeHeight / 2 }, + { x: dx + bbox.width / 2, y: dy - nodeHeight / 2 }, + { x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + { x: dx + width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + { x: dx + width / 2, y: dy + nodeHeight / 2 }, + { x: dx - width / 2, y: dy + nodeHeight / 2 }, + { x: dx - width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + { x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + ]; + } else { + points = [ + { x: dx - width / 2, y: dy - nodeHeight / 2 }, + { x: dx + width / 2, y: dy - nodeHeight / 2 }, + { x: dx + width / 2, y: dy - nodeHeight / 2 + height }, + { x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + height }, + { x: dx + bbox.width / 2 / 2, y: dy + nodeHeight / 2 }, + { x: dx - bbox.width / 2, y: dy + nodeHeight / 2 }, + { x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + height }, + { x: dx - width / 2, y: dy - nodeHeight / 2 + height }, + ]; + } + + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconCircle.ts new file mode 100644 index 0000000000..e8e16853a6 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconCircle.ts @@ -0,0 +1,104 @@ +import rough from 'roughjs'; +import { log } from '../../../logger.js'; +import { getIconSVG } from '../../icons.js'; +import type { Node, ShapeRenderOptions } from '../../types.js'; +import intersect from '../intersect/index.js'; +import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +export async function iconCircle( + parent: D3Selection, + node: Node, + { config: { themeVariables, flowchart } }: ShapeRenderOptions +) { + const { labelStyles } = styles2String(node); + node.labelStyle = labelStyles; + const assetHeight = node.assetHeight ?? 48; + const assetWidth = node.assetWidth ?? 48; + const iconSize = Math.max(assetHeight, assetWidth); + const defaultWidth = flowchart?.wrappingWidth; + node.width = Math.max(iconSize, defaultWidth ?? 0); + const { shapeSvg, bbox, label } = await labelHelper(parent, node, 'icon-shape default'); + + const padding = 20; + const labelPadding = node.label ? 8 : 0; + + const topLabel = node.pos === 't'; + + const { nodeBorder, mainBkg } = themeVariables; + const { stylesMap } = compileStyles(node); + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + const fill = stylesMap.get('fill'); + options.stroke = fill ?? mainBkg; + + const iconElem = shapeSvg.append('g'); + if (node.icon) { + iconElem.html( + `${await getIconSVG(node.icon, { + height: iconSize, + width: iconSize, + fallbackPrefix: '', + })}` + ); + } + const iconBBox = iconElem.node()!.getBBox(); + const iconWidth = iconBBox.width; + const iconHeight = iconBBox.height; + const iconX = iconBBox.x; + const iconY = iconBBox.y; + + const diameter = Math.max(iconWidth, iconHeight) * Math.SQRT2 + padding * 2; + const iconNode = rc.circle(0, 0, diameter, options); + + const outerWidth = Math.max(diameter, bbox.width); + const outerHeight = diameter + bbox.height + labelPadding; + + const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, { + ...options, + fill: 'transparent', + stroke: 'none', + }); + + const iconShape = shapeSvg.insert(() => iconNode, ':first-child'); + const outerShape = shapeSvg.insert(() => outerNode); + iconElem.attr( + 'transform', + `translate(${-iconWidth / 2 - iconX},${ + topLabel + ? bbox.height / 2 + labelPadding / 2 - iconHeight / 2 - iconY + : -bbox.height / 2 - labelPadding / 2 - iconHeight / 2 - iconY + })` + ); + iconElem.attr('style', `color: ${stylesMap.get('stroke') ?? nodeBorder};`); + label.attr( + 'transform', + `translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))},${ + topLabel ? -outerHeight / 2 : outerHeight / 2 - bbox.height + })` + ); + + iconShape.attr( + 'transform', + `translate(${0},${ + topLabel ? bbox.height / 2 + labelPadding / 2 : -bbox.height / 2 - labelPadding / 2 + })` + ); + + updateNodeBounds(node, outerShape); + + node.intersect = function (point) { + log.info('iconSquare intersect', node, point); + const pos = intersect.rect(node, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconRounded.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconRounded.ts new file mode 100644 index 0000000000..40c427ef5e --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconRounded.ts @@ -0,0 +1,145 @@ +import rough from 'roughjs'; +import { log } from '../../../logger.js'; +import { getIconSVG } from '../../icons.js'; +import type { Node, ShapeRenderOptions } from '../../types.js'; +import intersect from '../intersect/index.js'; +import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import { createRoundedRectPathD } from './roundedRectPath.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +export async function iconRounded( + parent: D3Selection, + node: Node, + { config: { themeVariables, flowchart } }: ShapeRenderOptions +) { + const { labelStyles } = styles2String(node); + node.labelStyle = labelStyles; + const assetHeight = node.assetHeight ?? 48; + const assetWidth = node.assetWidth ?? 48; + const iconSize = Math.max(assetHeight, assetWidth); + const defaultWidth = flowchart?.wrappingWidth; + node.width = Math.max(iconSize, defaultWidth ?? 0); + const { shapeSvg, bbox, halfPadding, label } = await labelHelper( + parent, + node, + 'icon-shape default' + ); + + const topLabel = node.pos === 't'; + + const height = iconSize + halfPadding * 2; + const width = iconSize + halfPadding * 2; + const { nodeBorder, mainBkg } = themeVariables; + const { stylesMap } = compileStyles(node); + + const x = -width / 2; + const y = -height / 2; + + const labelPadding = node.label ? 8 : 0; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + const fill = stylesMap.get('fill'); + options.stroke = fill ?? mainBkg; + + const iconNode = rc.path(createRoundedRectPathD(x, y, width, height, 5), options); + + const outerWidth = Math.max(width, bbox.width); + const outerHeight = height + bbox.height + labelPadding; + + const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, { + ...options, + fill: 'transparent', + stroke: 'none', + }); + + const iconShape = shapeSvg.insert(() => iconNode, ':first-child').attr('class', 'icon-shape2'); + const outerShape = shapeSvg.insert(() => outerNode); + + if (node.icon) { + const iconElem = shapeSvg.append('g'); + iconElem.html( + `${await getIconSVG(node.icon, { + height: iconSize, + width: iconSize, + fallbackPrefix: '', + })}` + ); + const iconBBox = iconElem.node()!.getBBox(); + const iconWidth = iconBBox.width; + const iconHeight = iconBBox.height; + const iconX = iconBBox.x; + const iconY = iconBBox.y; + iconElem.attr( + 'transform', + `translate(${-iconWidth / 2 - iconX},${ + topLabel + ? bbox.height / 2 + labelPadding / 2 - iconHeight / 2 - iconY + : -bbox.height / 2 - labelPadding / 2 - iconHeight / 2 - iconY + })` + ); + iconElem.attr('style', `color: ${stylesMap.get('stroke') ?? nodeBorder};`); + } + + label.attr( + 'transform', + `translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))},${ + topLabel ? -outerHeight / 2 : outerHeight / 2 - bbox.height + })` + ); + + iconShape.attr( + 'transform', + `translate(${0},${ + topLabel ? bbox.height / 2 + labelPadding / 2 : -bbox.height / 2 - labelPadding / 2 + })` + ); + + updateNodeBounds(node, outerShape); + + node.intersect = function (point) { + log.info('iconSquare intersect', node, point); + if (!node.label) { + return intersect.rect(node, point); + } + const dx = node.x ?? 0; + const dy = node.y ?? 0; + const nodeHeight = node.height ?? 0; + let points = []; + if (topLabel) { + points = [ + { x: dx - bbox.width / 2, y: dy - nodeHeight / 2 }, + { x: dx + bbox.width / 2, y: dy - nodeHeight / 2 }, + { x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + { x: dx + width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + { x: dx + width / 2, y: dy + nodeHeight / 2 }, + { x: dx - width / 2, y: dy + nodeHeight / 2 }, + { x: dx - width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + { x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + ]; + } else { + points = [ + { x: dx - width / 2, y: dy - nodeHeight / 2 }, + { x: dx + width / 2, y: dy - nodeHeight / 2 }, + { x: dx + width / 2, y: dy - nodeHeight / 2 + height }, + { x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + height }, + { x: dx + bbox.width / 2 / 2, y: dy + nodeHeight / 2 }, + { x: dx - bbox.width / 2, y: dy + nodeHeight / 2 }, + { x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + height }, + { x: dx - width / 2, y: dy - nodeHeight / 2 + height }, + ]; + } + + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts new file mode 100644 index 0000000000..e3536b89e4 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts @@ -0,0 +1,145 @@ +import rough from 'roughjs'; +import { log } from '../../../logger.js'; +import { getIconSVG } from '../../icons.js'; +import type { Node, ShapeRenderOptions } from '../../types.js'; +import intersect from '../intersect/index.js'; +import { createRoundedRectPathD } from './roundedRectPath.js'; +import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +export async function iconSquare( + parent: D3Selection, + node: Node, + { config: { themeVariables, flowchart } }: ShapeRenderOptions +) { + const { labelStyles } = styles2String(node); + node.labelStyle = labelStyles; + const assetHeight = node.assetHeight ?? 48; + const assetWidth = node.assetWidth ?? 48; + const iconSize = Math.max(assetHeight, assetWidth); + const defaultWidth = flowchart?.wrappingWidth; + node.width = Math.max(iconSize, defaultWidth ?? 0); + const { shapeSvg, bbox, halfPadding, label } = await labelHelper( + parent, + node, + 'icon-shape default' + ); + + const topLabel = node.pos === 't'; + + const height = iconSize + halfPadding * 2; + const width = iconSize + halfPadding * 2; + const { nodeBorder, mainBkg } = themeVariables; + const { stylesMap } = compileStyles(node); + + const x = -width / 2; + const y = -height / 2; + + const labelPadding = node.label ? 8 : 0; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + const fill = stylesMap.get('fill'); + options.stroke = fill ?? mainBkg; + + const iconNode = rc.path(createRoundedRectPathD(x, y, width, height, 0.1), options); + + const outerWidth = Math.max(width, bbox.width); + const outerHeight = height + bbox.height + labelPadding; + + const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, { + ...options, + fill: 'transparent', + stroke: 'none', + }); + + const iconShape = shapeSvg.insert(() => iconNode, ':first-child'); + const outerShape = shapeSvg.insert(() => outerNode); + + if (node.icon) { + const iconElem = shapeSvg.append('g'); + iconElem.html( + `${await getIconSVG(node.icon, { + height: iconSize, + width: iconSize, + fallbackPrefix: '', + })}` + ); + const iconBBox = iconElem.node()!.getBBox(); + const iconWidth = iconBBox.width; + const iconHeight = iconBBox.height; + const iconX = iconBBox.x; + const iconY = iconBBox.y; + iconElem.attr( + 'transform', + `translate(${-iconWidth / 2 - iconX},${ + topLabel + ? bbox.height / 2 + labelPadding / 2 - iconHeight / 2 - iconY + : -bbox.height / 2 - labelPadding / 2 - iconHeight / 2 - iconY + })` + ); + iconElem.attr('style', `color: ${stylesMap.get('stroke') ?? nodeBorder};`); + } + + label.attr( + 'transform', + `translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))},${ + topLabel ? -outerHeight / 2 : outerHeight / 2 - bbox.height + })` + ); + + iconShape.attr( + 'transform', + `translate(${0},${ + topLabel ? bbox.height / 2 + labelPadding / 2 : -bbox.height / 2 - labelPadding / 2 + })` + ); + + updateNodeBounds(node, outerShape); + + node.intersect = function (point) { + log.info('iconSquare intersect', node, point); + if (!node.label) { + return intersect.rect(node, point); + } + const dx = node.x ?? 0; + const dy = node.y ?? 0; + const nodeHeight = node.height ?? 0; + let points = []; + if (topLabel) { + points = [ + { x: dx - bbox.width / 2, y: dy - nodeHeight / 2 }, + { x: dx + bbox.width / 2, y: dy - nodeHeight / 2 }, + { x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + { x: dx + width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + { x: dx + width / 2, y: dy + nodeHeight / 2 }, + { x: dx - width / 2, y: dy + nodeHeight / 2 }, + { x: dx - width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + { x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + ]; + } else { + points = [ + { x: dx - width / 2, y: dy - nodeHeight / 2 }, + { x: dx + width / 2, y: dy - nodeHeight / 2 }, + { x: dx + width / 2, y: dy - nodeHeight / 2 + height }, + { x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + height }, + { x: dx + bbox.width / 2 / 2, y: dy + nodeHeight / 2 }, + { x: dx - bbox.width / 2, y: dy + nodeHeight / 2 }, + { x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + height }, + { x: dx - width / 2, y: dy - nodeHeight / 2 + height }, + ]; + } + + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/imageSquare.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/imageSquare.ts new file mode 100644 index 0000000000..5780ca1a6d --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/imageSquare.ts @@ -0,0 +1,149 @@ +import rough from 'roughjs'; +import { log } from '../../../logger.js'; +import type { Node, ShapeRenderOptions } from '../../types.js'; +import intersect from '../intersect/index.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +export async function imageSquare( + parent: D3Selection, + node: Node, + { config: { flowchart } }: ShapeRenderOptions +) { + const img = new Image(); + img.src = node?.img ?? ''; + await img.decode(); + + const imageNaturalWidth = Number(img.naturalWidth.toString().replace('px', '')); + const imageNaturalHeight = Number(img.naturalHeight.toString().replace('px', '')); + node.imageAspectRatio = imageNaturalWidth / imageNaturalHeight; + + const { labelStyles } = styles2String(node); + + node.labelStyle = labelStyles; + + const defaultWidth = flowchart?.wrappingWidth; + node.defaultWidth = flowchart?.wrappingWidth; + + const imageRawWidth = Math.max( + node.label ? (defaultWidth ?? 0) : 0, + node?.assetWidth ?? imageNaturalWidth + ); + + const imageWidth = + node.constraint === 'on' + ? node?.assetHeight + ? node.assetHeight * node.imageAspectRatio + : imageRawWidth + : imageRawWidth; + + const imageHeight = + node.constraint === 'on' + ? imageWidth / node.imageAspectRatio + : (node?.assetHeight ?? imageNaturalHeight); + node.width = Math.max(imageWidth, defaultWidth ?? 0); + const { shapeSvg, bbox, label } = await labelHelper(parent, node, 'image-shape default'); + + const topLabel = node.pos === 't'; + + const x = -imageWidth / 2; + const y = -imageHeight / 2; + + const labelPadding = node.label ? 8 : 0; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const imageNode = rc.rectangle(x, y, imageWidth, imageHeight, options); + + const outerWidth = Math.max(imageWidth, bbox.width); + const outerHeight = imageHeight + bbox.height + labelPadding; + + const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, { + ...options, + fill: 'none', + stroke: 'none', + }); + + const iconShape = shapeSvg.insert(() => imageNode, ':first-child'); + const outerShape = shapeSvg.insert(() => outerNode); + + if (node.img) { + const image = shapeSvg.append('image'); + + // Set the image attributes + image.attr('href', node.img); + image.attr('width', imageWidth); + image.attr('height', imageHeight); + image.attr('preserveAspectRatio', 'none'); + + image.attr( + 'transform', + `translate(${-imageWidth / 2},${topLabel ? outerHeight / 2 - imageHeight : -outerHeight / 2})` + ); + } + + label.attr( + 'transform', + `translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))},${ + topLabel + ? -imageHeight / 2 - bbox.height / 2 - labelPadding / 2 + : imageHeight / 2 - bbox.height / 2 + labelPadding / 2 + })` + ); + + iconShape.attr( + 'transform', + `translate(${0},${ + topLabel ? bbox.height / 2 + labelPadding / 2 : -bbox.height / 2 - labelPadding / 2 + })` + ); + + updateNodeBounds(node, outerShape); + + node.intersect = function (point) { + log.info('iconSquare intersect', node, point); + if (!node.label) { + return intersect.rect(node, point); + } + const dx = node.x ?? 0; + const dy = node.y ?? 0; + const nodeHeight = node.height ?? 0; + let points = []; + if (topLabel) { + points = [ + { x: dx - bbox.width / 2, y: dy - nodeHeight / 2 }, + { x: dx + bbox.width / 2, y: dy - nodeHeight / 2 }, + { x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + { x: dx + imageWidth / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + { x: dx + imageWidth / 2, y: dy + nodeHeight / 2 }, + { x: dx - imageWidth / 2, y: dy + nodeHeight / 2 }, + { x: dx - imageWidth / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + { x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding }, + ]; + } else { + points = [ + { x: dx - imageWidth / 2, y: dy - nodeHeight / 2 }, + { x: dx + imageWidth / 2, y: dy - nodeHeight / 2 }, + { x: dx + imageWidth / 2, y: dy - nodeHeight / 2 + imageHeight }, + { x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + imageHeight }, + { x: dx + bbox.width / 2 / 2, y: dy + nodeHeight / 2 }, + { x: dx - bbox.width / 2, y: dy + nodeHeight / 2 }, + { x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + imageHeight }, + { x: dx - imageWidth / 2, y: dy - nodeHeight / 2 + imageHeight }, + ]; + } + + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/insertPolygonShape.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/insertPolygonShape.ts new file mode 100644 index 0000000000..8749964440 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/insertPolygonShape.ts @@ -0,0 +1,21 @@ +import type { D3Selection } from '../../../types.js'; + +export function insertPolygonShape( + parent: D3Selection, + w: number, + h: number, + points: { x: number; y: number }[] +) { + return parent + .insert('polygon', ':first-child') + .attr( + 'points', + points + .map(function (d) { + return d.x + ',' + d.y; + }) + .join(' ') + ) + .attr('class', 'label-container') + .attr('transform', 'translate(' + -w / 2 + ',' + h / 2 + ')'); +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/invertedTrapezoid.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/invertedTrapezoid.ts new file mode 100644 index 0000000000..49e0f908ec --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/invertedTrapezoid.ts @@ -0,0 +1,78 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; +import type { D3Selection } from '../../../types.js'; + +// export const createInvertedTrapezoidPathD = ( +// x: number, +// y: number, +// width: number, +// height: number +// ): string => { +// return [ +// `M${x + height / 6},${y}`, +// `L${x + width - height / 6},${y}`, +// `L${x + width + (2 * height) / 6},${y - height}`, +// `L${x - (2 * height) / 6},${y - height}`, +// 'Z', +// ].join(' '); +// }; + +export async function inv_trapezoid( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + + const points = [ + { x: 0, y: 0 }, + { x: w, y: 0 }, + { x: w + (3 * h) / 6, y: -h }, + { x: (-3 * h) / 6, y: -h }, + ]; + + let polygon: typeof shapeSvg | ReturnType; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const pathData = createPathFromPoints(points); + // const pathData = createInvertedTrapezoidPathD(0, 0, w, h); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (nodeStyles) { + polygon.attr('style', nodeStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts new file mode 100644 index 0000000000..fba867a71a --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts @@ -0,0 +1,163 @@ +import { labelHelper, insertLabel, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node, KanbanNode, ShapeRenderOptions } from '../../types.js'; +import { createRoundedRectPathD } from './roundedRectPath.js'; +import { userNodeOverrides, styles2String } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +const colorFromPriority = (priority: NonNullable) => { + switch (priority) { + case 'Very High': + return 'red'; + case 'High': + return 'orange'; + case 'Medium': + return null; // no stroke + case 'Low': + return 'blue'; + case 'Very Low': + return 'lightblue'; + } +}; +export async function kanbanItem( + parent: D3Selection, + // Omit the 'shape' prop since otherwise, it causes a TypeScript circular dependency error + kanbanNode: Omit | Omit, + { config }: ShapeRenderOptions +) { + const { labelStyles, nodeStyles } = styles2String(kanbanNode); + kanbanNode.labelStyle = labelStyles || ''; + + const labelPaddingX = 10; + const orgWidth = kanbanNode.width; + kanbanNode.width = (kanbanNode.width ?? 200) - 10; + + const { + shapeSvg, + bbox, + label: labelElTitle, + } = await labelHelper(parent, kanbanNode, getNodeClasses(kanbanNode)); + const padding = kanbanNode.padding || 10; + + let ticketUrl = ''; + let link; + + if ('ticket' in kanbanNode && kanbanNode.ticket && config?.kanban?.ticketBaseUrl) { + ticketUrl = config?.kanban?.ticketBaseUrl.replace('#TICKET#', kanbanNode.ticket); + link = shapeSvg + .insert('svg:a', ':first-child') + .attr('class', 'kanban-ticket-link') + .attr('xlink:href', ticketUrl) + .attr('target', '_blank'); + } + + const options = { + useHtmlLabels: kanbanNode.useHtmlLabels, + labelStyle: kanbanNode.labelStyle || '', + width: kanbanNode.width, + img: kanbanNode.img, + padding: kanbanNode.padding || 8, + centerLabel: false, + }; + let labelEl, bbox2; + if (link) { + ({ label: labelEl, bbox: bbox2 } = await insertLabel( + link, + ('ticket' in kanbanNode && kanbanNode.ticket) || '', + options + )); + } else { + ({ label: labelEl, bbox: bbox2 } = await insertLabel( + shapeSvg, + ('ticket' in kanbanNode && kanbanNode.ticket) || '', + options + )); + } + const { label: labelElAssigned, bbox: bboxAssigned } = await insertLabel( + shapeSvg, + ('assigned' in kanbanNode && kanbanNode.assigned) || '', + options + ); + kanbanNode.width = orgWidth; + const labelPaddingY = 10; + const totalWidth = kanbanNode?.width || 0; + const heightAdj = Math.max(bbox2.height, bboxAssigned.height) / 2; + const totalHeight = + Math.max(bbox.height + labelPaddingY * 2, kanbanNode?.height || 0) + heightAdj; + const x = -totalWidth / 2; + const y = -totalHeight / 2; + labelElTitle.attr( + 'transform', + 'translate(' + (padding - totalWidth / 2) + ', ' + (-heightAdj - bbox.height / 2) + ')' + ); + labelEl.attr( + 'transform', + 'translate(' + (padding - totalWidth / 2) + ', ' + (-heightAdj + bbox.height / 2) + ')' + ); + labelElAssigned.attr( + 'transform', + 'translate(' + + (padding + totalWidth / 2 - bboxAssigned.width - 2 * labelPaddingX) + + ', ' + + (-heightAdj + bbox.height / 2) + + ')' + ); + + let rect; + + const { rx, ry } = kanbanNode; + const { cssStyles } = kanbanNode; + + if (kanbanNode.look === 'handDrawn') { + // @ts-ignore TODO: Fix rough typings + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(kanbanNode, {}); + + const roughNode = + rx || ry + ? rc.path(createRoundedRectPathD(x, y, totalWidth, totalHeight, rx || 0), options) + : rc.rectangle(x, y, totalWidth, totalHeight, options); + + rect = shapeSvg.insert(() => roughNode, ':first-child'); + rect.attr('class', 'basic label-container').attr('style', cssStyles ? cssStyles : null); + } else { + rect = shapeSvg.insert('rect', ':first-child'); + + rect + .attr('class', 'basic label-container __APA__') + .attr('style', nodeStyles) + .attr('rx', rx ?? 5) + .attr('ry', ry ?? 5) + .attr('x', x) + .attr('y', y) + .attr('width', totalWidth) + .attr('height', totalHeight); + + const priority = 'priority' in kanbanNode && kanbanNode.priority; + if (priority) { + const line = shapeSvg.append('line'); + const lineX = x + 2; + + const y1 = y + Math.floor((rx ?? 0) / 2); + const y2 = y + totalHeight - Math.floor((rx ?? 0) / 2); + line + .attr('x1', lineX) + .attr('y1', y1) + .attr('x2', lineX) + .attr('y2', y2) + + .attr('stroke-width', '4') + .attr('stroke', colorFromPriority(priority)); + } + } + + updateNodeBounds(kanbanNode, rect); + kanbanNode.height = totalHeight; + + kanbanNode.intersect = function (point) { + return intersect.rect(kanbanNode, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/labelRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/labelRect.ts new file mode 100644 index 0000000000..de53da4f1d --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/labelRect.ts @@ -0,0 +1,59 @@ +import type { Node, RectOptions } from '../../types.js'; +import { drawRect } from './drawRect.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import type { D3Selection } from '../../../types.js'; + +export async function roundedRect( + parent: D3Selection, + node: Node +) { + const options = { + rx: 5, + ry: 5, + classes: '', + labelPaddingX: (node?.padding || 0) * 1, + labelPaddingY: (node?.padding || 0) * 1, + } as RectOptions; + + return drawRect(parent, node, options); +} + +export async function labelRect(parent: D3Selection, node: Node) { + const { shapeSvg, bbox, label } = await labelHelper(parent, node, 'label'); + + // log.trace('Classes = ', node.class); + // add the rect + const rect = shapeSvg.insert('rect', ':first-child'); + + // Hide the rect we are only after the label + const totalWidth = 0.1; + const totalHeight = 0.1; + rect.attr('width', totalWidth).attr('height', totalHeight); + shapeSvg.attr('class', 'label edgeLabel'); + label.attr( + 'transform', + `translate(${-(bbox.width / 2) - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) - (bbox.y - (bbox.top ?? 0))})` + ); + + // if (node.props) { + // const propKeys = new Set(Object.keys(node.props)); + // if (node.props.borders) { + // applyNodePropertyBorders(rect, node.borders, totalWidth, totalHeight); + // propKeys.delete('borders'); + // } + // propKeys.forEach((propKey) => { + // log.warn(`Unknown node property ${propKey}`); + // }); + // } + + updateNodeBounds(node, rect); + // node.width = 1; + // node.height = 1; + + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanLeft.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanLeft.ts new file mode 100644 index 0000000000..de84b2a7fd --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanLeft.ts @@ -0,0 +1,58 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; +import type { D3Selection } from '../../../types.js'; + +export async function lean_left(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0), node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0), node?.height ?? 0); + const points = [ + { x: 0, y: 0 }, + { x: w + (3 * h) / 6, y: 0 }, + { x: w, y: -h }, + { x: -(3 * h) / 6, y: -h }, + ]; + + let polygon: typeof shapeSvg | ReturnType; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const pathData = createPathFromPoints(points); + // const pathData = createLeanLeftPathD(0, 0, w, h); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (nodeStyles) { + polygon.attr('style', nodeStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanRight.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanRight.ts new file mode 100644 index 0000000000..0b4595bd41 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanRight.ts @@ -0,0 +1,57 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; +import type { D3Selection } from '../../../types.js'; + +export async function lean_right(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0), node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0), node?.height ?? 0); + const points = [ + { x: (-3 * h) / 6, y: 0 }, + { x: w, y: 0 }, + { x: w + (3 * h) / 6, y: -h }, + { x: 0, y: -h }, + ]; + + let polygon: typeof shapeSvg | ReturnType; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const pathData = createPathFromPoints(points); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (nodeStyles) { + polygon.attr('style', nodeStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/lightningBolt.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/lightningBolt.ts new file mode 100644 index 0000000000..b1e16f9ac8 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/lightningBolt.ts @@ -0,0 +1,66 @@ +import { log } from '../../../logger.js'; +import { getNodeClasses, updateNodeBounds } from './util.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; +import { createPathFromPoints } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +export function lightningBolt(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.label = ''; + node.labelStyle = labelStyles; + const shapeSvg = parent + .insert('g') + .attr('class', getNodeClasses(node)) + .attr('id', node.domId ?? node.id); + const { cssStyles } = node; + const width = Math.max(35, node?.width ?? 0); + const height = Math.max(35, node?.height ?? 0); + const gap = 7; + + const points = [ + { x: width, y: 0 }, + { x: 0, y: height + gap / 2 }, + { x: width - 2 * gap, y: height + gap / 2 }, + { x: 0, y: 2 * height }, + { x: width, y: height - gap / 2 }, + { x: 2 * gap, y: height - gap / 2 }, + ]; + + // @ts-expect-error shapeSvg d3 class is incorrect? + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const linePath = createPathFromPoints(points); + const lineNode = rc.path(linePath, options); + + const lightningBolt = shapeSvg.insert(() => lineNode, ':first-child'); + + if (cssStyles && node.look !== 'handDrawn') { + lightningBolt.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + lightningBolt.selectAll('path').attr('style', nodeStyles); + } + + lightningBolt.attr('transform', `translate(-${width / 2},${-height})`); + + updateNodeBounds(node, lightningBolt); + + node.intersect = function (point) { + log.info('lightningBolt intersect', node, point); + const pos = intersect.polygon(node, points, point); + + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedCylinder.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedCylinder.ts new file mode 100644 index 0000000000..6cd348649a --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedCylinder.ts @@ -0,0 +1,139 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; + +export const createCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number, + outerOffset: number +): string => { + return [ + `M${x},${y + ry}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `a${rx},${ry} 0,0,0 ${-width},0`, + `l0,${height}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `l0,${-height}`, + `M${x},${y + ry + outerOffset}`, + `a${rx},${ry} 0,0,0 ${width},0`, + ].join(' '); +}; +export const createOuterCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number, + outerOffset: number +): string => { + return [ + `M${x},${y + ry}`, + `M${x + width},${y + ry}`, + `a${rx},${ry} 0,0,0 ${-width},0`, + `l0,${height}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `l0,${-height}`, + `M${x},${y + ry + outerOffset}`, + `a${rx},${ry} 0,0,0 ${width},0`, + ].join(' '); +}; +export const createInnerCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [`M${x - width / 2},${-height / 2}`, `a${rx},${ry} 0,0,0 ${width},0`].join(' '); +}; +export async function linedCylinder( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0), node.width ?? 0); + const rx = w / 2; + const ry = rx / (2.5 + w / 50); + const h = Math.max(bbox.height + ry + (node.padding ?? 0), node.height ?? 0); + const outerOffset = h * 0.1; // 10% of height + + let cylinder: typeof shapeSvg | D3Selection; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const outerPathData = createOuterCylinderPathD(0, 0, w, h, rx, ry, outerOffset); + const innerPathData = createInnerCylinderPathD(0, ry, w, h, rx, ry); + const options = userNodeOverrides(node, {}); + + const outerNode = rc.path(outerPathData, options); + const innerLine = rc.path(innerPathData, options); + + const innerLineEl = shapeSvg.insert(() => innerLine, ':first-child'); + innerLineEl.attr('class', 'line'); + cylinder = shapeSvg.insert(() => outerNode, ':first-child'); + cylinder.attr('class', 'basic label-container'); + if (cssStyles) { + cylinder.attr('style', cssStyles); + } + } else { + const pathData = createCylinderPathD(0, 0, w, h, rx, ry, outerOffset); + cylinder = shapeSvg + .insert('path', ':first-child') + .attr('d', pathData) + .attr('class', 'basic label-container') + .attr('style', handleUndefinedAttr(cssStyles)) + .attr('style', nodeStyles); + } + + // find label and move it down + cylinder.attr('label-offset-y', ry); + cylinder.attr('transform', `translate(${-w / 2}, ${-(h / 2 + ry)})`); + + updateNodeBounds(node, cylinder); + + label.attr( + 'transform', + `translate(${-(bbox.width / 2) - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) + ry - (bbox.y - (bbox.top ?? 0))})` + ); + + node.intersect = function (point) { + const pos = intersect.rect(node, point); + const x = pos.x - (node.x ?? 0); + + if ( + rx != 0 && + (Math.abs(x) < (node.width ?? 0) / 2 || + (Math.abs(x) == (node.width ?? 0) / 2 && + Math.abs(pos.y - (node.y ?? 0)) > (node.height ?? 0) / 2 - ry)) + ) { + let y = ry * ry * (1 - (x * x) / (rx * rx)); + if (y > 0) { + y = Math.sqrt(y); + } + y = ry - y; + if (point.y - (node.y ?? 0) > 0) { + y = -y; + } + + pos.y += y; + } + + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedWaveEdgedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedWaveEdgedRect.ts new file mode 100644 index 0000000000..6f81237427 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedWaveEdgedRect.ts @@ -0,0 +1,83 @@ +import { + labelHelper, + updateNodeBounds, + getNodeClasses, + generateFullSineWavePoints, +} from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import rough from 'roughjs'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import type { D3Selection } from '../../../types.js'; + +export async function linedWaveEdgedRect( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const waveAmplitude = h / 4; + const finalH = h + waveAmplitude; + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x: -w / 2 - (w / 2) * 0.1, y: -finalH / 2 }, + { x: -w / 2 - (w / 2) * 0.1, y: finalH / 2 }, + ...generateFullSineWavePoints( + -w / 2 - (w / 2) * 0.1, + finalH / 2, + w / 2 + (w / 2) * 0.1, + finalH / 2, + waveAmplitude, + 0.8 + ), + { x: w / 2 + (w / 2) * 0.1, y: -finalH / 2 }, + { x: -w / 2 - (w / 2) * 0.1, y: -finalH / 2 }, + { x: -w / 2, y: -finalH / 2 }, + { x: -w / 2, y: (finalH / 2) * 1.1 }, + { x: -w / 2, y: -finalH / 2 }, + ]; + + const poly = rc.polygon( + points.map((p) => [p.x, p.y]), + options + ); + + const waveEdgeRect = shapeSvg.insert(() => poly, ':first-child'); + + waveEdgeRect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + waveEdgeRect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + waveEdgeRect.selectAll('path').attr('style', nodeStyles); + } + + waveEdgeRect.attr('transform', `translate(0,${-waveAmplitude / 2})`); + label.attr( + 'transform', + `translate(${-w / 2 + (node.padding ?? 0) + ((w / 2) * 0.1) / 2 - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) - waveAmplitude / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, waveEdgeRect); + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiRect.ts new file mode 100644 index 0000000000..a82929ec4f --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiRect.ts @@ -0,0 +1,83 @@ +import { labelHelper, getNodeClasses, updateNodeBounds, createPathFromPoints } from './util.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; +import type { D3Selection } from '../../../types.js'; + +export async function multiRect(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const rectOffset = 5; + const x = -w / 2; + const y = -h / 2; + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + const outerPathPoints = [ + { x: x - rectOffset, y: y + rectOffset }, + { x: x - rectOffset, y: y + h + rectOffset }, + { x: x + w - rectOffset, y: y + h + rectOffset }, + { x: x + w - rectOffset, y: y + h }, + { x: x + w, y: y + h }, + { x: x + w, y: y + h - rectOffset }, + { x: x + w + rectOffset, y: y + h - rectOffset }, + { x: x + w + rectOffset, y: y - rectOffset }, + { x: x + rectOffset, y: y - rectOffset }, + { x: x + rectOffset, y: y }, + { x, y }, + { x, y: y + rectOffset }, + ]; + + const innerPathPoints = [ + { x, y: y + rectOffset }, + { x: x + w - rectOffset, y: y + rectOffset }, + { x: x + w - rectOffset, y: y + h }, + { x: x + w, y: y + h }, + { x: x + w, y }, + { x, y }, + ]; + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const outerPath = createPathFromPoints(outerPathPoints); + const outerNode = rc.path(outerPath, options); + const innerPath = createPathFromPoints(innerPathPoints); + const innerNode = rc.path(innerPath, { ...options, fill: 'none' }); + + const multiRect = shapeSvg.insert(() => innerNode, ':first-child'); + multiRect.insert(() => outerNode, ':first-child'); + + multiRect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + multiRect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + multiRect.selectAll('path').attr('style', nodeStyles); + } + + label.attr( + 'transform', + `translate(${-(bbox.width / 2) - rectOffset - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) + rectOffset - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, multiRect); + + node.intersect = function (point) { + const pos = intersect.polygon(node, outerPathPoints, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiWaveEdgedRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiWaveEdgedRectangle.ts new file mode 100644 index 0000000000..db436879d3 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiWaveEdgedRectangle.ts @@ -0,0 +1,108 @@ +import { + labelHelper, + updateNodeBounds, + getNodeClasses, + createPathFromPoints, + generateFullSineWavePoints, +} from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import rough from 'roughjs'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import type { D3Selection } from '../../../types.js'; + +export async function multiWaveEdgedRectangle( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const waveAmplitude = h / 4; + const finalH = h + waveAmplitude; + const x = -w / 2; + const y = -finalH / 2; + const rectOffset = 5; + + const { cssStyles } = node; + + const wavePoints = generateFullSineWavePoints( + x - rectOffset, + y + finalH + rectOffset, + x + w - rectOffset, + y + finalH + rectOffset, + waveAmplitude, + 0.8 + ); + + const lastWavePoint = wavePoints?.[wavePoints.length - 1]; + + const outerPathPoints = [ + { x: x - rectOffset, y: y + rectOffset }, + { x: x - rectOffset, y: y + finalH + rectOffset }, + ...wavePoints, + { x: x + w - rectOffset, y: lastWavePoint.y - rectOffset }, + { x: x + w, y: lastWavePoint.y - rectOffset }, + { x: x + w, y: lastWavePoint.y - 2 * rectOffset }, + { x: x + w + rectOffset, y: lastWavePoint.y - 2 * rectOffset }, + { x: x + w + rectOffset, y: y - rectOffset }, + { x: x + rectOffset, y: y - rectOffset }, + { x: x + rectOffset, y: y }, + { x, y }, + { x, y: y + rectOffset }, + ]; + + const innerPathPoints = [ + { x, y: y + rectOffset }, + { x: x + w - rectOffset, y: y + rectOffset }, + { x: x + w - rectOffset, y: lastWavePoint.y - rectOffset }, + { x: x + w, y: lastWavePoint.y - rectOffset }, + { x: x + w, y }, + { x, y }, + ]; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const outerPath = createPathFromPoints(outerPathPoints); + const outerNode = rc.path(outerPath, options); + const innerPath = createPathFromPoints(innerPathPoints); + const innerNode = rc.path(innerPath, options); + + const shape = shapeSvg.insert(() => outerNode, ':first-child'); + shape.insert(() => innerNode); + + shape.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + shape.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + shape.selectAll('path').attr('style', nodeStyles); + } + + shape.attr('transform', `translate(0,${-waveAmplitude / 2})`); + + label.attr( + 'transform', + `translate(${-(bbox.width / 2) - rectOffset - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) + rectOffset - waveAmplitude / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, shape); + + node.intersect = function (point) { + const pos = intersect.polygon(node, outerPathPoints, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/note.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/note.ts new file mode 100644 index 0000000000..4a7f66a877 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/note.ts @@ -0,0 +1,60 @@ +import rough from 'roughjs'; +import type { Node, ShapeRenderOptions } from '../../types.js'; +import intersect from '../intersect/index.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; +import { getConfig } from '../../../config.js'; + +export async function note( + parent: D3Selection, + node: Node, + { config: { themeVariables } }: ShapeRenderOptions +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const useHtmlLabels = node.useHtmlLabels || getConfig().flowchart?.htmlLabels !== false; + if (!useHtmlLabels) { + node.centerLabel = true; + } + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const totalWidth = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const totalHeight = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const x = -totalWidth / 2; + const y = -totalHeight / 2; + const { cssStyles } = node; + + // add the rect + // @ts-ignore TODO: Fix rough typings + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + fill: themeVariables.noteBkgColor, + stroke: themeVariables.noteBorderColor, + }); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const noteShapeNode = rc.rectangle(x, y, totalWidth, totalHeight, options); + + const rect = shapeSvg.insert(() => noteShapeNode, ':first-child'); + rect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + rect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + rect.selectAll('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, rect); + + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts new file mode 100644 index 0000000000..eef958169f --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts @@ -0,0 +1,76 @@ +import { log } from '../../../logger.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; +import type { D3Selection } from '../../../types.js'; + +export const createDecisionBoxPathD = (x: number, y: number, size: number): string => { + return [ + `M${x + size / 2},${y}`, + `L${x + size},${y - size / 2}`, + `L${x + size / 2},${y - size}`, + `L${x},${y - size / 2}`, + 'Z', + ].join(' '); +}; + +export async function question(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const s = w + h; + + const points = [ + { x: s / 2, y: 0 }, + { x: s, y: -s / 2 }, + { x: s / 2, y: -s }, + { x: 0, y: -s / 2 }, + ]; + + let polygon: typeof shapeSvg | ReturnType; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const pathData = createDecisionBoxPathD(0, 0, s); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-s / 2}, ${s / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, s, s, points); + } + + if (nodeStyles) { + polygon.attr('style', nodeStyles); + } + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + log.debug( + 'APA12 Intersect called SPLIT\npoint:', + point, + '\nnode:\n', + node, + '\nres:', + intersect.polygon(node, points, point) + ); + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectLeftInvArrow.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectLeftInvArrow.ts new file mode 100644 index 0000000000..1ad40c5540 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectLeftInvArrow.ts @@ -0,0 +1,68 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +export async function rect_left_inv_arrow( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + + const w = Math.max(bbox.width + (node.padding ?? 0), node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0), node?.height ?? 0); + + const x = -w / 2; + const y = -h / 2; + const notch = y / 2; + + const points = [ + { x: x + notch, y }, + { x: x, y: 0 }, + { x: x + notch, y: -y }, + { x: -x, y: -y }, + { x: -x, y }, + ]; + + const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const pathData = createPathFromPoints(points); + const roughNode = rc.path(pathData, options); + + const polygon = shapeSvg.insert(() => roughNode, ':first-child'); + + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectAll('path').attr('style', cssStyles); + } + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectAll('path').attr('style', nodeStyles); + } + + polygon.attr('transform', `translate(${-notch / 2},0)`); + + label.attr( + 'transform', + `translate(${-notch / 2 - bbox.width / 2 - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) - (bbox.y - (bbox.top ?? 0))})` + ); + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectWithTitle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectWithTitle.ts new file mode 100644 index 0000000000..bcaf2787aa --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectWithTitle.ts @@ -0,0 +1,158 @@ +import type { Node } from '../../types.js'; +import { select } from 'd3'; +import { evaluate } from '../../../diagrams/common/common.js'; +import { updateNodeBounds } from './util.js'; +import createLabel from '../createLabel.js'; +import intersect from '../intersect/index.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { getConfig } from '../../../diagram-api/diagramAPI.js'; +import { createRoundedRectPathD } from './roundedRectPath.js'; +import { log } from '../../../logger.js'; +import type { D3Selection } from '../../../types.js'; + +export async function rectWithTitle( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + let classes; + if (!node.cssClasses) { + classes = 'node default'; + } else { + classes = 'node ' + node.cssClasses; + } + + // Add outer g element + const shapeSvg = parent + // @ts-ignore - d3 typings are not correct + .insert('g') + .attr('class', classes) + .attr('id', node.domId || node.id); + + // Create the title label and insert it after the rect + const g = shapeSvg.insert('g'); + + const label = shapeSvg.insert('g').attr('class', 'label').attr('style', nodeStyles); + + const description = node.description; + + const title = node.label; + + const text = label.node()!.appendChild(await createLabel(title, node.labelStyle, true, true)); + let bbox = { width: 0, height: 0 }; + if (evaluate(getConfig()?.flowchart?.htmlLabels)) { + const div = text.children[0]; + const dv = select(text); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + log.info('Text 2', description); + const textRows = description || []; + const titleBox = text.getBBox(); + const descr = label + .node()! + .appendChild( + await createLabel( + textRows.join ? textRows.join('
') : textRows, + node.labelStyle, + true, + true + ) + ); + + //if (evaluate(getConfig()?.flowchart?.htmlLabels)) { + const div = descr.children[0]; + const dv = select(descr); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + // } + + const halfPadding = (node.padding || 0) / 2; + select(descr).attr( + 'transform', + 'translate( ' + + (bbox.width > titleBox.width ? 0 : (titleBox.width - bbox.width) / 2) + + ', ' + + (titleBox.height + halfPadding + 5) + + ')' + ); + select(text).attr( + 'transform', + 'translate( ' + + (bbox.width < titleBox.width ? 0 : -(titleBox.width - bbox.width) / 2) + + ', ' + + 0 + + ')' + ); + // Get the size of the label + + // Bounding box for title and text + bbox = label.node()!.getBBox(); + + // Center the label + label.attr( + 'transform', + 'translate(' + -bbox.width / 2 + ', ' + (-bbox.height / 2 - halfPadding + 3) + ')' + ); + + const totalWidth = bbox.width + (node.padding || 0); + const totalHeight = bbox.height + (node.padding || 0); + const x = -bbox.width / 2 - halfPadding; + const y = -bbox.height / 2 - halfPadding; + let rect; + let innerLine; + if (node.look === 'handDrawn') { + // @ts-ignore No typings for rough + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const roughNode = rc.path( + createRoundedRectPathD(x, y, totalWidth, totalHeight, node.rx || 0), + options + ); + + const roughLine = rc.line( + -bbox.width / 2 - halfPadding, + -bbox.height / 2 - halfPadding + titleBox.height + halfPadding, + bbox.width / 2 + halfPadding, + -bbox.height / 2 - halfPadding + titleBox.height + halfPadding, + options + ); + + innerLine = shapeSvg.insert(() => { + log.debug('Rough node insert CXC', roughNode); + return roughLine; + }, ':first-child'); + rect = shapeSvg.insert(() => { + log.debug('Rough node insert CXC', roughNode); + return roughNode; + }, ':first-child'); + } else { + rect = g.insert('rect', ':first-child'); + innerLine = g.insert('line'); + rect + .attr('class', 'outer title-state') + .attr('style', nodeStyles) + .attr('x', -bbox.width / 2 - halfPadding) + .attr('y', -bbox.height / 2 - halfPadding) + .attr('width', bbox.width + (node.padding || 0)) + .attr('height', bbox.height + (node.padding || 0)); + + innerLine + .attr('class', 'divider') + .attr('x1', -bbox.width / 2 - halfPadding) + .attr('x2', bbox.width / 2 + halfPadding) + .attr('y1', -bbox.height / 2 - halfPadding + titleBox.height + halfPadding) + .attr('y2', -bbox.height / 2 - halfPadding + titleBox.height + halfPadding); + } + updateNodeBounds(node, rect); + + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts new file mode 100644 index 0000000000..134bcdb6c8 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts @@ -0,0 +1,18 @@ +import type { Node, RectOptions } from '../../types.js'; +import type { D3Selection } from '../../../types.js'; +import { drawRect } from './drawRect.js'; + +export async function roundedRect( + parent: D3Selection, + node: Node +) { + const options = { + rx: 5, + ry: 5, + classes: '', + labelPaddingX: (node?.padding || 0) * 1, + labelPaddingY: (node?.padding || 0) * 1, + } as RectOptions; + + return drawRect(parent, node, options); +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRectPath.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRectPath.ts new file mode 100644 index 0000000000..031b935fdf --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRectPath.ts @@ -0,0 +1,53 @@ +export const createRoundedRectPathD = ( + x: number, + y: number, + totalWidth: number, + totalHeight: number, + radius: number +) => + [ + 'M', + x + radius, + y, // Move to the first point + 'H', + x + totalWidth - radius, // Draw horizontal line to the beginning of the right corner + 'A', + radius, + radius, + 0, + 0, + 1, + x + totalWidth, + y + radius, // Draw arc to the right top corner + 'V', + y + totalHeight - radius, // Draw vertical line down to the beginning of the right bottom corner + 'A', + radius, + radius, + 0, + 0, + 1, + x + totalWidth - radius, + y + totalHeight, // Draw arc to the right bottom corner + 'H', + x + radius, // Draw horizontal line to the beginning of the left bottom corner + 'A', + radius, + radius, + 0, + 0, + 1, + x, + y + totalHeight - radius, // Draw arc to the left bottom corner + 'V', + y + radius, // Draw vertical line up to the beginning of the left top corner + 'A', + radius, + radius, + 0, + 0, + 1, + x + radius, + y, // Draw arc to the left top corner + 'Z', // Close the path + ].join(' '); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/shadedProcess.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/shadedProcess.ts new file mode 100644 index 0000000000..835f55ee4d --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/shadedProcess.ts @@ -0,0 +1,71 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; + +export async function shadedProcess( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const halfPadding = node?.padding ?? 0; + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const x = -bbox.width / 2 - halfPadding; + const y = -bbox.height / 2 - halfPadding; + + const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x, y }, + { x: x + w + 8, y }, + { x: x + w + 8, y: y + h }, + { x: x - 8, y: y + h }, + { x: x - 8, y: y }, + { x, y }, + { x, y: y + h }, + ]; + + const roughNode = rc.polygon( + points.map((p) => [p.x, p.y]), + options + ); + + const rect = shapeSvg.insert(() => roughNode, ':first-child'); + + rect.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles)); + + if (nodeStyles && node.look !== 'handDrawn') { + rect.selectAll('path').attr('style', nodeStyles); + } + + if (cssStyles && node.look !== 'handDrawn') { + rect.selectAll('path').attr('style', nodeStyles); + } + + label.attr( + 'transform', + `translate(${-w / 2 + 4 + (node.padding ?? 0) - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, rect); + + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/slopedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/slopedRect.ts new file mode 100644 index 0000000000..d97e9fb6a3 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/slopedRect.ts @@ -0,0 +1,63 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +export async function slopedRect(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const x = -w / 2; + const y = -h / 2; + + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x, y }, + { x, y: y + h }, + { x: x + w, y: y + h }, + { x: x + w, y: y - h / 2 }, + ]; + + const pathData = createPathFromPoints(points); + const shapeNode = rc.path(pathData, options); + + const polygon = shapeSvg.insert(() => shapeNode, ':first-child'); + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', nodeStyles); + } + + polygon.attr('transform', `translate(0, ${h / 4})`); + label.attr( + 'transform', + `translate(${-w / 2 + (node.padding ?? 0) - (bbox.x - (bbox.left ?? 0))}, ${-h / 4 + (node.padding ?? 0) - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts new file mode 100644 index 0000000000..af72a798f5 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts @@ -0,0 +1,14 @@ +import type { Node, RectOptions } from '../../types.js'; +import type { D3Selection } from '../../../types.js'; +import { drawRect } from './drawRect.js'; + +export async function squareRect(parent: D3Selection, node: Node) { + const options = { + rx: 0, + ry: 0, + classes: '', + labelPaddingX: (node?.padding || 0) * 2, + labelPaddingY: (node?.padding || 0) * 1, + } as RectOptions; + return drawRect(parent, node, options); +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts new file mode 100644 index 0000000000..1b93aa1b31 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts @@ -0,0 +1,96 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { createRoundedRectPathD } from './roundedRectPath.js'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; + +export const createStadiumPathD = ( + x: number, + y: number, + totalWidth: number, + totalHeight: number +) => { + const radius = totalHeight / 2; + return [ + 'M', + x + radius, + y, // Move to the start of the top-left arc + 'H', + x + totalWidth - radius, // Draw horizontal line to the start of the top-right arc + 'A', + radius, + radius, + 0, + 0, + 1, + x + totalWidth, + y + radius, // Draw top-right arc + 'H', + x, // Draw horizontal line to the start of the bottom-right arc + 'A', + radius, + radius, + 0, + 0, + 1, + x + totalWidth - radius, + y + totalHeight, // Draw bottom-right arc + 'H', + x + radius, // Draw horizontal line to the start of the bottom-left arc + 'A', + radius, + radius, + 0, + 0, + 1, + x, + y + radius, // Draw bottom-left arc + 'Z', // Close the path + ].join(' '); +}; + +export async function stadium(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + + const h = bbox.height + node.padding; + const w = bbox.width + h / 4 + node.padding; + + let rect; + const { cssStyles } = node; + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + const pathData = createRoundedRectPathD(-w / 2, -h / 2, w, h, h / 2); + const roughNode = rc.path(pathData, options); + + rect = shapeSvg.insert(() => roughNode, ':first-child'); + rect.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles)); + } else { + rect = shapeSvg.insert('rect', ':first-child'); + + rect + .attr('class', 'basic label-container') + .attr('style', nodeStyles) + .attr('rx', h / 2) + .attr('ry', h / 2) + .attr('x', -w / 2) + .attr('y', -h / 2) + .attr('width', w) + .attr('height', h); + } + + updateNodeBounds(node, rect); + + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/state.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/state.ts new file mode 100644 index 0000000000..821195e64d --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/state.ts @@ -0,0 +1,12 @@ +import type { Node, RectOptions } from '../../types.js'; +import type { D3Selection } from '../../../types.js'; +import { drawRect } from './drawRect.js'; + +export async function state(parent: D3Selection, node: Node) { + const options = { + rx: 5, + ry: 5, + classes: 'flowchart-node', + } as RectOptions; + return drawRect(parent, node, options); +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateEnd.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateEnd.ts new file mode 100644 index 0000000000..4cc2838ae7 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateEnd.ts @@ -0,0 +1,62 @@ +import rough from 'roughjs'; +import type { Node, ShapeRenderOptions } from '../../types.js'; +import intersect from '../intersect/index.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import { updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +export function stateEnd( + parent: D3Selection, + node: Node, + { config: { themeVariables } }: ShapeRenderOptions +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { cssStyles } = node; + const { lineColor, stateBorder, nodeBorder } = themeVariables; + const shapeSvg = parent + .insert('g') + .attr('class', 'node default') + .attr('id', node.domId || node.id); + + // @ts-ignore TODO: Fix rough typings + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const roughNode = rc.circle(0, 0, 14, { + ...options, + stroke: lineColor, + strokeWidth: 2, + }); + const innerFill = stateBorder ?? nodeBorder; + const roughInnerNode = rc.circle(0, 0, 5, { + ...options, + fill: innerFill, + stroke: innerFill, + strokeWidth: 2, + fillStyle: 'solid', + }); + const circle = shapeSvg.insert(() => roughNode, ':first-child'); + circle.insert(() => roughInnerNode); + + if (cssStyles) { + circle.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles) { + circle.selectAll('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, circle); + + node.intersect = function (point) { + return intersect.circle(node, 7, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateStart.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateStart.ts new file mode 100644 index 0000000000..07a0f8f92d --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateStart.ts @@ -0,0 +1,41 @@ +import rough from 'roughjs'; +import type { Node, ShapeRenderOptions } from '../../types.js'; +import intersect from '../intersect/index.js'; +import { solidStateFill } from './handDrawnShapeStyles.js'; +import { updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; + +export function stateStart( + parent: D3Selection, + node: Node, + { config: { themeVariables } }: ShapeRenderOptions +) { + const { lineColor } = themeVariables; + + const shapeSvg = parent + .insert('g') + .attr('class', 'node default') + .attr('id', node.domId || node.id); + + let circle: D3Selection | D3Selection; + if (node.look === 'handDrawn') { + // @ts-ignore TODO: Fix rough typings + const rc = rough.svg(shapeSvg); + const roughNode = rc.circle(0, 0, 14, solidStateFill(lineColor)); + circle = shapeSvg.insert(() => roughNode); + // center the circle around its coordinate + circle.attr('class', 'state-start').attr('r', 7).attr('width', 14).attr('height', 14); + } else { + circle = shapeSvg.insert('circle', ':first-child'); + // center the circle around its coordinate + circle.attr('class', 'state-start').attr('r', 7).attr('width', 14).attr('height', 14); + } + + updateNodeBounds(node, circle); + + node.intersect = function (point) { + return intersect.circle(node, 7, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts new file mode 100644 index 0000000000..ab24af29c8 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts @@ -0,0 +1,87 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; + +export const createSubroutinePathD = ( + x: number, + y: number, + width: number, + height: number +): string => { + const offset = 8; + return [ + `M${x - offset},${y}`, + `H${x + width + offset}`, + `V${y + height}`, + `H${x - offset}`, + `V${y}`, + 'M', + x, + y, + 'H', + x + width, + 'V', + y + height, + 'H', + x, + 'Z', + ].join(' '); +}; + +export async function subroutine(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const halfPadding = (node?.padding || 0) / 2; + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const x = -bbox.width / 2 - halfPadding; + const y = -bbox.height / 2 - halfPadding; + + const points = [ + { x: 0, y: 0 }, + { x: w, y: 0 }, + { x: w, y: -h }, + { x: 0, y: -h }, + { x: 0, y: 0 }, + { x: -8, y: 0 }, + { x: w + 8, y: 0 }, + { x: w + 8, y: -h }, + { x: -8, y: -h }, + { x: -8, y: 0 }, + ]; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + const roughNode = rc.rectangle(x - 8, y, w + 16, h, options); + const l1 = rc.line(x, y, x, y + h, options); + const l2 = rc.line(x + w, y, x + w, y + h, options); + + shapeSvg.insert(() => l1, ':first-child'); + shapeSvg.insert(() => l2, ':first-child'); + const rect = shapeSvg.insert(() => roughNode, ':first-child'); + const { cssStyles } = node; + rect.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles)); + updateNodeBounds(node, rect); + } else { + const el = insertPolygonShape(shapeSvg, w, h, points); + if (nodeStyles) { + el.attr('style', nodeStyles); + } + updateNodeBounds(node, el); + } + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedRect.ts new file mode 100644 index 0000000000..3ae068a002 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedRect.ts @@ -0,0 +1,70 @@ +import { labelHelper, getNodeClasses, updateNodeBounds, createPathFromPoints } from './util.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; +import type { D3Selection } from '../../../types.js'; + +export async function taggedRect(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const x = -w / 2; + const y = -h / 2; + const tagWidth = 0.2 * h; + const tagHeight = 0.2 * h; + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + const rectPoints = [ + { x: x - tagWidth / 2, y }, + { x: x + w + tagWidth / 2, y }, + { x: x + w + tagWidth / 2, y: y + h }, + { x: x - tagWidth / 2, y: y + h }, + ]; + + const tagPoints = [ + { x: x + w - tagWidth / 2, y: y + h }, + { x: x + w + tagWidth / 2, y: y + h }, + { x: x + w + tagWidth / 2, y: y + h - tagHeight }, + ]; + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const rectPath = createPathFromPoints(rectPoints); + const rectNode = rc.path(rectPath, options); + + const tagPath = createPathFromPoints(tagPoints); + const tagNode = rc.path(tagPath, { ...options, fillStyle: 'solid' }); + + const taggedRect = shapeSvg.insert(() => tagNode, ':first-child'); + taggedRect.insert(() => rectNode, ':first-child'); + + taggedRect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + taggedRect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + taggedRect.selectAll('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, taggedRect); + + node.intersect = function (point) { + const pos = intersect.polygon(node, rectPoints, point); + + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedWaveEdgedRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedWaveEdgedRectangle.ts new file mode 100644 index 0000000000..1451bdacc5 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedWaveEdgedRectangle.ts @@ -0,0 +1,105 @@ +import { + labelHelper, + updateNodeBounds, + getNodeClasses, + generateFullSineWavePoints, + createPathFromPoints, +} from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import rough from 'roughjs'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import type { D3Selection } from '../../../types.js'; + +export async function taggedWaveEdgedRectangle( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const waveAmplitude = h / 4; + const tagWidth = 0.2 * w; + const tagHeight = 0.2 * h; + const finalH = h + waveAmplitude; + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x: -w / 2 - (w / 2) * 0.1, y: finalH / 2 }, + ...generateFullSineWavePoints( + -w / 2 - (w / 2) * 0.1, + finalH / 2, + w / 2 + (w / 2) * 0.1, + finalH / 2, + waveAmplitude, + 0.8 + ), + + { x: w / 2 + (w / 2) * 0.1, y: -finalH / 2 }, + { x: -w / 2 - (w / 2) * 0.1, y: -finalH / 2 }, + ]; + + const x = -w / 2 + (w / 2) * 0.1; + const y = -finalH / 2 - tagHeight * 0.4; + + const tagPoints = [ + { x: x + w - tagWidth, y: (y + h) * 1.4 }, + { x: x + w, y: y + h - tagHeight }, + { x: x + w, y: (y + h) * 0.9 }, + ...generateFullSineWavePoints( + x + w, + (y + h) * 1.3, + x + w - tagWidth, + (y + h) * 1.5, + -h * 0.03, + 0.5 + ), + ]; + + const waveEdgeRectPath = createPathFromPoints(points); + const waveEdgeRectNode = rc.path(waveEdgeRectPath, options); + + const taggedWaveEdgeRectPath = createPathFromPoints(tagPoints); + const taggedWaveEdgeRectNode = rc.path(taggedWaveEdgeRectPath, { + ...options, + fillStyle: 'solid', + }); + + const waveEdgeRect = shapeSvg.insert(() => taggedWaveEdgeRectNode, ':first-child'); + waveEdgeRect.insert(() => waveEdgeRectNode, ':first-child'); + + waveEdgeRect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + waveEdgeRect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + waveEdgeRect.selectAll('path').attr('style', nodeStyles); + } + + waveEdgeRect.attr('transform', `translate(0,${-waveAmplitude / 2})`); + label.attr( + 'transform', + `translate(${-w / 2 + (node.padding ?? 0) - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) - waveAmplitude / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, waveEdgeRect); + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/text.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/text.ts new file mode 100644 index 0000000000..cf253fd175 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/text.ts @@ -0,0 +1,37 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String } from './handDrawnShapeStyles.js'; +import type { D3Selection } from '../../../types.js'; + +export async function text(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + + const totalWidth = Math.max(bbox.width + node.padding, node?.width || 0); + const totalHeight = Math.max(bbox.height + node.padding, node?.height || 0); + const x = -totalWidth / 2; + const y = -totalHeight / 2; + + const rect = shapeSvg.insert('rect', ':first-child'); + + rect + .attr('class', 'text') + .attr('style', nodeStyles) + .attr('rx', 0) + .attr('ry', 0) + .attr('x', x) + .attr('y', y) + .attr('width', totalWidth) + .attr('height', totalHeight); + + updateNodeBounds(node, rect); + + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/tiltedCylinder.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/tiltedCylinder.ts new file mode 100644 index 0000000000..29f2c267f5 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/tiltedCylinder.ts @@ -0,0 +1,142 @@ +import { labelHelper, getNodeClasses, updateNodeBounds } from './util.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; + +export const createCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return `M${x},${y} + a${rx},${ry} 0,0,1 ${0},${-height} + l${width},${0} + a${rx},${ry} 0,0,1 ${0},${height} + M${width},${-height} + a${rx},${ry} 0,0,0 ${0},${height} + l${-width},${0}`; +}; + +export const createOuterCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [ + `M${x},${y}`, + `M${x + width},${y}`, + `a${rx},${ry} 0,0,0 ${0},${-height}`, + `l${-width},0`, + `a${rx},${ry} 0,0,0 ${0},${height}`, + `l${width},0`, + ].join(' '); +}; +export const createInnerCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [`M${x + width / 2},${-height / 2}`, `a${rx},${ry} 0,0,0 0,${height}`].join(' '); +}; + +export async function tiltedCylinder( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label, halfPadding } = await labelHelper( + parent, + node, + getNodeClasses(node) + ); + const labelPadding = node.look === 'neo' ? halfPadding * 2 : halfPadding; + const h = bbox.height + labelPadding; + const ry = h / 2; + const rx = ry / (2.5 + h / 50); + const w = bbox.width + rx + labelPadding; + const { cssStyles } = node; + + let cylinder: D3Selection | D3Selection; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const outerPathData = createOuterCylinderPathD(0, 0, w, h, rx, ry); + const innerPathData = createInnerCylinderPathD(0, 0, w, h, rx, ry); + const outerNode = rc.path(outerPathData, userNodeOverrides(node, {})); + const innerLine = rc.path(innerPathData, userNodeOverrides(node, { fill: 'none' })); + cylinder = shapeSvg.insert(() => innerLine, ':first-child'); + cylinder = shapeSvg.insert(() => outerNode, ':first-child'); + cylinder.attr('class', 'basic label-container'); + if (cssStyles) { + cylinder.attr('style', cssStyles); + } + } else { + const pathData = createCylinderPathD(0, 0, w, h, rx, ry); + cylinder = shapeSvg + .insert('path', ':first-child') + .attr('d', pathData) + .attr('class', 'basic label-container') + .attr('style', handleUndefinedAttr(cssStyles)) + .attr('style', nodeStyles); + cylinder.attr('class', 'basic label-container'); + + if (cssStyles) { + cylinder.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles) { + cylinder.selectAll('path').attr('style', nodeStyles); + } + } + + cylinder.attr('label-offset-x', rx); + cylinder.attr('transform', `translate(${-w / 2}, ${h / 2} )`); + + label.attr( + 'transform', + `translate(${-(bbox.width / 2) - rx - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, cylinder); + + node.intersect = function (point) { + const pos = intersect.rect(node, point); + const y = pos.y - (node.y ?? 0); + + if ( + ry != 0 && + (Math.abs(y) < (node.height ?? 0) / 2 || + (Math.abs(y) == (node.height ?? 0) / 2 && + Math.abs(pos.x - (node.x ?? 0)) > (node.width ?? 0) / 2 - rx)) + ) { + let x = rx * rx * (1 - (y * y) / (ry * ry)); + if (x != 0) { + x = Math.sqrt(Math.abs(x)); + } + x = rx - x; + if (point.x - (node.x ?? 0) > 0) { + x = -x; + } + + pos.x += x; + } + + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts new file mode 100644 index 0000000000..d0228d55bd --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts @@ -0,0 +1,73 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; +import type { D3Selection } from '../../../types.js'; + +// export const createTrapezoidPathD = ( +// x: number, +// y: number, +// width: number, +// height: number +// ): string => { +// return [ +// `M${x - (2 * height) / 6},${y}`, +// `L${x + width + (2 * height) / 6},${y}`, +// `L${x + width - height / 6},${y - height}`, +// `L${x + height / 6},${y - height}`, +// 'Z', +// ].join(' '); +// }; + +export async function trapezoid(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const points = [ + { x: (-3 * h) / 6, y: 0 }, + { x: w + (3 * h) / 6, y: 0 }, + { x: w, y: -h }, + { x: 0, y: -h }, + ]; + + let polygon: typeof shapeSvg | ReturnType; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const pathData = createPathFromPoints(points); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (nodeStyles) { + polygon.attr('style', nodeStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoidalPentagon.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoidalPentagon.ts new file mode 100644 index 0000000000..d4bfb03c04 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoidalPentagon.ts @@ -0,0 +1,61 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +export async function trapezoidalPentagon( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const minWidth = 60, + minHeight = 20; + const w = Math.max(minWidth, bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(minHeight, bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + + const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x: (-w / 2) * 0.8, y: -h / 2 }, + { x: (w / 2) * 0.8, y: -h / 2 }, + { x: w / 2, y: (-h / 2) * 0.6 }, + { x: w / 2, y: h / 2 }, + { x: -w / 2, y: h / 2 }, + { x: -w / 2, y: (-h / 2) * 0.6 }, + ]; + + const pathData = createPathFromPoints(points); + const shapeNode = rc.path(pathData, options); + + const polygon = shapeSvg.insert(() => shapeNode, ':first-child'); + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/triangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/triangle.ts new file mode 100644 index 0000000000..d4bd3c3478 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/triangle.ts @@ -0,0 +1,68 @@ +import { log } from '../../../logger.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { createPathFromPoints } from './util.js'; +import { evaluate } from '../../../diagrams/common/common.js'; +import { getConfig } from '../../../diagram-api/diagramAPI.js'; +import type { D3Selection } from '../../../types.js'; + +export async function triangle(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const useHtmlLabels = evaluate(getConfig().flowchart?.htmlLabels); + + const w = bbox.width + (node.padding ?? 0); + const h = w + bbox.height; + + const tw = w + bbox.height; + const points = [ + { x: 0, y: 0 }, + { x: tw, y: 0 }, + { x: tw / 2, y: -h }, + ]; + + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + const pathData = createPathFromPoints(points); + const roughNode = rc.path(pathData, options); + + const polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-h / 2}, ${h / 2})`); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', nodeStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, polygon); + + label.attr( + 'transform', + `translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))}, ${h / 2 - (bbox.height + (node.padding ?? 0) / (useHtmlLabels ? 2 : 1) - (bbox.y - (bbox.top ?? 0)))})` + ); + + node.intersect = function (point) { + log.info('Triangle intersect', node, points, point); + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts new file mode 100644 index 0000000000..52471ecc07 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts @@ -0,0 +1,282 @@ +import { createText } from '../../createText.js'; +import type { Node } from '../../types.js'; +import { getConfig } from '../../../diagram-api/diagramAPI.js'; +import { select } from 'd3'; +import defaultConfig from '../../../defaultConfig.js'; +import { evaluate, sanitizeText } from '../../../diagrams/common/common.js'; +import { decodeEntities, handleUndefinedAttr, parseFontSize } from '../../../utils.js'; +import type { D3Selection, Point } from '../../../types.js'; + +export const labelHelper = async ( + parent: D3Selection, + node: Node, + _classes?: string +) => { + let cssClasses; + const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.htmlLabels); + if (!_classes) { + cssClasses = 'node default'; + } else { + cssClasses = _classes; + } + + // Add outer g element + const shapeSvg = parent + .insert('g') + .attr('class', cssClasses) + .attr('id', node.domId || node.id); + + // Create the label and insert it after the rect + const labelEl = shapeSvg + .insert('g') + .attr('class', 'label') + .attr('style', handleUndefinedAttr(node.labelStyle)); + + // Replace label with default value if undefined + let label; + if (node.label === undefined) { + label = ''; + } else { + label = typeof node.label === 'string' ? node.label : node.label[0]; + } + + const text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), { + useHtmlLabels, + width: node.width || getConfig().flowchart?.wrappingWidth, + // @ts-expect-error -- This is currently not used. Should this be `classes` instead? + cssClasses: 'markdown-node-label', + style: node.labelStyle, + addSvgBackground: !!node.icon || !!node.img, + }); + // Get the size of the label + let bbox = text.getBBox(); + const halfPadding = (node?.padding ?? 0) / 2; + + if (useHtmlLabels) { + const div = text.children[0]; + const dv = select(text); + + // if there are images, need to wait for them to load before getting the bounding box + const images = div.getElementsByTagName('img'); + if (images) { + const noImgText = label.replace(/]*>/g, '').trim() === ''; + + await Promise.all( + [...images].map( + (img) => + new Promise((res) => { + /** + * + */ + function setupImage() { + img.style.display = 'flex'; + img.style.flexDirection = 'column'; + + if (noImgText) { + // default size if no text + const bodyFontSize = getConfig().fontSize + ? getConfig().fontSize + : window.getComputedStyle(document.body).fontSize; + const enlargingFactor = 5; + const [parsedBodyFontSize = defaultConfig.fontSize] = parseFontSize(bodyFontSize); + const width = parsedBodyFontSize * enlargingFactor + 'px'; + img.style.minWidth = width; + img.style.maxWidth = width; + } else { + img.style.width = '100%'; + } + res(img); + } + setTimeout(() => { + if (img.complete) { + setupImage(); + } + }); + img.addEventListener('error', setupImage); + img.addEventListener('load', setupImage); + }) + ) + ); + } + + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + + // Center the label + if (useHtmlLabels) { + labelEl.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')'); + } else { + labelEl.attr('transform', 'translate(' + 0 + ', ' + -bbox.height / 2 + ')'); + } + if (node.centerLabel) { + labelEl.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')'); + } + labelEl.insert('rect', ':first-child'); + return { shapeSvg, bbox, halfPadding, label: labelEl }; +}; +export const insertLabel = async ( + parent: D3Selection, + label: string, + options: { + labelStyle?: string | undefined; + icon?: boolean | undefined; + img?: string | undefined; + useHtmlLabels?: boolean | undefined; + padding: number; + width?: number | undefined; + centerLabel?: boolean | undefined; + addSvgBackground?: boolean | undefined; + } +) => { + const useHtmlLabels = options.useHtmlLabels || evaluate(getConfig()?.flowchart?.htmlLabels); + + // Create the label and insert it after the rect + const labelEl = parent + .insert('g') + .attr('class', 'label') + .attr('style', options.labelStyle || ''); + + const text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), { + useHtmlLabels, + width: options.width || getConfig()?.flowchart?.wrappingWidth, + style: options.labelStyle, + addSvgBackground: !!options.icon || !!options.img, + }); + // Get the size of the label + let bbox = text.getBBox(); + const halfPadding = options.padding / 2; + + if (evaluate(getConfig()?.flowchart?.htmlLabels)) { + const div = text.children[0]; + const dv = select(text); + + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + + // Center the label + if (useHtmlLabels) { + labelEl.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')'); + } else { + labelEl.attr('transform', 'translate(' + 0 + ', ' + -bbox.height / 2 + ')'); + } + if (options.centerLabel) { + labelEl.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')'); + } + labelEl.insert('rect', ':first-child'); + return { shapeSvg: parent, bbox, halfPadding, label: labelEl }; +}; +export const updateNodeBounds = ( + node: Node, + // D3Selection is for the roughjs case, D3Selection is for the non-roughjs case + element: D3Selection | D3Selection +) => { + const bbox = element.node()!.getBBox(); + node.width = bbox.width; + node.height = bbox.height; +}; + +/** + * @param parent - Parent element to append the polygon to + * @param w - Width of the polygon + * @param h - Height of the polygon + * @param points - Array of points to create the polygon + */ +export function insertPolygonShape( + parent: D3Selection, + w: number, + h: number, + points: Point[] +) { + return parent + .insert('polygon', ':first-child') + .attr( + 'points', + points + .map(function (d) { + return d.x + ',' + d.y; + }) + .join(' ') + ) + .attr('class', 'label-container') + .attr('transform', 'translate(' + -w / 2 + ',' + h / 2 + ')'); +} + +export const getNodeClasses = (node: Node, extra?: string) => + (node.look === 'handDrawn' ? 'rough-node' : 'node') + ' ' + node.cssClasses + ' ' + (extra || ''); + +export function createPathFromPoints(points: Point[]) { + const pointStrings = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`); + pointStrings.push('Z'); + return pointStrings.join(' '); +} + +export function generateFullSineWavePoints( + x1: number, + y1: number, + x2: number, + y2: number, + amplitude: number, + numCycles: number +) { + const points = []; + const steps = 50; // Number of segments to create a smooth curve + const deltaX = x2 - x1; + const deltaY = y2 - y1; + const cycleLength = deltaX / numCycles; + + // Calculate frequency and phase shift + const frequency = (2 * Math.PI) / cycleLength; + const midY = y1 + deltaY / 2; + + for (let i = 0; i <= steps; i++) { + const t = i / steps; + const x = x1 + t * deltaX; + const y = midY + amplitude * Math.sin(frequency * (x - x1)); + + points.push({ x, y }); + } + + return points; +} + +/** + * @param centerX - x-coordinate of center of circle + * @param centerY - y-coordinate of center of circle + * @param radius - radius of circle + * @param numPoints - total points required + * @param startAngle - angle where arc will start + * @param endAngle - angle where arc will end + */ +export function generateCirclePoints( + centerX: number, + centerY: number, + radius: number, + numPoints: number, + startAngle: number, + endAngle: number +) { + const points = []; + + // Convert angles to radians + const startAngleRad = (startAngle * Math.PI) / 180; + const endAngleRad = (endAngle * Math.PI) / 180; + + // Calculate the angle range in radians + const angleRange = endAngleRad - startAngleRad; + + // Calculate the angle step + const angleStep = angleRange / (numPoints - 1); + + for (let i = 0; i < numPoints; i++) { + const angle = startAngleRad + i * angleStep; + const x = centerX + radius * Math.cos(angle); + const y = centerY + radius * Math.sin(angle); + points.push({ x: -x, y: -y }); + } + + return points; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveEdgedRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveEdgedRectangle.ts new file mode 100644 index 0000000000..d2c1a525d4 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveEdgedRectangle.ts @@ -0,0 +1,83 @@ +import { + labelHelper, + updateNodeBounds, + getNodeClasses, + generateFullSineWavePoints, + createPathFromPoints, +} from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import rough from 'roughjs'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import type { D3Selection } from '../../../types.js'; + +export async function waveEdgedRectangle( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const waveAmplitude = h / 8; + const finalH = h + waveAmplitude; + const { cssStyles } = node; + + // To maintain minimum width + const minWidth = 70; + const widthDif = minWidth - w; + const extraW = widthDif > 0 ? widthDif / 2 : 0; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x: -w / 2 - extraW, y: finalH / 2 }, + ...generateFullSineWavePoints( + -w / 2 - extraW, + finalH / 2, + w / 2 + extraW, + finalH / 2, + waveAmplitude, + 0.8 + ), + { x: w / 2 + extraW, y: -finalH / 2 }, + { x: -w / 2 - extraW, y: -finalH / 2 }, + ]; + + const waveEdgeRectPath = createPathFromPoints(points); + const waveEdgeRectNode = rc.path(waveEdgeRectPath, options); + + const waveEdgeRect = shapeSvg.insert(() => waveEdgeRectNode, ':first-child'); + + waveEdgeRect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + waveEdgeRect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + waveEdgeRect.selectAll('path').attr('style', nodeStyles); + } + + waveEdgeRect.attr('transform', `translate(0,${-waveAmplitude / 2})`); + label.attr( + 'transform', + `translate(${-w / 2 + (node.padding ?? 0) - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) - waveAmplitude - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, waveEdgeRect); + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveRectangle.ts new file mode 100644 index 0000000000..4ee6dba475 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveRectangle.ts @@ -0,0 +1,84 @@ +import { + labelHelper, + updateNodeBounds, + getNodeClasses, + createPathFromPoints, + generateFullSineWavePoints, +} from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; + +export async function waveRectangle( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + + const minWidth = 100; // Minimum width + const minHeight = 50; // Minimum height + + const baseWidth = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const baseHeight = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + + const aspectRatio = baseWidth / baseHeight; + + let w = baseWidth; + let h = baseHeight; + + if (w > h * aspectRatio) { + h = w / aspectRatio; + } else { + w = h * aspectRatio; + } + + w = Math.max(w, minWidth); + h = Math.max(h, minHeight); + + const waveAmplitude = Math.min(h * 0.2, h / 4); + const finalH = h + waveAmplitude * 2; + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x: -w / 2, y: finalH / 2 }, + ...generateFullSineWavePoints(-w / 2, finalH / 2, w / 2, finalH / 2, waveAmplitude, 1), + { x: w / 2, y: -finalH / 2 }, + ...generateFullSineWavePoints(w / 2, -finalH / 2, -w / 2, -finalH / 2, waveAmplitude, -1), + ]; + + const waveRectPath = createPathFromPoints(points); + const waveRectNode = rc.path(waveRectPath, options); + + const waveRect = shapeSvg.insert(() => waveRectNode, ':first-child'); + + waveRect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + waveRect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + waveRect.selectAll('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, waveRect); + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts new file mode 100644 index 0000000000..e67c92a06f --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts @@ -0,0 +1,67 @@ +import { labelHelper, getNodeClasses, updateNodeBounds } from './util.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; +import type { D3Selection } from '../../../types.js'; + +export async function windowPane(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const rectOffset = 5; + const x = -w / 2; + const y = -h / 2; + const { cssStyles } = node; + + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + const outerPathPoints = [ + { x: x - rectOffset, y: y - rectOffset }, + { x: x - rectOffset, y: y + h }, + { x: x + w, y: y + h }, + { x: x + w, y: y - rectOffset }, + ]; + + const path = `M${x - rectOffset},${y - rectOffset} L${x + w},${y - rectOffset} L${x + w},${y + h} L${x - rectOffset},${y + h} L${x - rectOffset},${y - rectOffset} + M${x - rectOffset},${y} L${x + w},${y} + M${x},${y - rectOffset} L${x},${y + h}`; + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const no = rc.path(path, options); + + const windowPane = shapeSvg.insert(() => no, ':first-child'); + windowPane.attr('transform', `translate(${rectOffset / 2}, ${rectOffset / 2})`); + + windowPane.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + windowPane.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + windowPane.selectAll('path').attr('style', nodeStyles); + } + + label.attr( + 'transform', + `translate(${-(bbox.width / 2) + rectOffset / 2 - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) + rectOffset / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, windowPane); + + node.intersect = function (point) { + const pos = intersect.polygon(node, outerPathPoints, point); + return pos; + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/setupViewPortForSVG.ts b/packages/mermaid/src/rendering-util/setupViewPortForSVG.ts new file mode 100644 index 0000000000..d3df5ddb7a --- /dev/null +++ b/packages/mermaid/src/rendering-util/setupViewPortForSVG.ts @@ -0,0 +1,40 @@ +import { configureSvgSize } from '../setupGraphViewbox.js'; +import type { SVG } from '../diagram-api/types.js'; +import { log } from '../logger.js'; + +export const setupViewPortForSVG = ( + svg: SVG, + padding: number, + cssDiagram: string, + useMaxWidth: boolean +) => { + // Initialize the SVG element and set the diagram class + svg.attr('class', cssDiagram); + + // Calculate the dimensions and position with padding + const { width, height, x, y } = calculateDimensionsWithPadding(svg, padding); + + // Configure the size and aspect ratio of the SVG + configureSvgSize(svg, height, width, useMaxWidth); + + // Update the viewBox to ensure all content is visible with padding + const viewBox = createViewBox(x, y, width, height, padding); + svg.attr('viewBox', viewBox); + + // Log the viewBox configuration for debugging + log.debug(`viewBox configured: ${viewBox} with padding: ${padding}`); +}; + +const calculateDimensionsWithPadding = (svg: SVG, padding: number) => { + const bounds = svg.node()?.getBBox() || { width: 0, height: 0, x: 0, y: 0 }; + return { + width: bounds.width + padding * 2, + height: bounds.height + padding * 2, + x: bounds.x, + y: bounds.y, + }; +}; + +const createViewBox = (x: number, y: number, width: number, height: number, padding: number) => { + return `${x - padding} ${y - padding} ${width} ${height}`; +}; diff --git a/packages/mermaid/src/rendering-util/types.d.ts b/packages/mermaid/src/rendering-util/types.d.ts deleted file mode 100644 index d7a06a8fd9..0000000000 --- a/packages/mermaid/src/rendering-util/types.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type MarkdownWordType = 'normal' | 'strong' | 'em'; -export interface MarkdownWord { - content: string; - type: MarkdownWordType; -} -export type MarkdownLine = MarkdownWord[]; -/** Returns `true` if the line fits a constraint (e.g. it's under 𝑛 chars) */ -export type CheckFitFunction = (text: MarkdownLine) => boolean; diff --git a/packages/mermaid/src/rendering-util/types.ts b/packages/mermaid/src/rendering-util/types.ts new file mode 100644 index 0000000000..86cfd50b30 --- /dev/null +++ b/packages/mermaid/src/rendering-util/types.ts @@ -0,0 +1,179 @@ +export type MarkdownWordType = 'normal' | 'strong' | 'em'; +import type { MermaidConfig } from '../config.type.js'; +import type { ClusterShapeID } from './rendering-elements/clusters.js'; +import type { ShapeID } from './rendering-elements/shapes.js'; +export interface MarkdownWord { + content: string; + type: MarkdownWordType; +} +export type MarkdownLine = MarkdownWord[]; +/** Returns `true` if the line fits a constraint (e.g. it's under 𝑛 chars) */ +export type CheckFitFunction = (text: MarkdownLine) => boolean; + +interface BaseNode { + id: string; + label?: string; + description?: string[]; + parentId?: string; + position?: string; // Keep, this is for notes 'left of', 'right of', etc. Move into nodeNode + cssStyles?: string[]; // Renamed from `styles` to `cssStyles` + cssCompiledStyles?: string[]; + cssClasses?: string; // Renamed from `classes` to `cssClasses` + // style?: string; //REMOVE ✅ + // class?: string; //REMOVE ✅ + // labelText?: string; //REMOVE, use `label` instead ✅ + // props?: Record; //REMOVE ✅ + // type: string; // REMOVE, replace with isGroup: boolean, default false ✅ + // borders?: string; //REMOVE ✅ + labelStyle?: string; // REMOVE - use cssStyles instead ✅ + + // Flowchart specific properties + labelType?: string; // REMOVE? Always use markdown string, need to check for KaTeX - âŗ wait with this one + + domId?: string; // When you create the node in the getData function you do not have the domId yet + // Rendering specific properties for both Flowchart and State Diagram nodes + dir?: string; // Only relevant for isGroup true, i.e. a sub-graph or composite state. + haveCallback?: boolean; + link?: string; + linkTarget?: string; + tooltip?: string; + padding?: number; //REMOVE?, use from LayoutData.config - Keep, this could be shape specific + isGroup: boolean; + width?: number; + height?: number; + // Specific properties for State Diagram nodes TODO remove and use generic properties + intersect?: (point: any) => any; + + // Non-generic properties + rx?: number; // Used for rounded corners in Rect, Ellipse, etc.Maybe it to specialized RectNode, EllipseNode, etc. + ry?: number; + + useHtmlLabels?: boolean; + centerLabel?: boolean; //keep for now. + //Candidate for removal, maybe rely on labelStyle or a specific property labelPosition: Top, Center, Bottom + + //Node style properties + backgroundColor?: string; + borderColor?: string; + borderStyle?: string; + borderWidth?: number; + labelTextColor?: string; + + // Flowchart specific properties + x?: number; + y?: number; + + look?: string; + icon?: string; + pos?: 't' | 'b'; + img?: string; + assetWidth?: number; + assetHeight?: number; + defaultWidth?: number; + imageAspectRatio?: number; + constraint?: 'on' | 'off'; +} + +/** + * Group/cluster nodes, e.g. nodes that contain other nodes. + */ +export interface ClusterNode extends BaseNode { + shape?: ClusterShapeID; + isGroup: true; +} + +export interface NonClusterNode extends BaseNode { + shape?: ShapeID; + isGroup: false; +} + +// Common properties for any node in the system +export type Node = ClusterNode | NonClusterNode; + +// Common properties for any edge in the system +export interface Edge { + id: string; + label?: string; + classes?: string; + style?: string[]; + // Properties common to both Flowchart and State Diagram edges + arrowhead?: string; + arrowheadStyle?: string; + arrowTypeEnd?: string; + arrowTypeStart?: string; + // Flowchart specific properties + defaultInterpolate?: string; + end?: string; + interpolate?: string; + labelType?: string; + length?: number; + start?: string; + stroke?: string; + text?: string; + type: string; + // Class Diagram specific properties + startLabelRight?: string; + endLabelLeft?: string; + // Rendering specific properties + curve?: string; + labelpos?: string; + labelStyle?: string[]; + minlen?: number; + pattern?: string; + thickness?: 'normal' | 'thick' | 'invisible' | 'dotted'; + look?: string; +} + +export interface RectOptions { + rx: number; + ry: number; + labelPaddingX: number; + labelPaddingY: number; + classes: string; +} + +// Extending the Node interface for specific types if needed +export type ClassDiagramNode = Node & { + memberData: any; // Specific property for class diagram nodes +}; + +// Specific interfaces for layout and render data +export interface LayoutData { + nodes: Node[]; + edges: Edge[]; + config: MermaidConfig; + [key: string]: any; // Additional properties not yet defined +} + +export interface RenderData { + items: (Node | Edge)[]; + [key: string]: any; // Additional properties not yet defined +} + +// This refactored approach ensures that common properties are included in the base `Node` and `Edge` interfaces, with specific types extending these bases with additional properties as needed. This maintains flexibility while ensuring type safety and reducing redundancy. + +export type LayoutMethod = + | 'dagre' + | 'dagre-wrapper' + | 'elk' + | 'neato' + | 'dot' + | 'circo' + | 'fdp' + | 'osage' + | 'grid'; + +export interface ShapeRenderOptions { + config: MermaidConfig; + /** Some shapes render differently if a diagram has a direction `LR` */ + dir?: Node['dir']; +} + +export type KanbanNode = Node & { + // Kanban specif data + priority?: 'Very High' | 'High' | 'Medium' | 'Low' | 'Very Low'; + ticket?: string; + assigned?: string; + icon?: string; + level: number; +}; diff --git a/packages/mermaid/src/schemas/config.schema.yaml b/packages/mermaid/src/schemas/config.schema.yaml index d798ec63b2..e1014e889f 100644 --- a/packages/mermaid/src/schemas/config.schema.yaml +++ b/packages/mermaid/src/schemas/config.schema.yaml @@ -46,12 +46,15 @@ required: - quadrantChart - xyChart - requirement + - architecture - mindmap + - kanban - gitGraph - c4 - sankey - packet - block + - look properties: theme: description: | @@ -60,8 +63,9 @@ properties: type: string enum: - default - - forest + - base - dark + - forest - neutral - 'null' # should this be a `null`-type? meta:enum: @@ -71,6 +75,24 @@ properties: tsType: any themeCSS: type: string + look: + description: | + Defines which main look to use for the diagram. + type: string + enum: + - classic + - handDrawn + default: 'classic' + handDrawnSeed: + description: | + Defines the seed to be used when using handDrawn look. This is important for the automated tests as they will always find differences without the seed. The default value is 0 which gives a random seed. + type: number + default: 0 + layout: + description: | + Defines which layout algorithm to use for rendering the diagram. + type: string + default: 'dagre' maxTextSize: description: The maximum allowed size of the users text diagram type: number @@ -81,6 +103,35 @@ properties: type: integer default: 500 minimum: 0 + elk: + type: object + properties: + mergeEdges: + description: | + 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. + type: boolean + default: false + nodePlacementStrategy: + description: | + Elk specific option affecting how nodes are placed. + type: string + enum: + - SIMPLE + - NETWORK_SIMPLEX + - 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 @@ -179,7 +230,7 @@ properties: default: false forceLegacyMathML: description: | - This option forces Mermaid to rely on KaTeX's own stylesheet for rendering MathML. Due to differences between OS + This option forces Mermaid to rely on KaTeX's own stylesheet for rendering MathML. Due to differences between OS fonts and browser's MathML implementation, this option is recommended if consistent rendering is important. If set to true, ignores legacyMathML. type: boolean @@ -225,8 +276,12 @@ properties: $ref: '#/$defs/XYChartConfig' requirement: $ref: '#/$defs/RequirementDiagramConfig' + architecture: + $ref: '#/$defs/ArchitectureDiagramConfig' mindmap: $ref: '#/$defs/MindmapDiagramConfig' + kanban: + $ref: '#/$defs/KanbanDiagramConfig' gitGraph: $ref: '#/$defs/GitGraphDiagramConfig' c4: @@ -872,6 +927,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' }] @@ -890,6 +967,23 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file) type: number default: 200 + KanbanDiagramConfig: + title: Kanban Diagram Config + allOf: [{ $ref: '#/$defs/BaseDiagramConfig' }] + description: The object containing configurations specific for kanban diagrams + type: object + unevaluatedProperties: false + properties: + padding: + type: number + default: 8 + sectionWidth: + type: number + default: 200 + ticketBaseUrl: + type: string + default: '' + PieDiagramConfig: title: Pie Diagram Config allOf: [{ $ref: '#/$defs/BaseDiagramConfig' }] @@ -1255,6 +1349,16 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file) noteMargin: type: number default: 10 + nodeSpacing: + type: integer + minimum: 0 + # should the default value be 50? + # see https://github.com/mermaid-js/mermaid/blob/7647ae317a7b2130e32777248d25a9c4d24b8f9f/packages/mermaid/src/diagrams/class/classRenderer-v2.ts#L258 + rankSpacing: + type: integer + minimum: 0 + # should the default value be 50? + # see https://github.com/mermaid-js/mermaid/blob/7647ae317a7b2130e32777248d25a9c4d24b8f9f/packages/mermaid/src/diagrams/class/classRenderer-v2.ts#L259 forkWidth: type: number default: 70 @@ -1344,6 +1448,9 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file) htmlLabels: type: boolean default: false + hideEmptyMembersBox: + type: boolean + default: false JourneyDiagramConfig: title: Journey Diagram Config diff --git a/packages/mermaid/src/styles.ts b/packages/mermaid/src/styles.ts index 50d502bac5..78b514c40d 100644 --- a/packages/mermaid/src/styles.ts +++ b/packages/mermaid/src/styles.ts @@ -39,7 +39,7 @@ const getStyles = ( } & .edge-thickness-normal { - stroke-width: 2px; + stroke-width: 1px; } & .edge-thickness-thick { stroke-width: 3.5px @@ -47,7 +47,10 @@ const getStyles = ( & .edge-pattern-solid { stroke-dasharray: 0; } - + & .edge-thickness-invisible { + stroke-width: 0; + fill: none; + } & .edge-pattern-dashed{ stroke-dasharray: 3; } @@ -67,6 +70,9 @@ const getStyles = ( font-family: ${options.fontFamily}; font-size: ${options.fontSize}; } + & p { + margin: 0 + } ${diagramStyles} diff --git a/packages/mermaid/src/tests/MockedD3.ts b/packages/mermaid/src/tests/MockedD3.ts index 35871f14e6..d0d67773f8 100644 --- a/packages/mermaid/src/tests/MockedD3.ts +++ b/packages/mermaid/src/tests/MockedD3.ts @@ -104,7 +104,10 @@ export class MockedD3 { // This allows different tests to succeed -- some need a top level 'svg' and some need a 'svg' element to be the firstChild // Real implementation returns an HTML Element public node = vi.fn().mockImplementation(() => { + //create a top level svg element const topElem = this._containingHTMLdoc.createElement('svg'); + //@ts-ignore - this is a mock SVG element + topElem.getBBox = this.getBBox; const elem_svgChild = this._containingHTMLdoc.createElement('svg'); // another svg element topElem.appendChild(elem_svgChild); return topElem; diff --git a/packages/mermaid/src/themes/theme-base.js b/packages/mermaid/src/themes/theme-base.js index a92bd9e20d..6e572ea5f9 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 02104a0ea8..cf223d9892 100644 --- a/packages/mermaid/src/themes/theme-dark.js +++ b/packages/mermaid/src/themes/theme-dark.js @@ -6,7 +6,6 @@ class Theme { this.background = '#333'; this.primaryColor = '#1f2020'; this.secondaryColor = lighten(this.primaryColor, 16); - this.tertiaryColor = adjust(this.primaryColor, { h: -160 }); this.primaryBorderColor = invert(this.background); this.secondaryBorderColor = mkBorder(this.secondaryColor, this.darkMode); @@ -22,7 +21,7 @@ class Theme { this.mainContrastColor = 'lightgrey'; this.darkTextColor = lighten(invert('#323D47'), 10); this.lineColor = 'calculated'; - this.border1 = '#81B1DB'; + this.border1 = '#ccc'; this.border2 = rgba(255, 255, 255, 0.25); this.arrowheadColor = 'calculated'; this.fontFamily = '"trebuchet ms", verdana, arial, sans-serif'; @@ -85,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'; @@ -133,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; @@ -333,6 +343,8 @@ class Theme { this.attributeBackgroundColorEven = this.attributeBackgroundColorEven || lighten(this.background, 2); /* -------------------------------------------------- */ + + this.nodeBorder = this.nodeBorder || '#999'; } calculate(overrides) { if (typeof overrides !== 'object') { diff --git a/packages/mermaid/src/themes/theme-default.js b/packages/mermaid/src/themes/theme-default.js index d95ccf59e0..e0023758ea 100644 --- a/packages/mermaid/src/themes/theme-default.js +++ b/packages/mermaid/src/themes/theme-default.js @@ -34,7 +34,7 @@ class Theme { this.arrowheadColor = '#333333'; this.fontFamily = '"trebuchet ms", verdana, arial, sans-serif'; this.fontSize = '16px'; - this.labelBackground = '#e8e8e8'; + this.labelBackground = 'rgba(232,232,232, 0.8)'; this.textColor = '#333'; this.THEME_COLOR_LIMIT = 12; @@ -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 4bb7d24413..97c0682f3d 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 40963839e2..4a622cbccb 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/src/types.ts b/packages/mermaid/src/types.ts index 9f4d772253..5587ca3f4d 100644 --- a/packages/mermaid/src/types.ts +++ b/packages/mermaid/src/types.ts @@ -1,3 +1,19 @@ +export interface NodeMetaData { + shape?: string; + label?: string; + icon?: string; + form?: string; + pos?: 't' | 'b'; + img?: string; + w?: string; + h?: string; + constraint?: 'on' | 'off'; + priority: 'Very High' | 'High' | 'Medium' | 'Low' | 'Very Low'; + assigned?: string; + ticket?: string; +} +import type { MermaidConfig } from './config.type.js'; + export interface Point { x: number; y: number; @@ -48,10 +64,19 @@ export interface ParseResult { * The diagram type, e.g. 'flowchart', 'sequence', etc. */ diagramType: string; + /** + * The config passed as YAML frontmatter or directives + */ + config: MermaidConfig; } // This makes it clear that we're working with a d3 selected element of some kind, even though it's hard to specify the exact type. export type D3Element = any; +/** + * Helper type for d3 selections. + */ +export type D3Selection = d3.Selection; + export interface RenderResult { /** * The svg code for the rendered graph. @@ -72,3 +97,10 @@ export interface RenderResult { */ bindFunctions?: (element: Element) => void; } + +/** + * Can be converted back to `T` by awaiting/`Awaited`. + * + * This is useful for function types that may be either synchronous or asynchronous. + */ +export type MaybePromise = T | Promise; diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts index 631b6dd851..c1d6748344 100644 --- a/packages/mermaid/src/utils.ts +++ b/packages/mermaid/src/utils.ts @@ -1,5 +1,5 @@ import { sanitizeUrl } from '@braintree/sanitize-url'; -import type { CurveFactory } from 'd3'; +import type { BaseType, CurveFactory } from 'd3'; import { curveBasis, curveBasisClosed, @@ -824,6 +824,7 @@ export const insertTitle = ( parent .append('text') .text(title) + .attr('text-anchor', 'middle') .attr('x', bounds.x + bounds.width / 2) .attr('y', -titleTopMargin) .attr('class', cssClass); @@ -940,3 +941,15 @@ export const getEdgeId = ( ) => { return `${prefix ? `${prefix}_` : ''}${from}_${to}_${counter}${suffix ? `_${suffix}` : ''}`; }; + +/** + * D3's `selection.attr` method doesn't officially support `undefined`. + * + * However, it seems if you do pass `undefined`, it seems to be treated as `null` + * (e.g. it removes the attribute). + */ +export function handleUndefinedAttr( + attrValue: Parameters['attr']>[1] | undefined +) { + return attrValue ?? null; +} diff --git a/packages/mermaid/src/utils/lineWithOffset.ts b/packages/mermaid/src/utils/lineWithOffset.ts index 114dda2bd3..800a5ffaf7 100644 --- a/packages/mermaid/src/utils/lineWithOffset.ts +++ b/packages/mermaid/src/utils/lineWithOffset.ts @@ -9,7 +9,7 @@ const markerOffsets = { composition: 18, dependency: 6, lollipop: 13.5, - arrow_point: 5.3, + arrow_point: 4, } as const; /** @@ -52,18 +52,15 @@ export const getLineFunctionsWithOffset = ( data: (Point | [number, number])[] ) { let offset = 0; + const DIRECTION = + pointTransformer(data[0]).x < pointTransformer(data[data.length - 1]).x ? 'left' : 'right'; if (i === 0 && Object.hasOwn(markerOffsets, edge.arrowTypeStart)) { - // Handle first point - // Calculate the angle and delta between the first two points const { angle, deltaX } = calculateDeltaAndAngle(data[0], data[1]); - // Calculate the offset based on the angle and the marker's dimensions offset = markerOffsets[edge.arrowTypeStart as keyof typeof markerOffsets] * Math.cos(angle) * (deltaX >= 0 ? 1 : -1); } else if (i === data.length - 1 && Object.hasOwn(markerOffsets, edge.arrowTypeEnd)) { - // Handle last point - // Calculate the angle and delta between the last two points const { angle, deltaX } = calculateDeltaAndAngle( data[data.length - 1], data[data.length - 2] @@ -73,6 +70,41 @@ export const getLineFunctionsWithOffset = ( Math.cos(angle) * (deltaX >= 0 ? 1 : -1); } + + const differenceToEnd = Math.abs( + pointTransformer(d).x - pointTransformer(data[data.length - 1]).x + ); + const differenceInYEnd = Math.abs( + pointTransformer(d).y - pointTransformer(data[data.length - 1]).y + ); + const differenceToStart = Math.abs(pointTransformer(d).x - pointTransformer(data[0]).x); + const differenceInYStart = Math.abs(pointTransformer(d).y - pointTransformer(data[0]).y); + const startMarkerHeight = markerOffsets[edge.arrowTypeStart as keyof typeof markerOffsets]; + const endMarkerHeight = markerOffsets[edge.arrowTypeEnd as keyof typeof markerOffsets]; + const extraRoom = 1; + + // Adjust the offset if the difference is smaller than the marker height + if ( + differenceToEnd < endMarkerHeight && + differenceToEnd > 0 && + differenceInYEnd < endMarkerHeight + ) { + let adjustment = endMarkerHeight + extraRoom - differenceToEnd; + adjustment *= DIRECTION === 'right' ? -1 : 1; + // Adjust the offset by the amount needed to fit the marker + offset -= adjustment; + } + + if ( + differenceToStart < startMarkerHeight && + differenceToStart > 0 && + differenceInYStart < startMarkerHeight + ) { + let adjustment = startMarkerHeight + extraRoom - differenceToStart; + adjustment *= DIRECTION === 'right' ? -1 : 1; + offset += adjustment; + } + return pointTransformer(d).x + offset; }, y: function ( @@ -81,8 +113,9 @@ export const getLineFunctionsWithOffset = ( i: number, data: (Point | [number, number])[] ) { - // Same handling as X above let offset = 0; + const DIRECTION = + pointTransformer(data[0]).y < pointTransformer(data[data.length - 1]).y ? 'down' : 'up'; if (i === 0 && Object.hasOwn(markerOffsets, edge.arrowTypeStart)) { const { angle, deltaY } = calculateDeltaAndAngle(data[0], data[1]); offset = @@ -99,6 +132,40 @@ export const getLineFunctionsWithOffset = ( Math.abs(Math.sin(angle)) * (deltaY >= 0 ? 1 : -1); } + + const differenceToEnd = Math.abs( + pointTransformer(d).y - pointTransformer(data[data.length - 1]).y + ); + const differenceInXEnd = Math.abs( + pointTransformer(d).x - pointTransformer(data[data.length - 1]).x + ); + const differenceToStart = Math.abs(pointTransformer(d).y - pointTransformer(data[0]).y); + const differenceInXStart = Math.abs(pointTransformer(d).x - pointTransformer(data[0]).x); + const startMarkerHeight = markerOffsets[edge.arrowTypeStart as keyof typeof markerOffsets]; + const endMarkerHeight = markerOffsets[edge.arrowTypeEnd as keyof typeof markerOffsets]; + const extraRoom = 1; + + // Adjust the offset if the difference is smaller than the marker height + if ( + differenceToEnd < endMarkerHeight && + differenceToEnd > 0 && + differenceInXEnd < endMarkerHeight + ) { + let adjustment = endMarkerHeight + extraRoom - differenceToEnd; + adjustment *= DIRECTION === 'up' ? -1 : 1; + // Adjust the offset by the amount needed to fit the marker + offset -= adjustment; + } + + if ( + differenceToStart < startMarkerHeight && + differenceToStart > 0 && + differenceInXStart < startMarkerHeight + ) { + let adjustment = startMarkerHeight + extraRoom - differenceToStart; + adjustment *= DIRECTION === 'up' ? -1 : 1; + offset += adjustment; + } return pointTransformer(d).y + offset; }, }; diff --git a/packages/mermaid/tsconfig.json b/packages/mermaid/tsconfig.json index 78e3cf2de5..447a5bb0da 100644 --- a/packages/mermaid/tsconfig.json +++ b/packages/mermaid/tsconfig.json @@ -5,5 +5,11 @@ "outDir": "./dist", "types": ["vitest/importMeta", "vitest/globals"] }, - "include": ["./src/**/*.ts", "./package.json"] + "include": [ + "./src/**/*.ts", + "./package.json", + "src/diagrams/gantt/ganttDb.js", + "src/diagrams/git/gitGraphRenderer.js", + "src/diagrams/class/classRenderer.js" + ] } diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md new file mode 100644 index 0000000000..4cf29b73ab --- /dev/null +++ b/packages/parser/CHANGELOG.md @@ -0,0 +1,31 @@ +# @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 + +- [#5746](https://github.com/mermaid-js/mermaid/pull/5746) [`83926c9`](https://github.com/mermaid-js/mermaid/commit/83926c9707b09c34e300888186250191ee8ae30a) Thanks [@sidharthv96](https://github.com/sidharthv96)! - test changeset + +## 0.1.0 + +### Minor Changes + +- [#5744](https://github.com/mermaid-js/mermaid/pull/5744) [`5013484`](https://github.com/mermaid-js/mermaid/commit/50134849246141ec400e33e08c12c10539b84de9) Thanks [@sidharthv96](https://github.com/sidharthv96)! - Release parser, test changesets diff --git a/packages/parser/langium-config.json b/packages/parser/langium-config.json index c750f049d5..bf64493ad8 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 b937e37b9f..a26a2a71e5 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@mermaid-js/parser", - "version": "0.1.0-rc.1", + "version": "0.3.0", "description": "MermaidJS parser", "author": "Yokozuna59", "contributors": [ @@ -19,8 +19,7 @@ "scripts": { "clean": "rimraf dist src/language/generated", "langium:generate": "langium generate", - "langium:watch": "langium generate --watch", - "prepublishOnly": "pnpm -w run build" + "langium:watch": "langium generate --watch" }, "repository": { "type": "git", diff --git a/packages/parser/src/language/architecture/architecture.langium b/packages/parser/src/language/architecture/architecture.langium new file mode 100644 index 0000000000..11af262432 --- /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 0000000000..fd3c604b08 --- /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 0000000000..d4e730c2c2 --- /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 0000000000..6a7c6a37a4 --- /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 0000000000..c9475657bb --- /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 0000000000..1571ebba8a --- /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 0000000000..fd3c604b08 --- /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 0000000000..e2d45c8fa6 --- /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 0000000000..ccadf1a1f6 --- /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 9f1d92ba8a..c85a5a8b60 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 6e312e1721..cc412d7a1b 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 992b965060..86713c2f1b 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 0000000000..2d7c21bbe8 --- /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 9bdec348a1..5cb487758b 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/packages/parser/tsconfig.json b/packages/parser/tsconfig.json index 7e830e229e..4822488c9b 100644 --- a/packages/parser/tsconfig.json +++ b/packages/parser/tsconfig.json @@ -4,7 +4,6 @@ "rootDir": ".", "outDir": "./dist", "allowJs": false, - "preserveSymlinks": false, "strictPropertyInitialization": false }, "include": ["./src/**/*.ts", "./tests/**/*.ts"], diff --git a/patches/roughjs.patch b/patches/roughjs.patch new file mode 100644 index 0000000000..7a17084fe7 --- /dev/null +++ b/patches/roughjs.patch @@ -0,0 +1,10 @@ +diff --git a/bin/rough.d.ts b/bin/rough.d.ts +index 810db1fb34496a5b9f7c0734a7ad8be8605f5534..3624d5b6288a3d2b7d397dfcfffce60558f18cf9 100644 +--- a/bin/rough.d.ts ++++ b/bin/rough.d.ts +@@ -8,4 +8,4 @@ declare const _default: { + generator(config?: Config): RoughGenerator; + newSeed(): number; + }; +-export default _default; ++export = _default; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 86480266f4..7f3f4fa5f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,28 +4,39 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + roughjs: + hash: vxb6t6fqvzyhwhtjiliqr25jyq + path: patches/roughjs.patch + importers: .: devDependencies: '@applitools/eyes-cypress': - specifier: ^3.42.3 - version: 3.43.1(encoding@0.1.13)(typescript@5.4.5) + specifier: ^3.44.4 + version: 3.44.9(encoding@0.1.13)(typescript@5.4.5) '@argos-ci/cypress': - specifier: ^2.1.0 - version: 2.1.0(cypress@13.7.3) + specifier: ^2.2.2 + version: 2.2.2(cypress@13.15.0) + '@changesets/changelog-github': + specifier: ^0.5.0 + version: 0.5.0(encoding@0.1.13) + '@changesets/cli': + specifier: ^2.27.7 + version: 2.27.9 '@cspell/eslint-plugin': specifier: ^8.8.4 - version: 8.9.1(eslint@9.6.0) + version: 8.14.4(eslint@9.12.0(jiti@1.21.6)) '@cypress/code-coverage': specifier: ^3.12.30 - version: 3.12.41(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(babel-loader@9.1.3(@babel/core@7.24.7)(webpack@5.91.0(esbuild@0.21.5)))(cypress@13.7.3)(webpack@5.91.0(esbuild@0.21.5)) + version: 3.13.4(@babel/core@7.25.7)(@babel/preset-env@7.25.7(@babel/core@7.25.7))(babel-loader@9.2.1(@babel/core@7.25.7)(webpack@5.95.0(esbuild@0.21.5)))(cypress@13.15.0)(webpack@5.95.0(esbuild@0.21.5)) '@eslint/js': specifier: ^9.4.0 - version: 9.6.0 + version: 9.12.0 '@rollup/plugin-typescript': specifier: ^11.1.6 - version: 11.1.6(rollup@4.18.0)(tslib@2.6.3)(typescript@5.4.5) + version: 11.1.6(rollup@4.24.0)(tslib@2.7.0)(typescript@5.4.5) '@types/cors': specifier: ^2.8.17 version: 2.8.17 @@ -40,28 +51,28 @@ importers: version: 21.1.7 '@types/lodash': specifier: ^4.17.0 - version: 4.17.6 + version: 4.17.10 '@types/mdast': specifier: ^4.0.3 version: 4.0.4 '@types/node': specifier: ^20.11.30 - version: 20.12.14 + version: 20.16.11 '@types/rollup-plugin-visualizer': specifier: ^4.2.4 version: 4.2.4 '@vitest/coverage-v8': specifier: ^1.4.0 - version: 1.5.3(vitest@1.5.3(@types/node@20.12.14)(@vitest/ui@1.5.3)(jsdom@24.0.0)(terser@5.31.3)) + version: 1.6.0(vitest@1.6.0(@types/node@20.16.11)(@vitest/ui@1.6.0)(jsdom@24.1.3)(terser@5.34.1)) '@vitest/spy': specifier: ^1.4.0 - version: 1.5.3 + version: 1.6.0 '@vitest/ui': specifier: ^1.4.0 - version: 1.5.3(vitest@1.5.3) + version: 1.6.0(vitest@1.6.0) ajv: specifier: ^8.12.0 - version: 8.12.0 + version: 8.17.1 chokidar: specifier: ^3.6.0 version: 3.6.0 @@ -71,72 +82,78 @@ importers: cors: specifier: ^2.8.5 version: 2.8.5 + cpy-cli: + specifier: ^5.0.0 + version: 5.0.0 cross-env: specifier: ^7.0.3 version: 7.0.3 cspell: specifier: ^8.6.0 - version: 8.7.0 + version: 8.14.4 cypress: - specifier: ^13.7.1 - version: 13.7.3 + specifier: ^13.14.1 + version: 13.15.0 cypress-image-snapshot: specifier: ^4.0.1 - version: 4.0.1(cypress@13.7.3)(jest@29.7.0(@types/node@20.12.14)) + version: 4.0.1(cypress@13.15.0)(jest@29.7.0(@types/node@20.16.11)) + cypress-split: + specifier: ^1.24.0 + version: 1.24.0(@babel/core@7.25.7) esbuild: specifier: ^0.21.5 version: 0.21.5 eslint: specifier: ^9.4.0 - version: 9.6.0 + version: 9.12.0(jiti@1.21.6) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@9.6.0) + version: 9.1.0(eslint@9.12.0(jiti@1.21.6)) eslint-plugin-cypress: specifier: ^3.3.0 - version: 3.3.0(eslint@9.6.0) + version: 3.5.0(eslint@9.12.0(jiti@1.21.6)) eslint-plugin-html: specifier: ^8.1.1 - version: 8.1.1 + version: 8.1.2 eslint-plugin-jest: specifier: ^28.6.0 - version: 28.6.0(@typescript-eslint/eslint-plugin@7.6.0(@typescript-eslint/parser@8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5))(eslint@9.6.0)(typescript@5.4.5))(eslint@9.6.0)(jest@29.7.0(@types/node@20.12.14))(typescript@5.4.5) + version: 28.8.3(@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.12.0(jiti@1.21.6))(jest@29.7.0(@types/node@20.16.11))(typescript@5.4.5) eslint-plugin-jsdoc: - specifier: ^48.2.9 - version: 48.5.2(eslint@9.6.0) + specifier: ^50.0.0 + version: 50.3.1(eslint@9.12.0(jiti@1.21.6)) 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.6.0) + version: 8.0.0(eslint@9.12.0(jiti@1.21.6)) eslint-plugin-markdown: specifier: ^5.0.0 - version: 5.0.0(eslint@9.6.0) + version: 5.1.0(eslint@9.12.0(jiti@1.21.6)) eslint-plugin-no-only-tests: specifier: ^3.1.0 - version: 3.1.0 + version: 3.3.0 eslint-plugin-tsdoc: specifier: ^0.3.0 version: 0.3.0 eslint-plugin-unicorn: - specifier: ^54.0.0 - version: 54.0.0(eslint@9.6.0) + specifier: ^56.0.0 + version: 56.0.0(eslint@9.12.0(jiti@1.21.6)) express: specifier: ^4.19.1 - version: 4.19.2 + version: 4.21.0 globals: specifier: ^15.4.0 - version: 15.6.0 + version: 15.10.0 globby: specifier: ^14.0.1 version: 14.0.2 husky: specifier: ^9.0.11 - version: 9.0.11 + version: 9.1.6 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.12.14) + version: 29.7.0(@types/node@20.16.11) jison: specifier: ^0.4.18 version: 0.4.18 @@ -145,13 +162,13 @@ importers: version: 4.1.0 jsdom: specifier: ^24.0.0 - version: 24.0.0 + version: 24.1.3 langium-cli: specifier: 3.0.3 version: 3.0.3 lint-staged: specifier: ^15.2.2 - version: 15.2.7 + version: 15.2.10 markdown-table: specifier: ^3.0.3 version: 3.0.3 @@ -161,57 +178,63 @@ importers: path-browserify: specifier: ^1.0.1 version: 1.0.1 - pnpm: - specifier: ^8.15.5 - version: 8.15.8 prettier: specifier: ^3.2.5 - version: 3.2.5 + version: 3.3.3 prettier-plugin-jsdoc: specifier: ^1.3.0 - version: 1.3.0(prettier@3.2.5) + version: 1.3.0(prettier@3.3.3) rimraf: specifier: ^5.0.5 - version: 5.0.8 + version: 5.0.10 rollup-plugin-visualizer: specifier: ^5.12.0 - version: 5.12.0(rollup@4.18.0) + version: 5.12.0(rollup@4.24.0) start-server-and-test: specifier: ^2.0.3 - version: 2.0.4 + version: 2.0.8 tsx: specifier: ^4.7.1 - version: 4.7.3 + version: 4.19.1 typescript: specifier: ~5.4.5 version: 5.4.5 typescript-eslint: specifier: ^8.0.0-alpha.34 - version: 8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5) + version: 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5) vite: specifier: ^5.2.3 - version: 5.2.13(@types/node@20.12.14)(terser@5.31.3) + version: 5.4.8(@types/node@20.16.11)(terser@5.34.1) vite-plugin-istanbul: specifier: ^6.0.0 - version: 6.0.2(vite@5.2.13(@types/node@20.12.14)(terser@5.31.3)) + version: 6.0.2(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1)) vitest: specifier: ^1.4.0 - version: 1.5.3(@types/node@20.12.14)(@vitest/ui@1.5.3)(jsdom@24.0.0)(terser@5.31.3) + version: 1.6.0(@types/node@20.16.11)(@vitest/ui@1.6.0)(jsdom@24.1.3)(terser@5.34.1) packages/mermaid: dependencies: '@braintree/sanitize-url': specifier: ^7.0.1 - version: 7.0.4 + version: 7.1.0 + '@iconify/utils': + specifier: ^2.1.32 + version: 2.1.33 '@mermaid-js/parser': specifier: workspace:^ version: link:../parser + '@types/d3': + specifier: ^7.4.3 + version: 7.4.3 cytoscape: specifier: ^3.29.2 - version: 3.29.3 + version: 3.30.2 cytoscape-cose-bilkent: specifier: ^4.1.0 - version: 4.1.0(cytoscape@3.29.3) + version: 4.1.0(cytoscape@3.30.2) + cytoscape-fcose: + specifier: ^2.2.0 + version: 2.2.0(cytoscape@3.30.2) d3: specifier: ^7.9.0 version: 7.9.0 @@ -219,14 +242,14 @@ importers: specifier: ^0.12.3 version: 0.12.3 dagre-d3-es: - specifier: 7.0.10 - version: 7.0.10 + specifier: 7.0.11 + version: 7.0.11 dayjs: specifier: ^1.11.10 - version: 1.11.11 + version: 1.11.13 dompurify: - specifier: ^3.0.11 - version: 3.1.6 + specifier: ^3.2.1 + version: 3.2.1 katex: specifier: ^0.16.9 version: 0.16.11 @@ -238,10 +261,13 @@ importers: version: 4.17.21 marked: specifier: ^13.0.2 - version: 13.0.2 + version: 13.0.3 + roughjs: + specifier: ^4.6.6 + version: 4.6.6(patch_hash=vxb6t6fqvzyhwhtjiliqr25jyq) stylis: specifier: ^4.3.1 - version: 4.3.2 + version: 4.3.4 ts-dedent: specifier: ^2.2.0 version: 2.2.0 @@ -252,12 +278,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.4 - '@types/d3': - specifier: ^7.4.3 - version: 7.4.3 + version: 3.21.8 + '@types/cytoscape-fcose': + specifier: ^2.2.4 + version: 2.2.4 '@types/d3-sankey': specifier: ^0.12.4 version: 0.12.4 @@ -269,13 +298,10 @@ importers: version: 3.0.3 '@types/d3-selection': specifier: ^3.0.10 - version: 3.0.10 + version: 3.0.11 '@types/d3-shape': specifier: ^3.1.6 version: 3.1.6 - '@types/dompurify': - specifier: ^3.0.5 - version: 3.0.5 '@types/jsdom': specifier: ^21.1.6 version: 21.1.7 @@ -299,16 +325,13 @@ importers: version: 9.0.8 ajv: specifier: ^8.12.0 - version: 8.12.0 + version: 8.17.1 chokidar: specifier: ^3.6.0 version: 3.6.0 concurrently: specifier: ^8.2.2 version: 8.2.2 - cpy-cli: - specifier: ^5.0.0 - version: 5.0.0 csstree-validator: specifier: ^3.0.0 version: 3.0.0 @@ -323,19 +346,19 @@ importers: version: 3.7.7 jsdom: specifier: ^24.0.0 - version: 24.0.0 + 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 prettier: specifier: ^3.2.5 - version: 3.2.5 + version: 3.3.3 remark: specifier: ^15.0.1 version: 15.0.1 @@ -347,13 +370,13 @@ importers: version: 4.0.0 rimraf: specifier: ^5.0.5 - version: 5.0.8 + version: 5.0.10 start-server-and-test: specifier: ^2.0.3 - version: 2.0.4 + version: 2.0.8 type-fest: specifier: ^4.13.1 - version: 4.15.0 + version: 4.26.1 typedoc: specifier: ^0.25.12 version: 0.25.13(typescript@5.4.5) @@ -371,16 +394,16 @@ importers: version: 5.0.0 vitepress: specifier: ^1.0.1 - version: 1.1.4(@algolia/client-search@4.23.3)(@types/node@20.14.7)(axios@1.7.2)(postcss@8.4.39)(search-insights@2.13.0)(terser@5.31.1)(typescript@5.4.5) + version: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.16.11)(axios@1.7.7)(postcss@8.4.47)(search-insights@2.17.2)(terser@5.34.1)(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.23.3)(@types/node@20.14.7)(axios@1.7.2)(postcss@8.4.39)(search-insights@2.13.0)(terser@5.31.1)(typescript@5.4.5))(vue@3.4.30(typescript@5.4.5)) + version: 1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.1.4(@algolia/client-search@4.24.0)(@types/node@20.16.11)(axios@1.7.7)(postcss@8.4.47)(search-insights@2.17.2)(terser@5.34.1)(typescript@5.4.5))(vue@3.5.11(typescript@5.4.5)) packages/mermaid-example-diagram: dependencies: '@braintree/sanitize-url': specifier: ^7.0.0 - version: 7.0.4 + version: 7.1.0 d3: specifier: ^7.9.0 version: 7.9.0 @@ -396,38 +419,29 @@ importers: version: link:../mermaid rimraf: specifier: ^5.0.5 - version: 5.0.8 + version: 5.0.10 - packages/mermaid-flowchart-elk: + packages/mermaid-layout-elk: dependencies: d3: specifier: ^7.9.0 version: 7.9.0 - dagre-d3-es: - specifier: 7.0.10 - version: 7.0.10 elkjs: - specifier: ^0.9.2 + specifier: ^0.9.3 version: 0.9.3 - khroma: - specifier: ^2.1.0 - version: 2.1.0 devDependencies: - concurrently: - specifier: ^8.2.2 - version: 8.2.2 + '@types/d3': + specifier: ^7.4.3 + version: 7.4.3 mermaid: specifier: workspace:^ version: link:../mermaid - rimraf: - specifier: ^5.0.5 - version: 5.0.8 packages/mermaid-zenuml: dependencies: '@zenuml/core': specifier: ^3.23.27 - version: 3.23.28(typescript@5.4.5) + version: 3.24.12(typescript@5.6.2) devDependencies: mermaid: specifier: workspace:^ @@ -440,7 +454,7 @@ importers: version: 7.4.47 '@vueuse/core': specifier: ^10.9.0 - version: 10.9.0(vue@3.4.31(typescript@5.4.5)) + version: 10.11.1(vue@3.5.11(typescript@5.6.2)) font-awesome: specifier: ^4.7.0 version: 4.7.0 @@ -452,20 +466,20 @@ importers: version: link:../.. vue: specifier: ^3.4.21 - version: 3.4.31(typescript@5.4.5) + version: 3.5.11(typescript@5.6.2) devDependencies: '@iconify-json/carbon': specifier: ^1.1.31 - version: 1.1.36 + version: 1.2.1 '@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.2.13(@types/node@20.14.7)(terser@5.31.3))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.0.0)) + version: 0.4.0(vite-plugin-pwa@0.19.8(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1))(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.0.5(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3))(vue@3.4.31(typescript@5.4.5)) + version: 5.1.4(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1))(vue@3.5.11(typescript@5.6.2)) fast-glob: specifier: ^3.3.2 version: 3.3.2 @@ -477,22 +491,22 @@ importers: version: 1.1.2 unocss: specifier: ^0.59.0 - version: 0.59.4(postcss@8.4.39)(rollup@2.79.1)(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3)) + version: 0.59.4(postcss@8.4.47)(rollup@2.79.2)(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1)) unplugin-vue-components: specifier: ^0.26.0 - version: 0.26.0(@babel/parser@7.24.8)(rollup@2.79.1)(vue@3.4.31(typescript@5.4.5)) + version: 0.26.0(@babel/parser@7.25.7)(rollup@2.79.2)(vue@3.5.11(typescript@5.6.2))(webpack-sources@3.2.3) vite: specifier: ^5.0.0 - version: 5.2.13(@types/node@20.14.7)(terser@5.31.3) + version: 5.4.8(@types/node@20.16.11)(terser@5.34.1) vite-plugin-pwa: specifier: ^0.19.7 - version: 0.19.8(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.0.0) + version: 0.19.8(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1))(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.23.3)(@types/node@20.14.7)(axios@1.7.2)(postcss@8.4.39)(search-insights@2.13.0)(terser@5.31.3)(typescript@5.4.5) + version: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.16.11)(axios@1.7.7)(postcss@8.4.47)(search-insights@2.17.2)(terser@5.34.1)(typescript@5.6.2) workbox-window: specifier: ^7.0.0 - version: 7.0.0 + version: 7.1.0 packages/parser: dependencies: @@ -515,19 +529,27 @@ importers: devDependencies: webpack: specifier: ^5.91.0 - version: 5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0) + version: 5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0) webpack-cli: specifier: ^4.10.0 - version: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0) + version: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0) webpack-dev-server: specifier: ^4.15.2 - version: 4.15.2(webpack-cli@4.10.0)(webpack@5.91.0) + version: 4.15.2(webpack-cli@4.10.0)(webpack@5.95.0) packages: - '@aashutoshrathi/word-wrap@1.2.6': - resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} - engines: {node: '>=0.10.0'} + '@actions/core@1.11.1': + resolution: {integrity: sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==} + + '@actions/exec@1.1.1': + resolution: {integrity: sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==} + + '@actions/http-client@2.2.3': + resolution: {integrity: sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==} + + '@actions/io@1.1.3': + resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==} '@adobe/jsonschema2md@8.0.2': resolution: {integrity: sha512-g90Rtz1Xghp9HTfbtBH2Pf5IpuUY1Ry9Wps5NNuNmxucbtmnCY4/1KXmtwe0yKxnknPF7nt5/Y8TOekMh450tQ==} @@ -554,50 +576,50 @@ packages: '@algolia/client-search': '>= 4.9.1 < 6' algoliasearch: '>= 4.9.1 < 6' - '@algolia/cache-browser-local-storage@4.23.3': - resolution: {integrity: sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==} + '@algolia/cache-browser-local-storage@4.24.0': + resolution: {integrity: sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==} - '@algolia/cache-common@4.23.3': - resolution: {integrity: sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==} + '@algolia/cache-common@4.24.0': + resolution: {integrity: sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g==} - '@algolia/cache-in-memory@4.23.3': - resolution: {integrity: sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==} + '@algolia/cache-in-memory@4.24.0': + resolution: {integrity: sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w==} - '@algolia/client-account@4.23.3': - resolution: {integrity: sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==} + '@algolia/client-account@4.24.0': + resolution: {integrity: sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA==} - '@algolia/client-analytics@4.23.3': - resolution: {integrity: sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==} + '@algolia/client-analytics@4.24.0': + resolution: {integrity: sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg==} - '@algolia/client-common@4.23.3': - resolution: {integrity: sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==} + '@algolia/client-common@4.24.0': + resolution: {integrity: sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==} - '@algolia/client-personalization@4.23.3': - resolution: {integrity: sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==} + '@algolia/client-personalization@4.24.0': + resolution: {integrity: sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w==} - '@algolia/client-search@4.23.3': - resolution: {integrity: sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==} + '@algolia/client-search@4.24.0': + resolution: {integrity: sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==} - '@algolia/logger-common@4.23.3': - resolution: {integrity: sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==} + '@algolia/logger-common@4.24.0': + resolution: {integrity: sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==} - '@algolia/logger-console@4.23.3': - resolution: {integrity: sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==} + '@algolia/logger-console@4.24.0': + resolution: {integrity: sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg==} - '@algolia/recommend@4.23.3': - resolution: {integrity: sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==} + '@algolia/recommend@4.24.0': + resolution: {integrity: sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw==} - '@algolia/requester-browser-xhr@4.23.3': - resolution: {integrity: sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==} + '@algolia/requester-browser-xhr@4.24.0': + resolution: {integrity: sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==} - '@algolia/requester-common@4.23.3': - resolution: {integrity: sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==} + '@algolia/requester-common@4.24.0': + resolution: {integrity: sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==} - '@algolia/requester-node-http@4.23.3': - resolution: {integrity: sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==} + '@algolia/requester-node-http@4.24.0': + resolution: {integrity: sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==} - '@algolia/transporter@4.23.3': - resolution: {integrity: sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==} + '@algolia/transporter@4.24.0': + resolution: {integrity: sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==} '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} @@ -607,14 +629,11 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@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.6': - resolution: {integrity: sha512-pvFiLP2BeOKA/ZOS6jxx4XhKzdVLHDhGlFEaZ2flWWYf2xOqVniqpk38I04DFRyz+L0ASggl7SkItTc+ZLju4w==} - - '@antfu/utils@0.7.7': - resolution: {integrity: sha512-gFPqTG7otEJ8uP6wrhDv6mqwGWYZKNvAcCq6u9hOj0c+IKCEsY4L1oC9trPq2SaWIzAfHvqfBDxF591JkMf+kg==} + '@antfu/utils@0.7.10': + resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} '@apideck/better-ajv-errors@0.3.6': resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} @@ -622,37 +641,55 @@ packages: peerDependencies: ajv: '>=8' - '@applitools/core-base@1.10.1': - resolution: {integrity: sha512-s4q72eyPLaV1D5xXH1He72y/yCdSihL4oNs6vRe+IxP2CSDH8b42B+8Ps2nAPMNxatp7nzpHq/jRfXHv24Z1ZQ==} + '@applitools/core-base@1.16.1': + resolution: {integrity: sha512-T4/BCba4b77lJRh85Ir9Gwc2cKKwzLAdrPOWbzwx2euhl7ZPUdd4U4ncQpv9uKTYFwz5zu3v5TCeUxrRpRtXqg==} + engines: {node: '>=12.13.0'} + + '@applitools/core@4.18.2': + resolution: {integrity: sha512-loxNLlWyEdKBLTNUj4JUvDXImFxFVXZZ/NC/k5Z+LaXix3Xk5aIpCM+8Ii5Y96WBv8G7x/ZvQop7h823z3ai0Q==} engines: {node: '>=12.13.0'} + hasBin: true - '@applitools/core@4.11.1': - resolution: {integrity: sha512-j+LbBn6e7KMONzacFNI+VxEJjBB+3TxxntJrhzBJE9hUzhLuXYR8+oVcpJf0FHyfwruUEF9Fsi+A2K++d3uG5g==} + '@applitools/core@4.19.0': + resolution: {integrity: sha512-OzGSZpRTouDFidzZx7IpqStoVThBz5ympBI6iowh1xkfbVRsRjKXaHIjCuB3TAkfTNy4V7lm2Pmzex7Dn4Fq1w==} engines: {node: '>=12.13.0'} hasBin: true - '@applitools/css-tree@1.1.2': - resolution: {integrity: sha512-+DBY7Rf/PorHniPYNNG9rDbQcjuXvrVTx3vXXMz7h4m8h8wjUDq5afIUQ9QSTj3H8awXKHRg1o9XJl5yA6cxOg==} + '@applitools/css-tree@1.1.4': + resolution: {integrity: sha512-rH3aq/dkTweEUgS/MKuthD79CZDqpQVJlqmxqVxLZVAzbeFxYdTG/gnfG0zj6YJ025jzcPH2ktdW16Rl3QLutg==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - '@applitools/dom-capture@11.2.6': - resolution: {integrity: sha512-USNpYDaj+L8GcPX0pJFHbDpaHc/IFWJVvFiGrOWylgPPinBWtco52mj7lv5urSX9rVyxEF41awszA2BOFOIV3Q==} + '@applitools/dom-capture@11.4.0': + resolution: {integrity: sha512-8E5rjsuivGWx1TtZsjhwo32gF02dzwqvHf8NaN2fK+DTyomUvrh4QRD0ufUlKNeXIJhlVVgzjOkdfKjDj5pT/A==} + engines: {node: '>=12.13.0'} + + '@applitools/dom-capture@11.5.0': + resolution: {integrity: sha512-frsa+nztrxN0YyfnFNQ3fxs6Q8A93YmtqWw7v2rywv2vGk0bo1VzobFbfIFvwHEwk+oghobV+w94NdYk9jPVZA==} engines: {node: '>=12.13.0'} - '@applitools/dom-shared@1.0.13': - resolution: {integrity: sha512-FcZKhdnPcV42IT9tPK80Tlzs6Xxsv11hgfgMqKscOOtgZ02xK9d8w1tuSMRO9VFDzCLaEFe/QSLk8/FgrDMy7w==} + '@applitools/dom-shared@1.0.15': + resolution: {integrity: sha512-XN77SPfzXriU1x6gTcublSe0yUJHxlYwHesOnWQov2dMVfHx7y3qp0yrjdVC7LO2bDIJIzDlPJRhfg2otlbxig==} engines: {node: '>=12.13.0'} - '@applitools/dom-snapshot@4.9.2': - resolution: {integrity: sha512-edAoScuJvrwbj6MSy7WCph1+eK9SMB+8UAAcbGRK1kTlvsRSYJ4fjMyw6A6tEhVKeRY/u6Byn0LELY2YmQukLQ==} + '@applitools/dom-snapshot@4.11.3': + resolution: {integrity: sha512-jdEWSbEOmD9LbzashTQ/YzYDdIKrhSBwNqNTIk8qjV8YtbQfZ+NtgCtW7nOsbknAMk95CfYEUV3R1rxCXs1XfA==} engines: {node: '>=12.13.0'} - '@applitools/driver@1.16.5': - resolution: {integrity: sha512-/KfxNXRKX02Ru8h55R402H2pOfwQv0XSLvg83T8rJJ6SuAAGsnT+a2orvHmj2tfb7hkEBoe1AuXdRZPdQVZLYg==} + '@applitools/driver@1.19.0': + resolution: {integrity: sha512-fXNvT08/uR87Wi2nNURT9YXJYV/2ZG6DnKutk3jxsp29uNJXaHfruMXoA0p6guAWzo9gw592K0GKLTn1BB/3YA==} engines: {node: '>=12.13.0'} - '@applitools/ec-client@1.7.32': - resolution: {integrity: sha512-jwjrcpuQLAEManS3V5bCB6BMgl4+/SL95uRwM8yIRQpHw0+l0w6yTuqOAmrygPHTKphHNjDurDia0lmt30IrQA==} + '@applitools/driver@1.19.1': + resolution: {integrity: sha512-SWTOtdALeqrmaYo+gzeWupB3C4yDCNwEq/RFykW7k41yFg4145B/BgmubZjteDAr6W+4vmE8vXtbVWHNGPuFfA==} + engines: {node: '>=12.13.0'} + + '@applitools/ec-client@1.9.4': + resolution: {integrity: sha512-PFuvt/XrJxzoy/fXeLTq+bE5+0mitV0whi4MUWZAnESIvHj3k3+oUUTZxPmRQEiR1zzxGvN7ar3sMQfiW+houA==} + engines: {node: '>=12.13.0'} + hasBin: true + + '@applitools/ec-client@1.9.5': + resolution: {integrity: sha512-B2HvmSq5wKjszbV8y1b5jdMdQzR1izQ3DK3Egc/Y4ye/TXvuzsx6/t7/iV5TwLvGoS2jPWOr/iTgl3cCJjXUIg==} engines: {node: '>=12.13.0'} hasBin: true @@ -664,183 +701,155 @@ packages: resolution: {integrity: sha512-SjjDBFeiKspX3nHKOoSQ+l4JUiJK3xJiWAEaR8b+GuMvnGFLnrvAECHhuXXG00+LwBJM8WKmfxEe17nvZe+nhg==} engines: {node: '>=12'} - '@applitools/execution-grid-tunnel@2.1.10': - resolution: {integrity: sha512-d/haRUUehvfRQXu/idhxaWnJY0zThsjuGRz0wPTElQtLoYP2s5zmkrB0ahTqkLc9FsYdTrYKhFYWpp6R6yp17Q==} - engines: {node: '>=12.13.0'} + '@applitools/execution-grid-tunnel@3.0.8': + resolution: {integrity: sha512-4S6NcpxELH4NXketD3g6VUhWDUCuwAm4F1sCZdZLpPWOSMu5QwQDYUoe6/4t5KuktTQ4K7N90NmTzQrxiFtDKA==} + engines: {node: '>=14.0.0'} hasBin: true - '@applitools/eyes-cypress@3.43.1': - resolution: {integrity: sha512-qq7vKsMRiZj1s21G9s1umma9NfDAeproqjygKcrEwMQ7+dGTpPrBbttC25S+Ge50a4PmIoZ6yao4FxKviC3J+Q==} + '@applitools/eyes-cypress@3.44.9': + resolution: {integrity: sha512-hWwo02uMeFkwU7bG2396DqKrOsjMxAMowaIH8okP09ZPgK+nSJbnIHM111nj+4+eLKx4WAyDa9JljILuXZ4x9A==} engines: {node: '>=12.13.0'} hasBin: true - '@applitools/eyes@1.17.1': - resolution: {integrity: sha512-kgDgILH4yuKBEve4J93Y9XXqF2GPfoEPbrSM3lJY4JUvTFl2q6czfAVtZO7HfQkTFuW4Q/YK2wZg/sTGCWznnQ==} + '@applitools/eyes@1.22.2': + resolution: {integrity: sha512-72mMjSYjfBHAdVqyubtLWAKgK3f/lcFZcyTh8UacCZv+PJ+8+/JAC+ovloUOV1HHOtgcR+ocPdw3VJsxDZZuig==} engines: {node: '>=12.13.0'} '@applitools/functional-commons@1.6.0': resolution: {integrity: sha512-fwiF0CbeYHDEOTD/NKaFgaI8LvRcGYG2GaJJiRwcedKko16sQ8F3TK5wXfj2Ytjf+8gjwHwsEEX550z3yvDWxA==} engines: {node: '>=8.0.0'} - '@applitools/image@1.1.9': - resolution: {integrity: sha512-R86re+yofXSBamTuzSLwFB57fzaf7aiKvyx675uw8e/XfqQy3vhGbp8Bh23lUZX9y7ngf2ldrpnQ7nQrvmtJuA==} + '@applitools/image@1.1.13': + resolution: {integrity: sha512-oeSnsTJxhD6juNlWufeWsiWV9dbS0a3OL75/r/Bo2yauAi6AsRMDeh+McXJfYlf1NVZbrVG0+vNXn52mDVEIyw==} engines: {node: '>=12.13.0'} '@applitools/logger@1.1.53': resolution: {integrity: sha512-4mlzYxc0MgM3WIxEwKqIjn9W7G7kMtQc2bFRxozViKOXypTfr72j8iODs88wcetP0GsXtplhZQ5/6aZN5WY9ug==} engines: {node: '>=12.13.0'} - '@applitools/logger@2.0.14': - resolution: {integrity: sha512-oq/RPjs/3BjR3EdLohHhzzVufBYEMMhOUmZlCnvgmCJIhUsa3ceq8Ta2E99TUzSny9xkl962JoRDfLQg/vS+Ww==} + '@applitools/logger@2.0.18': + resolution: {integrity: sha512-d54OTreCXE+G9qUxiPDHHBzwof3EnXPrADdZ7ToB9AoI+kOgs/v6wjMx0ghAoXyyOiLvlvJnmdHSyJssRdv5GA==} + engines: {node: '>=12.13.0'} + + '@applitools/nml-client@1.8.10': + resolution: {integrity: sha512-avoZnD39XrWJg5x7PiFv+58YEDLbWPRIb+dHrH9LVD1HcQC8tmht2KfVLnTJLJtJgRQojqZh5H8rmplfT46t8w==} engines: {node: '>=12.13.0'} - '@applitools/nml-client@1.7.5': - resolution: {integrity: sha512-VeZy/aik9a9PMSE2NNq2LYgwu7FEj/hkGgwVv4M13jdH8/e+RI+fnjEVcky7hE6F1AjsY3HRHvbIrnUBDmF0/g==} + '@applitools/nml-client@1.8.11': + resolution: {integrity: sha512-Zoyjo9slRbvCGb/ldScNxTvRig5nuUdogXeiyV8jcKUocqb0LLfZZyNRRHnA0bmSk31mjqfB8HLG1wgBIKZ/eQ==} engines: {node: '>=12.13.0'} - '@applitools/req@1.6.5': - resolution: {integrity: sha512-EV6SNrABc/MEknQ5hSEUm0TgNlcOQXLM5W7VV2nObuVOMu35XL4BuVJH9Wivg4WiV6O1ZJ2rvpZ9ju0x4DHFsQ==} + '@applitools/req@1.7.2': + resolution: {integrity: sha512-L0tjPFGEJFAEGaifqtmtCghjkG7M0wnEwfzbHi6O+ThtTCbg4JSDRTaNvA+PLXQoS0mFvajG40/t5a4EgAG7QQ==} engines: {node: '>=16.13.0'} - '@applitools/screenshoter@3.8.27': - resolution: {integrity: sha512-YIByICLMOmfc7gDXIzT9Qc/lVeqzXKjkOu47aKncEmKy2pcOUkMDmbVdhlP+M7pRIaaoPOmsC0CQpdteTGFi/g==} + '@applitools/screenshoter@3.8.36': + resolution: {integrity: sha512-bzl+fs3c4L6J2t/PELxmoMGc40ZvjaExD0PMM6GvbNp3uPbDtGS348DC1ZYsSl481OxTae/uiO/iVOQP4bNZCQ==} + engines: {node: '>=12.13.0'} + + '@applitools/screenshoter@3.8.37': + resolution: {integrity: sha512-il7clR9bd3E2QzjWfR/JafmUyrykvQN8EzqaFG4rfNO5IUYYP/K2rYGAbWykk220weI3r9S09QrSDWVHwNJgHw==} + engines: {node: '>=12.13.0'} + + '@applitools/snippets@2.4.27': + resolution: {integrity: sha512-n6ckwbXWyJ+/DoV1T6bRiGXITgTgjayV0j4AzHiBx+HF3JdzygxIkWtn7yl1dJfzeqEGyrtBK6Sq1tTG2GoQcA==} + engines: {node: '>=12.13.0'} + + '@applitools/snippets@2.5.0': + resolution: {integrity: sha512-7PoDf2Xub68q7bfEcSxzRIOsK+QPUEzCKO5X3YKEq7/y55G1bFalZiY+V0TZEgIu4SSbq8BmCos9798w1J31uA==} engines: {node: '>=12.13.0'} - '@applitools/snippets@2.4.25': - resolution: {integrity: sha512-vnU9qq1IGkNpvh7Qy0m196t1u3mpx7NNUeHyJRVnJ53Ok4sb9s/KKrkrU9xYkKYY+T3AEvoN0Rp5LVVrKBHGQw==} + '@applitools/socket@1.1.18': + resolution: {integrity: sha512-EMI/MMfVH38ucuZhFWOTUR8cPvuoP9b+xi5yBJF8uLlJjxQEmGnvm+Pm3s9o3mfxQzDRddYGtpIo3TTZhMVZdQ==} engines: {node: '>=12.13.0'} - '@applitools/socket@1.1.14': - resolution: {integrity: sha512-o43hNnD/PN5T5MFR3cZ5OC+b5PpkV/PeTk8z844sNtGyziS9GEpO0vYfG2XLq/mZg0YQurrXtYupUMndV+0wDg==} + '@applitools/spec-driver-webdriver@1.1.12': + resolution: {integrity: sha512-r6PobChadcc3couBtnf3pTunL7Vi00cNcg2l1rTr0ApSEfJ1m1DdTcX8bgXU1jDzJ2QhCn7OoqsziTajQdWmoA==} engines: {node: '>=12.13.0'} + peerDependencies: + webdriver: '>=6.0.0' - '@applitools/spec-driver-webdriver@1.1.3': - resolution: {integrity: sha512-L9uhnKdX5Ts/FBS7J1Xq/SV9qQ6codgssKDPVwOytvQEhtJnohGRdnAIgRDdLANPq7j4JCV0SyeCNyYeljwErw==} + '@applitools/spec-driver-webdriver@1.1.13': + resolution: {integrity: sha512-LcX4mbXdptPjcgRifUvV17pANVhjMiSEYkfZkP0G/ZuPi1czQvgzsSkjeYTKuKJJYLaP19h4CFNjNttD3mSsDQ==} engines: {node: '>=12.13.0'} peerDependencies: webdriver: '>=6.0.0' - '@applitools/tunnel-client@1.4.1': - resolution: {integrity: sha512-/oGPWwk+p6qu/u3IUNXA7ZG1jkC9myg3Jv3yu014+i8Ltd9dp+OcUCH8Q4kN/W8RFBjLcRvahpbzWNd0cnYWQA==} + '@applitools/tunnel-client@1.5.8': + resolution: {integrity: sha512-SJByl2/I0NftENw5NvW+nHN+Vq64b0aeTsdCTYKhDhJBWqPEkGYwRR5ziYpk8MWYsL2hWcPUfg/S/hS+M3zmDg==} engines: {node: '>=12.13.0'} hasBin: true - '@applitools/ufg-client@1.10.2': - resolution: {integrity: sha512-2F2nSZwxnMF+zZ5wiQuOJhyAbD/s4Wui8zHet2YL69ZZkhDMtMwYLtU/6VtUd9ZrzSYtBX32KE2ynfWraZ+zbg==} + '@applitools/ufg-client@1.12.3': + resolution: {integrity: sha512-bSxLqxzAuc+ldum/nGoiM/iCcf97uku3bABxB90ilzUYT1DOu9vEGmaPxxGLDc+GRRVYlOYGNdIJF+DQP4dFTg==} engines: {node: '>=12.13.0'} '@applitools/utils@1.3.36': resolution: {integrity: sha512-eROEssh7wIW+V87PvLiHI2hUPxqoBxXFMRx3+z5qOZqXUPSR1Uz7EMFwxZcDDR7T6C3O3UDckB2aVB5fJAg5JA==} engines: {node: '>=12.13.0'} - '@applitools/utils@1.7.0': - resolution: {integrity: sha512-CvBxdfPZ3ss1hOD8Yr9y2SzVfqLKBA/0N3gfQd5qafMrBhI0wuCycQmiclpAQNEVNkbhqn8/t6dOeeYgapjyDw==} + '@applitools/utils@1.7.4': + 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.5.0': + resolution: {integrity: sha512-syJJmvLtJKQYXDmGYRb+ZKpzpSk/dReqhZZm2tnWn7ThxHaJRJ7Wu3J5nqDpCP3LxoYCVfvV/dmfoJO0v8+PbQ==} + engines: {node: '>=18.0.0'} + + '@argos-ci/browser@2.1.4': + resolution: {integrity: sha512-GursnbWL01wN92hRgEsa0c55ih9Sp6qGeYIXFWP4o42FDzm98LbxIy2e1WS+ezP+gBwsSBEMBTGcGCSSmVzacg==} engines: {node: '>=18.0.0'} - '@argos-ci/core@2.4.0': - resolution: {integrity: sha512-CY8IQsc71cuIeF2U47aePzH8+YWK4ePG1j+gK5aQ0r744Wc467U4xej7gChMVrAaGlZKsgoIIeXE1ezF+rCryw==} + '@argos-ci/core@2.8.1': + resolution: {integrity: sha512-5ygruMnfQ3OY6LvywnwTycZFg6oTG5UYvPCVdwQiOh+8FgUZUyJge7QBVfeWW+qC0UXFMo+f3eTQ5YFvTwc0ZA==} engines: {node: '>=18.0.0'} - '@argos-ci/cypress@2.1.0': - resolution: {integrity: sha512-EvzoWrX9owK40aeOcP3k3pMdML3m1PynLjDOGBtxWUG87gAGjsVd0rvVMTHQJTrCwhF4TRlqDv3vAllMWdZIAA==} + '@argos-ci/cypress@2.2.2': + resolution: {integrity: sha512-lwXu6y5DcP4ufYQEom4JtHSHjIYul6+GB4pniC8S97mfXLYq6KITJD4JHbrnfIiQGuV1xNPIaBc4MWX+atFDuw==} 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.2': - resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} - engines: {node: '>=6.9.0'} - - '@babel/code-frame@7.24.7': - resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.24.4': - resolution: {integrity: sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.24.7': - resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.24.9': - resolution: {integrity: sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.24.4': - resolution: {integrity: sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.24.5': - resolution: {integrity: sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.24.7': - resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.24.9': - resolution: {integrity: sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.24.10': - resolution: {integrity: sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.24.5': - resolution: {integrity: sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.24.7': - resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-annotate-as-pure@7.22.5': - resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} + '@babel/code-frame@7.25.7': + resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.24.7': - resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==} + '@babel/compat-data@7.25.7': + resolution: {integrity: sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==} engines: {node: '>=6.9.0'} - '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': - resolution: {integrity: sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==} + '@babel/core@7.25.7': + resolution: {integrity: sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.23.6': - resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} + '@babel/generator@7.25.7': + resolution: {integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.24.7': - resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} + '@babel/helper-annotate-as-pure@7.25.7': + resolution: {integrity: sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.24.8': - resolution: {integrity: sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==} + '@babel/helper-builder-binary-assignment-operator-visitor@7.25.7': + resolution: {integrity: sha512-12xfNeKNH7jubQNm7PAkzlLwEmCs1tfuX3UjIw6vP6QXi+leKh6+LyC/+Ed4EIQermwd58wsyh070yjDHFlNGg==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.24.5': - resolution: {integrity: sha512-uRc4Cv8UQWnE4NXlYTIIdM7wfFkOqlFztcC/gVXDKohKoVB3OyonfelUBaJzSwpBntZ2KYGF/9S7asCHsXwW6g==} + '@babel/helper-compilation-targets@7.25.7': + resolution: {integrity: sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-create-class-features-plugin@7.24.8': - resolution: {integrity: sha512-4f6Oqnmyp2PP3olgUMmOwC3akxSm5aBYraQ6YDdKy7NcAMkDECHWG0DEnV6M2UAkERgIBhYt8S27rURPg7SxWA==} + '@babel/helper-create-class-features-plugin@7.25.7': + resolution: {integrity: sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-create-regexp-features-plugin@7.24.7': - resolution: {integrity: sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==} + '@babel/helper-create-regexp-features-plugin@7.25.7': + resolution: {integrity: sha512-byHhumTj/X47wJ6C6eLpK7wW/WBEcnUeb7D0FNc/jFQnQVw7DOso3Zz5u9x/zLrFVkHa89ZGDbkAa1D54NdrCQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -850,209 +859,103 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - '@babel/helper-environment-visitor@7.22.20': - resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-environment-visitor@7.24.7': - resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-function-name@7.23.0': - resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-function-name@7.24.7': - resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-hoist-variables@7.22.5': - resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-hoist-variables@7.24.7': - resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-member-expression-to-functions@7.24.5': - resolution: {integrity: sha512-4owRteeihKWKamtqg4JmWSsEZU445xpFRXPEwp44HbgbxdWlUV1b4Agg4lkA806Lil5XM/e+FJyS0vj5T6vmcA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-member-expression-to-functions@7.24.8': - resolution: {integrity: sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.24.3': - resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.24.7': - resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.24.5': - resolution: {integrity: sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==} + '@babel/helper-member-expression-to-functions@7.25.7': + resolution: {integrity: sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-module-transforms@7.24.7': - resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} + '@babel/helper-module-imports@7.25.7': + resolution: {integrity: sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-module-transforms@7.24.9': - resolution: {integrity: sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==} + '@babel/helper-module-transforms@7.25.7': + resolution: {integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-optimise-call-expression@7.22.5': - resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-optimise-call-expression@7.24.7': - resolution: {integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==} - engines: {node: '>=6.9.0'} - - '@babel/helper-plugin-utils@7.24.5': - resolution: {integrity: sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-plugin-utils@7.24.8': - resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==} + '@babel/helper-optimise-call-expression@7.25.7': + resolution: {integrity: sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==} engines: {node: '>=6.9.0'} - '@babel/helper-remap-async-to-generator@7.24.7': - resolution: {integrity: sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==} + '@babel/helper-plugin-utils@7.25.7': + resolution: {integrity: sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-replace-supers@7.24.1': - resolution: {integrity: sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==} + '@babel/helper-remap-async-to-generator@7.25.7': + resolution: {integrity: sha512-kRGE89hLnPfcz6fTrlNU+uhgcwv0mBE4Gv3P9Ke9kLVJYpi4AMVVEElXvB5CabrPZW4nCM8P8UyyjrzCM0O2sw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-replace-supers@7.24.7': - resolution: {integrity: sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==} + '@babel/helper-replace-supers@7.25.7': + resolution: {integrity: sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-simple-access@7.24.5': - resolution: {integrity: sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-simple-access@7.24.7': - resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-skip-transparent-expression-wrappers@7.22.5': - resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} - engines: {node: '>=6.9.0'} - - '@babel/helper-skip-transparent-expression-wrappers@7.24.7': - resolution: {integrity: sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-split-export-declaration@7.24.5': - resolution: {integrity: sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==} - engines: {node: '>=6.9.0'} - - '@babel/helper-split-export-declaration@7.24.7': - resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.24.1': - resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.24.7': - resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.24.8': - resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.24.7': - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.23.5': - resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.24.7': - resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} + '@babel/helper-simple-access@7.25.7': + resolution: {integrity: sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.24.8': - resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} + '@babel/helper-skip-transparent-expression-wrappers@7.25.7': + resolution: {integrity: sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==} engines: {node: '>=6.9.0'} - '@babel/helper-wrap-function@7.24.7': - resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==} + '@babel/helper-string-parser@7.25.7': + resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.24.5': - resolution: {integrity: sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==} + '@babel/helper-validator-identifier@7.25.7': + resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.24.7': - resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} + '@babel/helper-validator-option@7.25.7': + resolution: {integrity: sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.24.8': - resolution: {integrity: sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==} + '@babel/helper-wrap-function@7.25.7': + resolution: {integrity: sha512-MA0roW3JF2bD1ptAaJnvcabsVlNQShUaThyJbCDD4bCp8NEgiFvpoqRI2YS22hHlc2thjO/fTg2ShLMC3jygAg==} engines: {node: '>=6.9.0'} - '@babel/highlight@7.24.2': - resolution: {integrity: sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==} + '@babel/helpers@7.25.7': + resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==} engines: {node: '>=6.9.0'} - '@babel/highlight@7.24.7': - resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + '@babel/highlight@7.25.7': + resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.24.5': - resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/parser@7.24.7': - resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} + '@babel/parser@7.25.7': + resolution: {integrity: sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@7.24.8': - resolution: {integrity: sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==} - engines: {node: '>=6.0.0'} - hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.7': + resolution: {integrity: sha512-UV9Lg53zyebzD1DwQoT9mzkEKa922LNUp5YkTJ6Uta0RbyXaQNUgcvSt7qIu1PpPzVb6rd10OVNTzkyBGeVmxQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7': - resolution: {integrity: sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==} + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.7': + resolution: {integrity: sha512-GDDWeVLNxRIkQTnJn2pDOM1pkCgYdSqPeT1a9vh9yIqu2uzzgw1zcqEb+IJOhy+dTBMlNdThrDIksr2o09qrrQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7': - resolution: {integrity: sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==} + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.7': + resolution: {integrity: sha512-wxyWg2RYaSUYgmd9MR0FyRGyeOMQE/Uzr1wzd/g5cf5bwi9A4v6HFdDm7y1MgDtod/fLOSTZY6jDgV0xU9d5bA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7': - resolution: {integrity: sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==} + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.7': + resolution: {integrity: sha512-Xwg6tZpLxc4iQjorYsyGMyfJE7nP5MV8t/Ka58BgiA7Jw0fRqQNcANlLfdJ/yvBt9z9LD2We+BEkT7vLqZRWng==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7': - resolution: {integrity: sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==} + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.7': + resolution: {integrity: sha512-UVATLMidXrnH+GMUIuxq55nejlj02HP7F5ETyBONzP6G87fPBogG4CH6kxrSrdIuAjdwNO9VzyaYsrZPscWUrw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -1094,14 +997,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-assertions@7.24.7': - resolution: {integrity: sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==} + '@babel/plugin-syntax-import-assertions@7.25.7': + resolution: {integrity: sha512-ZvZQRmME0zfJnDQnVBKYzHxXT7lYBB3Revz1GuS7oLXWMgqUPX4G+DDbT30ICClht9WKV34QVrZhSw6WdklwZQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.24.7': - resolution: {integrity: sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==} + '@babel/plugin-syntax-import-attributes@7.25.7': + resolution: {integrity: sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1116,8 +1019,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-jsx@7.24.1': - resolution: {integrity: sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==} + '@babel/plugin-syntax-jsx@7.25.7': + resolution: {integrity: sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1164,8 +1067,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.24.1': - resolution: {integrity: sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==} + '@babel/plugin-syntax-typescript@7.25.7': + resolution: {integrity: sha512-rR+5FDjpCHqqZN2bzZm18bVYGaejGq5ZkpVCJLXor/+zlSrSoc4KWcHI0URVWjl/68Dyr1uwZUz/1njycEAv9g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1176,314 +1079,308 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-arrow-functions@7.24.7': - resolution: {integrity: sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==} + '@babel/plugin-transform-arrow-functions@7.25.7': + resolution: {integrity: sha512-EJN2mKxDwfOUCPxMO6MUI58RN3ganiRAG/MS/S3HfB6QFNjroAMelQo/gybyYq97WerCBAZoyrAoW8Tzdq2jWg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-generator-functions@7.24.7': - resolution: {integrity: sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==} + '@babel/plugin-transform-async-generator-functions@7.25.7': + resolution: {integrity: sha512-4B6OhTrwYKHYYgcwErvZjbmH9X5TxQBsaBHdzEIB4l71gR5jh/tuHGlb9in47udL2+wVUcOz5XXhhfhVJwEpEg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-to-generator@7.24.7': - resolution: {integrity: sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==} + '@babel/plugin-transform-async-to-generator@7.25.7': + resolution: {integrity: sha512-ZUCjAavsh5CESCmi/xCpX1qcCaAglzs/7tmuvoFnJgA1dM7gQplsguljoTg+Ru8WENpX89cQyAtWoaE0I3X3Pg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoped-functions@7.24.7': - resolution: {integrity: sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==} + '@babel/plugin-transform-block-scoped-functions@7.25.7': + resolution: {integrity: sha512-xHttvIM9fvqW+0a3tZlYcZYSBpSWzGBFIt/sYG3tcdSzBB8ZeVgz2gBP7Df+sM0N1850jrviYSSeUuc+135dmQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoping@7.24.7': - resolution: {integrity: sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==} + '@babel/plugin-transform-block-scoping@7.25.7': + resolution: {integrity: sha512-ZEPJSkVZaeTFG/m2PARwLZQ+OG0vFIhPlKHK/JdIMy8DbRJ/htz6LRrTFtdzxi9EHmcwbNPAKDnadpNSIW+Aow==} engines: {node: '>=6.9.0'} 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.7': + resolution: {integrity: sha512-mhyfEW4gufjIqYFo9krXHJ3ElbFLIze5IDp+wQTxoPd+mwFb1NxatNAwmv8Q8Iuxv7Zc+q8EkiMQwc9IhyGf4g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-static-block@7.24.7': - resolution: {integrity: sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==} + '@babel/plugin-transform-class-static-block@7.25.7': + resolution: {integrity: sha512-rvUUtoVlkDWtDWxGAiiQj0aNktTPn3eFynBcMC2IhsXweehwgdI9ODe+XjWw515kEmv22sSOTp/rxIRuTiB7zg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.24.8': - resolution: {integrity: sha512-VXy91c47uujj758ud9wx+OMgheXm4qJfyhj1P18YvlrQkNOSrwsteHk+EFS3OMGfhMhpZa0A+81eE7G4QC+3CA==} + '@babel/plugin-transform-classes@7.25.7': + resolution: {integrity: sha512-9j9rnl+YCQY0IGoeipXvnk3niWicIB6kCsWRGLwX241qSXpbA4MKxtp/EdvFxsc4zI5vqfLxzOd0twIJ7I99zg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-computed-properties@7.24.7': - resolution: {integrity: sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==} + '@babel/plugin-transform-computed-properties@7.25.7': + resolution: {integrity: sha512-QIv+imtM+EtNxg/XBKL3hiWjgdLjMOmZ+XzQwSgmBfKbfxUjBzGgVPklUuE55eq5/uVoh8gg3dqlrwR/jw3ZeA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-destructuring@7.24.8': - resolution: {integrity: sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==} + '@babel/plugin-transform-destructuring@7.25.7': + resolution: {integrity: sha512-xKcfLTlJYUczdaM1+epcdh1UGewJqr9zATgrNHcLBcV2QmfvPPEixo/sK/syql9cEmbr7ulu5HMFG5vbbt/sEA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-dotall-regex@7.24.7': - resolution: {integrity: sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==} + '@babel/plugin-transform-dotall-regex@7.25.7': + resolution: {integrity: sha512-kXzXMMRzAtJdDEgQBLF4oaiT6ZCU3oWHgpARnTKDAqPkDJ+bs3NrZb310YYevR5QlRo3Kn7dzzIdHbZm1VzJdQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-duplicate-keys@7.24.7': - resolution: {integrity: sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==} + '@babel/plugin-transform-duplicate-keys@7.25.7': + resolution: {integrity: sha512-by+v2CjoL3aMnWDOyCIg+yxU9KXSRa9tN6MbqggH5xvymmr9p4AMjYkNlQy4brMceBnUyHZ9G8RnpvT8wP7Cfg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-dynamic-import@7.24.7': - resolution: {integrity: sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==} + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.7': + resolution: {integrity: sha512-HvS6JF66xSS5rNKXLqkk7L9c/jZ/cdIVIcoPVrnl8IsVpLggTjXs8OWekbLHs/VtYDDh5WXnQyeE3PPUGm22MA==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': ^7.0.0 - '@babel/plugin-transform-exponentiation-operator@7.24.7': - resolution: {integrity: sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==} + '@babel/plugin-transform-dynamic-import@7.25.7': + resolution: {integrity: sha512-UvcLuual4h7/GfylKm2IAA3aph9rwvAM2XBA0uPKU3lca+Maai4jBjjEVUS568ld6kJcgbouuumCBhMd/Yz17w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-export-namespace-from@7.24.7': - resolution: {integrity: sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==} + '@babel/plugin-transform-exponentiation-operator@7.25.7': + resolution: {integrity: sha512-yjqtpstPfZ0h/y40fAXRv2snciYr0OAoMXY/0ClC7tm4C/nG5NJKmIItlaYlLbIVAWNfrYuy9dq1bE0SbX0PEg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-for-of@7.24.7': - resolution: {integrity: sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==} + '@babel/plugin-transform-export-namespace-from@7.25.7': + resolution: {integrity: sha512-h3MDAP5l34NQkkNulsTNyjdaR+OiB0Im67VU//sFupouP8Q6m9Spy7l66DcaAQxtmCqGdanPByLsnwFttxKISQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-function-name@7.24.7': - resolution: {integrity: sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==} + '@babel/plugin-transform-for-of@7.25.7': + resolution: {integrity: sha512-n/TaiBGJxYFWvpJDfsxSj9lEEE44BFM1EPGz4KEiTipTgkoFVVcCmzAL3qA7fdQU96dpo4gGf5HBx/KnDvqiHw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-json-strings@7.24.7': - resolution: {integrity: sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==} + '@babel/plugin-transform-function-name@7.25.7': + resolution: {integrity: sha512-5MCTNcjCMxQ63Tdu9rxyN6cAWurqfrDZ76qvVPrGYdBxIj+EawuuxTu/+dgJlhK5eRz3v1gLwp6XwS8XaX2NiQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-literals@7.24.7': - resolution: {integrity: sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==} + '@babel/plugin-transform-json-strings@7.25.7': + resolution: {integrity: sha512-Ot43PrL9TEAiCe8C/2erAjXMeVSnE/BLEx6eyrKLNFCCw5jvhTHKyHxdI1pA0kz5njZRYAnMO2KObGqOCRDYSA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-logical-assignment-operators@7.24.7': - resolution: {integrity: sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==} + '@babel/plugin-transform-literals@7.25.7': + resolution: {integrity: sha512-fwzkLrSu2fESR/cm4t6vqd7ebNIopz2QHGtjoU+dswQo/P6lwAG04Q98lliE3jkz/XqnbGFLnUcE0q0CVUf92w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-member-expression-literals@7.24.7': - resolution: {integrity: sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==} + '@babel/plugin-transform-logical-assignment-operators@7.25.7': + resolution: {integrity: sha512-iImzbA55BjiovLyG2bggWS+V+OLkaBorNvc/yJoeeDQGztknRnDdYfp2d/UPmunZYEnZi6Lg8QcTmNMHOB0lGA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-amd@7.24.7': - resolution: {integrity: sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==} + '@babel/plugin-transform-member-expression-literals@7.25.7': + resolution: {integrity: sha512-Std3kXwpXfRV0QtQy5JJcRpkqP8/wG4XL7hSKZmGlxPlDqmpXtEPRmhF7ztnlTCtUN3eXRUJp+sBEZjaIBVYaw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-commonjs@7.24.1': - resolution: {integrity: sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==} + '@babel/plugin-transform-modules-amd@7.25.7': + resolution: {integrity: sha512-CgselSGCGzjQvKzghCvDTxKHP3iooenLpJDO842ehn5D2G5fJB222ptnDwQho0WjEvg7zyoxb9P+wiYxiJX5yA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-commonjs@7.24.8': - resolution: {integrity: sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==} + '@babel/plugin-transform-modules-commonjs@7.25.7': + resolution: {integrity: sha512-L9Gcahi0kKFYXvweO6n0wc3ZG1ChpSFdgG+eV1WYZ3/dGbJK7vvk91FgGgak8YwRgrCuihF8tE/Xg07EkL5COg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.24.7': - resolution: {integrity: sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==} + '@babel/plugin-transform-modules-systemjs@7.25.7': + resolution: {integrity: sha512-t9jZIvBmOXJsiuyOwhrIGs8dVcD6jDyg2icw1VL4A/g+FnWyJKwUfSSU2nwJuMV2Zqui856El9u+ElB+j9fV1g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-umd@7.24.7': - resolution: {integrity: sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==} + '@babel/plugin-transform-modules-umd@7.25.7': + resolution: {integrity: sha512-p88Jg6QqsaPh+EB7I9GJrIqi1Zt4ZBHUQtjw3z1bzEXcLh6GfPqzZJ6G+G1HBGKUNukT58MnKG7EN7zXQBCODw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-named-capturing-groups-regex@7.24.7': - resolution: {integrity: sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==} + '@babel/plugin-transform-named-capturing-groups-regex@7.25.7': + resolution: {integrity: sha512-BtAT9LzCISKG3Dsdw5uso4oV1+v2NlVXIIomKJgQybotJY3OwCwJmkongjHgwGKoZXd0qG5UZ12JUlDQ07W6Ow==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-new-target@7.24.7': - resolution: {integrity: sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==} + '@babel/plugin-transform-new-target@7.25.7': + resolution: {integrity: sha512-CfCS2jDsbcZaVYxRFo2qtavW8SpdzmBXC2LOI4oO0rP+JSRDxxF3inF4GcPsLgfb5FjkhXG5/yR/lxuRs2pySA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-nullish-coalescing-operator@7.24.7': - resolution: {integrity: sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==} + '@babel/plugin-transform-nullish-coalescing-operator@7.25.7': + resolution: {integrity: sha512-FbuJ63/4LEL32mIxrxwYaqjJxpbzxPVQj5a+Ebrc8JICV6YX8nE53jY+K0RZT3um56GoNWgkS2BQ/uLGTjtwfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-numeric-separator@7.24.7': - resolution: {integrity: sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==} + '@babel/plugin-transform-numeric-separator@7.25.7': + resolution: {integrity: sha512-8CbutzSSh4hmD+jJHIA8vdTNk15kAzOnFLVVgBSMGr28rt85ouT01/rezMecks9pkU939wDInImwCKv4ahU4IA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-rest-spread@7.24.7': - resolution: {integrity: sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==} + '@babel/plugin-transform-object-rest-spread@7.25.7': + resolution: {integrity: sha512-1JdVKPhD7Y5PvgfFy0Mv2brdrolzpzSoUq2pr6xsR+m+3viGGeHEokFKsCgOkbeFOQxfB1Vt2F0cPJLRpFI4Zg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-super@7.24.7': - resolution: {integrity: sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==} + '@babel/plugin-transform-object-super@7.25.7': + resolution: {integrity: sha512-pWT6UXCEW3u1t2tcAGtE15ornCBvopHj9Bps9D2DsH15APgNVOTwwczGckX+WkAvBmuoYKRCFa4DK+jM8vh5AA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-catch-binding@7.24.7': - resolution: {integrity: sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==} + '@babel/plugin-transform-optional-catch-binding@7.25.7': + resolution: {integrity: sha512-m9obYBA39mDPN7lJzD5WkGGb0GO54PPLXsbcnj1Hyeu8mSRz7Gb4b1A6zxNX32ZuUySDK4G6it8SDFWD1nCnqg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-chaining@7.24.8': - resolution: {integrity: sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==} + '@babel/plugin-transform-optional-chaining@7.25.7': + resolution: {integrity: sha512-h39agClImgPWg4H8mYVAbD1qP9vClFbEjqoJmt87Zen8pjqK8FTPUwrOXAvqu5soytwxrLMd2fx2KSCp2CHcNg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-parameters@7.24.7': - resolution: {integrity: sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==} + '@babel/plugin-transform-parameters@7.25.7': + resolution: {integrity: sha512-FYiTvku63me9+1Nz7TOx4YMtW3tWXzfANZtrzHhUZrz4d47EEtMQhzFoZWESfXuAMMT5mwzD4+y1N8ONAX6lMQ==} engines: {node: '>=6.9.0'} 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.7': + resolution: {integrity: sha512-KY0hh2FluNxMLwOCHbxVOKfdB5sjWG4M183885FmaqWWiGMhRZq4DQRKH6mHdEucbJnyDyYiZNwNG424RymJjA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-property-in-object@7.24.7': - resolution: {integrity: sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==} + '@babel/plugin-transform-private-property-in-object@7.25.7': + resolution: {integrity: sha512-LzA5ESzBy7tqj00Yjey9yWfs3FKy4EmJyKOSWld144OxkTji81WWnUT8nkLUn+imN/zHL8ZQlOu/MTUAhHaX3g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-property-literals@7.24.7': - resolution: {integrity: sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==} + '@babel/plugin-transform-property-literals@7.25.7': + resolution: {integrity: sha512-lQEeetGKfFi0wHbt8ClQrUSUMfEeI3MMm74Z73T9/kuz990yYVtfofjf3NuA42Jy3auFOpbjDyCSiIkTs1VIYw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.24.7': - resolution: {integrity: sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==} + '@babel/plugin-transform-regenerator@7.25.7': + resolution: {integrity: sha512-mgDoQCRjrY3XK95UuV60tZlFCQGXEtMg8H+IsW72ldw1ih1jZhzYXbJvghmAEpg5UVhhnCeia1CkGttUvCkiMQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-reserved-words@7.24.7': - resolution: {integrity: sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==} + '@babel/plugin-transform-reserved-words@7.25.7': + resolution: {integrity: sha512-3OfyfRRqiGeOvIWSagcwUTVk2hXBsr/ww7bLn6TRTuXnexA+Udov2icFOxFX9abaj4l96ooYkcNN1qi2Zvqwng==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-shorthand-properties@7.24.7': - resolution: {integrity: sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==} + '@babel/plugin-transform-shorthand-properties@7.25.7': + resolution: {integrity: sha512-uBbxNwimHi5Bv3hUccmOFlUy3ATO6WagTApenHz9KzoIdn0XeACdB12ZJ4cjhuB2WSi80Ez2FWzJnarccriJeA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-spread@7.24.7': - resolution: {integrity: sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==} + '@babel/plugin-transform-spread@7.25.7': + resolution: {integrity: sha512-Mm6aeymI0PBh44xNIv/qvo8nmbkpZze1KvR8MkEqbIREDxoiWTi18Zr2jryfRMwDfVZF9foKh060fWgni44luw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-sticky-regex@7.24.7': - resolution: {integrity: sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==} + '@babel/plugin-transform-sticky-regex@7.25.7': + resolution: {integrity: sha512-ZFAeNkpGuLnAQ/NCsXJ6xik7Id+tHuS+NT+ue/2+rn/31zcdnupCdmunOizEaP0JsUmTFSTOPoQY7PkK2pttXw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-template-literals@7.24.7': - resolution: {integrity: sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==} + '@babel/plugin-transform-template-literals@7.25.7': + resolution: {integrity: sha512-SI274k0nUsFFmyQupiO7+wKATAmMFf8iFgq2O+vVFXZ0SV9lNfT1NGzBEhjquFmD8I9sqHLguH+gZVN3vww2AA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typeof-symbol@7.24.8': - resolution: {integrity: sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==} + '@babel/plugin-transform-typeof-symbol@7.25.7': + resolution: {integrity: sha512-OmWmQtTHnO8RSUbL0NTdtpbZHeNTnm68Gj5pA4Y2blFNh+V4iZR68V1qL9cI37J21ZN7AaCnkfdHtLExQPf2uA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.24.4': - resolution: {integrity: sha512-79t3CQ8+oBGk/80SQ8MN3Bs3obf83zJ0YZjDmDaEZN8MqhMI760apl5z6a20kFeMXBwJX99VpKT8CKxEBp5H1g==} + '@babel/plugin-transform-typescript@7.25.7': + resolution: {integrity: sha512-VKlgy2vBzj8AmEzunocMun2fF06bsSWV+FvVXohtL6FGve/+L217qhHxRTVGHEDO/YR8IANcjzgJsd04J8ge5Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-escapes@7.24.7': - resolution: {integrity: sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==} + '@babel/plugin-transform-unicode-escapes@7.25.7': + resolution: {integrity: sha512-BN87D7KpbdiABA+t3HbVqHzKWUDN3dymLaTnPFAMyc8lV+KN3+YzNhVRNdinaCPA4AUqx7ubXbQ9shRjYBl3SQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-property-regex@7.24.7': - resolution: {integrity: sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==} + '@babel/plugin-transform-unicode-property-regex@7.25.7': + resolution: {integrity: sha512-IWfR89zcEPQGB/iB408uGtSPlQd3Jpq11Im86vUgcmSTcoWAiQMCTOa2K2yNNqFJEBVICKhayctee65Ka8OB0w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-regex@7.24.7': - resolution: {integrity: sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==} + '@babel/plugin-transform-unicode-regex@7.25.7': + resolution: {integrity: sha512-8JKfg/hiuA3qXnlLx8qtv5HWRbgyFx2hMMtpDDuU2rTckpKkGu4ycK5yYHwuEa16/quXfoxHBIApEsNyMWnt0g==} engines: {node: '>=6.9.0'} 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.7': + resolution: {integrity: sha512-YRW8o9vzImwmh4Q3Rffd09bH5/hvY0pxg+1H1i0f7APoUeg12G7+HhLj9ZFNIrYkgBXhIijPJ+IXypN0hLTIbw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.24.7': - resolution: {integrity: sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/preset-env@7.24.8': - resolution: {integrity: sha512-vObvMZB6hNWuDxhSaEPTKCwcqkAIuDtE+bQGn4XMXne1DSLzFVY8Vmj1bm+mUQXYNN8NmaQEO+r8MMbzPr1jBQ==} + '@babel/preset-env@7.25.7': + resolution: {integrity: sha512-Gibz4OUdyNqqLj+7OAvBZxOD7CklCtMA5/j0JgUEwOnaRULsPDXmic2iKxL2DX2vQduPR5wH2hjZas/Vr/Oc0g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1493,68 +1390,98 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - '@babel/preset-typescript@7.24.1': - resolution: {integrity: sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==} + '@babel/preset-typescript@7.25.7': + resolution: {integrity: sha512-rkkpaXJZOFN45Fb+Gki0c+KMIglk4+zZXOoMJuyEK8y8Kkc8Jd3BDmP7qPsz0zQMJj+UD7EprF+AqAXcILnexw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/regjsgen@0.8.0': - resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} - - '@babel/runtime@7.24.0': - resolution: {integrity: sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==} + '@babel/runtime@7.25.7': + resolution: {integrity: sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.24.4': - resolution: {integrity: sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==} + '@babel/template@7.25.7': + resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.24.8': - resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} + '@babel/traverse@7.25.7': + resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==} engines: {node: '>=6.9.0'} - '@babel/template@7.24.0': - resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} + '@babel/types@7.25.7': + resolution: {integrity: sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==} engines: {node: '>=6.9.0'} - '@babel/template@7.24.7': - resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} - engines: {node: '>=6.9.0'} + '@bcherny/json-schema-ref-parser@10.0.5-fork': + resolution: {integrity: sha512-E/jKbPoca1tfUPj3iSbitDZTGnq6FUFjkH6L8U2oDwSuwK1WhnnVtCG7oFOTg/DDnyoXbQYUiUiGOibHqaGVnw==} + engines: {node: '>= 16'} - '@babel/traverse@7.24.5': - resolution: {integrity: sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==} - engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@babel/traverse@7.24.7': - resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} - engines: {node: '>=6.9.0'} + '@braintree/sanitize-url@7.1.0': + resolution: {integrity: sha512-o+UlMLt49RvtCASlOMW0AkHnabN9wR9rwCCherxO0yG4Npy34GkvrAqdXQvrhNs+jh+gkK8gB8Lf05qL/O7KWg==} - '@babel/traverse@7.24.8': - resolution: {integrity: sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==} - engines: {node: '>=6.9.0'} + '@changesets/apply-release-plan@7.0.5': + resolution: {integrity: sha512-1cWCk+ZshEkSVEZrm2fSj1Gz8sYvxgUL4Q78+1ZZqeqfuevPTPk033/yUZ3df8BKMohkqqHfzj0HOOrG0KtXTw==} - '@babel/types@7.24.5': - resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==} - engines: {node: '>=6.9.0'} + '@changesets/assemble-release-plan@6.0.4': + resolution: {integrity: sha512-nqICnvmrwWj4w2x0fOhVj2QEGdlUuwVAwESrUo5HLzWMI1rE5SWfsr9ln+rDqWB6RQ2ZyaMZHUcU7/IRaUJS+Q==} - '@babel/types@7.24.7': - resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} - engines: {node: '>=6.9.0'} + '@changesets/changelog-git@0.2.0': + resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} - '@babel/types@7.24.9': - resolution: {integrity: sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==} - engines: {node: '>=6.9.0'} + '@changesets/changelog-github@0.5.0': + resolution: {integrity: sha512-zoeq2LJJVcPJcIotHRJEEA2qCqX0AQIeFE+L21L8sRLPVqDhSXY8ZWAt2sohtBpFZkBwu+LUwMSKRr2lMy3LJA==} - '@bcherny/json-schema-ref-parser@10.0.5-fork': - resolution: {integrity: sha512-E/jKbPoca1tfUPj3iSbitDZTGnq6FUFjkH6L8U2oDwSuwK1WhnnVtCG7oFOTg/DDnyoXbQYUiUiGOibHqaGVnw==} - engines: {node: '>= 16'} + '@changesets/cli@2.27.9': + resolution: {integrity: sha512-q42a/ZbDnxPpCb5Wkm6tMVIxgeI9C/bexntzTeCFBrQEdpisQqk8kCHllYZMDjYtEc1ZzumbMJAG8H0Z4rdvjg==} + hasBin: true - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@changesets/config@3.0.3': + resolution: {integrity: sha512-vqgQZMyIcuIpw9nqFIpTSNyc/wgm/Lu1zKN5vECy74u95Qx/Wa9g27HdgO4NkVAaq+BGA8wUc/qvbvVNs93n6A==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.2': + resolution: {integrity: sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==} + + '@changesets/get-github-info@0.6.0': + resolution: {integrity: sha512-v/TSnFVXI8vzX9/w3DU2Ol+UlTZcu3m0kXTjTT4KlAdwSvwutcByYwyYn9hwerPWfPkT2JfpoX0KgvCEi8Q/SA==} + + '@changesets/get-release-plan@4.0.4': + resolution: {integrity: sha512-SicG/S67JmPTrdcc9Vpu0wSQt7IiuN0dc8iR5VScnnTVPfIaLvKmEGRvIaF0kcn8u5ZqLbormZNTO77bCEvyWw==} - '@braintree/sanitize-url@7.0.4': - resolution: {integrity: sha512-hPYRrKFoI+nuckPgDJfyYAkybFvheo4usS0Vw0HNAe+fmGBQA5Az37b/yStO284atBoqqdOUhKJ3d9Zw3PQkcQ==} + '@changesets/get-version-range-type@0.4.0': + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + + '@changesets/git@3.0.1': + resolution: {integrity: sha512-pdgHcYBLCPcLd82aRcuO0kxCDbw/yISlOtkmwmE8Odo1L6hSiZrBOsRl84eYG7DRCab/iHnOkWqExqc4wxk2LQ==} + + '@changesets/logger@0.1.1': + resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + + '@changesets/parse@0.4.0': + resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + + '@changesets/pre@2.0.1': + resolution: {integrity: sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==} + + '@changesets/read@0.6.1': + resolution: {integrity: sha512-jYMbyXQk3nwP25nRzQQGa1nKLY0KfoOV7VLgwucI0bUO8t8ZLCr6LZmgjXsiKuRDc+5A6doKPr9w2d+FEJ55zQ==} + + '@changesets/should-skip-package@0.1.1': + resolution: {integrity: sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.0.0': + resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} + + '@changesets/write@0.3.2': + resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==} '@chevrotain/cst-dts-gen@11.0.3': resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} @@ -1575,73 +1502,44 @@ packages: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} - '@cspell/cspell-bundled-dicts@8.7.0': - resolution: {integrity: sha512-B5YQI7Dd9m0JHTmHgs7PiyP4BWXzl8ixpK+HGOwhxzh7GyfFt1Eo/gxMxBDX/9SaewEzeb2OjRpRKEFtEsto3A==} - engines: {node: '>=18'} - - '@cspell/cspell-bundled-dicts@8.9.1': - resolution: {integrity: sha512-etkor/qXSSqyh6lbudEGdTami0DooIi2AlQbJPUWRfowzYJRSYWPUbyQSUkFdRhCHni2oLOFbWaraRthNlLD/A==} - engines: {node: '>=18'} - - '@cspell/cspell-json-reporter@8.7.0': - resolution: {integrity: sha512-LTQPEvXvCqnc+ok9WXpSISZyt4/nGse9fVEM430g0BpGzKpt3RMx49B8uasvvnanzCuikaW9+wFLmwgvraERhA==} - engines: {node: '>=18'} - - '@cspell/cspell-pipe@8.7.0': - resolution: {integrity: sha512-ePqddIQ4arqPQgOkC146SkZxvZb9/jL7xIM5Igy2n3tiWTC5ijrX/mbHpPZ1VGcFck+1M0cJUuyhuJk+vMj3rg==} - engines: {node: '>=18'} - - '@cspell/cspell-pipe@8.9.1': - resolution: {integrity: sha512-wH5Xu8W3aMEWFSpOczMtH/04clLMfDGdbYMYB7w6BeHI/LDW8DZaRhigOOhx9FRgVk/YIVbKKAKVgvFrfD5cEA==} - engines: {node: '>=18'} - - '@cspell/cspell-resolver@8.7.0': - resolution: {integrity: sha512-grZwDFYqcBYQDaz4AkUtdyqc4UUH2J3/7yWVkBbYDPE+FQHa9ofFXzXxyjs56GJlPfi9ULpe5/Wz6uVLg8rQkQ==} + '@cspell/cspell-bundled-dicts@8.14.4': + resolution: {integrity: sha512-JHZOpCJzN6fPBapBOvoeMxZbr0ZA11ZAkwcqM4w0lKoacbi6TwK8GIYf66hHvwLmMeav75TNXWE6aPTvBLMMqA==} engines: {node: '>=18'} - '@cspell/cspell-resolver@8.9.1': - resolution: {integrity: sha512-Q2SOnIi2dnQ2zqPd+tcEYfom9qlsapGyLK4Mdx2Vv29MU2RDZ9VHFDncV6yo6O58gmlYl8sXtJsVceiHgwwlkQ==} + '@cspell/cspell-json-reporter@8.14.4': + resolution: {integrity: sha512-gJ6tQbGCNLyHS2iIimMg77as5MMAFv3sxU7W6tjLlZp8htiNZS7fS976g24WbT/hscsTT9Dd0sNHkpo8K3nvVw==} engines: {node: '>=18'} - '@cspell/cspell-service-bus@8.7.0': - resolution: {integrity: sha512-KW48iu0nTDzbedixc7iB7K7mlAZQ7QeMLuM/akxigOlvtOdVJrRa9Pfn44lwejts1ANb/IXil3GH8YylkVi76Q==} + '@cspell/cspell-pipe@8.14.4': + resolution: {integrity: sha512-CLLdouqfrQ4rqdQdPu0Oo+HHCU/oLYoEsK1nNPb28cZTFxnn0cuSPKB6AMPBJmMwdfJ6fMD0BCKNbEe1UNLHcw==} engines: {node: '>=18'} - '@cspell/cspell-service-bus@8.9.1': - resolution: {integrity: sha512-dPKpqkglGnwvrW9mgbHIdimDQZH3iy8uT8gm3dEO//UahxMBdMpvtdbC3R9kesQCSagvYRVE7hwJvOktSAK+Vg==} + '@cspell/cspell-resolver@8.14.4': + resolution: {integrity: sha512-s3uZyymJ04yn8+zlTp7Pt1WRSlAel6XVo+iZRxls3LSvIP819KK64DoyjCD2Uon0Vg9P/K7aAPt8GcxDcnJtgA==} engines: {node: '>=18'} - '@cspell/cspell-types@8.7.0': - resolution: {integrity: sha512-Rb+LCE5I9JEb/LE8nSViVSF8z1CWv/z4mPBIG37VMa7aUx2gAQa6gJekNfpY9YZiMzx4Tv3gDujN80ytks4pGA==} + '@cspell/cspell-service-bus@8.14.4': + resolution: {integrity: sha512-i3UG+ep63akNsDXZrtGgICNF3MLBHtvKe/VOIH6+L+NYaAaVHqqQvOY9MdUwt1HXh8ElzfwfoRp36wc5aAvt6g==} engines: {node: '>=18'} - '@cspell/cspell-types@8.9.1': - resolution: {integrity: sha512-Z/pTX2i+U5KwyCYRSw8BleJtw81jFifv91DDka4nqi2oyHJ3eEUljVovNOuZ3lotn/ArHdu4rY98s1w6Z69mYw==} + '@cspell/cspell-types@8.14.4': + resolution: {integrity: sha512-VXwikqdHgjOVperVVCn2DOe8W3rPIswwZtMHfRYnagpzZo/TOntIjkXPJSfTtl/cFyx5DnCBsDH8ytKGlMeHkw==} engines: {node: '>=18'} '@cspell/dict-ada@4.0.2': resolution: {integrity: sha512-0kENOWQeHjUlfyId/aCM/mKXtkEgV0Zu2RhUXCBr4hHo9F9vph+Uu8Ww2b0i5a4ZixoIkudGA+eJvyxrG1jUpA==} - '@cspell/dict-aws@4.0.1': - resolution: {integrity: sha512-NXO+kTPQGqaaJKa4kO92NAXoqS+i99dQzf3/L1BxxWVSBS3/k1f3uhmqIh7Crb/n22W793lOm0D9x952BFga3Q==} - - '@cspell/dict-aws@4.0.2': - resolution: {integrity: sha512-aNGHWSV7dRLTIn8WJemzLoMF62qOaiUQlgnsCwH5fRCD/00gsWCwg106pnbkmK4AyabyxzneOV4dfecDJWkSxw==} + '@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.5': + resolution: {integrity: sha512-YGim/h7E2U5HCCb2ckNufT6/yyWygt9nSZ5C7qw6oOD3bygbObqD1+rlPor1JW+YyO+3GwTIHE70uKEEU6VZYw==} - '@cspell/dict-companies@3.0.31': - resolution: {integrity: sha512-hKVpV/lcGKP4/DpEPS8P4osPvFH/YVLJaDn9cBIOH6/HSmL5LbFgJNKpMGaYRbhm2FEX56MKE3yn/MNeNYuesQ==} + '@cspell/dict-companies@3.1.4': + resolution: {integrity: sha512-y9e0amzEK36EiiKx3VAA+SHQJPpf2Qv5cCt5eTUSggpTkiFkCh6gRKQ97rVlrKh5GJrqinDwYIJtTsxuh2vy2Q==} - '@cspell/dict-companies@3.1.2': - resolution: {integrity: sha512-OwR5i1xbYuJX7FtHQySmTy3iJtPV1rZQ3jFCxFGwrA1xRQ4rtRcDQ+sTXBCIAoJHkXa84f9J3zsngOKmMGyS/w==} - - '@cspell/dict-cpp@5.1.10': - resolution: {integrity: sha512-BmIF0sAz2BgGEOwzYIeEm9ALneDjd1tcTbFbo+A1Hcq3zOKP8yViSgxS9CEN30KOZIyph6Tldp531UPEpoEl0Q==} - - '@cspell/dict-cpp@5.1.3': - resolution: {integrity: sha512-sqnriXRAInZH9W75C+APBh6dtben9filPqVbIsiRMUXGg+s02ekz0z6LbS7kXeJ5mD2qXoMLBrv13qH2eIwutQ==} + '@cspell/dict-cpp@5.1.19': + resolution: {integrity: sha512-i/odUPNFLdqWisOktu6c4qjUR4k+P9Al2RCri3Wso9EFblp53xt/5jIUdGMdDDVQGqX7s/KLtdqNxNKqP3/d+w==} '@cspell/dict-cryptocurrencies@5.0.0': resolution: {integrity: sha512-Z4ARIw5+bvmShL+4ZrhDzGhnc9znaAGHOEMaB/GURdS/jdoreEDY34wdN0NtdLHDO5KO7GduZnZyqGdRoiSmYA==} @@ -1649,17 +1547,14 @@ 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==} + '@cspell/dict-dart@2.2.1': + resolution: {integrity: sha512-yriKm7QkoPx3JPSSOcw6iX9gOb2N50bOo/wqWviqPYbhpMRh9Xiv6dkUy3+ot+21GuShZazO8X6U5+Vw67XEwg==} - '@cspell/dict-data-science@1.0.11': - resolution: {integrity: sha512-TaHAZRVe0Zlcc3C23StZqqbzC0NrodRwoSAc8dis+5qLeLLnOCtagYQeROQvDlcDg3X/VVEO9Whh4W/z4PAmYQ==} - - '@cspell/dict-data-science@2.0.1': - resolution: {integrity: sha512-xeutkzK0eBe+LFXOFU2kJeAYO6IuFUc1g7iRLr7HeCmlC4rsdGclwGHh61KmttL3+YHQytYStxaRBdGAXWC8Lw==} + '@cspell/dict-data-science@2.0.2': + resolution: {integrity: sha512-VwAck6OZQVqrscKyOrvllixIugIPF+Q6YoFNvXZCPhHGtNyOAVraD3S7kOgPYBdUjgno4QbdMWm92BUPqL1QjQ==} '@cspell/dict-django@4.1.0': resolution: {integrity: sha512-bKJ4gPyrf+1c78Z0Oc4trEB9MuhcB+Yg+uTTWsvhY6O2ncFYbB/LbEZfqhfmmuK/XJJixXfI1laF2zicyf+l0w==} @@ -1667,47 +1562,35 @@ packages: '@cspell/dict-docker@1.1.7': resolution: {integrity: sha512-XlXHAr822euV36GGsl2J1CkBIVg3fZ6879ZOg5dxTIssuhUOCiV2BuzKZmt6aIFmcdPmR14+9i9Xq+3zuxeX0A==} - '@cspell/dict-dotnet@5.0.0': - resolution: {integrity: sha512-EOwGd533v47aP5QYV8GlSSKkmM9Eq8P3G/eBzSpH3Nl2+IneDOYOBLEUraHuiCtnOkNsz0xtZHArYhAB2bHWAw==} - - '@cspell/dict-dotnet@5.0.2': - resolution: {integrity: sha512-UD/pO2A2zia/YZJ8Kck/F6YyDSpCMq0YvItpd4YbtDVzPREfTZ48FjZsbYi4Jhzwfvc6o8R56JusAE58P+4sNQ==} + '@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==} - '@cspell/dict-en-common-misspellings@2.0.0': - resolution: {integrity: sha512-NOg8dlv37/YqLkCfBs5OXeJm/Wcfb/CzeOmOZJ2ZXRuxwsNuolb4TREUce0yAXRqMhawahY5TSDRJJBgKjBOdw==} - - '@cspell/dict-en-common-misspellings@2.0.3': - resolution: {integrity: sha512-8nF1z9nUiSgMyikL66HTbDO7jCGtB24TxKBasXIBwkBKMDZgA2M883iXdeByy6m1JJUcCGFkSftVYp2W0bUgjw==} + '@cspell/dict-en-common-misspellings@2.0.4': + resolution: {integrity: sha512-lvOiRjV/FG4pAGZL3PN2GCVHSTCE92cwhfLGGkOsQtxSmef6WCHfHwp9auafkBlX0yFQSKDfq6/TlpQbjbJBtQ==} '@cspell/dict-en-gb@1.1.33': resolution: {integrity: sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==} - '@cspell/dict-en_us@4.3.18': - resolution: {integrity: sha512-D8jGT7Zi3+e8zIpT0NqGKvvzehcvUETFlOA0NxcRStkw7H7mgouHxKFU+u7t3tvCoGNwh2gwjCqZQlK8ZXwQHw==} - '@cspell/dict-en_us@4.3.23': resolution: {integrity: sha512-l0SoEQBsi3zDSl3OuL4/apBkxjuj4hLIg/oy6+gZ7LWh03rKdF6VNtSZNXWAmMY+pmb1cGA3ouleTiJIglbsIg==} - '@cspell/dict-filetypes@3.0.3': - resolution: {integrity: sha512-J9UP+qwwBLfOQ8Qg9tAsKtSY/WWmjj21uj6zXTI9hRLD1eG1uUOLcfVovAmtmVqUWziPSKMr87F6SXI3xmJXgw==} - '@cspell/dict-filetypes@3.0.4': resolution: {integrity: sha512-IBi8eIVdykoGgIv5wQhOURi5lmCNJq0we6DvqKoPQJHthXbgsuO1qrHSiUVydMiQl/XvcnUWTMeAlVUlUClnVg==} + '@cspell/dict-flutter@1.0.0': + resolution: {integrity: sha512-W7k1VIc4KeV8BjEBxpA3cqpzbDWjfb7oXkEb0LecBCBp5Z7kcfnjT1YVotTx/U9PGyAOBhDaEdgZACVGNQhayw==} + '@cspell/dict-fonts@4.0.0': resolution: {integrity: sha512-t9V4GeN/m517UZn63kZPUYP3OQg5f0OBLSd3Md5CU3eH1IFogSvTzHHnz4Wqqbv8NNRiBZ3HfdY/pqREZ6br3Q==} '@cspell/dict-fsharp@1.0.1': resolution: {integrity: sha512-23xyPcD+j+NnqOjRHgW3IU7Li912SX9wmeefcY0QxukbAxJ/vAN4rBpjSwwYZeQPAn3fxdfdNZs03fg+UM+4yQ==} - '@cspell/dict-fullstack@3.1.5': - resolution: {integrity: sha512-6ppvo1dkXUZ3fbYn/wwzERxCa76RtDDl5Afzv2lijLoijGGUw5yYdLBKJnx8PJBGNLh829X352ftE7BElG4leA==} - - '@cspell/dict-fullstack@3.1.8': - resolution: {integrity: sha512-YRlZupL7uqMCtEBK0bDP9BrcPnjDhz7m4GBqCc1EYqfXauHbLmDT8ELha7T/E7wsFKniHSjzwDZzhNXo2lusRQ==} + '@cspell/dict-fullstack@3.2.0': + resolution: {integrity: sha512-sIGQwU6G3rLTo+nx0GKyirR5dQSFeTIzFTOrURw51ISf+jKG9a3OmvsVtc2OANfvEAOLOC9Wfd8WYhmsO8KRDQ==} '@cspell/dict-gaming-terms@1.0.5': resolution: {integrity: sha512-C3riccZDD3d9caJQQs1+MPfrUrQ+0KHdlj9iUR1QD92FgTOF6UxoBpvHUUZ9YSezslcmpFQK4xQQ5FUGS7uWfw==} @@ -1715,11 +1598,8 @@ packages: '@cspell/dict-git@3.0.0': resolution: {integrity: sha512-simGS/lIiXbEaqJu9E2VPoYW1OTC2xrwPPXNXFMa2uo/50av56qOuaxDrZ5eH1LidFXwoc8HROCHYeKoNrDLSw==} - '@cspell/dict-golang@6.0.5': - resolution: {integrity: sha512-w4mEqGz4/wV+BBljLxduFNkMrd3rstBNDXmoX5kD4UTzIb4Sy0QybWCtg2iVT+R0KWiRRA56QKOvBsgXiddksA==} - - '@cspell/dict-golang@6.0.9': - resolution: {integrity: sha512-etDt2WQauyEQDA+qPS5QtkYTb2I9l5IfQftAllVoB1aOrT6bxxpHvMEpJ0Hsn/vezxrCqa/BmtUbRxllIxIuSg==} + '@cspell/dict-golang@6.0.13': + resolution: {integrity: sha512-uBUWi+AjFpluB6qF0rsC1gGyooqXeKPUdWHSmSXW/DCnS5PBSjRW6VWWp8efc1Fanob0QJxiZiYlc4U7oxuG6Q==} '@cspell/dict-google@1.0.1': resolution: {integrity: sha512-dQr4M3n95uOhtloNSgB9tYYGXGGEGEykkFyRtfcp5pFuEecYUa0BSgtlGKx9RXVtJtKgR+yFT/a5uQSlt8WjqQ==} @@ -1730,11 +1610,8 @@ packages: '@cspell/dict-html-symbol-entities@4.0.0': resolution: {integrity: sha512-HGRu+48ErJjoweR5IbcixxETRewrBb0uxQBd6xFGcxbEYCX8CnQFTAmKI5xNaIt2PKaZiJH3ijodGSqbKdsxhw==} - '@cspell/dict-html@4.0.5': - resolution: {integrity: sha512-p0brEnRybzSSWi8sGbuVEf7jSTDmXPx7XhQUb5bgG6b54uj+Z0Qf0V2n8b/LWwIPJNd1GygaO9l8k3HTCy1h4w==} - - '@cspell/dict-java@5.0.6': - resolution: {integrity: sha512-kdE4AHHHrixyZ5p6zyms1SLoYpaJarPxrz8Tveo6gddszBVVwIUZ+JkQE1bWNLK740GWzIXdkznpUfw1hP9nXw==} + '@cspell/dict-html@4.0.6': + resolution: {integrity: sha512-cLWHfuOhE4wqwC12up6Doxo2u1xxVhX1A8zriR4CUD+osFQzUIcBK1ykNXppga+rt1WyypaJdTU2eV6OpzYrgQ==} '@cspell/dict-java@5.0.7': resolution: {integrity: sha512-ejQ9iJXYIq7R09BScU2y5OUGrSqwcD+J5mHFOKbduuQ5s/Eh/duz45KOzykeMLI6KHPVxhBKpUPBWIsfewECpQ==} @@ -1742,11 +1619,8 @@ packages: '@cspell/dict-julia@1.0.1': resolution: {integrity: sha512-4JsCLCRhhLMLiaHpmR7zHFjj1qOauzDI5ZzCNQS31TUMfsOo26jAKDfo0jljFAKgw5M2fEG7sKr8IlPpQAYrmQ==} - '@cspell/dict-k8s@1.0.2': - resolution: {integrity: sha512-tLT7gZpNPnGa+IIFvK9SP1LrSpPpJ94a/DulzAPOb1Q2UBFwdpFd82UWhio0RNShduvKG/WiMZf/wGl98pn+VQ==} - - '@cspell/dict-k8s@1.0.5': - resolution: {integrity: sha512-Cj+/ZV4S+MKlwfocSJZqe/2UAd/sY8YtlZjbK25VN1nCnrsKrBjfkX29vclwSj1U9aJg4Z9jw/uMjoaKu9ZrpQ==} + '@cspell/dict-k8s@1.0.6': + resolution: {integrity: sha512-srhVDtwrd799uxMpsPOQqeDJY+gEocgZpoK06EFrb4GRYGhv7lXo9Fb+xQMyQytzOW9dw4DNOEck++nacDuymg==} '@cspell/dict-latex@4.0.0': resolution: {integrity: sha512-LPY4y6D5oI7D3d+5JMJHK/wxYTQa2lJMSNxps2JtuF8hbAnBQb3igoWEjEbIbRRH1XBM0X8dQqemnjQNCiAtxQ==} @@ -1763,68 +1637,41 @@ packages: '@cspell/dict-monkeyc@1.0.6': resolution: {integrity: sha512-oO8ZDu/FtZ55aq9Mb67HtaCnsLn59xvhO/t2mLLTHAp667hJFxpp7bCtr2zOrR1NELzFXmKln/2lw/PvxMSvrA==} - '@cspell/dict-node@4.0.3': - resolution: {integrity: sha512-sFlUNI5kOogy49KtPg8SMQYirDGIAoKBO3+cDLIwD4MLdsWy1q0upc7pzGht3mrjuyMiPRUV14Bb0rkVLrxOhg==} - '@cspell/dict-node@5.0.1': resolution: {integrity: sha512-lax/jGz9h3Dv83v8LHa5G0bf6wm8YVRMzbjJPG/9rp7cAGPtdrga+XANFq+B7bY5+jiSA3zvj10LUFCFjnnCCg==} - '@cspell/dict-npm@5.0.15': - resolution: {integrity: sha512-sX0X5YWNW54F4baW7b5JJB6705OCBIZtUqjOghlJNORS5No7QY1IX1zc5FxNNu4gsaCZITAmfMi4ityXEsEThA==} - - '@cspell/dict-npm@5.0.16': - resolution: {integrity: sha512-ZWPnLAziEcSCvV0c8k9Qj88pfMu+wZwM5Qks87ShsfBgI8uLZ9tGHravA7gmjH1Gd7Bgxy2ulvXtSqIWPh1lew==} - - '@cspell/dict-php@4.0.6': - resolution: {integrity: sha512-ySAXisf7twoVFZqBV2o/DKiCLIDTHNqfnj0EfH9OoOUR7HL3rb6zJkm0viLUFDO2G/8SyIi6YrN/6KX+Scjjjg==} - - '@cspell/dict-php@4.0.8': - resolution: {integrity: sha512-TBw3won4MCBQ2wdu7kvgOCR3dY2Tb+LJHgDUpuquy3WnzGiSDJ4AVelrZdE1xu7mjFJUr4q48aB21YT5uQqPZA==} + '@cspell/dict-npm@5.1.5': + resolution: {integrity: sha512-oAOGWuJYU3DlO+cAsStKMWN8YEkBue25cRC9EwdiL5Z84nchU20UIoYrLfIQejMlZca+1GyrNeyxRAgn4KiivA==} - '@cspell/dict-powershell@5.0.3': - resolution: {integrity: sha512-lEdzrcyau6mgzu1ie98GjOEegwVHvoaWtzQnm1ie4DyZgMr+N6D0Iyj1lzvtmt0snvsDFa5F2bsYzf3IMKcpcA==} + '@cspell/dict-php@4.0.10': + resolution: {integrity: sha512-NfTZdp6kcZDF1PvgQ6cY0zE4FUO5rSwNmBH/iwCBuaLfJAFQ97rgjxo+D2bic4CFwNjyHutnHPtjJBRANO5XQw==} - '@cspell/dict-powershell@5.0.4': - resolution: {integrity: sha512-eosDShapDgBWN9ULF7+sRNdUtzRnUdsfEdBSchDm8FZA4HOqxUSZy3b/cX/Rdw0Fnw0AKgk0kzgXw7tS6vwJMQ==} + '@cspell/dict-powershell@5.0.10': + resolution: {integrity: sha512-U4H0zm94sNK+YP7jSFb7xb160XLf2dKIPVt5sOYctKlEyR9M16sP8FHbyWV2Yp1YtxXugoNdeCm2vwGEDAd8sg==} - '@cspell/dict-public-licenses@2.0.6': - resolution: {integrity: sha512-bHqpSpJvLCUcWxj1ov/Ki8WjmESpYwRpQlqfdchekOTc93Huhvjm/RXVN1R4fVf4Hspyem1QVkCGqAmjJMj6sw==} + '@cspell/dict-public-licenses@2.0.8': + resolution: {integrity: sha512-Sup+tFS7cDV0fgpoKtUqEZ6+fA/H+XUgBiqQ/Fbs6vUE3WCjJHOIVsP+udHuyMH7iBfJ4UFYOYeORcY4EaKdMg==} - '@cspell/dict-public-licenses@2.0.7': - resolution: {integrity: sha512-KlBXuGcN3LE7tQi/GEqKiDewWGGuopiAD0zRK1QilOx5Co8XAvs044gk4MNIQftc8r0nHeUI+irJKLGcR36DIQ==} - - '@cspell/dict-python@4.1.11': - resolution: {integrity: sha512-XG+v3PumfzUW38huSbfT15Vqt3ihNb462ulfXifpQllPok5OWynhszCLCRQjQReV+dgz784ST4ggRxW452/kVg==} - - '@cspell/dict-python@4.2.1': - resolution: {integrity: sha512-9X2jRgyM0cxBoFQRo4Zc8oacyWnXi+0/bMI5FGibZNZV4y/o9UoFEr6agjU260/cXHTjIdkX233nN7eb7dtyRg==} + '@cspell/dict-python@4.2.8': + resolution: {integrity: sha512-4y5dynLiajvowhB3PqlcwJ2C4okK1y2Hombec1+TGcV9sUBfo8FYNw6VRFUUrpsxO+Ut/3ncIifdZS5/zAWi5w==} '@cspell/dict-r@2.0.1': resolution: {integrity: sha512-KCmKaeYMLm2Ip79mlYPc8p+B2uzwBp4KMkzeLd5E6jUlCL93Y5Nvq68wV5fRLDRTf7N1LvofkVFWfDcednFOgA==} - '@cspell/dict-ruby@5.0.2': - resolution: {integrity: sha512-cIh8KTjpldzFzKGgrqUX4bFyav5lC52hXDKo4LbRuMVncs3zg4hcSf4HtURY+f2AfEZzN6ZKzXafQpThq3dl2g==} - - '@cspell/dict-rust@4.0.2': - resolution: {integrity: sha512-RhziKDrklzOntxAbY3AvNR58wnFGIo3YS8+dNeLY36GFuWOvXDHFStYw5Pod4f/VXbO/+1tXtywCC4zWfB2p1w==} - - '@cspell/dict-rust@4.0.4': - resolution: {integrity: sha512-v9/LcZknt/Xq7m1jdTWiQEtmkVVKdE1etAfGL2sgcWpZYewEa459HeWndNA0gfzQrpWX9sYay18mt7pqClJEdA==} + '@cspell/dict-ruby@5.0.4': + resolution: {integrity: sha512-URw0jScj5pv8sKCVLNnde11qVCQR442rUpSd12u46Swl+5qBaSdnOUoCWQk419kd9/dpC6bB/3l4kOSY2fdYHw==} - '@cspell/dict-scala@5.0.0': - resolution: {integrity: sha512-ph0twaRoV+ylui022clEO1dZ35QbeEQaKTaV2sPOsdwIokABPIiK09oWwGK9qg7jRGQwVaRPEq0Vp+IG1GpqSQ==} + '@cspell/dict-rust@4.0.6': + resolution: {integrity: sha512-Buzy9PfLbdRPibSth8CV1D8ZsYqybo26yNIlAN+8ehU0pSBss0Jv4aleL4vKQ3FjouXeAC27rtEsLd7yaMZTog==} - '@cspell/dict-scala@5.0.2': - resolution: {integrity: sha512-v97ClgidZt99JUm7OjhQugDHmhx4U8fcgunHvD/BsXWjXNj4cTr0m0YjofyZoL44WpICsNuFV9F/sv9OM5HUEw==} + '@cspell/dict-scala@5.0.3': + resolution: {integrity: sha512-4yGb4AInT99rqprxVNT9TYb1YSpq58Owzq7zi3ZS5T0u899Y4VsxsBiOgHnQ/4W+ygi+sp+oqef8w8nABR2lkg==} - '@cspell/dict-software-terms@3.3.18': - resolution: {integrity: sha512-LJZGGMGqS8KzgXJrSMs3T+6GoqHG9z8Bc+rqLzLzbtoR3FbsMasE9U8oP2PmS3q7jJLFjQkzmg508DrcuZuo2g==} + '@cspell/dict-software-terms@4.1.7': + resolution: {integrity: sha512-+fFTALseXszDN8/khonF1DpTcYzwyNqYxhATLakr7CUPtUCO1fCH4lidMtBN4UtPVpE6tbjc5D8tj51PJxEOcw==} - '@cspell/dict-software-terms@3.4.8': - resolution: {integrity: sha512-r3gvmSGd8wZp4bbofTey/2Tu3gdBc5kxTRoFo1MaCh5vMLiBOSCLvyZgzr0DcMl8c5dxL7nFpNwbWZJxmKmtUA==} - - '@cspell/dict-sql@2.1.3': - resolution: {integrity: sha512-SEyTNKJrjqD6PAzZ9WpdSu6P7wgdNtGV2RV8Kpuw1x6bV+YsSptuClYG+JSdRExBTE6LwIe1bTklejUp3ZP8TQ==} + '@cspell/dict-sql@2.1.5': + resolution: {integrity: sha512-FmxanytHXss7GAWAXmgaxl3icTCW7YxlimyOSPNfm+njqeUDjw3kEv4mFNDDObBJv8Ec5AWCbUDkWIpkE3IpKg==} '@cspell/dict-svelte@1.0.2': resolution: {integrity: sha512-rPJmnn/GsDs0btNvrRBciOhngKV98yZ9SHmg8qI6HLS8hZKvcXc0LMsf9LLuMK1TmS2+WQFAan6qeqg6bBxL2Q==} @@ -1832,46 +1679,39 @@ packages: '@cspell/dict-swift@2.0.1': resolution: {integrity: sha512-gxrCMUOndOk7xZFmXNtkCEeroZRnS2VbeaIPiymGRHj5H+qfTAzAKxtv7jJbVA3YYvEzWcVE2oKDP4wcbhIERw==} - '@cspell/dict-terraform@1.0.0': - resolution: {integrity: sha512-Ak+vy4HP/bOgzf06BAMC30+ZvL9mzv21xLM2XtfnBLTDJGdxlk/nK0U6QT8VfFLqJ0ZZSpyOxGsUebWDCTr/zQ==} - - '@cspell/dict-typescript@3.1.3': - resolution: {integrity: sha512-TdD789OWwOImH/IMyz/QRA6LJz7ScI/qbn1YOkcAW3AROvgbc0oKAxzp08+Xu8tj4GROrJ9UqZdh0t9xQCPkPg==} + '@cspell/dict-terraform@1.0.2': + resolution: {integrity: sha512-UZdJwWIpib2Rx02w6vtXTU3z+M/VMZU0F1dhSL3Ab9otQsFntT8U1CX7wBSqQCLg8bJiCfnUyVvMK3UBm3SR8A==} - '@cspell/dict-typescript@3.1.5': - resolution: {integrity: sha512-EkIwwNV/xqEoBPJml2S16RXj65h1kvly8dfDLgXerrKw6puybZdvAHerAph6/uPTYdtLcsPyJYkPt5ISOJYrtw==} + '@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.7.0': - resolution: {integrity: sha512-xlEPdiHVDu+4xYkvwjL9MgklxOi9XB+Pr1H9s3Ww9WEq+q6BA3xOHxLIU/k8mhqFTMZGFZRCsdy/EwMu6SyRhQ==} - engines: {node: '>=18.0'} - - '@cspell/dynamic-import@8.9.1': - resolution: {integrity: sha512-ao4IDqQ8MyRqiB3NHA8R7ThRsuDLXdSCFm7Pvz8EqDnWaX3NAuClzgT3EoxJlw9pyyPQX3tW5Vg7ft3GSsBFUw==} + '@cspell/dynamic-import@8.14.4': + resolution: {integrity: sha512-GjKsBJvPXp4dYRqsMn7n1zpnKbnpfJnlKLOVeoFBh8fi4n06G50xYr+G25CWX1WT3WFaALAavvVICEUPrVsuqg==} engines: {node: '>=18.0'} - '@cspell/eslint-plugin@8.9.1': - resolution: {integrity: sha512-S2j47UyzXrJ69zHw6E7fb24b+Mkk1tp8lh7VgaYJ1wjOhhW7eg/7SrO3csRt5XvOjcn12FAtOoMJ7aHcvV1wfA==} + '@cspell/eslint-plugin@8.14.4': + resolution: {integrity: sha512-Wv6Jkttp/rsEm1nadLFQrUrYg9nTWQFwJu47KO2cfWP39TeH0zXQpmyas1xNlcDx5QJ9JJw9urTT/iw2tsHeRA==} engines: {node: '>=18'} peerDependencies: eslint: ^7 || ^8 || ^9 - '@cspell/strong-weak-map@8.7.0': - resolution: {integrity: sha512-0bo0WwDr2lzGoCP7vbpWbDpPyuOrHKK+218txnUpx6Pn1EDBLfcDQsiZED5B6zlpwgbGi6y3vc0rWtJbjKvwzg==} + '@cspell/filetypes@8.14.4': + resolution: {integrity: sha512-qd68dD7xTA4Mnf/wjIKYz2SkiTBshIM+yszOUtLa06YJm0aocoNQ25FHXyYEQYm9NQXCYnRWWA02sFMGs8Sv/w==} engines: {node: '>=18'} - '@cspell/strong-weak-map@8.9.1': - resolution: {integrity: sha512-onD/UPJW7rBQrRDqYNvPUAoWoBp1G2g+mijAD7EkuseyAKTKlKz624rXpHUOTqI814owmhFMNSf2QyYy8gFM6Q==} + '@cspell/strong-weak-map@8.14.4': + resolution: {integrity: sha512-Uyfck64TfVU24wAP3BLGQ5EsAfzIZiLfN90NhttpEM7GlOBmbGrEJd4hNOwfpYsE/TT80eGWQVPRTLr5SDbXFA==} engines: {node: '>=18'} - '@cspell/url@8.9.1': - resolution: {integrity: sha512-2AncPKGq9fnytwnL7V4KfoSjiEU0m8tVDFerGiDMNmTMWiQ4zj0kTATai118XT1eBVKiyrAotYRLSrsuUo9U3g==} + '@cspell/url@8.14.4': + resolution: {integrity: sha512-htHhNF8WrM/NfaLSWuTYw0NqVgFRVHYSyHlRT3i/Yv5xvErld8Gw7C6ldm+0TLjoGlUe6X1VV72JSir7+yLp/Q==} engines: {node: '>=18.0'} - '@cypress/code-coverage@3.12.41': - resolution: {integrity: sha512-82ndoXij1t1jlUC7id9MV2u+TB4qQds7BnNxYk+Bnz8879lj4wFIpqE3TnI9w+0Y0qayaJh7ZBJnwBbht6Ib2A==} + '@cypress/code-coverage@3.13.4': + resolution: {integrity: sha512-4Bne95y/Vkye9tfctyiKjYHirA/0LZq7Z5MiCrrT2mlyWfaOSnPeUHG84BdxuycgVOLzMFxqvc+uNQO5lupvTg==} peerDependencies: '@babel/core': ^7.0.1 '@babel/preset-env': ^7.0.0 @@ -1879,8 +1719,8 @@ packages: cypress: '*' webpack: ^4 || ^5 - '@cypress/request@3.0.1': - resolution: {integrity: sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==} + '@cypress/request@3.0.5': + resolution: {integrity: sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==} engines: {node: '>= 6'} '@cypress/webpack-preprocessor@6.0.2': @@ -1894,18 +1734,22 @@ packages: '@cypress/xvfb@1.2.4': resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==} + '@dependents/detective-less@5.0.0': + resolution: {integrity: sha512-D/9dozteKcutI5OdxJd8rU+fL6XgaaRg60sPPJWkT33OCiRfkCu5wO5B/yXTaaL2e6EB0lcCBGe5E0XscZCvvQ==} + engines: {node: '>=18'} + '@discoveryjs/json-ext@0.5.7': resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} - '@docsearch/css@3.6.0': - resolution: {integrity: sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==} + '@docsearch/css@3.6.2': + resolution: {integrity: sha512-vKNZepO2j7MrYBTZIGXvlUOIR+v9KRf70FApRgovWrj3GTs1EITz/Xb0AOlm1xsQBp16clVZj1SY/qaOJbQtZw==} - '@docsearch/js@3.6.0': - resolution: {integrity: sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==} + '@docsearch/js@3.6.2': + resolution: {integrity: sha512-pS4YZF+VzUogYrkblCucQ0Oy2m8Wggk8Kk7lECmZM60hTbaydSIhJTTiCrmoxtBqV8wxORnOqcqqOfbmkkQEcA==} - '@docsearch/react@3.6.0': - resolution: {integrity: sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==} + '@docsearch/react@3.6.2': + resolution: {integrity: sha512-rtZce46OOkVflCQH71IdbXSFK+S8iJZlUF56XBW5rIgx/eG5qoomC7Ag3anZson1bBac/JFQn7XOBfved/IMRA==} peerDependencies: '@types/react': '>= 16.8.0 < 19.0.0' react: '>= 16.8.0 < 19.0.0' @@ -1921,42 +1765,24 @@ packages: search-insights: optional: true - '@emnapi/runtime@1.2.0': - resolution: {integrity: sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==} + '@emnapi/runtime@1.3.0': + resolution: {integrity: sha512-XMBySMuNZs3DM96xcJmLW4EfGnf+uGmFNjzpehMjuX5PLB5j87ar2Zc4e3PVeZ3I5g3tYtAqskB28manlF69Zw==} - '@es-joy/jsdoccomment@0.43.1': - resolution: {integrity: sha512-I238eDtOolvCuvtxrnqtlBaw0BwdQuYqK7eA6XIonicMdOOOb75mqdIzkGDUbS04+1Di007rgm9snFRNeVrOog==} + '@es-joy/jsdoccomment@0.48.0': + resolution: {integrity: sha512-G6QUWIcC+KvSwXNsJyDTHvqUdNoAVJPPgkc3+Uk4WBKqZvoXhlvazOgm9aL0HwihJLQf0l+tOE2UFzXBqCqgDw==} engines: {node: '>=16'} - '@esbuild/aix-ppc64@0.19.12': - resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.20.2': - resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.19.12': - resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.20.2': - resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] + '@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==} @@ -1964,16 +1790,10 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.19.12': - resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.20.2': - resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} - engines: {node: '>=12'} - cpu: [arm] + '@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': @@ -1982,16 +1802,10 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-x64@0.19.12': - resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.20.2': - resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} - engines: {node: '>=12'} - cpu: [x64] + '@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': @@ -2000,17 +1814,11 @@ packages: cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.19.12': - resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.20.2': - resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] + '@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==} @@ -2018,16 +1826,10 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.19.12': - resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.20.2': - resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} - engines: {node: '>=12'} - cpu: [x64] + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.21.5': @@ -2036,17 +1838,11 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.19.12': - resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.20.2': - resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] + '@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==} @@ -2054,16 +1850,10 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.19.12': - resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.20.2': - resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} - engines: {node: '>=12'} - cpu: [x64] + '@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': @@ -2072,17 +1862,11 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.19.12': - resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.20.2': - resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] + '@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==} @@ -2090,16 +1874,10 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.19.12': - resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.20.2': - resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} - engines: {node: '>=12'} - cpu: [arm] + '@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': @@ -2108,16 +1886,10 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.19.12': - resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.20.2': - resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} - engines: {node: '>=12'} - cpu: [ia32] + '@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': @@ -2126,16 +1898,10 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.19.12': - resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.20.2': - resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} - engines: {node: '>=12'} - cpu: [loong64] + '@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': @@ -2144,16 +1910,10 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.19.12': - resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.20.2': - resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} - engines: {node: '>=12'} - cpu: [mips64el] + '@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': @@ -2162,16 +1922,10 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.19.12': - resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.20.2': - resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} - engines: {node: '>=12'} - cpu: [ppc64] + '@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': @@ -2180,16 +1934,10 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.19.12': - resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.20.2': - resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} - engines: {node: '>=12'} - cpu: [riscv64] + '@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': @@ -2198,16 +1946,10 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.19.12': - resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.20.2': - resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} - engines: {node: '>=12'} - cpu: [s390x] + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.21.5': @@ -2216,16 +1958,10 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.19.12': - resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.20.2': - resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} - engines: {node: '>=12'} - cpu: [x64] + '@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': @@ -2234,17 +1970,11 @@ packages: cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.19.12': - resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.20.2': - resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} cpu: [x64] - os: [netbsd] + os: [linux] '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} @@ -2252,16 +1982,16 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.19.12': - resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} - engines: {node: '>=12'} + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} cpu: [x64] - os: [openbsd] + os: [netbsd] - '@esbuild/openbsd-x64@0.20.2': - resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} - engines: {node: '>=12'} - cpu: [x64] + '@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': @@ -2270,17 +2000,11 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.19.12': - resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.20.2': - resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} - engines: {node: '>=12'} + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} cpu: [x64] - os: [sunos] + os: [openbsd] '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} @@ -2288,17 +2012,11 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.19.12': - resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.20.2': - resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] + '@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==} @@ -2306,16 +2024,10 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.19.12': - resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.20.2': - resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} - engines: {node: '>=12'} - cpu: [ia32] + '@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': @@ -2324,21 +2036,21 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.19.12': - resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} - engines: {node: '>=12'} - cpu: [x64] + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.20.2': - resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -2348,47 +2060,61 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.10.0': - resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + '@eslint-community/regexpp@4.11.1': + resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint-community/regexpp@4.11.0': - resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint/config-array@0.18.0': + resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-array@0.17.0': - resolution: {integrity: sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==} + '@eslint/core@0.6.0': + resolution: {integrity: sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.1.0': resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.6.0': - resolution: {integrity: sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==} + '@eslint/js@9.12.0': + resolution: {integrity: sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.4': resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@fastify/ajv-compiler@1.1.0': - resolution: {integrity: sha512-gvCOUNpXsWrIQ3A4aXCLIdblL0tDq42BG/2Xw7oxbil9h11uow10ztS2GuFazNBfjbrsZ5nl+nPl5jDSjj5TSg==} + '@eslint/plugin-kit@0.2.0': + resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@fastify/ajv-compiler@3.6.0': + resolution: {integrity: sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==} + + '@fastify/busboy@2.1.1': + resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} + engines: {node: '>=14'} + + '@fastify/error@3.4.1': + resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} + + '@fastify/fast-json-stringify-compiler@4.3.0': + resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==} - '@fastify/error@2.0.0': - resolution: {integrity: sha512-wI3fpfDT0t7p8E6dA2eTECzzOd+bZsZCJ2Hcv+Onn2b7ZwK3RwD27uW2QDaMtQhAfWQQP+WNK7nKf0twLsBf9w==} + '@fastify/merge-json-schemas@0.1.1': + resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} - '@floating-ui/core@1.6.4': - resolution: {integrity: sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==} + '@floating-ui/core@1.6.8': + resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==} - '@floating-ui/dom@1.6.7': - resolution: {integrity: sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==} + '@floating-ui/dom@1.6.11': + resolution: {integrity: sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==} - '@floating-ui/utils@0.2.4': - resolution: {integrity: sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==} + '@floating-ui/utils@0.2.8': + resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==} - '@floating-ui/vue@1.1.1': - resolution: {integrity: sha512-cyawjk9etPZPl/RVtMRnWrwtAhWbPVSrRVYARgOzhLIqxr0k2up1APrrFjqP9QwRQ0AwjKSvbWg4YC6jESutow==} + '@floating-ui/vue@1.1.5': + resolution: {integrity: sha512-ynL1p5Z+woPVSwgMGqeDrx6HrJfGIDzFyESFkyqJKilGW1+h/8yVY29Khn0LaU6wHBRwZ13ntG6reiHWK6jyzw==} '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} @@ -2396,8 +2122,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.4': + resolution: {integrity: sha512-MSyWCxUTueeex+veRCf++q4KM/fa4HOe9HDttzGrtgVDBULkGduFK6ItJh7EHJp2U/dY7qpyDUqp2KCHpCEplw==} peerDependencies: '@headlessui/vue': ^1.0.0 vue: ^3.0.0 @@ -2408,139 +2134,139 @@ packages: peerDependencies: tailwindcss: ^3.0 - '@headlessui/vue@1.7.22': - resolution: {integrity: sha512-Hoffjoolq1rY+LOfJ+B/OvkhuBXXBFgd8oBlN+l1TApma2dB0En0ucFZrwQtb33SmcCqd32EQd0y07oziXWNYg==} + '@headlessui/vue@1.7.23': + resolution: {integrity: sha512-JzdCNqurrtuu0YW6QaDtR2PIYCKPUWq28csDyMvN4zmGccmE7lz40Is6hc3LA4HFeCI7sekZ/PQMTNmn9I/4Wg==} engines: {node: '>=10'} peerDependencies: vue: ^3.2.0 + '@humanfs/core@0.19.0': + resolution: {integrity: sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.5': + resolution: {integrity: sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==} + engines: {node: '>=18.18.0'} + '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/retry@0.3.0': - resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} engines: {node: '>=18.18'} - '@iconify-json/carbon@1.1.36': - resolution: {integrity: sha512-NC3VcqLtwLZpi7+LeXj+99/byv+asrnCQxiDNCZV6hKr9WcNh6C25kJguJYfN+dV54kOkw78e+6PitQi2Bppnw==} + '@iconify-json/carbon@1.2.1': + resolution: {integrity: sha512-dIMY6OOY9LnwR3kOqAtfz4phGFG+KNfESEwSL6muCprBelSlSPpRXtdqvEEO/qWhkf5AJ9hWrOV3Egi5Z2IuKA==} '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - '@iconify/utils@2.1.23': - resolution: {integrity: sha512-YGNbHKM5tyDvdWZ92y2mIkrfvm5Fvhe6WJSkWu7vvOFhMtYDP0casZpoRz0XEHZCrYsR4stdGT3cZ52yp5qZdQ==} + '@iconify/utils@2.1.33': + resolution: {integrity: sha512-jP9h6v/g0BIZx0p7XGJJVtkVnydtbgTgt9mVNcGDYwaa7UhdHdI9dvoq+gKj9sijMSJKxUPEG2JyjsgXjxL7Kw==} - '@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'} + '@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] @@ -2637,8 +2363,8 @@ packages: '@jridgewell/source-map@0.3.6': resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} @@ -2646,8 +2372,14 @@ packages: '@jsdevtools/ono@7.1.3': resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} - '@leichtgewicht/ip-codec@2.0.4': - resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==} + '@leichtgewicht/ip-codec@2.0.5': + resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} '@mdi/font@7.4.47': resolution: {integrity: sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==} @@ -2678,8 +2410,8 @@ packages: resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@polka/url@1.0.0-next.25': - resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} + '@polka/url@1.0.0-next.28': + resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} '@rollup/plugin-babel@5.3.1': resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} @@ -2692,8 +2424,8 @@ packages: '@types/babel__core': optional: true - '@rollup/plugin-node-resolve@15.2.3': - resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==} + '@rollup/plugin-node-resolve@15.3.0': + resolution: {integrity: sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^2.78.0||^3.0.0||^4.0.0 @@ -2734,8 +2466,8 @@ packages: peerDependencies: rollup: ^1.20.0||^2.0.0 - '@rollup/pluginutils@5.1.0': - resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} + '@rollup/pluginutils@5.1.2': + resolution: {integrity: sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -2743,91 +2475,103 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.18.0': - resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==} + '@rollup/rollup-android-arm-eabi@4.24.0': + resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.18.0': - resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} + '@rollup/rollup-android-arm64@4.24.0': + resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.18.0': - resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} + '@rollup/rollup-darwin-arm64@4.24.0': + resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.18.0': - resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} + '@rollup/rollup-darwin-x64@4.24.0': + resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': - resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': + resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.18.0': - resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} + '@rollup/rollup-linux-arm-musleabihf@4.24.0': + resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.18.0': - resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} + '@rollup/rollup-linux-arm64-gnu@4.24.0': + resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.18.0': - resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} + '@rollup/rollup-linux-arm64-musl@4.24.0': + resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': - resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': + resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.18.0': - resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} + '@rollup/rollup-linux-riscv64-gnu@4.24.0': + resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.18.0': - resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} + '@rollup/rollup-linux-s390x-gnu@4.24.0': + resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.18.0': - resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} + '@rollup/rollup-linux-x64-gnu@4.24.0': + resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.18.0': - resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} + '@rollup/rollup-linux-x64-musl@4.24.0': + resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.18.0': - resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} + '@rollup/rollup-win32-arm64-msvc@4.24.0': + resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.18.0': - resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} + '@rollup/rollup-win32-ia32-msvc@4.24.0': + resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.18.0': - resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} + '@rollup/rollup-win32-x64-msvc@4.24.0': + resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==} cpu: [x64] os: [win32] - '@shikijs/core@1.4.0': - resolution: {integrity: sha512-CxpKLntAi64h3j+TwWqVIQObPTED0FyXLHTTh3MKXtqiQNn2JGcMQQ362LftDbc9kYbDtrksNMNoVmVXzKFYUQ==} + '@shikijs/core@1.22.0': + resolution: {integrity: sha512-S8sMe4q71TJAW+qG93s5VaiihujRK6rqDFqBnxqvga/3LvqHEnxqBIOPkt//IdXVtHkQWKu4nOQNk0uBGicU7Q==} + + '@shikijs/engine-javascript@1.22.0': + resolution: {integrity: sha512-AeEtF4Gcck2dwBqCFUKYfsCq0s+eEbCEbkUuFou53NZ0sTGnJnJ/05KHQFZxpii5HMXbocV9URYVowOP2wH5kw==} + + '@shikijs/engine-oniguruma@1.22.0': + resolution: {integrity: sha512-5iBVjhu/DYs1HB0BKsRRFipRrD7rqjxlWTj4F2Pf+nQSPqc3kcyqFFeZXnBMzDf0HdqaFVvhDRAGiYNvyLP+Mw==} - '@shikijs/transformers@1.4.0': - resolution: {integrity: sha512-kzvlWmWYYSeaLKRce/kgmFFORUtBtFahfXRKndor0b60ocYiXufBQM6d6w1PlMuUkdk55aor9xLvy9wy7hTEJg==} + '@shikijs/transformers@1.22.0': + resolution: {integrity: sha512-k7iMOYuGQA62KwAuJOQBgH2IQb5vP8uiB3lMvAMGUgAMMurePOx3Z7oNqJdcpxqZP6I9cc7nc4DNqSKduCxmdg==} + + '@shikijs/types@1.22.0': + resolution: {integrity: sha512-Fw/Nr7FGFhlQqHfxzZY8Cwtwk5E9nKDUgeLjZgt3UuhcM3yJR9xj3ZGNravZZok8XmEZMiYkSMTPlPkULB8nww==} + + '@shikijs/vscode-textmate@9.3.0': + resolution: {integrity: sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==} '@sideway/address@4.1.5': resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} @@ -2862,11 +2606,11 @@ packages: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} - '@tanstack/virtual-core@3.8.1': - resolution: {integrity: sha512-uNtAwenT276M9QYCjTBoHZ8X3MUeCRoGK59zPi92hMIxdfS9AyHjkDWJ94WroDxnv48UE+hIeo21BU84jKc8aQ==} + '@tanstack/virtual-core@3.10.8': + resolution: {integrity: sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA==} - '@tanstack/vue-virtual@3.8.1': - resolution: {integrity: sha512-uhty1LzUbbcVc5zdMMSUjUt/ECTlMCtK49Ww7dH2m4lNNLGYwkj5SbfrAD8uCZxV1VeV7DRMXqhwUTELyR5rrA==} + '@tanstack/vue-virtual@3.10.8': + resolution: {integrity: sha512-DB5QA8c/LfqOqIUCpSs3RdOTVroRRdqeHMqBkYrcashSZtOzIv8xbiqHgg7RYxDfkH5F3Y+e0MkuuyGNDVB0BQ==} peerDependencies: vue: ^2.7.0 || ^3.0.0 @@ -2886,14 +2630,14 @@ packages: '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.20.5': - resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==} + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} - '@types/bonjour@3.5.10': - resolution: {integrity: sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==} + '@types/bonjour@3.5.13': + resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} '@types/braces@3.0.4': resolution: {integrity: sha512-0WR3b8eaISjEW7RpZnclONaLFDf7buaowRHdqLp4vLj54AsSAYWfh3DRbfiYJY9XDxMgx1B4sE1Afw2PGpuHOA==} @@ -2901,8 +2645,8 @@ packages: '@types/cacheable-request@6.0.3': resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} - '@types/connect-history-api-fallback@1.5.0': - resolution: {integrity: sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==} + '@types/connect-history-api-fallback@1.5.4': + resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -2910,8 +2654,11 @@ packages: '@types/cors@2.8.17': resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} - '@types/cytoscape@3.21.4': - resolution: {integrity: sha512-g4M57Zho+AMNYuEptTWH+aZ5VpyF8VcaI2t0+1/PWRZp5XQM6hku1Vo5/Ui721i+L+WCGkI1EjGAeHcYUQ2PRg==} + '@types/cytoscape-fcose@2.2.4': + resolution: {integrity: sha512-QwWtnT8HI9h+DHhG5krGc1ZY0Ex+cn85MvX96ZNAjSxuXiZDnjIZW/ypVkvvubTjIY4rSdkJY1D/Nsn8NDpmAw==} + + '@types/cytoscape@3.21.8': + resolution: {integrity: sha512-6Bo9ZDrv0vfwe8Sg/ERc5VL0yU0gYvP4dgZi0fAXYkKHfyHaNqWRMcwYm3mu4sLsXbB8ZuXE75sR7qnaOL5JgQ==} '@types/d3-array@3.2.1': resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} @@ -2949,8 +2696,8 @@ packages: '@types/d3-fetch@3.0.7': resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} - '@types/d3-force@3.0.9': - resolution: {integrity: sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==} + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} '@types/d3-format@3.0.4': resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} @@ -2958,8 +2705,8 @@ packages: '@types/d3-geo@3.1.0': resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} - '@types/d3-hierarchy@3.1.6': - resolution: {integrity: sha512-qlmD/8aMk5xGorUvTUWHCiumvgaUXYldYjNVOWtYoTYY/L+WwIEAmJxUmTgr9LoGNG0PPAOmqMDJVDPc7DOpPw==} + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} '@types/d3-interpolate@3.0.4': resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} @@ -2988,8 +2735,8 @@ packages: '@types/d3-scale@4.0.8': resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==} - '@types/d3-selection@3.0.10': - resolution: {integrity: sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==} + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} '@types/d3-shape@1.3.12': resolution: {integrity: sha512-8oMzcd4+poSLGgV0R1Q1rOlx/xdmozS4Xab7np0eamFFUYq71AU9pOCJEFnkXW2aI/oXdVYJzw6pssbSut7Z9Q==} @@ -3006,8 +2753,8 @@ packages: '@types/d3-timer@3.0.2': resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} - '@types/d3-transition@3.0.8': - resolution: {integrity: sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==} + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} '@types/d3-zoom@3.0.8': resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} @@ -3018,29 +2765,23 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - '@types/dompurify@3.0.5': - resolution: {integrity: sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==} - - '@types/eslint-scope@3.7.7': - resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} - - '@types/eslint@8.56.10': - resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} - '@types/estree@0.0.39': resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} - '@types/estree@1.0.5': - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - '@types/express-serve-static-core@4.17.43': - resolution: {integrity: sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==} + '@types/express-serve-static-core@4.19.6': + resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==} + + '@types/express-serve-static-core@5.0.0': + resolution: {integrity: sha512-AbXMTZGt40T+KON9/Fdxx0B2WK5hsgxcfXJLr5bFpZ7b4JCex2WyQPTEKdXqfHiY5nKKBScZ7yCoO6Pvgxfvnw==} '@types/express@4.17.21': resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} - '@types/flexsearch@0.7.3': - resolution: {integrity: sha512-HXwADeHEP4exXkCIwy2n1+i0f1ilP1ETQOH5KDOugjkTFZPntWo0Gr8stZOaebkxsdx+k0X/K6obU/+it07ocg==} + '@types/flexsearch@0.7.6': + resolution: {integrity: sha512-H5IXcRn96/gaDmo+rDl2aJuIJsob8dgOXDqf8K0t8rWZd1AFNaaspmRsElESiU+EWE33qfbFPgI0OC/B1g9FCA==} '@types/geojson@7946.0.14': resolution: {integrity: sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==} @@ -3054,14 +2795,17 @@ packages: '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/http-cache-semantics@4.0.4': resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} '@types/http-errors@2.0.4': resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - '@types/http-proxy@1.17.11': - resolution: {integrity: sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==} + '@types/http-proxy@1.17.15': + resolution: {integrity: sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==} '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -3087,35 +2831,29 @@ packages: '@types/keyv@3.1.4': resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} - '@types/linkify-it@3.0.5': - resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} - '@types/lodash@4.17.0': - resolution: {integrity: sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==} - - '@types/lodash@4.17.6': - resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} + '@types/lodash@4.17.10': + resolution: {integrity: sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==} '@types/markdown-it@12.2.3': resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==} - '@types/markdown-it@14.0.1': - resolution: {integrity: sha512-6WfOG3jXR78DW8L5cTYCVVGAsIFZskRHCDo5tbqa+qtKVt4oDRVH7hyIWu1SpDQJlmIoEivNQZ5h+AGAOrgOtQ==} + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} '@types/mdast@3.0.15': resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} - '@types/mdast@4.0.3': - resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} - '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - '@types/mdurl@1.0.5': - resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} '@types/micromatch@4.0.9': resolution: {integrity: sha512-7V+8ncr22h4UoYRLnLXSpTxjQrNUXtWHGeMPRJt1nULXI57G9bIcpyrHlmrQ7QK24EyyuXvYcSSWAM8GA9nqCg==} @@ -3123,26 +2861,26 @@ packages: '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - '@types/mime@3.0.4': - resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} - '@types/minimatch@5.1.2': resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - '@types/node@18.19.38': - resolution: {integrity: sha512-SApYXUF7si4JJ+lO2o6X60OPOnA6wPpbiB09GMCkQ+JAwpa9hxUVG8p7GzA08TKQn5OhzK57rj1wFj+185YsGg==} + '@types/node-forge@1.3.11': + resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@20.12.14': - resolution: {integrity: sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==} + '@types/node@18.19.55': + resolution: {integrity: sha512-zzw5Vw52205Zr/nmErSEkN5FLqXPuKX/k5d1D7RKHATGqU7y6YfX9QxZraUzUrFGqH6XzOzG196BC35ltJC4Cw==} - '@types/node@20.14.7': - resolution: {integrity: sha512-uTr2m2IbJJucF3KUxgnGOZvYbN0QgkGyWxG6973HCpMYFy2KfcgYuIwkJQMQkt1VbBMlvWRbpshFTLxnxCZjKQ==} + '@types/node@20.16.11': + resolution: {integrity: sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==} - '@types/normalize-package-data@2.4.1': - resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} '@types/prettier@2.7.3': resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} @@ -3151,8 +2889,8 @@ packages: resolution: {integrity: sha512-mFMBfMOz8QxhYVbuINtswBp9VL2b4Y0QqYHwqLz3YbgtfAcat2Dl6Y1o4e22S/OVE6Ebl9m7wWiMT2lSbAs1wA==} deprecated: This is a stub types definition. prettier provides its own type definitions, so you do not need this installed. - '@types/qs@6.9.12': - resolution: {integrity: sha512-bZcOkJ6uWrL0Qb2NAWKa7TBU+mJHPzhx9jjLL1KHF+XpzEcR7EXHvjbHlGtR/IsP1vyPrehuS6XqkmaePy//mg==} + '@types/qs@6.9.16': + resolution: {integrity: sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==} '@types/ramda@0.28.25': resolution: {integrity: sha512-HrQNqQAGcITpn9HAJFamDxm7iZeeXiP/95pN5OMbNniDjzCCeOHbBKNGmUy8NRi0fhYS+/cXeo91MFC+06gbow==} @@ -3172,17 +2910,14 @@ packages: '@types/rollup-plugin-visualizer@4.2.4': resolution: {integrity: sha512-BW4Q6D1Qy5gno5qHWrnMDC2dOe/TAKXvqCpckOggCCu+XpS+ZZJJ1lq1+K3bvYccoO3Y7f5kglbFAgYGqCgULg==} - '@types/semver@7.5.8': - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - '@types/send@0.17.4': resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} - '@types/serve-index@1.9.1': - resolution: {integrity: sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==} + '@types/serve-index@1.9.4': + resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} - '@types/serve-static@1.15.5': - resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + '@types/serve-static@1.15.7': + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} '@types/sinonjs__fake-timers@8.1.1': resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==} @@ -3190,8 +2925,8 @@ packages: '@types/sizzle@2.3.8': resolution: {integrity: sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==} - '@types/sockjs@0.3.33': - resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==} + '@types/sockjs@0.3.36': + resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -3202,17 +2937,14 @@ packages: '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} - '@types/trusted-types@2.0.3': - resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==} - '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - '@types/unist@2.0.10': - resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - '@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==} @@ -3220,31 +2952,23 @@ packages: '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + '@types/ws@8.5.12': + resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} + '@types/ws@8.5.5': resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - '@types/yargs@17.0.32': - resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@7.6.0': - resolution: {integrity: sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/eslint-plugin@8.0.0-alpha.44': - resolution: {integrity: sha512-3hqJa/Ak3ahypkcNoNmkkmUg54zV3AWSaalSWAKTQKF5UtXMvRjM5w3nKqS2AQP0dQAkM1u9cXCnOuLeUZr7rw==} + '@typescript-eslint/eslint-plugin@8.8.1': + resolution: {integrity: sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 @@ -3254,8 +2978,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.0.0-alpha.44': - resolution: {integrity: sha512-ho5CiKhp3hDCvkFVpLqiHlMUbgvGELmdVfvpIiKQ1TFGyDcEVpSJUZCDO+gyymgZreJyTfUDHH6eKhF3pgkb0Q==} + '@typescript-eslint/parser@8.8.1': + resolution: {integrity: sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -3264,26 +2988,12 @@ packages: typescript: optional: true - '@typescript-eslint/scope-manager@7.6.0': - resolution: {integrity: sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/scope-manager@8.0.0-alpha.44': - resolution: {integrity: sha512-0w0pDILwfwRXSz9lQBXnJmeGaIbSBgl4vAw/lB2kCnOKYl2SXCVbdNOHPwxWigvQ08QVpuaKy+wEjbFKr9Xwfg==} + '@typescript-eslint/scope-manager@8.8.1': + resolution: {integrity: sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@7.6.0': - resolution: {integrity: sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/type-utils@8.0.0-alpha.44': - resolution: {integrity: sha512-52V6rQxNiebKYLxjcRTzIuTMw/wgrCcLncV27u2O142WyD07gLbICGcxtrxurDIQLMwQ/BuStV2x0cypKSwwdw==} + '@typescript-eslint/type-utils@8.8.1': + resolution: {integrity: sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -3291,20 +3001,16 @@ packages: typescript: optional: true - '@typescript-eslint/types@7.15.0': - resolution: {integrity: sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/types@7.6.0': - resolution: {integrity: sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==} + '@typescript-eslint/types@7.18.0': + resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/types@8.0.0-alpha.44': - resolution: {integrity: sha512-FNBBUTJBNbIaTJhhBbSNxKv+qS8lrwwnpBg36APp5fhDRu8K/YFQZP/VEa19nKBz+8+QUK7R6wV9DHYjj56S7w==} + '@typescript-eslint/types@8.8.1': + resolution: {integrity: sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@7.6.0': - resolution: {integrity: sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==} + '@typescript-eslint/typescript-estree@7.18.0': + resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: typescript: '*' @@ -3312,8 +3018,8 @@ packages: typescript: optional: true - '@typescript-eslint/typescript-estree@8.0.0-alpha.44': - resolution: {integrity: sha512-IyLELYPMFaleWpEVrcYhSfgFXFx4/505P4/vi9Dfp6s6T2xapyAdti6WL9iZbnXk72SL5M0wMp3V73nHn8ce1A==} + '@typescript-eslint/typescript-estree@8.8.1': + resolution: {integrity: sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -3321,26 +3027,23 @@ packages: typescript: optional: true - '@typescript-eslint/utils@7.6.0': - resolution: {integrity: sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - - '@typescript-eslint/utils@8.0.0-alpha.44': - resolution: {integrity: sha512-gOSA4Yo1jufcOuV68yX3hzpwzufd/Ru6KYL04od1T1c5tt6cvN3i5D5Tc3BBJ3xYFE7ge821mJbUJMTc+BMaWg==} + '@typescript-eslint/utils@8.8.1': + resolution: {integrity: sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - '@typescript-eslint/visitor-keys@7.6.0': - resolution: {integrity: sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==} + '@typescript-eslint/visitor-keys@7.18.0': + resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/visitor-keys@8.0.0-alpha.44': - resolution: {integrity: sha512-geWzLM8S6vYGdhA01mWJyGh2V/7VRzAmsD6ZKuc/rLkeJhYjvkMY0g0uMDw/7wmNLeRrpjHnL8HJklrpAlrb9g==} + '@typescript-eslint/visitor-keys@8.8.1': + resolution: {integrity: sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@unocss/astro@0.59.4': resolution: {integrity: sha512-DU3OR5MMR1Uvvec4/wB9EetDASHRg19Moy6z/MiIhn8JWJ0QzWYgSeJcfUX8exomMYv6WUEQJL+CyLI34Wmn8w==} peerDependencies: @@ -3436,133 +3139,102 @@ packages: '@vite-pwa/assets-generator': optional: true - '@vitejs/plugin-vue@5.0.5': - resolution: {integrity: sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==} + '@vitejs/plugin-vue@5.1.4': + resolution: {integrity: sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==} engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: vite: ^5.0.0 vue: ^3.2.25 - '@vitest/coverage-v8@1.5.3': - resolution: {integrity: sha512-DPyGSu/fPHOJuPxzFSQoT4N/Fu/2aJfZRtEpEp8GI7NHsXBGE94CQ+pbEGBUMFjatsHPDJw/+TAF9r4ens2CNw==} + '@vitest/coverage-v8@1.6.0': + resolution: {integrity: sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==} peerDependencies: - vitest: 1.5.3 + vitest: 1.6.0 - '@vitest/expect@1.5.3': - resolution: {integrity: sha512-y+waPz31pOFr3rD7vWTbwiLe5+MgsMm40jTZbQE8p8/qXyBX3CQsIXRx9XK12IbY7q/t5a5aM/ckt33b4PxK2g==} + '@vitest/expect@1.6.0': + resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} - '@vitest/runner@1.5.3': - resolution: {integrity: sha512-7PlfuReN8692IKQIdCxwir1AOaP5THfNkp0Uc4BKr2na+9lALNit7ub9l3/R7MP8aV61+mHKRGiqEKRIwu6iiQ==} + '@vitest/runner@1.6.0': + resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} - '@vitest/snapshot@1.5.3': - resolution: {integrity: sha512-K3mvIsjyKYBhNIDujMD2gfQEzddLe51nNOAf45yKRt/QFJcUIeTQd2trRvv6M6oCBHNVnZwFWbQ4yj96ibiDsA==} + '@vitest/snapshot@1.6.0': + resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} - '@vitest/spy@1.5.3': - resolution: {integrity: sha512-Llj7Jgs6lbnL55WoshJUUacdJfjU2honvGcAJBxhra5TPEzTJH8ZuhI3p/JwqqfnTr4PmP7nDmOXP53MS7GJlg==} + '@vitest/spy@1.6.0': + resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} - '@vitest/ui@1.5.3': - resolution: {integrity: sha512-DoSA5YxcUmeBEK7kJHzXiL2I0d9AijWI33arnUrwiWFDxgZPDxTjvSVsiXhe8qfqhloIHkwazl5E2rhlDd/ErA==} + '@vitest/ui@1.6.0': + resolution: {integrity: sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==} peerDependencies: - vitest: 1.5.3 + vitest: 1.6.0 - '@vitest/utils@1.5.3': - resolution: {integrity: sha512-rE9DTN1BRhzkzqNQO+kw8ZgfeEBCLXiHJwetk668shmNBpSagQxneT5eSqEBLP+cqSiAeecvQmbpFfdMyLcIQA==} + '@vitest/utils@1.6.0': + resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} - '@vue/compat@3.4.31': - resolution: {integrity: sha512-ZmtZNZD881gtwLphxfaRo4y1+Ym4gHmxJlEpsnUHwlsZzKRXdlSE/7qkAxFe+pc5bC6rZU14GUDqqXueljZ9Qg==} + '@vue/compat@3.5.11': + resolution: {integrity: sha512-ESH2z/vUZQi6yRDBCDjBgip6a0Rk48KiT4Dk1LkxSYnqM++3mlqyMo0MgXFxfLhQ1uMaL6pquSCMgKfivrRqRg==} peerDependencies: - vue: 3.4.31 - - '@vue/compiler-core@3.4.30': - resolution: {integrity: sha512-ZL8y4Xxdh8O6PSwfdZ1IpQ24PjTAieOz3jXb/MDTfDtANcKBMxg1KLm6OX2jofsaQGYfIVzd3BAG22i56/cF1w==} - - '@vue/compiler-core@3.4.31': - resolution: {integrity: sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==} - - '@vue/compiler-dom@3.4.30': - resolution: {integrity: sha512-+16Sd8lYr5j/owCbr9dowcNfrHd+pz+w2/b5Lt26Oz/kB90C9yNbxQ3bYOvt7rI2bxk0nqda39hVcwDFw85c2Q==} + vue: 3.5.11 - '@vue/compiler-dom@3.4.31': - resolution: {integrity: sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ==} + '@vue/compiler-core@3.5.11': + resolution: {integrity: sha512-PwAdxs7/9Hc3ieBO12tXzmTD+Ln4qhT/56S+8DvrrZ4kLDn4Z/AMUr8tXJD0axiJBS0RKIoNaR0yMuQB9v9Udg==} - '@vue/compiler-sfc@3.4.30': - resolution: {integrity: sha512-8vElKklHn/UY8+FgUFlQrYAPbtiSB2zcgeRKW7HkpSRn/JjMRmZvuOtwDx036D1aqKNSTtXkWRfqx53Qb+HmMg==} + '@vue/compiler-dom@3.5.11': + resolution: {integrity: sha512-pyGf8zdbDDRkBrEzf8p7BQlMKNNF5Fk/Cf/fQ6PiUz9at4OaUfyXW0dGJTo2Vl1f5U9jSLCNf0EZJEogLXoeew==} - '@vue/compiler-sfc@3.4.31': - resolution: {integrity: sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==} + '@vue/compiler-sfc@3.5.11': + resolution: {integrity: sha512-gsbBtT4N9ANXXepprle+X9YLg2htQk1sqH/qGJ/EApl+dgpUBdTv3yP7YlR535uHZY3n6XaR0/bKo0BgwwDniw==} - '@vue/compiler-ssr@3.4.30': - resolution: {integrity: sha512-ZJ56YZGXJDd6jky4mmM0rNaNP6kIbQu9LTKZDhcpddGe/3QIalB1WHHmZ6iZfFNyj5mSypTa4+qDJa5VIuxMSg==} + '@vue/compiler-ssr@3.5.11': + resolution: {integrity: sha512-P4+GPjOuC2aFTk1Z4WANvEhyOykcvEd5bIj2KVNGKGfM745LaXGr++5njpdBTzVz5pZifdlR1kpYSJJpIlSePA==} - '@vue/compiler-ssr@3.4.31': - resolution: {integrity: sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==} + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} - '@vue/devtools-api@6.6.3': - resolution: {integrity: sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==} + '@vue/devtools-api@7.4.6': + resolution: {integrity: sha512-XipBV5k0/IfTr0sNBDTg7OBUCp51cYMMXyPxLXJZ4K/wmUeMqt8cVdr2ZZGOFq+si/jTyCYnNxeKoyev5DOUUA==} - '@vue/devtools-api@7.1.3': - resolution: {integrity: sha512-W8IwFJ/o5iUk78jpqhvScbgCsPiOp2uileDVC0NDtW38gCWhsnu9SeBTjcdu3lbwLdsjc+H1c5Msd/x9ApbcFA==} + '@vue/devtools-kit@7.4.6': + resolution: {integrity: sha512-NbYBwPWgEic1AOd9bWExz9weBzFdjiIfov0yRn4DrRfR+EQJCI9dn4I0XS7IxYGdkmUJi8mFW42LLk18WsGqew==} - '@vue/devtools-kit@7.1.3': - resolution: {integrity: sha512-NFskFSJMVCBXTkByuk2llzI3KD3Blcm7WqiRorWjD6nClHPgkH5BobDH08rfulqq5ocRt5xV+3qOT1Q9FXJrwQ==} - peerDependencies: - vue: ^3.0.0 - - '@vue/devtools-shared@7.1.3': - resolution: {integrity: sha512-KJ3AfgjTn3tJz/XKF+BlVShNPecim3G21oHRue+YQOsooW+0s+qXvm09U09aO7yBza5SivL1QgxSrzAbiKWjhQ==} - - '@vue/reactivity@3.4.30': - resolution: {integrity: sha512-bVJurnCe3LS0JII8PPoAA63Zd2MBzcKrEzwdQl92eHCcxtIbxD2fhNwJpa+KkM3Y/A4T5FUnmdhgKwOf6BfbcA==} + '@vue/devtools-shared@7.4.6': + resolution: {integrity: sha512-rPeSBzElnHYMB05Cc056BQiJpgocQjY8XVulgni+O9a9Gr9tNXgPteSzFFD+fT/iWMxNuUgGKs9CuW5DZewfIg==} - '@vue/reactivity@3.4.31': - resolution: {integrity: sha512-VGkTani8SOoVkZNds1PfJ/T1SlAIOf8E58PGAhIOUDYPC4GAmFA2u/E14TDAFcf3vVDKunc4QqCe/SHr8xC65Q==} + '@vue/reactivity@3.5.11': + resolution: {integrity: sha512-Nqo5VZEn8MJWlCce8XoyVqHZbd5P2NH+yuAaFzuNSR96I+y1cnuUiq7xfSG+kyvLSiWmaHTKP1r3OZY4mMD50w==} - '@vue/runtime-core@3.4.30': - resolution: {integrity: sha512-qaFEbnNpGz+tlnkaualomogzN8vBLkgzK55uuWjYXbYn039eOBZrWxyXWq/7qh9Bz2FPifZqGjVDl/FXiq9L2g==} + '@vue/runtime-core@3.5.11': + resolution: {integrity: sha512-7PsxFGqwfDhfhh0OcDWBG1DaIQIVOLgkwA5q6MtkPiDFjp5gohVnJEahSktwSFLq7R5PtxDKy6WKURVN1UDbzA==} - '@vue/runtime-core@3.4.31': - resolution: {integrity: sha512-LDkztxeUPazxG/p8c5JDDKPfkCDBkkiNLVNf7XZIUnJ+66GVGkP+TIh34+8LtPisZ+HMWl2zqhIw0xN5MwU1cw==} - - '@vue/runtime-dom@3.4.30': - resolution: {integrity: sha512-tV6B4YiZRj5QsaJgw2THCy5C1H+2UeywO9tqgWEc21tn85qHEERndHN/CxlyXvSBFrpmlexCIdnqPuR9RM9thw==} - - '@vue/runtime-dom@3.4.31': - resolution: {integrity: sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==} - - '@vue/server-renderer@3.4.30': - resolution: {integrity: sha512-TBD3eqR1DeDc0cMrXS/vEs/PWzq1uXxnvjoqQuDGFIEHFIwuDTX/KWAQKIBjyMWLFHEeTDGYVsYci85z2UbTDg==} - peerDependencies: - vue: 3.4.30 + '@vue/runtime-dom@3.5.11': + resolution: {integrity: sha512-GNghjecT6IrGf0UhuYmpgaOlN7kxzQBhxWEn08c/SQDxv1yy4IXI1bn81JgEpQ4IXjRxWtPyI8x0/7TF5rPfYQ==} - '@vue/server-renderer@3.4.31': - resolution: {integrity: sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==} + '@vue/server-renderer@3.5.11': + resolution: {integrity: sha512-cVOwYBxR7Wb1B1FoxYvtjJD8X/9E5nlH4VSkJy2uMA1MzYNdzAAB//l8nrmN9py/4aP+3NjWukf9PZ3TeWULaA==} peerDependencies: - vue: 3.4.31 + vue: 3.5.11 - '@vue/shared@3.4.30': - resolution: {integrity: sha512-CLg+f8RQCHQnKvuHY9adMsMaQOcqclh6Z5V9TaoMgy0ut0tz848joZ7/CYFFyF/yZ5i2yaw7Fn498C+CNZVHIg==} + '@vue/shared@3.5.11': + resolution: {integrity: sha512-W8GgysJVnFo81FthhzurdRAWP/byq3q2qIw70e0JWblzVhjgOMiC2GyovXrZTFQJnFVryYaKGP3Tc9vYzYm6PQ==} - '@vue/shared@3.4.31': - resolution: {integrity: sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==} + '@vueuse/core@10.11.1': + resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==} - '@vueuse/core@10.9.0': - resolution: {integrity: sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==} - - '@vueuse/integrations@10.9.0': - resolution: {integrity: sha512-acK+A01AYdWSvL4BZmCoJAcyHJ6EqhmkQEXbQLwev1MY7NBnS+hcEMx/BzVoR9zKI+UqEPMD9u6PsyAuiTRT4Q==} + '@vueuse/integrations@10.11.1': + resolution: {integrity: sha512-Y5hCGBguN+vuVYTZmdd/IMXLOdfS60zAmDmFYc4BKBcMUPZH1n4tdyDECCPjXm0bNT3ZRUy1xzTLGaUje8Xyaw==} peerDependencies: - async-validator: '*' - axios: '*' - change-case: '*' - drauu: '*' - focus-trap: '*' - fuse.js: '*' - idb-keyval: '*' - jwt-decode: '*' - nprogress: '*' - qrcode: '*' - sortablejs: '*' - universal-cookie: '*' + async-validator: ^4 + axios: ^1 + change-case: ^4 + drauu: ^0.3 + focus-trap: ^7 + fuse.js: ^6 + idb-keyval: ^6 + jwt-decode: ^3 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^6 peerDependenciesMeta: async-validator: optional: true @@ -3589,11 +3261,11 @@ packages: universal-cookie: optional: true - '@vueuse/metadata@10.9.0': - resolution: {integrity: sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA==} + '@vueuse/metadata@10.11.1': + resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==} - '@vueuse/shared@10.9.0': - resolution: {integrity: sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==} + '@vueuse/shared@10.11.1': + resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==} '@wdio/config@7.31.1': resolution: {integrity: sha512-WAfswbCatwiaDVqy6kfF/5T8/WS/US/SRhBGUFrfBuGMIe+RRoHgy7jURFWSvUIE7CNHj8yvs46fLUcxhXjzcQ==} @@ -3695,8 +3367,8 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} - '@zenuml/core@3.23.28': - resolution: {integrity: sha512-RmgHwFei9pM+7uad34Wh7oTS3doLGIg/NLKWvBzWle1jCc+YglFs4KdPpsmFUvBD4UxopIUY7N6/aZEXNXxEpw==} + '@zenuml/core@3.24.12': + resolution: {integrity: sha512-SM9TYgyWl1Bm7oWc4lZLq0q9ejT6RdqxBYav8a4BhVvWkFgND088YCL9xlvo9vPJenwIuVNK+xukgqL1nwfztw==} engines: {node: '>=12.0.0'} JSONSelect@0.4.0: @@ -3717,8 +3389,8 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} - acorn-import-assertions@1.9.0: - resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: acorn: ^8 @@ -3727,19 +3399,9 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-walk@8.3.2: - resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} - engines: {node: '>=0.4.0'} - - acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} - engines: {node: '>=0.4.0'} - hasBin: true - - acorn@8.12.0: - resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==} + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} - hasBin: true acorn@8.12.1: resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} @@ -3750,8 +3412,8 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - agent-base@7.1.0: - resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} aggregate-error@3.1.0: @@ -3770,6 +3432,14 @@ packages: ajv: optional: true + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv-keywords@3.5.2: resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} peerDependencies: @@ -3786,14 +3456,11 @@ packages: ajv@8.12.0: resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} - ajv@8.16.0: - resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} - ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - algoliasearch@4.23.3: - resolution: {integrity: sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==} + algoliasearch@4.24.0: + resolution: {integrity: sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==} amdefine@1.0.1: resolution: {integrity: sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==} @@ -3810,9 +3477,9 @@ packages: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} - ansi-escapes@6.2.1: - resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==} - engines: {node: '>=14.16'} + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} ansi-html-community@0.0.8: resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} @@ -3827,8 +3494,8 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} ansi-sequence-parser@1.1.1: @@ -3865,6 +3532,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + app-module-path@2.2.0: + resolution: {integrity: sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==} + app-path@3.3.0: resolution: {integrity: sha512-EAgEXkdcxH1cgEePOSsmUtw9ItPl0KTxnh/pj9ZbhvbKbij9x0oX6PWpGnorDr0DS5AosLgoa5n3T/hZmKQpYA==} engines: {node: '>=8'} @@ -3902,9 +3572,6 @@ packages: array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - array-flatten@2.1.2: - resolution: {integrity: sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==} - array-timsort@1.0.3: resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} @@ -3930,12 +3597,16 @@ packages: assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + ast-module-types@6.0.0: + resolution: {integrity: sha512-LFRg7178Fw5R4FAEwZxVqiRI8IxSM+Ay2UBrHoCerXNme+kMMMfz7T3xDGV/c2fer87hcrtgJGsnSOfUrPK6ng==} + engines: {node: '>=18'} + astral-regex@2.0.0: 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==} @@ -3952,17 +3623,17 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - avvio@7.2.5: - resolution: {integrity: sha512-AOhBxyLVdpOad3TujtC9kL/9r3HnTkxwQ5ggOsYrvvZP1cCFvzHWJd5XxZDFuTn+IN8vkKSG5SEJrd27vCSbeA==} + avvio@8.4.0: + resolution: {integrity: sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==} aws-sign2@0.7.0: resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} - aws4@1.12.0: - resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==} + aws4@1.13.2: + resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} - axios@1.7.2: - resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} + axios@1.7.7: + resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} babel-jest@29.7.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} @@ -3970,8 +3641,8 @@ packages: peerDependencies: '@babel/core': ^7.8.0 - babel-loader@9.1.3: - resolution: {integrity: sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==} + babel-loader@9.2.1: + resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} engines: {node: '>= 14.15.0'} peerDependencies: '@babel/core': ^7.12.0 @@ -3990,8 +3661,8 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-corejs3@0.10.4: - resolution: {integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==} + babel-plugin-polyfill-corejs3@0.10.6: + resolution: {integrity: sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -4000,8 +3671,8 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-preset-current-node-syntax@1.0.1: - resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + babel-preset-current-node-syntax@1.1.0: + resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==} peerDependencies: '@babel/core': ^7.0.0 @@ -4026,8 +3697,12 @@ packages: bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} - binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} binary-searching@2.0.5: @@ -4036,6 +3711,9 @@ packages: binary@0.3.0: resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} + birpc@0.2.17: + resolution: {integrity: sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg==} + blob-util@2.0.2: resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} @@ -4048,12 +3726,12 @@ packages: bmpimagejs@1.0.4: resolution: {integrity: sha512-21oKU7kbRt2OgOOj7rdiNr/yznDNUQ585plxR00rsmECcZr+6O1oCwB8OIoSHk/bDhbG8mFXIdeQuCPHgZ6QBw==} - body-parser@1.20.2: - resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - bonjour-service@1.1.1: - resolution: {integrity: sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==} + bonjour-service@1.2.1: + resolution: {integrity: sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==} boxen@5.1.2: resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} @@ -4065,21 +3743,12 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} - engines: {node: '>=8'} - braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.23.0: - resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - browserslist@4.23.1: - resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} + browserslist@4.24.0: + resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -4157,11 +3826,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001594: - resolution: {integrity: sha512-VblSX6nYqyJVs8DKFMldE2IVCJjZ225LW00ydtUWwh5hk9IfkTOffO6r8gJNsH0qqqeAF8KrbMYA2VEwTlGW5g==} - - caniuse-lite@1.0.30001636: - resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==} + caniuse-lite@1.0.30001667: + resolution: {integrity: sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==} caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -4169,8 +3835,8 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + chai@4.5.0: + resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} engines: {node: '>=4'} chainsaw@0.1.0: @@ -4204,9 +3870,15 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + character-entities-legacy@1.1.4: resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + character-entities@1.2.4: resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} @@ -4216,6 +3888,9 @@ packages: character-reference-invalid@1.1.4: resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} @@ -4235,8 +3910,8 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - chrome-trace-event@1.0.3: - resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} ci-info@3.9.0: @@ -4247,8 +3922,8 @@ packages: resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} engines: {node: '>=8'} - cjs-module-lexer@1.2.3: - resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + cjs-module-lexer@1.4.1: + resolution: {integrity: sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==} cjson@0.3.0: resolution: {integrity: sha512-bBRQcCIHzI1IVH59fR0bwGrFmi3Btb/JNwM/n401i1DnYgWndpsUBiQRAddLflkZage20A2d25OAWZZk0vBRlA==} @@ -4286,12 +3961,12 @@ packages: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} - cli-cursor@4.0.0: - resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} - cli-table3@0.6.4: - resolution: {integrity: sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==} + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} engines: {node: 10.* || >= 12.*} cli-truncate@2.1.0: @@ -4316,6 +3991,10 @@ packages: clone-response@1.0.3: resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -4354,14 +4033,13 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@11.0.0: resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==} engines: {node: '>=16'} - commander@12.0.0: - resolution: {integrity: sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==} - engines: {node: '>=18'} - commander@12.1.0: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} @@ -4389,8 +4067,8 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - comment-json@4.2.3: - resolution: {integrity: sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==} + comment-json@4.2.5: + resolution: {integrity: sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==} engines: {node: '>= 6'} comment-parser@1.4.1: @@ -4423,12 +4101,8 @@ packages: engines: {node: ^14.13.0 || >=16.0.0} hasBin: true - confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - - configstore@6.0.0: - resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} - engines: {node: '>=12'} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} connect-history-api-fallback@2.0.0: resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} @@ -4438,6 +4112,10 @@ packages: resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} engines: {node: ^14.18.0 || >=16.10.0} + console.table@0.10.0: + resolution: {integrity: sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==} + engines: {node: '> 0.10'} + content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -4459,16 +4137,20 @@ packages: cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - cookie@0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} - engines: {node: '>= 0.6'} - cookie@0.6.0: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} - core-js-compat@3.37.1: - resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + + core-js-compat@3.38.1: + resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==} core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} @@ -4483,6 +4165,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'} @@ -4506,6 +4191,9 @@ packages: engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} hasBin: true + cross-spawn@5.1.0: + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + cross-spawn@6.0.5: resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} engines: {node: '>=4.8'} @@ -4518,75 +4206,42 @@ packages: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} - crypto-random-string@4.0.0: - resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} - engines: {node: '>=12'} - - cspell-config-lib@8.7.0: - resolution: {integrity: sha512-depsd01GbLBo71/tfRrL5iECWQLS4CjCxA9C01dVkFAJqVB0s+K9KLKjTlq5aHOhcvo9Z3dHV+bGQCf5/Q7bfw==} - engines: {node: '>=18'} - - cspell-config-lib@8.9.1: - resolution: {integrity: sha512-gSXAazmeX+CCpFCsNQQqHRO/nn01kMnCoB0v+7AM0Bip2iDXRl+LmUEJGNcnFaiJG3liaZ8+S5/qCDbza010VQ==} - engines: {node: '>=18'} - - cspell-dictionary@8.7.0: - resolution: {integrity: sha512-S6IpZSzIMxlOO/33NgCOuP0TPH2mZbw8d5CP44z5jajflloq8l74MeJLkeDzYfCRcm0Rtk0A5drBeMg+Ai34OA==} + cspell-config-lib@8.14.4: + resolution: {integrity: sha512-cnUeJfniTiebqCaQmIUnbSrPrTH7xzKRQjJDHAEV0WYnOG2MhRXI13OzytdFdhkVBdStmgTzTCJKE7x+kmU2NA==} engines: {node: '>=18'} - cspell-dictionary@8.9.1: - resolution: {integrity: sha512-sJy9gApLxJNE+YqWeulCTj3XC/ME4aacOHEl/SZ5bsaxkGx3KzBlzCMG7LfqUjOM8rwfBPsYO7zWPCiJQgxGPg==} + cspell-dictionary@8.14.4: + resolution: {integrity: sha512-pZvQHxpAW5fZAnt3ZKKy3s7M+3CX2t8tCS3uJrpEHIynlCawpG0fPF78rVE5o+g0dON36Lguc/BUuSN4IWKLmQ==} engines: {node: '>=18'} - cspell-gitignore@8.7.0: - resolution: {integrity: sha512-yvUZ86qyopUpDgn+YXP1qTpUe/lp65ZFvpMtw21lWHTFlg1OWKntr349EQU/5ben/K6koxk1FiElCBV7Lr4uFg==} + cspell-gitignore@8.14.4: + resolution: {integrity: sha512-RwfQEW5hD7CpYwS7m3b0ONG0nTLKP6bL2tvMdl7qtaYkL7ztGdsBTtLD1pmwqUsCbiN5RuaOxhYOYeRcpFRIkQ==} engines: {node: '>=18'} hasBin: true - cspell-glob@8.7.0: - resolution: {integrity: sha512-AMdfx0gvROA/aIL8t8b5Y5NtMgscGZELFj6WhCSZiQSuWRxXUKiLGGLUFjx2y0hgXN9LUYOo6aBjvhnxI/v71g==} - engines: {node: '>=18'} - - cspell-glob@8.9.1: - resolution: {integrity: sha512-b60WfczgG3NgGp5pyS4NfwSu7FEF7AmkP1btJqj17UAWsm/idUdGdOgaZazZuPgQJbcQvOlpBQP0+SEi8Jo3QA==} - engines: {node: '>=18'} - - cspell-grammar@8.7.0: - resolution: {integrity: sha512-SGcXc7322wU2WNRi7vtpToWDXTqZHhxqvR+aIXHT2kkxlMSWp3Rvfpshd0ckgY54nZtgw7R/JtKND2jeACRpwQ==} + cspell-glob@8.14.4: + resolution: {integrity: sha512-C/xTS5nujMRMuguibq92qMVP767mtxrur7DcVolCvpzcivm1RB5NtIN0OctQxTyMbnmKeQv1t4epRKQ9A8vWRg==} engines: {node: '>=18'} - hasBin: true - cspell-grammar@8.9.1: - resolution: {integrity: sha512-BqaDp3Z+baLZyb3A5h/zWESsO7e8vUaOlrDt1RRVEnpboIUnj7iNkcFmDp3s9PTpBCURlgHHs8SR/+c49aKDGg==} + cspell-grammar@8.14.4: + resolution: {integrity: sha512-yaSKAAJDiamsw3FChbw4HXb2RvTQrDsLelh1+T4MavarOIcAxXrqAJ8ysqm++g+S/ooJz2YO8YWIyzJKxcMf8g==} engines: {node: '>=18'} hasBin: true - cspell-io@8.7.0: - resolution: {integrity: sha512-o7OltyyvVkRG1gQrIqGpN5pUkHNnv6rvihb7Qu6cJ8jITinLGuWJuEQpgt0eF5yIr624jDbFwSzAxsFox8riQg==} - engines: {node: '>=18'} - - cspell-io@8.9.1: - resolution: {integrity: sha512-O2F79Rzj28Mvmj4AQLkDWOXWaLnvkJhxPm/Yb3viKlbhwmL5BWUi0APbWA3dtyF+ImX1W27YrNFyvT/PGNZ5Dw==} - engines: {node: '>=18'} - - cspell-lib@8.7.0: - resolution: {integrity: sha512-qDSHZGekwiDmouYRECTQokE+hgAuPqREm+Hb+G3DoIo3ZK5H47TtEUo8fNCw22XsKefcF8X28LiyoZwiYHVpSg==} + cspell-io@8.14.4: + resolution: {integrity: sha512-o6OTWRyx/Az+PFhr1B0wMAwqG070hFC9g73Fkxd8+rHX0rfRS69QZH7LgSmZytqbZIMxCTDGdsLl33MFGWCbZQ==} engines: {node: '>=18'} - cspell-lib@8.9.1: - resolution: {integrity: sha512-xrtoXvSjkMcwE1yUcyjiqLFPZiK0CNQjOKKS9PQaaK7ZBoERPQ7grz05uFCYdboSXt0FhlP8tC9E5oEt+xtGCA==} + cspell-lib@8.14.4: + resolution: {integrity: sha512-qdkUkKtm+nmgpA4jQbmQTuepDfjHBDWvs3zDuEwVIVFq/h8gnXrRr75gJ3RYdTy+vOOqHPoLLqgxyqkUUrUGXA==} engines: {node: '>=18'} - cspell-trie-lib@8.7.0: - resolution: {integrity: sha512-W3Nh2cO7gMV91r+hLqyTMgKlvRl4W5diKs5YiyOxjZumRkMBy42IzcNYtgIIacOxghklv96F5Bd1Vx/zY6ylGA==} + cspell-trie-lib@8.14.4: + resolution: {integrity: sha512-zu8EJ33CH+FA5lwTRGqS//Q6phO0qtgEmODMR1KPlD7WlrfTFMb3bWFsLo/tiv5hjpsn7CM6dYDAAgBOSkoyhQ==} engines: {node: '>=18'} - cspell-trie-lib@8.9.1: - resolution: {integrity: sha512-rUED/lNlFcsRfkMal6+zLz7JW3/cV79KGhwxnwu1fjNS0nlLSAUGTTiAQBQSR+pU/UW+BTkmULHVuNh+DUN93w==} - engines: {node: '>=18'} - - cspell@8.7.0: - resolution: {integrity: sha512-77nRPgLl240C6FK8RKVKo34lP15Lzp/6bk+SKYJFwUKKXlcgWXDis+Lw4JolA741/JgHtuxmhW1C8P7dCKjJ3w==} + cspell@8.14.4: + resolution: {integrity: sha512-R5Awb3i/RKaVVcZzFt8dkN3M6VnifIEDYBcbzbmYjZ/Eq+ASF+QTmI0E9WPhMEcFM1nd7YOyXnETo560yRdoKw==} engines: {node: '>=18'} hasBin: true @@ -4599,8 +4254,8 @@ packages: engines: {node: '>=4'} hasBin: true - cssstyle@4.0.1: - resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} + cssstyle@4.1.0: + resolution: {integrity: sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==} engines: {node: '>=18'} csstree-validator@3.0.0: @@ -4620,11 +4275,15 @@ packages: peerDependencies: cypress: ^4.5.0 - cypress-wait-until@3.0.1: - resolution: {integrity: sha512-kpoa8yL6Bi/JNsThGBbrrm7g4SNzYyBUv9M5pF6/NTVm/ClY0HnJzeuWnHiAUZKIZ5l86Oedb12wQyjx7/CWPg==} + cypress-split@1.24.0: + resolution: {integrity: sha512-ZEFh1m6z+HwPWpB1h9YAF1L6K/wkPBR3vD+v8Rrg8BRm50sZ7oSx6Dw+sv6zfr5Pfqv247CnobLewdFBLlPIBQ==} + hasBin: true + + cypress-wait-until@3.0.2: + resolution: {integrity: sha512-iemies796dD5CgjG5kV0MnpEmKSH+s7O83ZoJLVzuVbZmm4lheMsZqAVT73hlMx4QlkwhxbyUzhOBUOZwoOe0w==} - cypress@13.7.3: - resolution: {integrity: sha512-uoecY6FTCAuIEqLUYkTrxamDBjMHTYak/1O7jtgwboHiTnS1NaMOoR08KcTrbRZFCBvYOiS4tEkQRmsV+xcrag==} + cypress@13.15.0: + resolution: {integrity: sha512-53aO7PwOfi604qzOkCSzNlWquCynLlKE/rmmpSPcziRH6LNfaDUAklQT6WJIsD8ywxlIy+uVZsnTMCCQVd2kTw==} engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} hasBin: true @@ -4633,8 +4292,13 @@ packages: peerDependencies: cytoscape: ^3.2.0 - cytoscape@3.29.3: - resolution: {integrity: sha512-ffoRk1p1G0TcKeLJMrtYbEYomU1ZLP3pA6wGRU0AmPomNPXOaKC8u6hiw9SltlNuOmjbiF6DIs7HH15YgHNlnA==} + cytoscape-fcose@2.2.0: + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.30.2: + resolution: {integrity: sha512-oICxQsjW8uSaRmn4UK/jkczKOqTrVqt5/1WL0POiJUT2EKNc9STM4hYFHv917yu55aTBMFNRzymlJhVAiWPCxw==} engines: {node: '>=0.10'} d3-array@2.12.1: @@ -4697,8 +4361,8 @@ packages: resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} engines: {node: '>=12'} - d3-geo@3.1.0: - resolution: {integrity: sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==} + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} engines: {node: '>=12'} d3-hierarchy@3.1.2: @@ -4731,8 +4395,8 @@ packages: d3-sankey@0.12.3: resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} - d3-scale-chromatic@3.0.0: - resolution: {integrity: sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==} + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} engines: {node: '>=12'} d3-scale@4.0.2: @@ -4780,8 +4444,8 @@ packages: resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} engines: {node: '>=0.12'} - dagre-d3-es@7.0.10: - resolution: {integrity: sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==} + dagre-d3-es@7.0.11: + resolution: {integrity: sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw==} dashdash@1.14.1: resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} @@ -4807,15 +4471,15 @@ packages: resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} engines: {node: '>= 0.4'} + dataloader@1.4.0: + resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} + date-fns@2.30.0: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} - dayjs@1.11.10: - resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} - - dayjs@1.11.11: - resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} @@ -4851,8 +4515,8 @@ packages: supports-color: optional: true - debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -4874,18 +4538,22 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - dedent@1.5.1: - resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} + dedent@1.5.3: + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: babel-plugin-macros: optional: true - deep-eql@4.1.3: - resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} engines: {node: '>=6'} + deep-equal@2.2.3: + resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} + engines: {node: '>= 0.4'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -4901,6 +4569,9 @@ packages: resolution: {integrity: sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==} engines: {node: '>=8'} + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + defer-to-connect@2.0.1: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} engines: {node: '>=10'} @@ -4935,6 +4606,11 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} + dependency-tree@11.0.1: + resolution: {integrity: sha512-eCt7HSKIC9NxgIykG2DRq3Aewn9UhVS14MB3rEn6l/AsEI1FBg6ZGSlCU0SZ6Tjm2kkhj6/8c2pViinuyKELhg==} + engines: {node: '>=18'} + hasBin: true + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -4946,6 +4622,10 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + detect-libc@2.0.3: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} @@ -4957,6 +4637,49 @@ packages: detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + detective-amd@6.0.0: + resolution: {integrity: sha512-NTqfYfwNsW7AQltKSEaWR66hGkTeD52Kz3eRQ+nfkA9ZFZt3iifRCWh+yZ/m6t3H42JFwVFTrml/D64R2PAIOA==} + engines: {node: '>=18'} + hasBin: true + + detective-cjs@6.0.0: + resolution: {integrity: sha512-R55jTS6Kkmy6ukdrbzY4x+I7KkXiuDPpFzUViFV/tm2PBGtTCjkh9ZmTuJc1SaziMHJOe636dtiZLEuzBL9drg==} + engines: {node: '>=18'} + + detective-es6@5.0.0: + resolution: {integrity: sha512-NGTnzjvgeMW1khUSEXCzPDoraLenWbUjCFjwxReH+Ir+P6LGjYtaBbAvITWn2H0VSC+eM7/9LFOTAkrta6hNYg==} + engines: {node: '>=18'} + + detective-postcss@7.0.0: + resolution: {integrity: sha512-pSXA6dyqmBPBuERpoOKKTUUjQCZwZPLRbd1VdsTbt6W+m/+6ROl4BbE87yQBUtLoK7yX8pvXHdKyM/xNIW9F7A==} + engines: {node: ^14.0.0 || >=16.0.0} + peerDependencies: + postcss: ^8.4.38 + + detective-sass@6.0.0: + resolution: {integrity: sha512-h5GCfFMkPm4ZUUfGHVPKNHKT8jV7cSmgK+s4dgQH4/dIUNh9/huR1fjEQrblOQNDalSU7k7g+tiW9LJ+nVEUhg==} + engines: {node: '>=18'} + + detective-scss@5.0.0: + resolution: {integrity: sha512-Y64HyMqntdsCh1qAH7ci95dk0nnpA29g319w/5d/oYcHolcGUVJbIhOirOFjfN1KnMAXAFm5FIkZ4l2EKFGgxg==} + engines: {node: '>=18'} + + detective-stylus@5.0.0: + resolution: {integrity: sha512-KMHOsPY6aq3196WteVhkY5FF+6Nnc/r7q741E+Gq+Ax9mhE2iwj8Hlw8pl+749hPDRDBHZ2WlgOjP+twIG61vQ==} + engines: {node: '>=18'} + + detective-typescript@13.0.0: + resolution: {integrity: sha512-tcMYfiFWoUejSbvSblw90NDt76/4mNftYCX0SMnVRYzSXv8Fvo06hi4JOPdNvVNxRtCAKg3MJ3cBJh+ygEMH+A==} + engines: {node: ^14.14.0 || >=16.0.0} + peerDependencies: + typescript: ^5.4.4 + + detective-vue2@2.0.3: + resolution: {integrity: sha512-AgWdSfVnft8uPGnUkdvE1EDadEENDCzoSRMt2xZfpxsjqVO617zGWXbB8TGIxHaqHz/nHa6lOSgAB8/dt0yEug==} + engines: {node: '>=18'} + peerDependencies: + typescript: ^5.4.4 + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -4974,11 +4697,8 @@ packages: dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - dns-equal@1.0.0: - resolution: {integrity: sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==} - - dns-packet@5.6.0: - resolution: {integrity: sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==} + dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} engines: {node: '>=6'} dom-serializer@2.0.0: @@ -4994,26 +4714,29 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} - dompurify@3.1.6: - resolution: {integrity: sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==} + dompurify@3.2.1: + resolution: {integrity: sha512-NBHEsc0/kzRYQd+AY6HR6B/IgsqzBABrqJbpCDQII/OK6h7B7LXzweZTDsqSW2LkTRpoxf18YUP+YjGySk6B3w==} domutils@3.1.0: resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} - dot-prop@6.0.1: - resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} - engines: {node: '>=10'} - dotenv@16.4.5: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} + dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + easy-table@1.1.0: + resolution: {integrity: sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==} + ebnf-parser@0.1.10: resolution: {integrity: sha512-urvSxVQ6XJcoTpc+/x2pWhhuOX4aljCNQpwzw+ifZvV1andZkAmiJc3Rq1oGEAQmcjiLceyMXOy1l8ms8qs2fQ==} @@ -5028,11 +4751,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.4.692: - resolution: {integrity: sha512-d5rZRka9n2Y3MkWRN74IoAsxR0HK3yaAt7T50e3iT9VZmCCQDT3geXUO5ZRMhDToa1pkCeQXuNo+0g+NfDOVPA==} - - electron-to-chromium@1.4.808: - resolution: {integrity: sha512-0ItWyhPYnww2VOuCGF4s1LTfbrdAV2ajy/TN+ZTuhR23AHI6rWHCrBXJ/uxoXOvRRqw8qjYVrG81HFI7x/2wdQ==} + electron-to-chromium@1.5.33: + resolution: {integrity: sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==} elkjs@0.9.3: resolution: {integrity: sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==} @@ -5041,8 +4761,8 @@ packages: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} - emoji-regex@10.3.0: - resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -5054,14 +4774,18 @@ packages: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - enhanced-resolve@5.16.0: - resolution: {integrity: sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==} + enhanced-resolve@5.17.1: + resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} engines: {node: '>=10.13.0'} enquirer@2.4.1: @@ -5080,11 +4804,15 @@ packages: resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - envinfo@7.10.0: - resolution: {integrity: sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw==} + envinfo@7.14.0: + resolution: {integrity: sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==} engines: {node: '>=4'} hasBin: true + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -5100,8 +4828,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@1.4.1: - resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==} + es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} es-module-lexer@1.5.4: resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} @@ -5139,23 +4867,18 @@ packages: es6-weak-map@2.0.3: resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} - esbuild@0.19.12: - resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.20.2: - resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} - engines: {node: '>=12'} - hasBin: true - esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} hasBin: true - escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} escape-html@1.0.3: @@ -5182,26 +4905,31 @@ packages: engines: {node: '>=0.10.0'} hasBin: true + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + eslint-config-prettier@9.1.0: resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-plugin-cypress@3.3.0: - resolution: {integrity: sha512-HPHMPzYBIshzJM8wqgKSKHG2p/8R0Gbg4Pb3tcdC9WrmkuqxiKxSKbjunUrajhV5l7gCIFrh1P7C7GuBqH6YuQ==} + eslint-plugin-cypress@3.5.0: + resolution: {integrity: sha512-JZQ6XnBTNI8h1B9M7wJSFzc48SYbh7VMMKaNTQOFa3BQlnmXPrVc4PKen8R+fpv6VleiPeej6VxloGb42zdRvw==} peerDependencies: eslint: '>=7' - eslint-plugin-html@8.1.1: - resolution: {integrity: sha512-6qmlJsc40D2m3Dn9oEH+0PAOkJhxVu0f5sVItqpCE0YWgYnyP4xCjBc3UWTHaJcY9ARkWOLIIuXLq0ndRnQOHw==} + eslint-plugin-html@8.1.2: + resolution: {integrity: sha512-pbRchDV2SmqbCi/Ev/q3aAikzG9BcFe0IjjqjtMn8eTLq71ZUggyJB6CDmuwGAXmYZHrXI12XTfCqvgcnPRqGw==} engines: {node: '>=16.0.0'} - eslint-plugin-jest@28.6.0: - resolution: {integrity: sha512-YG28E1/MIKwnz+e2H7VwYPzHUYU4aMa19w0yGcwXnnmJH6EfgHahTJ2un3IyraUxNfnz/KUhJAFXNNwWPo12tg==} + eslint-plugin-jest@28.8.3: + resolution: {integrity: sha512-HIQ3t9hASLKm2IhIOqnu+ifw7uLZkIlR7RYNv7fMcEi/p0CIiJmfriStQS2LDkgtY4nyLbIZAD+JL347Yc2ETQ==} engines: {node: ^16.10.0 || ^18.12.0 || >=20.0.0} peerDependencies: - '@typescript-eslint/eslint-plugin': ^6.0.0 || ^7.0.0 + '@typescript-eslint/eslint-plugin': ^6.0.0 || ^7.0.0 || ^8.0.0 eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 jest: '*' peerDependenciesMeta: @@ -5210,14 +4938,14 @@ packages: jest: optional: true - eslint-plugin-jsdoc@48.5.2: - resolution: {integrity: sha512-VXBJFviQz30rynlOEQ+dNWLmeopjoAgutUVrWOZwm6Ki4EVDm4XkyIqAV/Zhf7FcDr0AG0aGmRn5FxxCtAF0tA==} + eslint-plugin-jsdoc@50.3.1: + resolution: {integrity: sha512-SY9oUuTMr6aWoJggUS40LtMjsRzJPB5ZT7F432xZIHK3EfHF+8i48GbUBpwanrtlL9l1gILNTHK9o8gEhYLcKA==} engines: {node: '>=18'} 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: @@ -5226,21 +4954,21 @@ packages: peerDependencies: eslint: '>=9.0.0' - eslint-plugin-markdown@5.0.0: - resolution: {integrity: sha512-kY2u9yDhzvfZ0kmRTsvgm3mTnvZgTSGIIPeHg3yesSx4R5CTCnITUjCPhzCD1MUhNcqHU5Tr6lzx+02EclVPbw==} + eslint-plugin-markdown@5.1.0: + resolution: {integrity: sha512-SJeyKko1K6GwI0AN6xeCDToXDkfKZfXcexA6B+O2Wr2btUS9GrC+YgwSyVli5DJnctUHjFXcQ2cqTaAmVoLi2A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=8' - eslint-plugin-no-only-tests@3.1.0: - resolution: {integrity: sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw==} + eslint-plugin-no-only-tests@3.3.0: + resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==} engines: {node: '>=5.0.0'} eslint-plugin-tsdoc@0.3.0: resolution: {integrity: sha512-0MuFdBrrJVBjT/gyhkP2BqpD0np1NxNLfQ38xXDlSs/KVVpKI2A6vN7jx2Rve/CyUsvOsMGwp9KKrinv7q9g3A==} - eslint-plugin-unicorn@54.0.0: - resolution: {integrity: sha512-XxYLRiYtAWiAjPv6z4JREby1TAE2byBC7wlh0V4vWDCpccOSU1KovWV//jqPXF6bq3WKxqX9rdjoRQ1EhdmNdQ==} + eslint-plugin-unicorn@56.0.0: + resolution: {integrity: sha512-aXpddVz/PQMmd69uxO98PA4iidiVNvA0xOtbpUoz1WhBd4RxOQQYqN618v68drY0hmy5uU2jy1bheKEVWBjlPw==} engines: {node: '>=18.18'} peerDependencies: eslint: '>=8.56.0' @@ -5249,29 +4977,34 @@ packages: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} - eslint-scope@8.0.1: - resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==} + eslint-scope@8.1.0: + resolution: {integrity: sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.0.0: - resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} + eslint-visitor-keys@4.1.0: + resolution: {integrity: sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.6.0: - resolution: {integrity: sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==} + eslint@9.12.0: + resolution: {integrity: sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true esniff@2.0.1: resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} engines: {node: '>=0.10'} - espree@10.1.0: - resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} + espree@10.2.0: + resolution: {integrity: sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esprima@1.1.1: @@ -5284,8 +5017,8 @@ packages: engines: {node: '>=4'} hasBin: true - esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -5376,12 +5109,8 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - express@4.19.1: - resolution: {integrity: sha512-K4w1/Bp7y8iSiVObmCrtq8Cs79XjJc/RU2YYkZQ7wpUu5ZyZ7MtPHkqoMz4pf+mgXfNvo2qft8D9OnrH2ABk9w==} - engines: {node: '>= 0.10.0'} - - express@4.19.2: - resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} + express@4.21.0: + resolution: {integrity: sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==} engines: {node: '>= 0.10.0'} ext@1.7.0: @@ -5390,6 +5119,13 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -5419,22 +5155,27 @@ packages: fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - fast-json-stringify@2.7.13: - resolution: {integrity: sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA==} - engines: {node: '>= 10.0.0'} + fast-json-stringify@5.16.1: + resolution: {integrity: sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + fast-redact@3.5.0: resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} engines: {node: '>=6'} - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-shuffle@6.1.0: + resolution: {integrity: sha512-3aj8oO6bvZFKYDGvXNmmEuxyOjre8trCpIbtFSM/DSKd+o3iSbQQPb5BZQeJ7SPYVivn9EeW3gKh0QdnD027MQ==} + + fast-uri@2.4.0: + resolution: {integrity: sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==} - fast-uri@3.0.1: - resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + fast-uri@3.0.2: + resolution: {integrity: sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==} fastest-levenshtein@1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} @@ -5446,8 +5187,8 @@ packages: fastify-plugin@3.0.1: resolution: {integrity: sha512-qKcDXmuZadJqdTm6vlCqioEbyewF60b/0LOFCcYN1B6BIZGlYJumWWOYs70SFYLDAH4YqdE1cxH/RKMG7rFxgA==} - fastify@3.29.5: - resolution: {integrity: sha512-FBDgb1gkenZxxh4sTD6AdI6mFnZnsgckpjIXzIvfLSYCa4isfQeD8QWGPib63dxq6btnY0l1j8I0xYhMvUb+sw==} + fastify@4.28.1: + resolution: {integrity: sha512-kFWUtpNr4i7t5vY2EJPCN2KgMVpuqfU4NjnJNCgiNB900oiDeYqaNDRcAfeBbOF5hGixixxcKnOU4KN9z6QncQ==} fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -5465,6 +5206,14 @@ packages: fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fdir@6.4.0: + resolution: {integrity: sha512-3oB133prH1o4j/L5lLW7uOCF1PlD+/It2L0eL/iAqWMB91RBbqTewABqxhj0ibBd90EEmWZq7ntIWzVaWcXTGQ==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + ferrum@1.9.4: resolution: {integrity: sha512-ooNerLoIht/dK4CQJux93z/hnt9JysrXniJCI3r6YRgmHeXC57EJ8XaTCT1Gm8LfhIAeWxyJA0O7d/W3pqDYRg==} @@ -5483,22 +5232,27 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + file-entry-cache@9.1.0: + resolution: {integrity: sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==} + engines: {node: '>=18'} + file-saver@2.0.5: resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} + filing-cabinet@5.0.2: + resolution: {integrity: sha512-RZlFj8lzyu6jqtFBeXNqUjjNG6xm+gwXue3T70pRxw1W40kJwlgq0PSWAmh0nAnn5DHuBIecLXk9+1VKS9ICXA==} + engines: {node: '>=18'} + hasBin: true fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@1.2.0: - resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + finalhandler@1.3.1: + resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} find-cache-dir@3.3.2: @@ -5509,14 +5263,23 @@ packages: resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} engines: {node: '>=14.16'} - find-my-way@4.5.1: - resolution: {integrity: sha512-kE0u7sGoUFbMXcOG/xpkmz4sRLCklERnBcg7Ftuu1iAxsfEt2S46RLJ3Sq7vshsEy2wJT2hZxE58XZK27qa8kg==} - engines: {node: '>=10'} + find-cypress-specs@1.43.4: + resolution: {integrity: sha512-GAdz6lfBndbOq9OOJ3psThQ56hqgL8tZUCOLnl60d/l56bvHkC0TNwyqlLfBObiscirSZWSgyGL86jJkrpFMrA==} + engines: {node: '>=18'} + hasBin: true + + find-my-way@8.2.2: + resolution: {integrity: sha512-Dobi7gcTEq8yszimcfp/R7+owiT4WncAJ7VTTgFH1jYJ5GaG1FbhjwDG820hptN0QDFvzVY3RfCzdInvGPGzjA==} + engines: {node: '>=14'} find-process@1.4.7: resolution: {integrity: sha512-/U4CYp1214Xrp3u3Fqr9yNynUrr5Le4y0SsJh2lMDDSbpwYSz3M2SMWQC+wqcx79cN8PQtHQIL8KnuY9M66fdg==} hasBin: true + find-test-names@1.28.18: + resolution: {integrity: sha512-hhnGdkWK+qEA5Z02Tu0OqGQIUjFZNyOCE4WaJpbhW4hAF1+NZ7OCr0Bss9RCaj7BBtjoIjkU93utobQ8pg2iVg==} + hasBin: true + find-up-simple@1.0.0: resolution: {integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==} engines: {node: '>=18'} @@ -5541,8 +5304,13 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatstr@1.0.12: - resolution: {integrity: sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==} + flat-cache@5.0.0: + resolution: {integrity: sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==} + engines: {node: '>=18'} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} @@ -5550,20 +5318,11 @@ packages: flexsearch@0.7.43: resolution: {integrity: sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg==} - focus-trap@7.5.4: - resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==} - - follow-redirects@1.15.5: - resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true + focus-trap@7.6.0: + resolution: {integrity: sha512-1td0l3pMkWJLFipobUcGaf+5DTY4PLDDrcqoSaKP8ediO/CoWCCYk/fT/Y2A4e6TNB+Sh6clRJCjOPPnKoNHnQ==} - follow-redirects@1.15.6: - resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -5582,17 +5341,13 @@ packages: resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} engines: {node: '>=8.0.0'} - foreground-child@3.2.1: - resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} - form-data@2.3.3: - resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} - engines: {node: '>= 0.12'} - form-data@4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} @@ -5631,12 +5386,16 @@ packages: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} - fs-monkey@1.0.4: - resolution: {integrity: sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==} + fs-monkey@1.0.6: + resolution: {integrity: sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==} fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -5664,6 +5423,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-amd-module-type@6.0.0: + resolution: {integrity: sha512-hFM7oivtlgJ3d6XWD6G47l8Wyh/C6vFw5G24Kk1Tbq85yh5gcM8Fne5/lFhiuxB+RT6+SI7I1ThB9lG4FBh3jw==} + engines: {node: '>=18'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -5718,8 +5481,8 @@ packages: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} - get-tsconfig@4.7.3: - resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==} + get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} getos@3.2.1: resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==} @@ -5747,9 +5510,8 @@ packages: glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - glob@10.4.3: - resolution: {integrity: sha512-Q38SGlYRpVtDBPSWEylRyctn7uDeTp4NQERTLiCT1FqA9JXPYWqAVmQU6qh4r/zMM5ehxTcbaO8EjhWnvEhmyg==} - engines: {node: '>=18'} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true glob@7.2.3: @@ -5781,8 +5543,8 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.6.0: - resolution: {integrity: sha512-UzcJi88Hw//CurUIRa9Jxb0vgOCcuD/MNjwmXp633cyaRKkCWACkoqHCtfZv43b1kqXGg/fpOa8bwgacCeXsVg==} + globals@15.10.0: + resolution: {integrity: sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==} engines: {node: '>=18'} globalthis@1.0.4: @@ -5804,6 +5566,11 @@ packages: glur@1.1.2: resolution: {integrity: sha512-l+8esYHTKOx2G/Aao4lEQ0bnHWg4fWtJbVoZZT9Knxi01pB8C80BR85nONLFwkkQoFRCmXY+BUcGZN3yZ2QsRA==} + gonzales-pe@4.3.0: + resolution: {integrity: sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==} + engines: {node: '>=0.6.0'} + hasBin: true + gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} @@ -5821,6 +5588,9 @@ packages: resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} engines: {node: '>=10'} + hachure-fill@0.5.2: + resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + handle-thing@2.0.1: resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} @@ -5863,10 +5633,6 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - hasha@5.2.2: resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} engines: {node: '>=8'} @@ -5875,6 +5641,12 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-to-html@9.0.3: + resolution: {integrity: sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -5891,8 +5663,8 @@ packages: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} - html-entities@2.4.0: - resolution: {integrity: sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==} + html-entities@2.5.2: + resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==} html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -5900,6 +5672,9 @@ packages: html-to-image@1.11.11: resolution: {integrity: sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==} + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + htmlparser2@9.1.0: resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} @@ -5928,8 +5703,8 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} - http-proxy-middleware@2.0.6: - resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==} + http-proxy-middleware@2.0.7: + resolution: {integrity: sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==} engines: {node: '>=12.0.0'} peerDependencies: '@types/express': ^4.17.13 @@ -5941,8 +5716,8 @@ packages: resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} engines: {node: '>=8.0.0'} - http-signature@1.3.6: - resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==} + http-signature@1.4.0: + resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==} engines: {node: '>=0.10'} http2-wrapper@1.0.3: @@ -5957,10 +5732,13 @@ packages: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} - https-proxy-agent@7.0.4: - resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==} + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} engines: {node: '>= 14'} + human-id@1.0.2: + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + human-signals@1.1.1: resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} engines: {node: '>=8.12.0'} @@ -5973,8 +5751,11 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} - husky@9.0.11: - resolution: {integrity: sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==} + humanize-duration@3.32.1: + resolution: {integrity: sha512-inh5wue5XdfObhu/IGEMiA1nUXigSGcaKNemcbLRKa7jXYGDZXr3LoT9pTIzq2hPEbld7w/qv9h+ikWGz8fL1g==} + + husky@9.1.6: + resolution: {integrity: sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==} engines: {node: '>=18'} hasBin: true @@ -5992,22 +5773,19 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + 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'} - import-local@3.1.0: - resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} engines: {node: '>=8'} hasBin: true - import-meta-resolve@4.0.0: - resolution: {integrity: sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==} - import-meta-resolve@4.1.0: resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} @@ -6056,6 +5834,10 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} + interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + interpret@2.2.0: resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} engines: {node: '>= 0.10'} @@ -6064,8 +5846,8 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - ipaddr.js@2.1.0: - resolution: {integrity: sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==} + ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} engines: {node: '>= 10'} is-alphabetical@1.0.4: @@ -6074,6 +5856,10 @@ packages: is-alphanumerical@1.0.4: resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + is-arguments@1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + is-array-buffer@3.0.4: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} @@ -6107,11 +5893,9 @@ packages: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} hasBin: true - is-core-module@2.13.0: - resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} - - is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} is-data-view@1.0.1: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} @@ -6164,6 +5948,10 @@ packages: resolution: {integrity: sha512-vlgs2cSgMOfnKU8c1ewgKPyum9rVrjjLLW2HBdL5i0iAJjOs8NY55ZBd/hqUTaYR0EO9CKZd3hVSC2HlIbygTQ==} engines: {node: '>=12'} + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} @@ -6183,10 +5971,6 @@ packages: resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} engines: {node: '>=0.10.0'} - is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} @@ -6217,6 +6001,10 @@ packages: resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} engines: {node: '>=0.10.0'} + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + is-shared-array-buffer@1.0.3: resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} engines: {node: '>= 0.4'} @@ -6237,6 +6025,10 @@ packages: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + is-symbol@1.0.4: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} @@ -6252,9 +6044,28 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} + is-url-superb@4.0.0: + resolution: {integrity: sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==} + engines: {node: '>=10'} + + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} + + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} @@ -6279,10 +6090,6 @@ packages: isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} - istanbul-lib-coverage@3.2.0: - resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} - engines: {node: '>=8'} - istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -6299,8 +6106,8 @@ packages: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} - istanbul-lib-instrument@6.0.2: - resolution: {integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==} + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} istanbul-lib-processinfo@2.0.3: @@ -6315,14 +6122,10 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} - istanbul-lib-source-maps@5.0.4: - resolution: {integrity: sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==} + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} engines: {node: '>=10'} - istanbul-reports@3.1.6: - resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} - engines: {node: '>=8'} - istanbul-reports@3.1.7: resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} @@ -6331,12 +6134,11 @@ packages: resolution: {integrity: sha512-IoiNVk4SMPu6uTcK+1nA5QaHNok2BMDLjSl5UomrOixe5g4GkylhPwuiGdw00ysSCrXAKNMfFTu+u/Lk5f6OLQ==} engines: {node: '>=8'} - jackspeak@3.4.1: - resolution: {integrity: sha512-U23pQPDnmYybVkYjObcuYMk43VRlMLLqLI+RdZy8s8WV8WsxO9SnqSroKaluuvcNOdCAlauKszDwd+umbot5Mg==} - engines: {node: '>=18'} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jake@10.9.1: - resolution: {integrity: sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==} + jake@10.9.2: + resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} engines: {node: '>=10'} hasBin: true @@ -6522,12 +6324,12 @@ packages: jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - jsdoc-type-pratt-parser@4.0.0: - resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==} + jsdoc-type-pratt-parser@4.1.0: + resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} engines: {node: '>=12.0.0'} - jsdom@24.0.0: - resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==} + jsdom@24.1.3: + resolution: {integrity: sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==} engines: {node: '>=18'} peerDependencies: canvas: ^2.11.2 @@ -6539,11 +6341,6 @@ packages: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true - jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - jsesc@3.0.2: resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} engines: {node: '>=6'} @@ -6555,6 +6352,9 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-ref-resolver@1.0.1: + resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} + json-schema-to-typescript@13.1.2: resolution: {integrity: sha512-17G+mjx4nunvOpkPvcz7fdwUwYCEwyH8vR3Ym3rFiQ8uzAL3go+c1306Kk7iGRk8HuXBXqy+JJJmpYl0cvOllw==} engines: {node: '>=12.0.0'} @@ -6580,8 +6380,8 @@ packages: engines: {node: '>=6'} hasBin: true - jsonc-parser@3.2.1: - resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -6646,16 +6446,23 @@ packages: resolution: {integrity: sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg==} engines: {node: '>=16.0.0'} - launch-editor@2.6.1: - resolution: {integrity: sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==} + launch-editor@2.9.1: + resolution: {integrity: sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==} 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'} + lazy-ass@2.0.3: + resolution: {integrity: sha512-/O3/DoQmI1XAhklDvF1dAjFf/epE8u3lzOZegQfLZ8G7Ud5bTRSZiFOpukHCu6jODrCA4gtIdwUCC7htxcDACA==} + engines: {node: '> 0.8'} + leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -6667,8 +6474,8 @@ packages: lex-parser@0.1.4: resolution: {integrity: sha512-DuAEISsr1H4LOpmFLkyMc8YStiRWZCO8hMsoXAXSbgyfvs2WQhSt0+/FBv3ZU/JBFZMGcE+FWzEBSzwUU7U27w==} - light-my-request@4.12.0: - resolution: {integrity: sha512-0y+9VIfJEsPVzK5ArSIJ8Dkxp8QMP7/aCuxCUtG/tr9a2NoOf/snATE/OUc05XUplJCEnRh6gTkH7xh9POt1DQ==} + light-my-request@5.14.0: + resolution: {integrity: sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==} lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} @@ -6684,8 +6491,8 @@ packages: linkify-it@4.0.1: resolution: {integrity: sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==} - lint-staged@15.2.7: - resolution: {integrity: sha512-+FdVbbCZ+yoh7E/RosSdqKJyUM2OEjTciH0TFNkawKgvFp1zbGlEC39RADg+xKBG1R4mhoH2j85myBQZ5wR+lw==} + lint-staged@15.2.10: + resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==} engines: {node: '>=18.12.0'} hasBin: true @@ -6698,8 +6505,8 @@ packages: enquirer: optional: true - listr2@8.2.2: - resolution: {integrity: sha512-sy0dq+JPS+RAFiFk2K8Nbub7khNmeeoFALNUJ4Wzk34wZKAzaOhEXqGWs4RA5aui0RaM6Hgn7VEKhCj0mlKNLA==} + listr2@8.2.5: + resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==} engines: {node: '>=18.0.0'} loader-runner@4.3.0: @@ -6754,6 +6561,9 @@ packages: lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -6765,17 +6575,20 @@ packages: resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} engines: {node: '>=10'} - log-update@6.0.0: - resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==} + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} loglevel-plugin-prefix@0.8.4: resolution: {integrity: sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==} - loglevel@1.9.1: - resolution: {integrity: sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==} + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} engines: {node: '>= 0.6.0'} + long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -6786,17 +6599,15 @@ packages: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} - lru-cache@10.4.0: - resolution: {integrity: sha512-bfJaPTuEiTYBu+ulDaeQ0F+uLmlfFkMgXj4cbwfuMSjgObGMzb55FMMbDvbRU0fAHZ4sLGkz2mKwcMg8Dvm8Ww==} - engines: {node: '>=18'} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - lru-queue@0.1.0: resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} @@ -6806,15 +6617,11 @@ packages: magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} - magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} - - magic-string@0.30.5: - resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} - engines: {node: '>=12'} + magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} - magicast@0.3.4: - resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==} + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} @@ -6833,15 +6640,15 @@ packages: mark.js@8.11.1: resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} - markdown-it@13.0.1: - resolution: {integrity: sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==} + markdown-it@13.0.2: + resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==} hasBin: true markdown-table@3.0.3: resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} - marked@13.0.2: - resolution: {integrity: sha512-J6CPjP8pS5sgrRqxVRvkCIkZ6MFdRIjDkwUwgJ9nL2fbmM6qGQeB2C16hi8Cc9BOzj6xXzy0jyi0iPIfnMHYzA==} + marked@13.0.3: + resolution: {integrity: sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==} engines: {node: '>= 18'} hasBin: true @@ -6865,8 +6672,8 @@ packages: mdast-util-frontmatter@2.0.1: resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==} - mdast-util-gfm-autolink-literal@2.0.0: - resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==} + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} mdast-util-gfm-footnote@2.0.0: resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} @@ -6886,6 +6693,9 @@ packages: mdast-util-phrasing@4.1.0: resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + mdast-util-to-markdown@2.1.0: resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} @@ -6912,15 +6722,16 @@ packages: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} - memoizee@0.4.15: - resolution: {integrity: sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==} + memoizee@0.4.17: + resolution: {integrity: sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==} + engines: {node: '>=0.12'} meow@12.1.1: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} - merge-descriptors@1.0.1: - resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -6939,23 +6750,23 @@ packages: micromark-extension-frontmatter@2.0.0: resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==} - micromark-extension-gfm-autolink-literal@2.0.0: - resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==} + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} - micromark-extension-gfm-footnote@2.0.0: - resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==} + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} - micromark-extension-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==} + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} - micromark-extension-gfm-table@2.0.0: - resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==} + micromark-extension-gfm-table@2.1.0: + resolution: {integrity: sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==} micromark-extension-gfm-tagfilter@2.0.0: resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} - micromark-extension-gfm-task-list-item@2.0.1: - resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==} + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} micromark-extension-gfm@3.0.0: resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} @@ -7023,14 +6834,18 @@ 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: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.53.0: + resolution: {integrity: sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} @@ -7048,6 +6863,10 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + mimic-response@1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} @@ -7070,14 +6889,6 @@ packages: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} - minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} - - minimatch@9.0.4: - resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} - engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -7104,8 +6915,22 @@ packages: engines: {node: '>=10'} hasBin: true - mlly@1.6.1: - resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==} + mlly@1.7.2: + resolution: {integrity: sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==} + + module-definition@6.0.0: + resolution: {integrity: sha512-sEGP5nKEXU7fGSZUML/coJbrO+yQtxcppDAYWRE9ovWsTbFoUHB2qDUx564WUzDaBHXsD46JBbIK5WVTwCyu3w==} + engines: {node: '>=18'} + hasBin: true + + module-lookup-amd@9.0.2: + resolution: {integrity: sha512-p7PzSVEWiW9fHRX9oM+V4aV5B2nCVddVNv4DZ/JB6t9GsXY4E+ZVhPpnwUX7bbJyGeeVZqhS8q/JZ/H77IqPFA==} + engines: {node: '>=18'} + hasBin: true + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} mrmime@2.0.0: resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} @@ -7170,6 +6995,15 @@ packages: encoding: optional: true + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-fetch@3.3.1: resolution: {integrity: sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7185,8 +7019,12 @@ packages: resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==} engines: {node: '>=8'} - node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + node-source-walk@7.0.0: + resolution: {integrity: sha512-1uiY543L+N7Og4yswvlm5NCKgPKDEXd9AUR9Jh3gen6oOeBsesr6LqhXom1er3eRzSUcVRWXzhv8tSNrIfGHKw==} + engines: {node: '>=18'} nomnom@1.5.2: resolution: {integrity: sha512-fiVbT7BqxiQqjlR9U3FDGOSERFCKoXVCdxV2FwZuNN7/cmJ42iQx35nUFOAFDcyvemu9Adp+IlsCGlKQYLmBKw==} @@ -7215,8 +7053,8 @@ packages: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - nwsapi@2.2.7: - resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} + nwsapi@2.2.13: + resolution: {integrity: sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==} nyc@15.1.0: resolution: {integrity: sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==} @@ -7231,13 +7069,14 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - object-inspect@1.13.2: resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} engines: {node: '>= 0.4'} + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -7249,8 +7088,8 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - ofetch@1.3.4: - resolution: {integrity: sha512-KLIET85ik3vhEfS+3fDlc/BAZiAp+43QEC/yCo5zkNoY2YaKvNkOaFr/6wCFgFH1kuYQM5pMNi0Tg8koiIemtw==} + ofetch@1.4.0: + resolution: {integrity: sha512-MuHgsEhU6zGeX+EMh+8mSMrYTnsqJQQrpM00Q6QHMKNqQ0bKy0B43tk8tL1wg+CnsSTy1kg4Ir2T5Ig6rD+dfQ==} omggif@1.0.10: resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==} @@ -7278,17 +7117,37 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + oniguruma-to-js@0.4.3: + resolution: {integrity: sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==} + open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} - optionator@0.9.3: - resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + openapi-fetch@0.11.3: + resolution: {integrity: sha512-r18fERgpxFrI4pv79ABD1dqFetWz7pTfwRd7jQmRm/lFdCDpWF43kvHUiOqOZu+tWsMydDJMpJN1hlZ9inRvfA==} + + openapi-typescript-helpers@0.0.13: + resolution: {integrity: sha512-z44WK2e7ygW3aUtAtiurfEACohf/Qt9g6BsejmIYgEoY4REHeRzgFJmO3ium0libsuzPc145I+8lE9aiiZrQvQ==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + ospath@1.2.2: resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + p-cancelable@2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} @@ -7297,6 +7156,10 @@ packages: resolution: {integrity: sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + p-filter@3.0.0: resolution: {integrity: sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7341,6 +7204,10 @@ packages: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + p-map@3.0.0: resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==} engines: {node: '>=8'} @@ -7373,8 +7240,11 @@ packages: resolution: {integrity: sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==} engines: {node: '>=8'} - package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + package-manager-detector@0.2.1: + resolution: {integrity: sha512-/hVW2fZvAdEas+wyKh0SnlZ2mx0NIa1+j11YaQkogEJkcMErbwchHCuo8z7lEtajZJQZ6rgZNVTWMVVd71Bjng==} pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} @@ -7390,8 +7260,8 @@ packages: parse-entities@2.0.0: resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} - parse-imports@2.1.1: - resolution: {integrity: sha512-TDT4HqzUiTMO1wJRwg/t/hYk8Wdp3iF/ToMIlAoVQfL1Xs/sTxq1dKWSMjMbQmIarfWKymOyly40+zmPHXMqCA==} + parse-imports@2.2.1: + resolution: {integrity: sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==} engines: {node: '>= 18'} parse-json@5.2.0: @@ -7408,6 +7278,9 @@ packages: path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-data-parser@0.1.0: + resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} @@ -7443,8 +7316,8 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-to-regexp@0.1.7: - resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + path-to-regexp@0.1.10: + resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -7463,6 +7336,9 @@ packages: pause-stream@0.0.11: resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + pcg@1.0.0: + resolution: {integrity: sha512-6wjoSJZ4TEJhI0rLDOKd5mOu6TwS4svn9oBaRsD1PCrhlDNLWAaTimWJgBABmIGJxzkI+RbaHJYRLGVf9QFE5Q==} + pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -7472,16 +7348,17 @@ packages: performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - - picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pidtree@0.6.0: resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} engines: {node: '>=0.10'} @@ -7491,23 +7368,27 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + pino-abstract-transport@1.2.0: resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} - pino-std-serializers@3.2.0: - resolution: {integrity: sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==} - pino-std-serializers@6.2.2: resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} - pino@6.14.0: - resolution: {integrity: sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==} - hasBin: true + pino-std-serializers@7.0.0: + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} pino@8.21.0: resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} hasBin: true + pino@9.4.0: + resolution: {integrity: sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==} + hasBin: true + pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} @@ -7528,8 +7409,8 @@ packages: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} engines: {node: '>=14.16'} - pkg-types@1.1.0: - resolution: {integrity: sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==} + pkg-types@1.2.0: + resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==} plist@3.1.0: resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} @@ -7550,10 +7431,11 @@ packages: resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==} engines: {node: '>=12.13.0'} - pnpm@8.15.8: - resolution: {integrity: sha512-0aAp4aRHrZC8ls1YsPrUhtKZPVMYVjlve6vy2D6xgju4PFo9D8GPZ1stEDIdSesWH+zjb+gTSqWCPs0hX+7Tkg==} - engines: {node: '>=16.14'} - hasBin: true + points-on-curve@0.2.0: + resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} + + points-on-path@0.2.1: + resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} @@ -7583,29 +7465,36 @@ packages: ts-node: optional: true - postcss-nested@6.0.1: - resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 - postcss-selector-parser@6.1.0: - resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==} + 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.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} - engines: {node: ^10 || ^12 || >=14} + postcss-values-parser@6.0.2: + resolution: {integrity: sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==} + engines: {node: '>=10'} + peerDependencies: + postcss: ^8.2.9 - postcss@8.4.39: - resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} - preact@10.21.0: - resolution: {integrity: sha512-aQAIxtzWEwH8ou+OovWVSVNlFImL7xUCwJX3YMqA3U8iKCNC34999fFOnWjYNsylgfPgMexpbk7WYOLtKr/mxg==} + preact@10.24.2: + resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==} + + precinct@12.1.2: + resolution: {integrity: sha512-x2qVN3oSOp3D05ihCd8XdkIPuEQsyte7PSxzLqiRgktu79S5Dr1I75/S+zAup8/0cwjoiJTQztE9h0/sWp9bJQ==} + engines: {node: '>=18'} + hasBin: true prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -7622,8 +7511,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.2.5: - resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} engines: {node: '>=14'} hasBin: true @@ -7646,12 +7535,12 @@ packages: resolution: {integrity: sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==} engines: {node: '>=8'} - process-warning@1.0.0: - resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} - process-warning@3.0.0: resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + process-warning@4.0.0: + resolution: {integrity: sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==} + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -7660,6 +7549,9 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -7675,25 +7567,24 @@ packages: engines: {node: '>= 0.10'} hasBin: true + pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - pump@3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + pump@3.0.2: + resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@6.0.4: - resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - qs@6.10.4: - resolution: {integrity: sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==} - engines: {node: '>=0.6'} - - qs@6.11.0: - resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} querystringify@2.2.0: @@ -7709,12 +7600,18 @@ packages: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} + quote-unquote@1.0.0: + resolution: {integrity: sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==} + railroad-diagrams@1.0.0: resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} ramda@0.28.0: resolution: {integrity: sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==} + ramda@0.29.0: + resolution: {integrity: sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==} + randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} @@ -7726,8 +7623,8 @@ packages: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} - react-is@18.2.0: - resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} @@ -7740,6 +7637,10 @@ packages: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} engines: {node: '>=8'} + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -7759,12 +7660,16 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} + rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + rechoir@0.7.1: resolution: {integrity: sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==} engines: {node: '>= 0.10'} - regenerate-unicode-properties@10.1.1: - resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} + regenerate-unicode-properties@10.2.0: + resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==} engines: {node: '>=4'} regenerate@1.4.2: @@ -7776,24 +7681,30 @@ packages: regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + regex@4.3.3: + resolution: {integrity: sha512-r/AadFO7owAq1QJVeZ/nq9jNS1vyZt+6t1p/E59B56Rn2GCya+gr1KSyOzNL/er+r+B7phv5jG2xU2Nz1YkmJg==} + regexp-tree@0.1.27: resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} hasBin: true - regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + regexp.prototype.flags@1.5.3: + resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==} engines: {node: '>= 0.4'} - regexpu-core@5.3.2: - resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} + regexpu-core@6.1.1: + resolution: {integrity: sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==} engines: {node: '>=4'} + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + regjsparser@0.10.0: resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==} hasBin: true - regjsparser@0.9.1: - resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} + regjsparser@0.11.1: + resolution: {integrity: sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==} hasBin: true release-zalgo@1.0.0: @@ -7822,6 +7733,10 @@ packages: request-progress@3.0.0: resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} + require-and-forget@1.0.1: + resolution: {integrity: sha512-Sea861D/seGo3cptxc857a34Df0oEijXit8Q3IDodiwZMzVmyXrRI9EgQQa3hjkhoEjNzCBvv0t/0fMgebmWLg==} + engines: {node: '>=6'} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -7833,6 +7748,15 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + requirejs-config-file@4.0.0: + resolution: {integrity: sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==} + engines: {node: '>=10.13.0'} + + requirejs@2.3.7: + resolution: {integrity: sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==} + engines: {node: '>=0.4.0'} + hasBin: true + requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} @@ -7843,6 +7767,10 @@ packages: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} + resolve-dependency-path@4.0.0: + resolution: {integrity: sha512-hlY1SybBGm5aYN3PC4rp15MzsJLM1w+MEA/4KU3UBPfz4S0lL3FL6mgv7JgaA8a+ZTeEQAiF1a1BuN2nkqiIlg==} + engines: {node: '>=18'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -7858,10 +7786,6 @@ packages: resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} engines: {node: '>=10'} - resolve@1.22.4: - resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} - hasBin: true - resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -7873,13 +7797,13 @@ packages: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} - restore-cursor@4.0.0: - resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} - ret@0.2.2: - resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} - engines: {node: '>=4'} + ret@0.4.3: + resolution: {integrity: sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==} + engines: {node: '>=10'} retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} @@ -7889,9 +7813,6 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.3.1: - resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} - rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} @@ -7905,9 +7826,8 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rimraf@5.0.8: - resolution: {integrity: sha512-XSh0V2/yNhDEi8HwdIefD8MLgs4LQXPag/nEJWs3YUc3Upn+UHa1GyIkEg9xSSNt7HnkO5FjTvmcRzgf+8UZuw==} - engines: {node: '>=18'} + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true robust-predicates@3.0.2: @@ -7923,18 +7843,21 @@ packages: rollup: optional: true - rollup@2.79.1: - resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} + rollup@2.79.2: + resolution: {integrity: sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==} engines: {node: '>=10.0.0'} hasBin: true - rollup@4.18.0: - resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} + rollup@4.24.0: + resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rrweb-cssom@0.6.0: - resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} + roughjs@4.6.6: + resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + + rrweb-cssom@0.7.1: + resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -7959,16 +7882,21 @@ packages: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} - safe-regex2@2.0.0: - resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} + safe-regex2@3.1.0: + resolution: {integrity: sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==} - 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: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sass-lookup@6.0.1: + resolution: {integrity: sha512-nl9Wxbj9RjEJA5SSV0hSDoU2zYGtE+ANaDS4OFUR7nYrquvBFvPKZZtQHe3lvnxCcylEDV00KUijjdMTUElcVQ==} + engines: {node: '>=18'} + hasBin: true + saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -7981,8 +7909,8 @@ packages: resolution: {integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==} engines: {node: '>= 12.13.0'} - search-insights@2.13.0: - resolution: {integrity: sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==} + search-insights@2.17.2: + resolution: {integrity: sha512-zFNpOpUO+tY2D85KrxJ+aqwnIfdEGi06UH2+xEb+Bp9Mwznmauqc9djbnBibJO5mpfUPPa8st6Sx65+vbeO45g==} secure-json-parse@2.7.0: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} @@ -7990,13 +7918,10 @@ packages: select-hose@2.0.0: resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} - selfsigned@2.1.1: - resolution: {integrity: sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==} + selfsigned@2.4.1: + resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} engines: {node: '>=10'} - semver-store@0.3.0: - resolution: {integrity: sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg==} - semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true @@ -8005,16 +7930,6 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.5.4: - resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} - engines: {node: '>=10'} - hasBin: true - - semver@7.6.0: - resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} - engines: {node: '>=10'} - hasBin: true - semver@7.6.2: resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} engines: {node: '>=10'} @@ -8025,8 +7940,8 @@ packages: engines: {node: '>=10'} hasBin: true - send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} serialize-javascript@6.0.2: @@ -8036,15 +7951,15 @@ packages: resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} engines: {node: '>= 0.8.0'} - serve-static@1.15.0: - resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - set-cookie-parser@2.6.0: - resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + set-cookie-parser@2.7.0: + resolution: {integrity: sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ==} set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} @@ -8064,9 +7979,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==} @@ -8087,11 +8002,16 @@ packages: shell-quote@1.8.1: resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + shelljs@0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} + engines: {node: '>=4'} + hasBin: true + shiki@0.14.7: resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} - shiki@1.4.0: - resolution: {integrity: sha512-5WIn0OL8PWm7JhnTwRWXniy6eEDY234mRrERVlFa646V2ErQqwIFd2UML7e0Pq9eqSKLoMa3Ke+xbsF+DAuy+Q==} + shiki@1.22.0: + resolution: {integrity: sha512-/t5LlhNs+UOKQCYBtl5ZsH/Vclz73GIqT2yQsCBygr8L/ppTdmpL4w3kPLoZJbMKVWtoG77Ue1feOjZfDxvMkw==} side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} @@ -8107,6 +8027,10 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-bin-help@1.8.0: + resolution: {integrity: sha512-0LxHn+P1lF5r2WwVB/za3hLRIsYoLaNq1CXqjbrs3ZvLuvlWnRKrUjEWzV7umZL7hpQ7xULiQMV+0iXdRa5iFg==} + engines: {node: '>=14.16'} + simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} @@ -8154,22 +8078,18 @@ packages: sockjs@0.3.24: resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} - sonic-boom@1.4.1: - resolution: {integrity: sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==} - sonic-boom@3.8.1: resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + sonic-boom@4.1.0: + resolution: {integrity: sha512-NGipjjRicyJJ03rPiZCJYjwlsuP2d1/5QUviozRXC7S3WdVWNK5e3Ojieb9CCyfhq2UC+3+SRd9nG3I2lPRvUw==} + source-map-js@1.0.1: resolution: {integrity: sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==} engines: {node: '>=0.10.0'} - source-map-js@1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} - engines: {node: '>=0.10.0'} - - source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} source-map-support@0.5.13: @@ -8198,6 +8118,9 @@ packages: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} deprecated: Please use @jridgewell/sourcemap-codec instead + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + spawn-command@0.0.2: resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} @@ -8205,6 +8128,9 @@ packages: resolution: {integrity: sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==} engines: {node: '>=8'} + spawndamnit@2.0.0: + resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -8217,8 +8143,8 @@ packages: spdx-expression-parse@4.0.0: resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} - spdx-license-ids@3.0.18: - resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} + spdx-license-ids@3.0.20: + resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} spdy-transport@3.0.0: resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} @@ -8231,6 +8157,10 @@ packages: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} + spec-change@1.11.11: + resolution: {integrity: sha512-wSxi1XKeNr8JxBlYj13Lw8TTMpojYU2zRlkmyXl0kHp+xGkwYUpXaVXcofiEmEqrq1HoGg7pwKEzvMjYcQubtw==} + hasBin: true + split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -8256,8 +8186,8 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - start-server-and-test@2.0.4: - resolution: {integrity: sha512-CKNeBTcP0hVqIlNismHMudb9q3lLdAjcVPO13/7gfI66fcJpeIb/o4NzQd1JK/CD+lfWVqr10ZH9Y14+OwlJuw==} + start-server-and-test@2.0.8: + resolution: {integrity: sha512-v2fV6NV2F7tL1ocwfI4Wpait+IKjRbT5l3ZZ+ZikXdMLmxYsS8ynGAsCQAUVXkVyGyS+UibsRnvgHkMvJIvCsw==} engines: {node: '>=16'} hasBin: true @@ -8272,6 +8202,10 @@ packages: std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + stop-iteration-iterator@1.0.0: + resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} + engines: {node: '>= 0.4'} + stream-combiner@0.0.4: resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} @@ -8283,10 +8217,6 @@ packages: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} - string-similarity@4.0.4: - resolution: {integrity: sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -8295,8 +8225,8 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} - string-width@7.1.0: - resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} string.prototype.matchall@4.0.11: @@ -8320,6 +8250,9 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + stringify-object@3.3.0: resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} engines: {node: '>=4'} @@ -8336,6 +8269,10 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} @@ -8367,14 +8304,23 @@ 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==} + + stylus-lookup@6.0.0: + resolution: {integrity: sha512-RaWKxAvPnIXrdby+UWCr1WRfa+lrPMSJPySte4Q6a+rWyjeJyFOLJxr5GrAVfcMCsfVlCuzTAJ/ysYT8p8do7Q==} + engines: {node: '>=18'} + hasBin: true sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true + superjson@2.2.1: + resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==} + engines: {node: '>=16'} + supports-color@2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} engines: {node: '>=0.8.0'} @@ -8398,15 +8344,15 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - synckit@0.9.0: - resolution: {integrity: sha512-7RnqIMq572L8PeEzKeBINYEJDDxpcH8JEgLwUqBd3TkofhFRbkq4QLR0u+36avGAhCRbk2nnmjcW9SE531hPDg==} + synckit@0.9.2: + resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} engines: {node: ^14.18.0 || >=16.0.0} tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} - tailwindcss@3.4.4: - resolution: {integrity: sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==} + tailwindcss@3.4.13: + resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==} engines: {node: '>=14.0.0'} hasBin: true @@ -8430,6 +8376,10 @@ packages: resolution: {integrity: sha512-DFpBhaF5j+2f7kheKFc1ajsAUUDGOaNPpKPtiIMxlbfud6mvfFZuWGnTRpaujUa5J7yl6cIw/h6nyr4mSsENPg==} engines: {node: '>=8'} + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + terser-webpack-plugin@5.3.10: resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} @@ -8446,18 +8396,8 @@ packages: uglify-js: optional: true - terser@5.29.2: - resolution: {integrity: sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==} - engines: {node: '>=10'} - hasBin: true - - terser@5.31.1: - resolution: {integrity: sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==} - engines: {node: '>=10'} - hasBin: true - - terser@5.31.3: - resolution: {integrity: sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==} + terser@5.34.1: + resolution: {integrity: sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==} engines: {node: '>=10'} hasBin: true @@ -8478,6 +8418,9 @@ packages: thread-stream@2.7.0: resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + throat@6.0.2: resolution: {integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==} @@ -8490,15 +8433,19 @@ packages: thunky@1.1.0: resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} - timers-ext@0.1.7: - resolution: {integrity: sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==} + timers-ext@0.1.8: + resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==} + engines: {node: '>=0.12'} - tiny-lru@8.0.2: - resolution: {integrity: sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==} - engines: {node: '>=6'} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + tinyexec@0.3.0: + resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + + tinyglobby@0.2.9: + resolution: {integrity: sha512-8or1+BGEdk1Zkkw2ii16qSS7uVrQJPre5A9o/XkWPATkk23FZh/15BKFxPnlTy6vkljZxLqYCzzBMj30ZrSvjw==} + engines: {node: '>=12.0.0'} tinypool@0.8.4: resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} @@ -8508,6 +8455,10 @@ packages: resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} engines: {node: '>=14.0.0'} + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + tmp@0.2.3: resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} engines: {node: '>=14.14'} @@ -8523,6 +8474,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -8531,8 +8486,8 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} - tough-cookie@4.1.3: - resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==} + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} tr46@0.0.3: @@ -8552,6 +8507,9 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} @@ -8571,20 +8529,25 @@ packages: ts-toolbelt@6.15.5: resolution: {integrity: sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==} - tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} - tslib@2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} - tsx@4.7.3: - resolution: {integrity: sha512-+fQnMqIp/jxZEXLcj6WzYy9FhcS5/Dfk8y4AtzJ6ejKcKqmfTF8Gso/jtrzDggCF2zTU20gJa6n8XqPYwDAUYQ==} + tsx@4.19.1: + resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} engines: {node: '>=18.0.0'} hasBin: true tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + tunnel@0.0.6: + resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} + engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + tweetnacl@0.14.5: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} @@ -8596,6 +8559,10 @@ packages: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + type-fest@0.16.0: resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} engines: {node: '>=10'} @@ -8616,20 +8583,16 @@ packages: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} - type-fest@1.4.0: - resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} - engines: {node: '>=10'} - - type-fest@4.15.0: - resolution: {integrity: sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==} + type-fest@4.26.1: + resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==} engines: {node: '>=16'} type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} - type@2.7.2: - resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} typed-array-buffer@1.0.2: resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} @@ -8662,8 +8625,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-alpha.44: - resolution: {integrity: sha512-4oRisGPvIJFnLqpfrpdcFjkFZg4/mhbE+0faGiomEFv9r5ziXETxuGY6VmwACPXHEakp2nDEqnp3ZdU0bsuiHQ==} + typescript-eslint@8.8.1: + resolution: {integrity: sha512-R0dsXFt6t4SAFjUSKFjMh4pXDtq04SsFKCVGDP3ZOzNP7itF0jBcZYU4fMsZr4y7O7V7Nc751dDeESbe4PbQMQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -8676,14 +8639,19 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.6.2: + resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + engines: {node: '>=14.17'} + hasBin: true + uc.micro@1.0.6: resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} - ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} - uglify-js@3.17.4: - resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} hasBin: true @@ -8699,16 +8667,23 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - unicode-canonical-property-names-ecmascript@2.0.0: - resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + undici@5.28.4: + resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} + engines: {node: '>=14.0'} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} unicode-match-property-ecmascript@2.0.0: resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} engines: {node: '>=4'} - unicode-match-property-value-ecmascript@2.1.0: - resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==} + unicode-match-property-value-ecmascript@2.2.0: + resolution: {integrity: sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==} engines: {node: '>=4'} unicode-property-aliases-ecmascript@2.1.0: @@ -8722,14 +8697,13 @@ packages: unified@11.0.4: resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + unique-string@2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} - unique-string@3.0.0: - resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} - engines: {node: '>=12'} - unist-util-flatmap@1.0.0: resolution: {integrity: sha512-IG32jcKJlhARCYT2LsYPJWdoXYkzz3ESAdl1aa2hn9Auh+cgUmU6wgkII4yCc/1GgeWibRdELdCZh/p3QKQ1dQ==} @@ -8739,6 +8713,9 @@ packages: unist-util-is@6.0.0: resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + unist-util-stringify-position@2.0.3: resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} @@ -8792,8 +8769,14 @@ packages: '@nuxt/kit': optional: true - unplugin@1.4.0: - resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==} + unplugin@1.14.1: + resolution: {integrity: sha512-lBlHbfSFPToDYp9pjXlUEFVxYLaue9f9T1HC+4OHlmj+HnMDdz9oZY+erXfoCe/5V/7gKUSY2jpXPb9S7f0f/w==} + engines: {node: '>=14.0.0'} + peerDependencies: + webpack-sources: ^3 + peerDependenciesMeta: + webpack-sources: + optional: true untildify@4.0.0: resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} @@ -8803,14 +8786,8 @@ packages: resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} engines: {node: '>=4'} - update-browserslist-db@1.0.13: - resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - update-browserslist-db@1.0.16: - resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==} + update-browserslist-db@1.1.1: + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -8836,8 +8813,8 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - v8-to-istanbul@9.2.0: - resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} validate-npm-package-license@3.0.4: @@ -8854,11 +8831,11 @@ packages: vfile-message@4.0.2: resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} - vfile@6.0.1: - resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite-node@1.5.3: - resolution: {integrity: sha512-axFo00qiCpU/JLd8N1gu9iEYL3xTbMbMrbe5nDp9GL0nb6gurIdZLkkFogZXWnE8Oyy5kfSLwNVIcVsnhE7lgQ==} + vite-node@1.6.0: + resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -8879,8 +8856,8 @@ packages: '@vite-pwa/assets-generator': optional: true - vite@5.2.13: - resolution: {integrity: sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==} + vite@5.4.8: + resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -8888,6 +8865,7 @@ packages: less: '*' lightningcss: ^1.21.0 sass: '*' + sass-embedded: '*' stylus: '*' sugarss: '*' terser: ^5.4.0 @@ -8900,6 +8878,8 @@ packages: optional: true sass: optional: true + sass-embedded: + optional: true stylus: optional: true sugarss: @@ -8927,15 +8907,15 @@ packages: postcss: optional: true - vitest@1.5.3: - resolution: {integrity: sha512-2oM7nLXylw3mQlW6GXnRriw+7YvZFk/YNV8AxIC3Z3MfFbuziLGWP9GPxxu/7nRlXhqyxBikpamr+lEEj1sUEw==} + vitest@1.6.0: + resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.5.3 - '@vitest/ui': 1.5.3 + '@vitest/browser': 1.6.0 + '@vitest/ui': 1.6.0 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -8962,8 +8942,8 @@ packages: vscode-languageserver-protocol@3.17.5: resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} - vscode-languageserver-textdocument@1.0.11: - resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==} + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} vscode-languageserver-types@3.17.5: resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} @@ -8984,19 +8964,8 @@ packages: vscode-uri@3.0.8: resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} - vue-demi@0.14.7: - resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} - engines: {node: '>=12'} - hasBin: true - peerDependencies: - '@vue/composition-api': ^1.0.0-rc.1 - vue: ^3.0.0-0 || ^2.6.0 - peerDependenciesMeta: - '@vue/composition-api': - optional: true - - vue-demi@0.14.8: - resolution: {integrity: sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==} + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} engines: {node: '>=12'} hasBin: true peerDependencies: @@ -9006,16 +8975,8 @@ packages: '@vue/composition-api': optional: true - vue@3.4.30: - resolution: {integrity: sha512-NcxtKCwkdf1zPsr7Y8+QlDBCGqxvjLXF2EX+yi76rV5rrz90Y6gK1cq0olIhdWGgrlhs9ElHuhi9t3+W5sG5Xw==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - vue@3.4.31: - resolution: {integrity: sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==} + vue@3.5.11: + resolution: {integrity: sha512-/8Wurrd9J3lb72FTQS7gRMNQD4nztTtKPmuDuPuhqXmmpD6+skVjAeahNpVzsuky6Sy9gy7wn8UadqPtt9SQIg==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -9031,21 +8992,24 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - wait-on@7.2.0: - resolution: {integrity: sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==} + wait-on@8.0.1: + resolution: {integrity: sha512-1wWQOyR2LVVtaqrcIL2+OM+x7bkpmzVROa0Nf6FryXkS+er5Sa1kzFGjzZRqLnHa3n1rACFLeTwUqE1ETL9Mig==} engines: {node: '>=12.0.0'} hasBin: true walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - watchpack@2.4.1: - resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==} + watchpack@2.4.2: + resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==} engines: {node: '>=10.13.0'} wbuf@1.7.3: resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -9103,19 +9067,19 @@ packages: webpack-cli: optional: true - webpack-merge@5.9.0: - resolution: {integrity: sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==} + webpack-merge@5.10.0: + resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==} engines: {node: '>=10.0.0'} webpack-sources@3.2.3: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} - webpack-virtual-modules@0.5.0: - resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - webpack@5.91.0: - resolution: {integrity: sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==} + webpack@5.95.0: + resolution: {integrity: sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -9153,6 +9117,10 @@ packages: which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} @@ -9169,8 +9137,8 @@ packages: engines: {node: '>= 8'} hasBin: true - why-is-node-running@2.2.2: - resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} hasBin: true @@ -9181,6 +9149,10 @@ packages: wildcard@2.0.1: resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} @@ -9190,16 +9162,13 @@ packages: workbox-broadcast-update@7.1.0: resolution: {integrity: sha512-O36hIfhjej/c5ar95pO67k1GQw0/bw5tKP7CERNgK+JdxBANQhDmIuOXZTNvwb2IHBx9hj2kxvcDyRIh5nzOgQ==} - workbox-build@7.1.0: - resolution: {integrity: sha512-F6R94XAxjB2j4ETMkP1EXKfjECOtDmyvt0vz3BzgWJMI68TNSXIVNkgatwUKBlPGOfy9n2F/4voYRNAhEvPJNg==} + workbox-build@7.1.1: + resolution: {integrity: sha512-WdkVdC70VMpf5NBCtNbiwdSZeKVuhTEd5PV3mAwpTQCGAB5XbOny1P9egEgNdetv4srAMmMKjvBk4RD58LpooA==} engines: {node: '>=16.0.0'} workbox-cacheable-response@7.1.0: resolution: {integrity: sha512-iwsLBll8Hvua3xCuBB9h92+/e0wdsmSVgR2ZlvcfjepZWwhd3osumQB3x9o7flj+FehtWM2VHbZn8UJeBXXo6Q==} - workbox-core@7.0.0: - resolution: {integrity: sha512-81JkAAZtfVP8darBpfRTovHg8DGAVrKFgHpOArZbdFd78VqHr5Iw65f2guwjE2NlCFbPFDoez3D3/6ZvhI/rwQ==} - workbox-core@7.1.0: resolution: {integrity: sha512-5KB4KOY8rtL31nEF7BfvU7FMzKT4B5TkbYa2tzkS+Peqj0gayMT9SytSFtNzlrvMaWgv6y/yvP9C0IbpFjV30Q==} @@ -9233,9 +9202,6 @@ packages: workbox-sw@7.1.0: resolution: {integrity: sha512-Hml/9+/njUXBglv3dtZ9WBKHI235AQJyLBV1G7EFmh4/mUdSQuXui80RtjDeVRrXnm/6QWgRUEHG3/YBVbxtsA==} - workbox-window@7.0.0: - resolution: {integrity: sha512-j7P/bsAWE/a7sxqTzXo3P2ALb1reTfZdvVp6OJ/uLr/C2kZAMvjeWGm8V4htQhor7DOvYg0sSbFN2+flT5U0qA==} - workbox-window@7.1.0: resolution: {integrity: sha512-ZHeROyqR+AS5UPzholQRDttLFqGMwP0Np8MKWAdyxsDETxq3qOAyXvqessc3GniohG6e0mAqSQyKOHmT8zPF7g==} @@ -9265,8 +9231,8 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - ws@8.13.0: - resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -9277,8 +9243,8 @@ packages: utf-8-validate: optional: true - ws@8.16.0: - resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -9289,18 +9255,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'} @@ -9326,14 +9280,14 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - yaml@2.4.5: - resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + yaml@2.5.1: + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} engines: {node: '>= 14'} hasBin: true @@ -9364,10 +9318,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.0.0: - resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} - engines: {node: '>=12.20'} - yocto-queue@1.1.1: resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} engines: {node: '>=12.20'} @@ -9377,12 +9327,26 @@ packages: snapshots: - '@aashutoshrathi/word-wrap@1.2.6': {} + '@actions/core@1.11.1': + dependencies: + '@actions/exec': 1.1.1 + '@actions/http-client': 2.2.3 + + '@actions/exec@1.1.1': + dependencies: + '@actions/io': 1.1.3 + + '@actions/http-client@2.2.3': + dependencies: + tunnel: 0.0.6 + undici: 5.28.4 + + '@actions/io@1.1.3': {} '@adobe/jsonschema2md@8.0.2': dependencies: '@types/json-schema': 7.0.15 - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 es2015-i18n-tag: 1.6.1 ferrum: 1.9.4 fs-extra: 11.2.0 @@ -9401,109 +9365,109 @@ snapshots: transitivePeerDependencies: - supports-color - '@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.13.0)': + '@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.17.2)': dependencies: - '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.13.0) - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) + '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.17.2) + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - search-insights - '@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.13.0)': + '@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.17.2)': dependencies: - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) - search-insights: 2.13.0 + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + search-insights: 2.17.2 transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - '@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)': + '@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)': dependencies: - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) - '@algolia/client-search': 4.23.3 - algoliasearch: 4.23.3 + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + '@algolia/client-search': 4.24.0 + algoliasearch: 4.24.0 - '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)': + '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)': dependencies: - '@algolia/client-search': 4.23.3 - algoliasearch: 4.23.3 + '@algolia/client-search': 4.24.0 + algoliasearch: 4.24.0 - '@algolia/cache-browser-local-storage@4.23.3': + '@algolia/cache-browser-local-storage@4.24.0': dependencies: - '@algolia/cache-common': 4.23.3 + '@algolia/cache-common': 4.24.0 - '@algolia/cache-common@4.23.3': {} + '@algolia/cache-common@4.24.0': {} - '@algolia/cache-in-memory@4.23.3': + '@algolia/cache-in-memory@4.24.0': dependencies: - '@algolia/cache-common': 4.23.3 + '@algolia/cache-common': 4.24.0 - '@algolia/client-account@4.23.3': + '@algolia/client-account@4.24.0': dependencies: - '@algolia/client-common': 4.23.3 - '@algolia/client-search': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common': 4.24.0 + '@algolia/client-search': 4.24.0 + '@algolia/transporter': 4.24.0 - '@algolia/client-analytics@4.23.3': + '@algolia/client-analytics@4.24.0': dependencies: - '@algolia/client-common': 4.23.3 - '@algolia/client-search': 4.23.3 - '@algolia/requester-common': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common': 4.24.0 + '@algolia/client-search': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/transporter': 4.24.0 - '@algolia/client-common@4.23.3': + '@algolia/client-common@4.24.0': dependencies: - '@algolia/requester-common': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/requester-common': 4.24.0 + '@algolia/transporter': 4.24.0 - '@algolia/client-personalization@4.23.3': + '@algolia/client-personalization@4.24.0': dependencies: - '@algolia/client-common': 4.23.3 - '@algolia/requester-common': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/transporter': 4.24.0 - '@algolia/client-search@4.23.3': + '@algolia/client-search@4.24.0': dependencies: - '@algolia/client-common': 4.23.3 - '@algolia/requester-common': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/transporter': 4.24.0 - '@algolia/logger-common@4.23.3': {} + '@algolia/logger-common@4.24.0': {} - '@algolia/logger-console@4.23.3': + '@algolia/logger-console@4.24.0': dependencies: - '@algolia/logger-common': 4.23.3 + '@algolia/logger-common': 4.24.0 - '@algolia/recommend@4.23.3': + '@algolia/recommend@4.24.0': dependencies: - '@algolia/cache-browser-local-storage': 4.23.3 - '@algolia/cache-common': 4.23.3 - '@algolia/cache-in-memory': 4.23.3 - '@algolia/client-common': 4.23.3 - '@algolia/client-search': 4.23.3 - '@algolia/logger-common': 4.23.3 - '@algolia/logger-console': 4.23.3 - '@algolia/requester-browser-xhr': 4.23.3 - '@algolia/requester-common': 4.23.3 - '@algolia/requester-node-http': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/cache-browser-local-storage': 4.24.0 + '@algolia/cache-common': 4.24.0 + '@algolia/cache-in-memory': 4.24.0 + '@algolia/client-common': 4.24.0 + '@algolia/client-search': 4.24.0 + '@algolia/logger-common': 4.24.0 + '@algolia/logger-console': 4.24.0 + '@algolia/requester-browser-xhr': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/requester-node-http': 4.24.0 + '@algolia/transporter': 4.24.0 - '@algolia/requester-browser-xhr@4.23.3': + '@algolia/requester-browser-xhr@4.24.0': dependencies: - '@algolia/requester-common': 4.23.3 + '@algolia/requester-common': 4.24.0 - '@algolia/requester-common@4.23.3': {} + '@algolia/requester-common@4.24.0': {} - '@algolia/requester-node-http@4.23.3': + '@algolia/requester-node-http@4.24.0': dependencies: - '@algolia/requester-common': 4.23.3 + '@algolia/requester-common': 4.24.0 - '@algolia/transporter@4.23.3': + '@algolia/transporter@4.24.0': dependencies: - '@algolia/cache-common': 4.23.3 - '@algolia/logger-common': 4.23.3 - '@algolia/requester-common': 4.23.3 + '@algolia/cache-common': 4.24.0 + '@algolia/logger-common': 4.24.0 + '@algolia/requester-common': 4.24.0 '@alloc/quick-lru@5.2.0': {} @@ -9512,14 +9476,12 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@antfu/install-pkg@0.1.1': + '@antfu/install-pkg@0.4.1': dependencies: - execa: 5.1.1 - find-up: 5.0.0 + package-manager-detector: 0.2.1 + tinyexec: 0.3.0 - '@antfu/utils@0.7.6': {} - - '@antfu/utils@0.7.7': {} + '@antfu/utils@0.7.10': {} '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': dependencies: @@ -9528,40 +9490,71 @@ snapshots: jsonpointer: 5.0.1 leven: 3.1.0 - '@applitools/core-base@1.10.1': + '@applitools/core-base@1.16.1': dependencies: - '@applitools/image': 1.1.9 - '@applitools/logger': 2.0.14 - '@applitools/req': 1.6.5 - '@applitools/utils': 1.7.0 + '@applitools/image': 1.1.13 + '@applitools/logger': 2.0.18 + '@applitools/req': 1.7.2 + '@applitools/utils': 1.7.4 abort-controller: 3.0.0 throat: 6.0.2 transitivePeerDependencies: - supports-color - '@applitools/core@4.11.1(encoding@0.1.13)(typescript@5.4.5)': - dependencies: - '@applitools/core-base': 1.10.1 - '@applitools/dom-capture': 11.2.6 - '@applitools/dom-snapshot': 4.9.2 - '@applitools/driver': 1.16.5 - '@applitools/ec-client': 1.7.32(typescript@5.4.5) - '@applitools/logger': 2.0.14 - '@applitools/nml-client': 1.7.5 - '@applitools/req': 1.6.5 - '@applitools/screenshoter': 3.8.27 - '@applitools/snippets': 2.4.25 - '@applitools/socket': 1.1.14 - '@applitools/spec-driver-webdriver': 1.1.3(webdriver@7.31.1(typescript@5.4.5)) - '@applitools/ufg-client': 1.10.2 - '@applitools/utils': 1.7.0 + '@applitools/core@4.18.2(encoding@0.1.13)(typescript@5.4.5)': + dependencies: + '@applitools/core-base': 1.16.1 + '@applitools/dom-capture': 11.4.0 + '@applitools/dom-snapshot': 4.11.3 + '@applitools/driver': 1.19.0 + '@applitools/ec-client': 1.9.4(typescript@5.4.5) + '@applitools/logger': 2.0.18 + '@applitools/nml-client': 1.8.10 + '@applitools/req': 1.7.2 + '@applitools/screenshoter': 3.8.36 + '@applitools/snippets': 2.4.27 + '@applitools/socket': 1.1.18 + '@applitools/spec-driver-webdriver': 1.1.12(webdriver@7.31.1(typescript@5.4.5)) + '@applitools/ufg-client': 1.12.3 + '@applitools/utils': 1.7.4 + '@types/ws': 8.5.5 + abort-controller: 3.0.0 + chalk: 4.1.2 + node-fetch: 2.6.7(encoding@0.1.13) + semver: 7.6.2 + webdriver: 7.31.1(typescript@5.4.5) + ws: 8.17.1 + yargs: 17.7.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - typescript + - utf-8-validate + + '@applitools/core@4.19.0(encoding@0.1.13)(typescript@5.4.5)': + dependencies: + '@applitools/core-base': 1.16.1 + '@applitools/dom-capture': 11.5.0 + '@applitools/dom-snapshot': 4.11.3 + '@applitools/driver': 1.19.1 + '@applitools/ec-client': 1.9.5(typescript@5.4.5) + '@applitools/logger': 2.0.18 + '@applitools/nml-client': 1.8.11 + '@applitools/req': 1.7.2 + '@applitools/screenshoter': 3.8.37 + '@applitools/snippets': 2.5.0 + '@applitools/socket': 1.1.18 + '@applitools/spec-driver-webdriver': 1.1.13(webdriver@7.31.1(typescript@5.4.5)) + '@applitools/ufg-client': 1.12.3 + '@applitools/utils': 1.7.4 '@types/ws': 8.5.5 abort-controller: 3.0.0 chalk: 4.1.2 node-fetch: 2.6.7(encoding@0.1.13) - semver: 7.5.4 + semver: 7.6.2 webdriver: 7.31.1(typescript@5.4.5) - ws: 8.13.0 + ws: 8.17.1 yargs: 17.7.2 transitivePeerDependencies: - bufferutil @@ -9570,44 +9563,75 @@ snapshots: - typescript - utf-8-validate - '@applitools/css-tree@1.1.2': + '@applitools/css-tree@1.1.4': dependencies: mdn-data: 2.1.0 source-map-js: 1.0.1 - '@applitools/dom-capture@11.2.6': + '@applitools/dom-capture@11.4.0': + dependencies: + '@applitools/dom-shared': 1.0.15 + '@applitools/functional-commons': 1.6.0 + + '@applitools/dom-capture@11.5.0': dependencies: - '@applitools/dom-shared': 1.0.13 + '@applitools/dom-shared': 1.0.15 '@applitools/functional-commons': 1.6.0 - '@applitools/dom-shared@1.0.13': {} + '@applitools/dom-shared@1.0.15': {} - '@applitools/dom-snapshot@4.9.2': + '@applitools/dom-snapshot@4.11.3': dependencies: - '@applitools/css-tree': 1.1.2 - '@applitools/dom-shared': 1.0.13 + '@applitools/css-tree': 1.1.4 + '@applitools/dom-shared': 1.0.15 '@applitools/functional-commons': 1.6.0 pako: 1.0.11 - '@applitools/driver@1.16.5': + '@applitools/driver@1.19.0': + dependencies: + '@applitools/logger': 2.0.18 + '@applitools/snippets': 2.4.27 + '@applitools/utils': 1.7.4 + semver: 7.6.2 + transitivePeerDependencies: + - supports-color + + '@applitools/driver@1.19.1': + dependencies: + '@applitools/logger': 2.0.18 + '@applitools/snippets': 2.5.0 + '@applitools/utils': 1.7.4 + semver: 7.6.2 + transitivePeerDependencies: + - supports-color + + '@applitools/ec-client@1.9.4(typescript@5.4.5)': dependencies: - '@applitools/logger': 2.0.14 - '@applitools/snippets': 2.4.25 - '@applitools/utils': 1.7.0 - semver: 7.5.4 + '@applitools/core-base': 1.16.1 + '@applitools/driver': 1.19.0 + '@applitools/logger': 2.0.18 + '@applitools/req': 1.7.2 + '@applitools/socket': 1.1.18 + '@applitools/spec-driver-webdriver': 1.1.12(webdriver@7.31.1(typescript@5.4.5)) + '@applitools/tunnel-client': 1.5.8 + '@applitools/utils': 1.7.4 + abort-controller: 3.0.0 + webdriver: 7.31.1(typescript@5.4.5) + yargs: 17.7.2 transitivePeerDependencies: - supports-color + - typescript - '@applitools/ec-client@1.7.32(typescript@5.4.5)': + '@applitools/ec-client@1.9.5(typescript@5.4.5)': dependencies: - '@applitools/core-base': 1.10.1 - '@applitools/driver': 1.16.5 - '@applitools/logger': 2.0.14 - '@applitools/req': 1.6.5 - '@applitools/socket': 1.1.14 - '@applitools/spec-driver-webdriver': 1.1.3(webdriver@7.31.1(typescript@5.4.5)) - '@applitools/tunnel-client': 1.4.1 - '@applitools/utils': 1.7.0 + '@applitools/core-base': 1.16.1 + '@applitools/driver': 1.19.1 + '@applitools/logger': 2.0.18 + '@applitools/req': 1.7.2 + '@applitools/socket': 1.1.18 + '@applitools/spec-driver-webdriver': 1.1.13(webdriver@7.31.1(typescript@5.4.5)) + '@applitools/tunnel-client': 1.5.8 + '@applitools/utils': 1.7.4 abort-controller: 3.0.0 webdriver: 7.31.1(typescript@5.4.5) yargs: 17.7.2 @@ -9622,14 +9646,14 @@ snapshots: binary: 0.3.0 is-localhost-ip: 2.0.0 - '@applitools/execution-grid-tunnel@2.1.10': + '@applitools/execution-grid-tunnel@3.0.8': dependencies: '@applitools/eg-frpc': 1.0.5 '@applitools/eg-socks5-proxy-server': 0.5.6 '@applitools/logger': 1.1.53 dotenv: 16.4.5 encoding: 0.1.13 - fastify: 3.29.5 + fastify: 4.28.1 fastify-plugin: 3.0.1 find-process: 1.4.7 ini: 3.0.1 @@ -9637,21 +9661,22 @@ snapshots: node-fetch: 2.6.7(encoding@0.1.13) p-retry: 4.6.2 teen_process: 1.16.0 + uuid: 9.0.1 transitivePeerDependencies: - supports-color - '@applitools/eyes-cypress@3.43.1(encoding@0.1.13)(typescript@5.4.5)': + '@applitools/eyes-cypress@3.44.9(encoding@0.1.13)(typescript@5.4.5)': dependencies: - '@applitools/core': 4.11.1(encoding@0.1.13)(typescript@5.4.5) - '@applitools/eyes': 1.17.1(encoding@0.1.13)(typescript@5.4.5) + '@applitools/core': 4.19.0(encoding@0.1.13)(typescript@5.4.5) + '@applitools/eyes': 1.22.2(encoding@0.1.13)(typescript@5.4.5) '@applitools/functional-commons': 1.6.0 - '@applitools/logger': 2.0.14 - '@applitools/utils': 1.7.0 + '@applitools/logger': 2.0.18 + '@applitools/utils': 1.7.4 boxen: 5.1.2 chalk: 3.0.0 - semver: 7.5.4 + semver: 7.6.2 uuid: 8.3.2 - ws: 8.5.0 + ws: 8.17.1 transitivePeerDependencies: - bufferutil - encoding @@ -9659,11 +9684,11 @@ snapshots: - typescript - utf-8-validate - '@applitools/eyes@1.17.1(encoding@0.1.13)(typescript@5.4.5)': + '@applitools/eyes@1.22.2(encoding@0.1.13)(typescript@5.4.5)': dependencies: - '@applitools/core': 4.11.1(encoding@0.1.13)(typescript@5.4.5) - '@applitools/logger': 2.0.14 - '@applitools/utils': 1.7.0 + '@applitools/core': 4.18.2(encoding@0.1.13)(typescript@5.4.5) + '@applitools/logger': 2.0.18 + '@applitools/utils': 1.7.4 transitivePeerDependencies: - bufferutil - encoding @@ -9673,9 +9698,9 @@ snapshots: '@applitools/functional-commons@1.6.0': {} - '@applitools/image@1.1.9': + '@applitools/image@1.1.13': dependencies: - '@applitools/utils': 1.7.0 + '@applitools/utils': 1.7.4 bmpimagejs: 1.0.4 jpeg-js: 0.4.4 omggif: 1.0.10 @@ -9689,25 +9714,33 @@ snapshots: transitivePeerDependencies: - supports-color - '@applitools/logger@2.0.14': + '@applitools/logger@2.0.18': dependencies: - '@applitools/utils': 1.7.0 + '@applitools/utils': 1.7.4 chalk: 4.1.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + + '@applitools/nml-client@1.8.10': + dependencies: + '@applitools/logger': 2.0.18 + '@applitools/req': 1.7.2 + '@applitools/utils': 1.7.4 transitivePeerDependencies: - supports-color - '@applitools/nml-client@1.7.5': + '@applitools/nml-client@1.8.11': dependencies: - '@applitools/logger': 2.0.14 - '@applitools/req': 1.6.5 - '@applitools/utils': 1.7.0 + '@applitools/logger': 2.0.18 + '@applitools/req': 1.7.2 + '@applitools/utils': 1.7.4 transitivePeerDependencies: - supports-color - '@applitools/req@1.6.5': + '@applitools/req@1.7.2': dependencies: - '@applitools/utils': 1.7.0 + '@applitools/utils': 1.7.4 abort-controller: 3.0.0 http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 @@ -9715,53 +9748,74 @@ snapshots: transitivePeerDependencies: - supports-color - '@applitools/screenshoter@3.8.27': + '@applitools/screenshoter@3.8.36': dependencies: - '@applitools/image': 1.1.9 - '@applitools/logger': 2.0.14 - '@applitools/snippets': 2.4.25 - '@applitools/utils': 1.7.0 + '@applitools/image': 1.1.13 + '@applitools/logger': 2.0.18 + '@applitools/snippets': 2.4.27 + '@applitools/utils': 1.7.4 transitivePeerDependencies: - supports-color - '@applitools/snippets@2.4.25': {} + '@applitools/screenshoter@3.8.37': + dependencies: + '@applitools/image': 1.1.13 + '@applitools/logger': 2.0.18 + '@applitools/snippets': 2.5.0 + '@applitools/utils': 1.7.4 + transitivePeerDependencies: + - supports-color + + '@applitools/snippets@2.4.27': {} + + '@applitools/snippets@2.5.0': {} + + '@applitools/socket@1.1.18': + dependencies: + '@applitools/logger': 2.0.18 + '@applitools/utils': 1.7.4 + transitivePeerDependencies: + - supports-color - '@applitools/socket@1.1.14': + '@applitools/spec-driver-webdriver@1.1.12(webdriver@7.31.1(typescript@5.4.5))': dependencies: - '@applitools/logger': 2.0.14 - '@applitools/utils': 1.7.0 + '@applitools/driver': 1.19.0 + '@applitools/utils': 1.7.4 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + webdriver: 7.31.1(typescript@5.4.5) transitivePeerDependencies: - supports-color - '@applitools/spec-driver-webdriver@1.1.3(webdriver@7.31.1(typescript@5.4.5))': + '@applitools/spec-driver-webdriver@1.1.13(webdriver@7.31.1(typescript@5.4.5))': dependencies: - '@applitools/driver': 1.16.5 - '@applitools/utils': 1.7.0 + '@applitools/driver': 1.19.1 + '@applitools/utils': 1.7.4 http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 webdriver: 7.31.1(typescript@5.4.5) transitivePeerDependencies: - supports-color - '@applitools/tunnel-client@1.4.1': + '@applitools/tunnel-client@1.5.8': dependencies: - '@applitools/execution-grid-tunnel': 2.1.10 - '@applitools/logger': 2.0.14 - '@applitools/req': 1.6.5 - '@applitools/socket': 1.1.14 - '@applitools/utils': 1.7.0 + '@applitools/execution-grid-tunnel': 3.0.8 + '@applitools/logger': 2.0.18 + '@applitools/req': 1.7.2 + '@applitools/socket': 1.1.18 + '@applitools/utils': 1.7.4 abort-controller: 3.0.0 yargs: 17.7.2 transitivePeerDependencies: - supports-color - '@applitools/ufg-client@1.10.2': + '@applitools/ufg-client@1.12.3': dependencies: - '@applitools/css-tree': 1.1.2 - '@applitools/image': 1.1.9 - '@applitools/logger': 2.0.14 - '@applitools/req': 1.6.5 - '@applitools/utils': 1.7.0 + '@applitools/css-tree': 1.1.4 + '@applitools/image': 1.1.13 + '@applitools/logger': 2.0.18 + '@applitools/req': 1.7.2 + '@applitools/utils': 1.7.4 '@xmldom/xmldom': 0.8.10 abort-controller: 3.0.0 throat: 6.0.2 @@ -9770,1742 +9824,1002 @@ snapshots: '@applitools/utils@1.3.36': {} - '@applitools/utils@1.7.0': {} + '@applitools/utils@1.7.4': {} - '@argos-ci/browser@2.1.2': {} + '@argos-ci/api-client@0.5.0': + dependencies: + openapi-fetch: 0.11.3 + + '@argos-ci/browser@2.1.4': {} - '@argos-ci/core@2.4.0': + '@argos-ci/core@2.8.1': dependencies: - '@argos-ci/util': 2.1.0 - axios: 1.7.2(debug@4.3.5) + '@argos-ci/api-client': 0.5.0 + '@argos-ci/util': 2.1.1 + axios: 1.7.7(debug@4.3.7) convict: 6.2.4 - debug: 4.3.5 + debug: 4.3.7(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.0(cypress@13.7.3)': + '@argos-ci/cypress@2.2.2(cypress@13.15.0)': dependencies: - '@argos-ci/browser': 2.1.2 - '@argos-ci/core': 2.4.0 - '@argos-ci/util': 2.1.0 - cypress: 13.7.3 - cypress-wait-until: 3.0.1 + '@argos-ci/browser': 2.1.4 + '@argos-ci/core': 2.8.1 + '@argos-ci/util': 2.1.1 + cypress: 13.15.0 + cypress-wait-until: 3.0.2 transitivePeerDependencies: - supports-color - '@argos-ci/util@2.1.0': {} - - '@babel/code-frame@7.24.2': - dependencies: - '@babel/highlight': 7.24.2 - picocolors: 1.0.1 + '@argos-ci/util@2.1.1': {} - '@babel/code-frame@7.24.7': + '@babel/code-frame@7.25.7': dependencies: - '@babel/highlight': 7.24.7 - picocolors: 1.0.1 + '@babel/highlight': 7.25.7 + picocolors: 1.1.0 - '@babel/compat-data@7.24.4': {} + '@babel/compat-data@7.25.7': {} - '@babel/compat-data@7.24.7': {} - - '@babel/compat-data@7.24.9': {} - - '@babel/core@7.24.4': + '@babel/core@7.25.7': dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.5 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.4) - '@babel/helpers': 7.24.5 - '@babel/parser': 7.24.7 - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.5 - '@babel/types': 7.24.5 + '@babel/code-frame': 7.25.7 + '@babel/generator': 7.25.7 + '@babel/helper-compilation-targets': 7.25.7 + '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.7) + '@babel/helpers': 7.25.7 + '@babel/parser': 7.25.7 + '@babel/template': 7.25.7 + '@babel/traverse': 7.25.7 + '@babel/types': 7.25.7 convert-source-map: 2.0.0 - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/core@7.24.5': + '@babel/generator@7.25.7': dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.5 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) - '@babel/helpers': 7.24.5 - '@babel/parser': 7.24.7 - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.5 - '@babel/types': 7.24.5 - convert-source-map: 2.0.0 - debug: 4.3.5 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 + '@babel/types': 7.25.7 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.0.2 + + '@babel/helper-annotate-as-pure@7.25.7': + dependencies: + '@babel/types': 7.25.7 + + '@babel/helper-builder-binary-assignment-operator-visitor@7.25.7': + dependencies: + '@babel/traverse': 7.25.7 + '@babel/types': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/core@7.24.7': + '@babel/helper-compilation-targets@7.25.7': dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.24.7 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) - '@babel/helpers': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/template': 7.24.7 - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 - convert-source-map: 2.0.0 - debug: 4.3.5 - gensync: 1.0.0-beta.2 - json5: 2.2.3 + '@babel/compat-data': 7.25.7 + '@babel/helper-validator-option': 7.25.7 + browserslist: 4.24.0 + lru-cache: 5.1.1 semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/core@7.24.9': + '@babel/helper-create-class-features-plugin@7.25.7(@babel/core@7.25.7)': dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.24.10 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9) - '@babel/helpers': 7.24.8 - '@babel/parser': 7.24.8 - '@babel/template': 7.24.7 - '@babel/traverse': 7.24.8 - '@babel/types': 7.24.9 - convert-source-map: 2.0.0 - debug: 4.3.5 - gensync: 1.0.0-beta.2 - json5: 2.2.3 + '@babel/core': 7.25.7 + '@babel/helper-annotate-as-pure': 7.25.7 + '@babel/helper-member-expression-to-functions': 7.25.7 + '@babel/helper-optimise-call-expression': 7.25.7 + '@babel/helper-replace-supers': 7.25.7(@babel/core@7.25.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.25.7 + '@babel/traverse': 7.25.7 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.24.10': - dependencies: - '@babel/types': 7.24.9 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 2.5.2 - - '@babel/generator@7.24.5': + '@babel/helper-create-regexp-features-plugin@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/types': 7.24.5 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 2.5.2 + '@babel/core': 7.25.7 + '@babel/helper-annotate-as-pure': 7.25.7 + regexpu-core: 6.1.1 + semver: 6.3.1 - '@babel/generator@7.24.7': + '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.25.7)': dependencies: - '@babel/types': 7.24.7 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 2.5.2 + '@babel/core': 7.25.7 + '@babel/helper-compilation-targets': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + debug: 4.3.7(supports-color@8.1.1) + lodash.debounce: 4.0.8 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color - '@babel/helper-annotate-as-pure@7.22.5': + '@babel/helper-member-expression-to-functions@7.25.7': dependencies: - '@babel/types': 7.24.7 + '@babel/traverse': 7.25.7 + '@babel/types': 7.25.7 + transitivePeerDependencies: + - supports-color - '@babel/helper-annotate-as-pure@7.24.7': + '@babel/helper-module-imports@7.25.7': dependencies: - '@babel/types': 7.24.9 + '@babel/traverse': 7.25.7 + '@babel/types': 7.25.7 + transitivePeerDependencies: + - supports-color - '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': + '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/traverse': 7.24.8 - '@babel/types': 7.24.9 + '@babel/core': 7.25.7 + '@babel/helper-module-imports': 7.25.7 + '@babel/helper-simple-access': 7.25.7 + '@babel/helper-validator-identifier': 7.25.7 + '@babel/traverse': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/helper-compilation-targets@7.23.6': + '@babel/helper-optimise-call-expression@7.25.7': dependencies: - '@babel/compat-data': 7.24.4 - '@babel/helper-validator-option': 7.23.5 - browserslist: 4.23.1 - lru-cache: 5.1.1 - semver: 6.3.1 + '@babel/types': 7.25.7 - '@babel/helper-compilation-targets@7.24.7': - dependencies: - '@babel/compat-data': 7.24.7 - '@babel/helper-validator-option': 7.24.7 - browserslist: 4.23.1 - lru-cache: 5.1.1 - semver: 6.3.1 + '@babel/helper-plugin-utils@7.25.7': {} - '@babel/helper-compilation-targets@7.24.8': + '@babel/helper-remap-async-to-generator@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/compat-data': 7.24.9 - '@babel/helper-validator-option': 7.24.8 - browserslist: 4.23.1 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-create-class-features-plugin@7.24.5(@babel/core@7.24.5)': - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.5 - '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.24.1(@babel/core@7.24.5) - '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/helper-split-export-declaration': 7.24.7 - semver: 6.3.1 - - '@babel/helper-create-class-features-plugin@7.24.8(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 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.24.7(@babel/core@7.24.7) - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - semver: 6.3.1 + '@babel/core': 7.25.7 + '@babel/helper-annotate-as-pure': 7.25.7 + '@babel/helper-wrap-function': 7.25.7 + '@babel/traverse': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/helper-create-class-features-plugin@7.24.8(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 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.24.7(@babel/core@7.24.9) - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - semver: 6.3.1 + '@babel/helper-replace-supers@7.25.7(@babel/core@7.25.7)': + dependencies: + '@babel/core': 7.25.7 + '@babel/helper-member-expression-to-functions': 7.25.7 + '@babel/helper-optimise-call-expression': 7.25.7 + '@babel/traverse': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.24.7)': + '@babel/helper-simple-access@7.25.7': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - regexpu-core: 5.3.2 - semver: 6.3.1 - - '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-annotate-as-pure': 7.24.7 - regexpu-core: 5.3.2 - semver: 6.3.1 - - '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-plugin-utils': 7.24.8 - debug: 4.3.5 - lodash.debounce: 4.0.8 - resolve: 1.22.8 + '@babel/traverse': 7.25.7 + '@babel/types': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.9)': + '@babel/helper-skip-transparent-expression-wrappers@7.25.7': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-plugin-utils': 7.24.8 - debug: 4.3.5 - lodash.debounce: 4.0.8 - resolve: 1.22.8 + '@babel/traverse': 7.25.7 + '@babel/types': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/helper-environment-visitor@7.22.20': {} - - '@babel/helper-environment-visitor@7.24.7': - dependencies: - '@babel/types': 7.24.7 - - '@babel/helper-function-name@7.23.0': - dependencies: - '@babel/template': 7.24.0 - '@babel/types': 7.24.5 - - '@babel/helper-function-name@7.24.7': - dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.7 + '@babel/helper-string-parser@7.25.7': {} - '@babel/helper-hoist-variables@7.22.5': - dependencies: - '@babel/types': 7.24.5 + '@babel/helper-validator-identifier@7.25.7': {} - '@babel/helper-hoist-variables@7.24.7': - dependencies: - '@babel/types': 7.24.7 + '@babel/helper-validator-option@7.25.7': {} - '@babel/helper-member-expression-to-functions@7.24.5': + '@babel/helper-wrap-function@7.25.7': dependencies: - '@babel/types': 7.24.7 - - '@babel/helper-member-expression-to-functions@7.24.8': - dependencies: - '@babel/traverse': 7.24.8 - '@babel/types': 7.24.9 + '@babel/template': 7.25.7 + '@babel/traverse': 7.25.7 + '@babel/types': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.24.3': + '@babel/helpers@7.25.7': dependencies: - '@babel/types': 7.24.5 + '@babel/template': 7.25.7 + '@babel/types': 7.25.7 - '@babel/helper-module-imports@7.24.7': + '@babel/highlight@7.25.7': dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.24.5(@babel/core@7.24.4)': - dependencies: - '@babel/core': 7.24.4 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.24.3 - '@babel/helper-simple-access': 7.24.5 - '@babel/helper-split-export-declaration': 7.24.5 - '@babel/helper-validator-identifier': 7.24.7 - - '@babel/helper-module-transforms@7.24.5(@babel/core@7.24.5)': - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.24.3 - '@babel/helper-simple-access': 7.24.5 - '@babel/helper-split-export-declaration': 7.24.5 - '@babel/helper-validator-identifier': 7.24.7 - - '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-simple-access': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 - transitivePeerDependencies: - - supports-color + '@babel/helper-validator-identifier': 7.25.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.1.0 - '@babel/helper-module-transforms@7.24.9(@babel/core@7.24.7)': + '@babel/parser@7.25.7': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-simple-access': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 - transitivePeerDependencies: - - supports-color + '@babel/types': 7.25.7 - '@babel/helper-module-transforms@7.24.9(@babel/core@7.24.9)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-simple-access': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/traverse': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/helper-optimise-call-expression@7.22.5': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/types': 7.24.7 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helper-optimise-call-expression@7.24.7': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/types': 7.24.9 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helper-plugin-utils@7.24.5': {} - - '@babel/helper-plugin-utils@7.24.8': {} - - '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-wrap-function': 7.24.7 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.7 + '@babel/plugin-transform-optional-chaining': 7.25.7(@babel/core@7.25.7) transitivePeerDependencies: - supports-color - '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-wrap-function': 7.24.7 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/traverse': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.24.1(@babel/core@7.24.5)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.5 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.5 - '@babel/helper-optimise-call-expression': 7.22.5 + '@babel/core': 7.25.7 - '@babel/helper-replace-supers@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.8 - '@babel/helper-optimise-call-expression': 7.24.7 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helper-replace-supers@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.8 - '@babel/helper-optimise-call-expression': 7.24.7 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helper-simple-access@7.24.5': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.25.7)': dependencies: - '@babel/types': 7.24.7 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helper-simple-access@7.24.7': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.25.7)': dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helper-skip-transparent-expression-wrappers@7.22.5': + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.25.7)': dependencies: - '@babel/types': 7.24.7 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helper-skip-transparent-expression-wrappers@7.24.7': + '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.25.7)': dependencies: - '@babel/traverse': 7.24.8 - '@babel/types': 7.24.9 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helper-split-export-declaration@7.24.5': + '@babel/plugin-syntax-import-assertions@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/types': 7.24.5 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helper-split-export-declaration@7.24.7': + '@babel/plugin-syntax-import-attributes@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/types': 7.24.7 - - '@babel/helper-string-parser@7.24.1': {} - - '@babel/helper-string-parser@7.24.7': {} + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helper-string-parser@7.24.8': {} - - '@babel/helper-validator-identifier@7.24.7': {} - - '@babel/helper-validator-option@7.23.5': {} - - '@babel/helper-validator-option@7.24.7': {} - - '@babel/helper-validator-option@7.24.8': {} + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.25.7)': + dependencies: + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helper-wrap-function@7.24.7': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.25.7)': dependencies: - '@babel/helper-function-name': 7.24.7 - '@babel/template': 7.24.7 - '@babel/traverse': 7.24.8 - '@babel/types': 7.24.9 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helpers@7.24.5': + '@babel/plugin-syntax-jsx@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.5 - '@babel/types': 7.24.5 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helpers@7.24.7': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.25.7)': dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.7 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/helpers@7.24.8': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.25.7)': dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.9 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/highlight@7.24.2': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.25.7)': dependencies: - '@babel/helper-validator-identifier': 7.24.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.0.1 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/highlight@7.24.7': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.25.7)': dependencies: - '@babel/helper-validator-identifier': 7.24.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.0.1 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/parser@7.24.5': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.25.7)': dependencies: - '@babel/types': 7.24.5 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/parser@7.24.7': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.25.7)': dependencies: - '@babel/types': 7.24.7 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/parser@7.24.8': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.25.7)': dependencies: - '@babel/types': 7.24.9 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-syntax-typescript@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-create-regexp-features-plugin': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-arrow-functions@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-async-generator-functions@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/plugin-transform-optional-chaining': 7.24.8(@babel/core@7.24.7) + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-remap-async-to-generator': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.7) + '@babel/traverse': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-async-to-generator@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/plugin-transform-optional-chaining': 7.24.8(@babel/core@7.24.9) + '@babel/core': 7.25.7 + '@babel/helper-module-imports': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-remap-async-to-generator': 7.25.7(@babel/core@7.25.7) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-jsx@7.24.1(@babel/core@7.24.5)': - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-jsx@7.24.1(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.9)': + '@babel/plugin-transform-block-scoped-functions@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.5 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)': + '@babel/plugin-transform-block-scoping@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-typescript@7.24.1(@babel/core@7.24.5)': - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-typescript@7.24.1(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.5 - - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-class-properties@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.9) + '@babel/core': 7.25.7 + '@babel/helper-create-class-features-plugin': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-class-static-block@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.25.7 + '@babel/helper-create-class-features-plugin': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.7) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-classes@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.9) + '@babel/core': 7.25.7 + '@babel/helper-annotate-as-pure': 7.25.7 + '@babel/helper-compilation-targets': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-replace-supers': 7.25.7(@babel/core@7.25.7) + '@babel/traverse': 7.25.7 + globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-computed-properties@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/template': 7.25.7 - '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-destructuring@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-dotall-regex@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-create-regexp-features-plugin': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-duplicate-keys@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.8(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-create-regexp-features-plugin': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-dynamic-import@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-create-class-features-plugin': 7.24.8(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.7) - '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-exponentiation-operator@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.8(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7) + '@babel/core': 7.25.7 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-export-namespace-from@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-create-class-features-plugin': 7.24.8(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.9) - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.25.7) - '@babel/plugin-transform-classes@7.24.8(@babel/core@7.24.7)': + '@babel/plugin-transform-for-of@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) - '@babel/helper-split-export-declaration': 7.24.7 - globals: 11.12.0 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.24.8(@babel/core@7.24.9)': + '@babel/plugin-transform-function-name@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.9) - '@babel/helper-split-export-declaration': 7.24.7 - globals: 11.12.0 + '@babel/core': 7.25.7 + '@babel/helper-compilation-targets': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/traverse': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/template': 7.24.7 - - '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/template': 7.24.7 - - '@babel/plugin-transform-destructuring@7.24.8(@babel/core@7.24.7)': + '@babel/plugin-transform-json-strings@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.7) - '@babel/plugin-transform-destructuring@7.24.8(@babel/core@7.24.9)': + '@babel/plugin-transform-literals@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-logical-assignment-operators@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.7) - '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-member-expression-literals@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-modules-amd@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) - - '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.9) - - '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-modules-commonjs@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-simple-access': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7) - - '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.9) - - '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-modules-systemjs@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/core': 7.25.7 + '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-validator-identifier': 7.25.7 + '@babel/traverse': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-modules-umd@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/core': 7.25.7 + '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-named-capturing-groups-regex@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-create-regexp-features-plugin': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-new-target@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-nullish-coalescing-operator@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.7) - '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-numeric-separator@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.9) + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.7) - '@babel/plugin-transform-literals@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-object-rest-spread@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-compilation-targets': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-transform-parameters': 7.25.7(@babel/core@7.25.7) - '@babel/plugin-transform-literals@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-object-super@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) - - '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.9) - - '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-replace-supers': 7.25.7(@babel/core@7.25.7) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-optional-catch-binding@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.7) - '@babel/plugin-transform-modules-commonjs@7.24.1(@babel/core@7.24.5)': + '@babel/plugin-transform-optional-chaining@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.5 - '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) - '@babel/helper-plugin-utils': 7.24.5 - '@babel/helper-simple-access': 7.24.5 - - '@babel/plugin-transform-modules-commonjs@7.24.8(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-simple-access': 7.24.7 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-modules-commonjs@7.24.8(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-simple-access': 7.24.7 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-hoist-variables': 7.24.7 - '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-validator-identifier': 7.24.7 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.7 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.7) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-parameters@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-hoist-variables': 7.24.7 - '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-validator-identifier': 7.24.7 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-private-methods@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-create-class-features-plugin': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-private-property-in-object@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-annotate-as-pure': 7.25.7 + '@babel/helper-create-class-features-plugin': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.7) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-property-literals@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-regenerator@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.9) - - '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) - - '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.9) + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + regenerator-transform: 0.15.2 - '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-reserved-words@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-shorthand-properties@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.9) - '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.9) + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-spread@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-sticky-regex@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.9) - transitivePeerDependencies: - - supports-color + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-template-literals@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-typeof-symbol@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.9) + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-transform-optional-chaining@7.24.8(@babel/core@7.24.7)': + '@babel/plugin-transform-typescript@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + '@babel/core': 7.25.7 + '@babel/helper-annotate-as-pure': 7.25.7 + '@babel/helper-create-class-features-plugin': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.7 + '@babel/plugin-syntax-typescript': 7.25.7(@babel/core@7.25.7) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-chaining@7.24.8(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.9) + '@babel/plugin-transform-unicode-escapes@7.25.7(@babel/core@7.25.7)': + dependencies: + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + + '@babel/plugin-transform-unicode-property-regex@7.25.7(@babel/core@7.25.7)': + dependencies: + '@babel/core': 7.25.7 + '@babel/helper-create-regexp-features-plugin': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 + + '@babel/plugin-transform-unicode-regex@7.25.7(@babel/core@7.25.7)': + dependencies: + '@babel/core': 7.25.7 + '@babel/helper-create-regexp-features-plugin': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 + + '@babel/plugin-transform-unicode-sets-regex@7.25.7(@babel/core@7.25.7)': + dependencies: + '@babel/core': 7.25.7 + '@babel/helper-create-regexp-features-plugin': 7.25.7(@babel/core@7.25.7) + '@babel/helper-plugin-utils': 7.25.7 + + '@babel/preset-env@7.25.7(@babel/core@7.25.7)': + dependencies: + '@babel/compat-data': 7.25.7 + '@babel/core': 7.25.7 + '@babel/helper-compilation-targets': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-validator-option': 7.25.7 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.25.7) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.7) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.25.7) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.7) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-import-assertions': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-syntax-import-attributes': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.25.7) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.7) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.7) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.7) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.25.7) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.25.7) + '@babel/plugin-transform-arrow-functions': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-async-generator-functions': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-async-to-generator': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-block-scoped-functions': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-block-scoping': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-class-properties': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-class-static-block': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-classes': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-computed-properties': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-destructuring': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-dotall-regex': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-duplicate-keys': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-dynamic-import': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-exponentiation-operator': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-export-namespace-from': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-for-of': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-function-name': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-json-strings': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-literals': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-logical-assignment-operators': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-member-expression-literals': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-modules-amd': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-modules-commonjs': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-modules-systemjs': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-modules-umd': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-named-capturing-groups-regex': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-new-target': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-nullish-coalescing-operator': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-numeric-separator': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-object-rest-spread': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-object-super': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-optional-catch-binding': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-optional-chaining': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-parameters': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-private-methods': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-private-property-in-object': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-property-literals': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-regenerator': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-reserved-words': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-shorthand-properties': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-spread': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-sticky-regex': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-template-literals': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-typeof-symbol': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-unicode-escapes': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-unicode-property-regex': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-unicode-regex': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-unicode-sets-regex': 7.25.7(@babel/core@7.25.7) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.25.7) + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.25.7) + babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.25.7) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.25.7) + core-js-compat: 3.38.1 + semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.24.7)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/types': 7.25.7 + esutils: 2.0.3 - '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.24.7)': + '@babel/preset-typescript@7.25.7(@babel/core@7.25.7)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.8(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.25.7 + '@babel/helper-plugin-utils': 7.25.7 + '@babel/helper-validator-option': 7.25.7 + '@babel/plugin-syntax-jsx': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-modules-commonjs': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-transform-typescript': 7.25.7(@babel/core@7.25.7) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.24.9)': + '@babel/runtime@7.25.7': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-create-class-features-plugin': 7.24.8(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 - transitivePeerDependencies: - - supports-color + regenerator-runtime: 0.14.1 - '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.24.7)': + '@babel/template@7.25.7': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.8(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7) - transitivePeerDependencies: - - supports-color + '@babel/code-frame': 7.25.7 + '@babel/parser': 7.25.7 + '@babel/types': 7.25.7 - '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.24.9)': + '@babel/traverse@7.25.7': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.8(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.9) + '@babel/code-frame': 7.25.7 + '@babel/generator': 7.25.7 + '@babel/parser': 7.25.7 + '@babel/template': 7.25.7 + '@babel/types': 7.25.7 + debug: 4.3.7(supports-color@8.1.1) + globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.24.7)': + '@babel/types@7.25.7': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-string-parser': 7.25.7 + '@babel/helper-validator-identifier': 7.25.7 + to-fast-properties: 2.0.0 - '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.7)': + '@bcherny/json-schema-ref-parser@10.0.5-fork': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - regenerator-transform: 0.15.2 + '@jsdevtools/ono': 7.1.3 + '@types/json-schema': 7.0.15 + call-me-maybe: 1.0.2 + js-yaml: 4.1.0 - '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - regenerator-transform: 0.15.2 + '@bcoe/v8-coverage@0.2.3': {} - '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@braintree/sanitize-url@7.1.0': {} - '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.24.9)': + '@changesets/apply-release-plan@7.0.5': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@changesets/config': 3.0.3 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.1 + '@changesets/should-skip-package': 0.1.1 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.6.3 - '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.24.9)': + '@changesets/assemble-release-plan@6.0.4': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.2 + '@changesets/should-skip-package': 0.1.1 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.6.3 - '@babel/plugin-transform-spread@7.24.7(@babel/core@7.24.7)': + '@changesets/changelog-git@0.2.0': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - transitivePeerDependencies: - - supports-color + '@changesets/types': 6.0.0 - '@babel/plugin-transform-spread@7.24.7(@babel/core@7.24.9)': + '@changesets/changelog-github@0.5.0(encoding@0.1.13)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@changesets/get-github-info': 0.6.0(encoding@0.1.13) + '@changesets/types': 6.0.0 + dotenv: 8.6.0 transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + - encoding - '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 + '@changesets/cli@2.27.9': + dependencies: + '@changesets/apply-release-plan': 7.0.5 + '@changesets/assemble-release-plan': 6.0.4 + '@changesets/changelog-git': 0.2.0 + '@changesets/config': 3.0.3 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.2 + '@changesets/get-release-plan': 4.0.4 + '@changesets/git': 3.0.1 + '@changesets/logger': 0.1.1 + '@changesets/pre': 2.0.1 + '@changesets/read': 0.6.1 + '@changesets/should-skip-package': 0.1.1 + '@changesets/types': 6.0.0 + '@changesets/write': 0.3.2 + '@manypkg/get-packages': 1.1.3 + ansi-colors: 4.1.3 + ci-info: 3.9.0 + enquirer: 2.4.1 + external-editor: 3.1.0 + fs-extra: 7.0.1 + mri: 1.2.0 + p-limit: 2.3.0 + package-manager-detector: 0.2.1 + picocolors: 1.1.0 + resolve-from: 5.0.0 + semver: 7.6.3 + spawndamnit: 2.0.0 + term-size: 2.2.1 - '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.24.7)': + '@changesets/config@3.0.3': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.2 + '@changesets/logger': 0.1.1 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.8 - '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.24.9)': + '@changesets/errors@0.2.0': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 + extendable-error: 0.1.7 - '@babel/plugin-transform-typeof-symbol@7.24.8(@babel/core@7.24.7)': + '@changesets/get-dependents-graph@2.1.2': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + picocolors: 1.1.0 + semver: 7.6.3 - '@babel/plugin-transform-typeof-symbol@7.24.8(@babel/core@7.24.9)': + '@changesets/get-github-info@0.6.0(encoding@0.1.13)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-typescript@7.24.4(@babel/core@7.24.5)': - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.24.5(@babel/core@7.24.5) - '@babel/helper-plugin-utils': 7.24.5 - '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.5) - - '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.9) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/preset-env@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/compat-data': 7.24.9 - '@babel/core': 7.24.7 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-validator-option': 7.24.8 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.7) - '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-classes': 7.24.8(@babel/core@7.24.7) - '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-destructuring': 7.24.8(@babel/core@7.24.7) - '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.24.7) - '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-optional-chaining': 7.24.8(@babel/core@7.24.7) - '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-typeof-symbol': 7.24.8(@babel/core@7.24.7) - '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.24.7) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.7) - babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.7) - babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.7) - babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.7) - core-js-compat: 3.37.1 - semver: 6.3.1 + dataloader: 1.4.0 + node-fetch: 2.7.0(encoding@0.1.13) transitivePeerDependencies: - - supports-color - - '@babel/preset-env@7.24.8(@babel/core@7.24.9)': - dependencies: - '@babel/compat-data': 7.24.9 - '@babel/core': 7.24.9 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-validator-option': 7.24.8 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.9) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.9) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.9) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.9) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.9) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.9) - '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.9) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.9) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.9) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.9) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.9) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.9) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.9) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.9) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.9) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.9) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.9) - '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-classes': 7.24.8(@babel/core@7.24.9) - '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-destructuring': 7.24.8(@babel/core@7.24.9) - '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.24.9) - '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-optional-chaining': 7.24.8(@babel/core@7.24.9) - '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-typeof-symbol': 7.24.8(@babel/core@7.24.9) - '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.24.9) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.9) - babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.9) - babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.9) - babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.9) - core-js-compat: 3.37.1 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/types': 7.24.9 - esutils: 2.0.3 - - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.9)': - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/types': 7.24.9 - esutils: 2.0.3 + - encoding - '@babel/preset-typescript@7.24.1(@babel/core@7.24.5)': + '@changesets/get-release-plan@4.0.4': dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.5 - '@babel/helper-validator-option': 7.23.5 - '@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.5) - '@babel/plugin-transform-modules-commonjs': 7.24.1(@babel/core@7.24.5) - '@babel/plugin-transform-typescript': 7.24.4(@babel/core@7.24.5) - - '@babel/regjsgen@0.8.0': {} + '@changesets/assemble-release-plan': 6.0.4 + '@changesets/config': 3.0.3 + '@changesets/pre': 2.0.1 + '@changesets/read': 0.6.1 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 - '@babel/runtime@7.24.0': - dependencies: - regenerator-runtime: 0.14.1 + '@changesets/get-version-range-type@0.4.0': {} - '@babel/runtime@7.24.4': + '@changesets/git@3.0.1': dependencies: - regenerator-runtime: 0.14.1 + '@changesets/errors': 0.2.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.8 + spawndamnit: 2.0.0 - '@babel/runtime@7.24.8': + '@changesets/logger@0.1.1': dependencies: - regenerator-runtime: 0.14.1 + picocolors: 1.1.0 - '@babel/template@7.24.0': + '@changesets/parse@0.4.0': dependencies: - '@babel/code-frame': 7.24.2 - '@babel/parser': 7.24.7 - '@babel/types': 7.24.5 + '@changesets/types': 6.0.0 + js-yaml: 3.14.1 - '@babel/template@7.24.7': + '@changesets/pre@2.0.1': dependencies: - '@babel/code-frame': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 - '@babel/traverse@7.24.5': + '@changesets/read@0.6.1': dependencies: - '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.5 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.24.5 - '@babel/parser': 7.24.7 - '@babel/types': 7.24.5 - debug: 4.3.5 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - '@babel/traverse@7.24.7': - dependencies: - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-hoist-variables': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 - debug: 4.3.5 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - '@babel/traverse@7.24.8': - dependencies: - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.24.10 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-hoist-variables': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/parser': 7.24.8 - '@babel/types': 7.24.9 - debug: 4.3.5 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color + '@changesets/git': 3.0.1 + '@changesets/logger': 0.1.1 + '@changesets/parse': 0.4.0 + '@changesets/types': 6.0.0 + fs-extra: 7.0.1 + p-filter: 2.1.0 + picocolors: 1.1.0 - '@babel/types@7.24.5': + '@changesets/should-skip-package@0.1.1': dependencies: - '@babel/helper-string-parser': 7.24.1 - '@babel/helper-validator-identifier': 7.24.7 - to-fast-properties: 2.0.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 - '@babel/types@7.24.7': - dependencies: - '@babel/helper-string-parser': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 - to-fast-properties: 2.0.0 + '@changesets/types@4.1.0': {} - '@babel/types@7.24.9': - dependencies: - '@babel/helper-string-parser': 7.24.8 - '@babel/helper-validator-identifier': 7.24.7 - to-fast-properties: 2.0.0 + '@changesets/types@6.0.0': {} - '@bcherny/json-schema-ref-parser@10.0.5-fork': + '@changesets/write@0.3.2': dependencies: - '@jsdevtools/ono': 7.1.3 - '@types/json-schema': 7.0.15 - call-me-maybe: 1.0.2 - js-yaml: 4.1.0 - - '@bcoe/v8-coverage@0.2.3': {} - - '@braintree/sanitize-url@7.0.4': {} + '@changesets/types': 6.0.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + prettier: 2.8.8 '@chevrotain/cst-dts-gen@11.0.3': dependencies: @@ -11527,206 +10841,125 @@ snapshots: '@colors/colors@1.5.0': optional: true - '@cspell/cspell-bundled-dicts@8.7.0': + '@cspell/cspell-bundled-dicts@8.14.4': dependencies: '@cspell/dict-ada': 4.0.2 - '@cspell/dict-aws': 4.0.1 - '@cspell/dict-bash': 4.1.3 - '@cspell/dict-companies': 3.0.31 - '@cspell/dict-cpp': 5.1.3 + '@cspell/dict-aws': 4.0.4 + '@cspell/dict-bash': 4.1.5 + '@cspell/dict-companies': 3.1.4 + '@cspell/dict-cpp': 5.1.19 '@cspell/dict-cryptocurrencies': 5.0.0 '@cspell/dict-csharp': 4.0.2 - '@cspell/dict-css': 4.0.12 - '@cspell/dict-dart': 2.0.3 + '@cspell/dict-css': 4.0.13 + '@cspell/dict-dart': 2.2.1 '@cspell/dict-django': 4.1.0 '@cspell/dict-docker': 1.1.7 - '@cspell/dict-dotnet': 5.0.0 + '@cspell/dict-dotnet': 5.0.5 '@cspell/dict-elixir': 4.0.3 - '@cspell/dict-en-common-misspellings': 2.0.0 - '@cspell/dict-en-gb': 1.1.33 - '@cspell/dict-en_us': 4.3.18 - '@cspell/dict-filetypes': 3.0.3 - '@cspell/dict-fonts': 4.0.0 - '@cspell/dict-fsharp': 1.0.1 - '@cspell/dict-fullstack': 3.1.5 - '@cspell/dict-gaming-terms': 1.0.5 - '@cspell/dict-git': 3.0.0 - '@cspell/dict-golang': 6.0.5 - '@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.6 - '@cspell/dict-julia': 1.0.1 - '@cspell/dict-k8s': 1.0.2 - '@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': 4.0.3 - '@cspell/dict-npm': 5.0.15 - '@cspell/dict-php': 4.0.6 - '@cspell/dict-powershell': 5.0.3 - '@cspell/dict-public-licenses': 2.0.6 - '@cspell/dict-python': 4.1.11 - '@cspell/dict-r': 2.0.1 - '@cspell/dict-ruby': 5.0.2 - '@cspell/dict-rust': 4.0.2 - '@cspell/dict-scala': 5.0.0 - '@cspell/dict-software-terms': 3.3.18 - '@cspell/dict-sql': 2.1.3 - '@cspell/dict-svelte': 1.0.2 - '@cspell/dict-swift': 2.0.1 - '@cspell/dict-terraform': 1.0.0 - '@cspell/dict-typescript': 3.1.3 - '@cspell/dict-vue': 3.0.0 - - '@cspell/cspell-bundled-dicts@8.9.1': - dependencies: - '@cspell/dict-ada': 4.0.2 - '@cspell/dict-aws': 4.0.2 - '@cspell/dict-bash': 4.1.3 - '@cspell/dict-companies': 3.1.2 - '@cspell/dict-cpp': 5.1.10 - '@cspell/dict-cryptocurrencies': 5.0.0 - '@cspell/dict-csharp': 4.0.2 - '@cspell/dict-css': 4.0.12 - '@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-elixir': 4.0.3 - '@cspell/dict-en-common-misspellings': 2.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-flutter': 1.0.0 '@cspell/dict-fonts': 4.0.0 '@cspell/dict-fsharp': 1.0.1 - '@cspell/dict-fullstack': 3.1.8 + '@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.13 '@cspell/dict-google': 1.0.1 '@cspell/dict-haskell': 4.0.1 - '@cspell/dict-html': 4.0.5 + '@cspell/dict-html': 4.0.6 '@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.5 + '@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.0.16 - '@cspell/dict-php': 4.0.8 - '@cspell/dict-powershell': 5.0.4 - '@cspell/dict-public-licenses': 2.0.7 - '@cspell/dict-python': 4.2.1 + '@cspell/dict-npm': 5.1.5 + '@cspell/dict-php': 4.0.10 + '@cspell/dict-powershell': 5.0.10 + '@cspell/dict-public-licenses': 2.0.8 + '@cspell/dict-python': 4.2.8 '@cspell/dict-r': 2.0.1 - '@cspell/dict-ruby': 5.0.2 - '@cspell/dict-rust': 4.0.4 - '@cspell/dict-scala': 5.0.2 - '@cspell/dict-software-terms': 3.4.8 - '@cspell/dict-sql': 2.1.3 + '@cspell/dict-ruby': 5.0.4 + '@cspell/dict-rust': 4.0.6 + '@cspell/dict-scala': 5.0.3 + '@cspell/dict-software-terms': 4.1.7 + '@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.5 + '@cspell/dict-terraform': 1.0.2 + '@cspell/dict-typescript': 3.1.6 '@cspell/dict-vue': 3.0.0 - '@cspell/cspell-json-reporter@8.7.0': + '@cspell/cspell-json-reporter@8.14.4': dependencies: - '@cspell/cspell-types': 8.7.0 + '@cspell/cspell-types': 8.14.4 - '@cspell/cspell-pipe@8.7.0': {} + '@cspell/cspell-pipe@8.14.4': {} - '@cspell/cspell-pipe@8.9.1': {} - - '@cspell/cspell-resolver@8.7.0': - dependencies: - global-directory: 4.0.1 - - '@cspell/cspell-resolver@8.9.1': + '@cspell/cspell-resolver@8.14.4': dependencies: global-directory: 4.0.1 - '@cspell/cspell-service-bus@8.7.0': {} - - '@cspell/cspell-service-bus@8.9.1': {} - - '@cspell/cspell-types@8.7.0': {} + '@cspell/cspell-service-bus@8.14.4': {} - '@cspell/cspell-types@8.9.1': {} + '@cspell/cspell-types@8.14.4': {} '@cspell/dict-ada@4.0.2': {} - '@cspell/dict-aws@4.0.1': {} + '@cspell/dict-aws@4.0.4': {} - '@cspell/dict-aws@4.0.2': {} + '@cspell/dict-bash@4.1.5': {} - '@cspell/dict-bash@4.1.3': {} + '@cspell/dict-companies@3.1.4': {} - '@cspell/dict-companies@3.0.31': {} - - '@cspell/dict-companies@3.1.2': {} - - '@cspell/dict-cpp@5.1.10': {} - - '@cspell/dict-cpp@5.1.3': {} + '@cspell/dict-cpp@5.1.19': {} '@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-dart@2.2.1': {} - '@cspell/dict-data-science@1.0.11': {} - - '@cspell/dict-data-science@2.0.1': {} + '@cspell/dict-data-science@2.0.2': {} '@cspell/dict-django@4.1.0': {} '@cspell/dict-docker@1.1.7': {} - '@cspell/dict-dotnet@5.0.0': {} - - '@cspell/dict-dotnet@5.0.2': {} + '@cspell/dict-dotnet@5.0.5': {} '@cspell/dict-elixir@4.0.3': {} - '@cspell/dict-en-common-misspellings@2.0.0': {} - - '@cspell/dict-en-common-misspellings@2.0.3': {} + '@cspell/dict-en-common-misspellings@2.0.4': {} '@cspell/dict-en-gb@1.1.33': {} - '@cspell/dict-en_us@4.3.18': {} - '@cspell/dict-en_us@4.3.23': {} - '@cspell/dict-filetypes@3.0.3': {} - '@cspell/dict-filetypes@3.0.4': {} + '@cspell/dict-flutter@1.0.0': {} + '@cspell/dict-fonts@4.0.0': {} '@cspell/dict-fsharp@1.0.1': {} - '@cspell/dict-fullstack@3.1.5': {} - - '@cspell/dict-fullstack@3.1.8': {} + '@cspell/dict-fullstack@3.2.0': {} '@cspell/dict-gaming-terms@1.0.5': {} '@cspell/dict-git@3.0.0': {} - '@cspell/dict-golang@6.0.5': {} - - '@cspell/dict-golang@6.0.9': {} + '@cspell/dict-golang@6.0.13': {} '@cspell/dict-google@1.0.1': {} @@ -11734,17 +10967,13 @@ snapshots: '@cspell/dict-html-symbol-entities@4.0.0': {} - '@cspell/dict-html@4.0.5': {} - - '@cspell/dict-java@5.0.6': {} + '@cspell/dict-html@4.0.6': {} '@cspell/dict-java@5.0.7': {} '@cspell/dict-julia@1.0.1': {} - '@cspell/dict-k8s@1.0.2': {} - - '@cspell/dict-k8s@1.0.5': {} + '@cspell/dict-k8s@1.0.6': {} '@cspell/dict-latex@4.0.0': {} @@ -11756,134 +10985,109 @@ snapshots: '@cspell/dict-monkeyc@1.0.6': {} - '@cspell/dict-node@4.0.3': {} - '@cspell/dict-node@5.0.1': {} - '@cspell/dict-npm@5.0.15': {} - - '@cspell/dict-npm@5.0.16': {} - - '@cspell/dict-php@4.0.6': {} + '@cspell/dict-npm@5.1.5': {} - '@cspell/dict-php@4.0.8': {} + '@cspell/dict-php@4.0.10': {} - '@cspell/dict-powershell@5.0.3': {} + '@cspell/dict-powershell@5.0.10': {} - '@cspell/dict-powershell@5.0.4': {} + '@cspell/dict-public-licenses@2.0.8': {} - '@cspell/dict-public-licenses@2.0.6': {} - - '@cspell/dict-public-licenses@2.0.7': {} - - '@cspell/dict-python@4.1.11': + '@cspell/dict-python@4.2.8': dependencies: - '@cspell/dict-data-science': 1.0.11 - - '@cspell/dict-python@4.2.1': - dependencies: - '@cspell/dict-data-science': 2.0.1 + '@cspell/dict-data-science': 2.0.2 '@cspell/dict-r@2.0.1': {} - '@cspell/dict-ruby@5.0.2': {} - - '@cspell/dict-rust@4.0.2': {} - - '@cspell/dict-rust@4.0.4': {} + '@cspell/dict-ruby@5.0.4': {} - '@cspell/dict-scala@5.0.0': {} + '@cspell/dict-rust@4.0.6': {} - '@cspell/dict-scala@5.0.2': {} + '@cspell/dict-scala@5.0.3': {} - '@cspell/dict-software-terms@3.3.18': {} + '@cspell/dict-software-terms@4.1.7': {} - '@cspell/dict-software-terms@3.4.8': {} - - '@cspell/dict-sql@2.1.3': {} + '@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.3': {} + '@cspell/dict-terraform@1.0.2': {} - '@cspell/dict-typescript@3.1.5': {} + '@cspell/dict-typescript@3.1.6': {} '@cspell/dict-vue@3.0.0': {} - '@cspell/dynamic-import@8.7.0': - dependencies: - import-meta-resolve: 4.0.0 - - '@cspell/dynamic-import@8.9.1': + '@cspell/dynamic-import@8.14.4': dependencies: import-meta-resolve: 4.1.0 - '@cspell/eslint-plugin@8.9.1(eslint@9.6.0)': + '@cspell/eslint-plugin@8.14.4(eslint@9.12.0(jiti@1.21.6))': dependencies: - '@cspell/cspell-types': 8.9.1 - cspell-lib: 8.9.1 - eslint: 9.6.0 - synckit: 0.9.0 + '@cspell/cspell-types': 8.14.4 + '@cspell/url': 8.14.4 + cspell-lib: 8.14.4 + eslint: 9.12.0(jiti@1.21.6) + synckit: 0.9.2 - '@cspell/strong-weak-map@8.7.0': {} + '@cspell/filetypes@8.14.4': {} - '@cspell/strong-weak-map@8.9.1': {} + '@cspell/strong-weak-map@8.14.4': {} - '@cspell/url@8.9.1': {} + '@cspell/url@8.14.4': {} - '@cypress/code-coverage@3.12.41(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(babel-loader@9.1.3(@babel/core@7.24.7)(webpack@5.91.0(esbuild@0.21.5)))(cypress@13.7.3)(webpack@5.91.0(esbuild@0.21.5))': + '@cypress/code-coverage@3.13.4(@babel/core@7.25.7)(@babel/preset-env@7.25.7(@babel/core@7.25.7))(babel-loader@9.2.1(@babel/core@7.25.7)(webpack@5.95.0(esbuild@0.21.5)))(cypress@13.15.0)(webpack@5.95.0(esbuild@0.21.5))': dependencies: - '@babel/core': 7.24.7 - '@babel/preset-env': 7.24.7(@babel/core@7.24.7) - '@cypress/webpack-preprocessor': 6.0.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(babel-loader@9.1.3(@babel/core@7.24.7)(webpack@5.91.0(esbuild@0.21.5)))(webpack@5.91.0(esbuild@0.21.5)) - babel-loader: 9.1.3(@babel/core@7.24.7)(webpack@5.91.0(esbuild@0.21.5)) + '@babel/core': 7.25.7 + '@babel/preset-env': 7.25.7(@babel/core@7.25.7) + '@cypress/webpack-preprocessor': 6.0.2(@babel/core@7.25.7)(@babel/preset-env@7.25.7(@babel/core@7.25.7))(babel-loader@9.2.1(@babel/core@7.25.7)(webpack@5.95.0(esbuild@0.21.5)))(webpack@5.95.0(esbuild@0.21.5)) + babel-loader: 9.2.1(@babel/core@7.25.7)(webpack@5.95.0(esbuild@0.21.5)) chalk: 4.1.2 - cypress: 13.7.3 - dayjs: 1.11.10 - debug: 4.3.4(supports-color@8.1.1) + cypress: 13.15.0 + dayjs: 1.11.13 + debug: 4.3.7(supports-color@8.1.1) execa: 4.1.0 globby: 11.1.0 istanbul-lib-coverage: 3.2.2 js-yaml: 4.1.0 nyc: 15.1.0 - webpack: 5.91.0(esbuild@0.21.5) + webpack: 5.95.0(esbuild@0.21.5) transitivePeerDependencies: - supports-color - '@cypress/request@3.0.1': + '@cypress/request@3.0.5': dependencies: aws-sign2: 0.7.0 - aws4: 1.12.0 + aws4: 1.13.2 caseless: 0.12.0 combined-stream: 1.0.8 extend: 3.0.2 forever-agent: 0.6.1 - form-data: 2.3.3 - http-signature: 1.3.6 + form-data: 4.0.0 + http-signature: 1.4.0 is-typedarray: 1.0.0 isstream: 0.1.2 json-stringify-safe: 5.0.1 mime-types: 2.1.35 performance-now: 2.1.0 - qs: 6.10.4 + qs: 6.13.0 safe-buffer: 5.2.1 - tough-cookie: 4.1.3 + tough-cookie: 4.1.4 tunnel-agent: 0.6.0 uuid: 8.3.2 - '@cypress/webpack-preprocessor@6.0.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(babel-loader@9.1.3(@babel/core@7.24.7)(webpack@5.91.0(esbuild@0.21.5)))(webpack@5.91.0(esbuild@0.21.5))': + '@cypress/webpack-preprocessor@6.0.2(@babel/core@7.25.7)(@babel/preset-env@7.25.7(@babel/core@7.25.7))(babel-loader@9.2.1(@babel/core@7.25.7)(webpack@5.95.0(esbuild@0.21.5)))(webpack@5.95.0(esbuild@0.21.5))': dependencies: - '@babel/core': 7.24.7 - '@babel/preset-env': 7.24.7(@babel/core@7.24.7) - babel-loader: 9.1.3(@babel/core@7.24.7)(webpack@5.91.0(esbuild@0.21.5)) + '@babel/core': 7.25.7 + '@babel/preset-env': 7.25.7(@babel/core@7.25.7) + babel-loader: 9.2.1(@babel/core@7.25.7)(webpack@5.95.0(esbuild@0.21.5)) bluebird: 3.7.1 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) lodash: 4.17.21 - webpack: 5.91.0(esbuild@0.21.5) + webpack: 5.95.0(esbuild@0.21.5) transitivePeerDependencies: - supports-color @@ -11894,14 +11098,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@dependents/detective-less@5.0.0': + dependencies: + gonzales-pe: 4.3.0 + node-source-walk: 7.0.0 + '@discoveryjs/json-ext@0.5.7': {} - '@docsearch/css@3.6.0': {} + '@docsearch/css@3.6.2': {} - '@docsearch/js@3.6.0(@algolia/client-search@4.23.3)(search-insights@2.13.0)': + '@docsearch/js@3.6.2(@algolia/client-search@4.24.0)(search-insights@2.17.2)': dependencies: - '@docsearch/react': 3.6.0(@algolia/client-search@4.23.3)(search-insights@2.13.0) - preact: 10.21.0 + '@docsearch/react': 3.6.2(@algolia/client-search@4.24.0)(search-insights@2.17.2) + preact: 10.24.2 transitivePeerDependencies: - '@algolia/client-search' - '@types/react' @@ -11909,262 +11118,193 @@ snapshots: - react-dom - search-insights - '@docsearch/react@3.6.0(@algolia/client-search@4.23.3)(search-insights@2.13.0)': + '@docsearch/react@3.6.2(@algolia/client-search@4.24.0)(search-insights@2.17.2)': dependencies: - '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.13.0) - '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) - '@docsearch/css': 3.6.0 - algoliasearch: 4.23.3 + '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.17.2) + '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + '@docsearch/css': 3.6.2 + algoliasearch: 4.24.0 optionalDependencies: - search-insights: 2.13.0 + search-insights: 2.17.2 transitivePeerDependencies: - '@algolia/client-search' - '@emnapi/runtime@1.2.0': + '@emnapi/runtime@1.3.0': dependencies: - tslib: 2.6.3 + tslib: 2.7.0 optional: true - '@es-joy/jsdoccomment@0.43.1': + '@es-joy/jsdoccomment@0.48.0': dependencies: - '@types/eslint': 8.56.10 - '@types/estree': 1.0.5 - '@typescript-eslint/types': 7.15.0 comment-parser: 1.4.1 - esquery: 1.5.0 - jsdoc-type-pratt-parser: 4.0.0 - - '@esbuild/aix-ppc64@0.19.12': - optional: true - - '@esbuild/aix-ppc64@0.20.2': - optional: true + esquery: 1.6.0 + jsdoc-type-pratt-parser: 4.1.0 '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/android-arm64@0.19.12': - optional: true - - '@esbuild/android-arm64@0.20.2': + '@esbuild/aix-ppc64@0.23.1': optional: true '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm@0.19.12': - optional: true - - '@esbuild/android-arm@0.20.2': + '@esbuild/android-arm64@0.23.1': optional: true '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-x64@0.19.12': - optional: true - - '@esbuild/android-x64@0.20.2': + '@esbuild/android-arm@0.23.1': optional: true '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.19.12': - optional: true - - '@esbuild/darwin-arm64@0.20.2': + '@esbuild/android-x64@0.23.1': optional: true '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-x64@0.19.12': - optional: true - - '@esbuild/darwin-x64@0.20.2': + '@esbuild/darwin-arm64@0.23.1': optional: true '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.19.12': - optional: true - - '@esbuild/freebsd-arm64@0.20.2': + '@esbuild/darwin-x64@0.23.1': optional: true '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.19.12': - optional: true - - '@esbuild/freebsd-x64@0.20.2': + '@esbuild/freebsd-arm64@0.23.1': optional: true '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/linux-arm64@0.19.12': - optional: true - - '@esbuild/linux-arm64@0.20.2': + '@esbuild/freebsd-x64@0.23.1': optional: true '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm@0.19.12': - optional: true - - '@esbuild/linux-arm@0.20.2': + '@esbuild/linux-arm64@0.23.1': optional: true '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-ia32@0.19.12': - optional: true - - '@esbuild/linux-ia32@0.20.2': + '@esbuild/linux-arm@0.23.1': optional: true '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-loong64@0.19.12': - optional: true - - '@esbuild/linux-loong64@0.20.2': + '@esbuild/linux-ia32@0.23.1': optional: true '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-mips64el@0.19.12': - optional: true - - '@esbuild/linux-mips64el@0.20.2': - optional: true - - '@esbuild/linux-mips64el@0.21.5': - optional: true - - '@esbuild/linux-ppc64@0.19.12': + '@esbuild/linux-loong64@0.23.1': optional: true - '@esbuild/linux-ppc64@0.20.2': + '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-mips64el@0.23.1': optional: true - '@esbuild/linux-riscv64@0.19.12': + '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.20.2': + '@esbuild/linux-ppc64@0.23.1': optional: true '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-s390x@0.19.12': - optional: true - - '@esbuild/linux-s390x@0.20.2': + '@esbuild/linux-riscv64@0.23.1': optional: true '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-x64@0.19.12': - optional: true - - '@esbuild/linux-x64@0.20.2': + '@esbuild/linux-s390x@0.23.1': optional: true '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.19.12': - optional: true - - '@esbuild/netbsd-x64@0.20.2': + '@esbuild/linux-x64@0.23.1': optional: true '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.19.12': + '@esbuild/netbsd-x64@0.23.1': optional: true - '@esbuild/openbsd-x64@0.20.2': + '@esbuild/openbsd-arm64@0.23.1': optional: true '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.19.12': - optional: true - - '@esbuild/sunos-x64@0.20.2': + '@esbuild/openbsd-x64@0.23.1': optional: true '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/win32-arm64@0.19.12': - optional: true - - '@esbuild/win32-arm64@0.20.2': + '@esbuild/sunos-x64@0.23.1': optional: true '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-ia32@0.19.12': - optional: true - - '@esbuild/win32-ia32@0.20.2': + '@esbuild/win32-arm64@0.23.1': optional: true '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-x64@0.19.12': + '@esbuild/win32-ia32@0.23.1': optional: true - '@esbuild/win32-x64@0.20.2': + '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.21.5': + '@esbuild/win32-x64@0.23.1': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@9.6.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.12.0(jiti@1.21.6))': dependencies: - eslint: 9.6.0 + eslint: 9.12.0(jiti@1.21.6) eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.10.0': {} - - '@eslint-community/regexpp@4.11.0': {} + '@eslint-community/regexpp@4.11.1': {} - '@eslint/config-array@0.17.0': + '@eslint/config-array@0.18.0': dependencies: '@eslint/object-schema': 2.1.4 - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color + '@eslint/core@0.6.0': {} + '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 - debug: 4.3.5 - espree: 10.1.0 + debug: 4.3.7(supports-color@8.1.1) + espree: 10.2.0 globals: 14.0.0 - ignore: 5.3.1 + ignore: 5.3.2 import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -12172,32 +11312,48 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.6.0': {} + '@eslint/js@9.12.0': {} '@eslint/object-schema@2.1.4': {} - '@fastify/ajv-compiler@1.1.0': + '@eslint/plugin-kit@0.2.0': dependencies: - ajv: 6.12.6 + levn: 0.4.1 + + '@fastify/ajv-compiler@3.6.0': + dependencies: + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + fast-uri: 2.4.0 - '@fastify/error@2.0.0': {} + '@fastify/busboy@2.1.1': {} - '@floating-ui/core@1.6.4': + '@fastify/error@3.4.1': {} + + '@fastify/fast-json-stringify-compiler@4.3.0': + dependencies: + fast-json-stringify: 5.16.1 + + '@fastify/merge-json-schemas@0.1.1': + dependencies: + fast-deep-equal: 3.1.3 + + '@floating-ui/core@1.6.8': dependencies: - '@floating-ui/utils': 0.2.4 + '@floating-ui/utils': 0.2.8 - '@floating-ui/dom@1.6.7': + '@floating-ui/dom@1.6.11': dependencies: - '@floating-ui/core': 1.6.4 - '@floating-ui/utils': 0.2.4 + '@floating-ui/core': 1.6.8 + '@floating-ui/utils': 0.2.8 - '@floating-ui/utils@0.2.4': {} + '@floating-ui/utils@0.2.8': {} - '@floating-ui/vue@1.1.1(vue@3.4.31(typescript@5.4.5))': + '@floating-ui/vue@1.1.5(vue@3.5.11(typescript@5.6.2))': dependencies: - '@floating-ui/dom': 1.6.7 - '@floating-ui/utils': 0.2.4 - vue-demi: 0.14.8(vue@3.4.31(typescript@5.4.5)) + '@floating-ui/dom': 1.6.11 + '@floating-ui/utils': 0.2.8 + vue-demi: 0.14.10(vue@3.5.11(typescript@5.6.2)) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -12208,120 +11364,127 @@ snapshots: dependencies: '@hapi/hoek': 9.3.0 - '@headlessui-float/vue@0.14.0(@headlessui/vue@1.7.22(vue@3.4.31(typescript@5.4.5)))(vue@3.4.31(typescript@5.4.5))': + '@headlessui-float/vue@0.14.4(@headlessui/vue@1.7.23(vue@3.5.11(typescript@5.6.2)))(vue@3.5.11(typescript@5.6.2))': dependencies: - '@floating-ui/core': 1.6.4 - '@floating-ui/dom': 1.6.7 - '@floating-ui/vue': 1.1.1(vue@3.4.31(typescript@5.4.5)) - '@headlessui/vue': 1.7.22(vue@3.4.31(typescript@5.4.5)) - vue: 3.4.31(typescript@5.4.5) + '@floating-ui/core': 1.6.8 + '@floating-ui/dom': 1.6.11 + '@floating-ui/vue': 1.1.5(vue@3.5.11(typescript@5.6.2)) + '@headlessui/vue': 1.7.23(vue@3.5.11(typescript@5.6.2)) + vue: 3.5.11(typescript@5.6.2) transitivePeerDependencies: - '@vue/composition-api' - '@headlessui/tailwindcss@0.2.1(tailwindcss@3.4.4)': + '@headlessui/tailwindcss@0.2.1(tailwindcss@3.4.13)': + dependencies: + tailwindcss: 3.4.13 + + '@headlessui/vue@1.7.23(vue@3.5.11(typescript@5.6.2))': dependencies: - tailwindcss: 3.4.4 + '@tanstack/vue-virtual': 3.10.8(vue@3.5.11(typescript@5.6.2)) + vue: 3.5.11(typescript@5.6.2) - '@headlessui/vue@1.7.22(vue@3.4.31(typescript@5.4.5))': + '@humanfs/core@0.19.0': {} + + '@humanfs/node@0.16.5': dependencies: - '@tanstack/vue-virtual': 3.8.1(vue@3.4.31(typescript@5.4.5)) - vue: 3.4.31(typescript@5.4.5) + '@humanfs/core': 0.19.0 + '@humanwhocodes/retry': 0.3.1 '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/retry@0.3.0': {} + '@humanwhocodes/retry@0.3.1': {} - '@iconify-json/carbon@1.1.36': + '@iconify-json/carbon@1.2.1': dependencies: '@iconify/types': 2.0.0 '@iconify/types@2.0.0': {} - '@iconify/utils@2.1.23': + '@iconify/utils@2.1.33': dependencies: - '@antfu/install-pkg': 0.1.1 - '@antfu/utils': 0.7.7 + '@antfu/install-pkg': 0.4.1 + '@antfu/utils': 0.7.10 '@iconify/types': 2.0.0 - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) kolorist: 1.8.0 local-pkg: 0.5.0 - mlly: 1.6.1 + mlly: 1.7.2 transitivePeerDependencies: - supports-color - '@img/sharp-darwin-arm64@0.33.4': + '@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 + '@emnapi/runtime': 1.3.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': @@ -12346,7 +11509,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 20.12.14 + '@types/node': 20.16.11 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -12359,14 +11522,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.14 + '@types/node': 20.16.11 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.12.14) + jest-config: 29.7.0(@types/node@20.16.11) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -12378,7 +11541,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 @@ -12391,7 +11554,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.14 + '@types/node': 20.16.11 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -12409,7 +11572,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.12.14 + '@types/node': 20.16.11 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -12431,14 +11594,14 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 20.12.14 + '@types/node': 20.16.11 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 glob: 7.2.3 graceful-fs: 4.2.11 istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.2 + istanbul-lib-instrument: 6.0.3 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 4.0.1 istanbul-reports: 3.1.7 @@ -12448,7 +11611,7 @@ snapshots: slash: 3.0.0 string-length: 4.0.2 strip-ansi: 6.0.1 - v8-to-istanbul: 9.2.0 + v8-to-istanbul: 9.3.0 transitivePeerDependencies: - supports-color @@ -12478,7 +11641,7 @@ snapshots: '@jest/transform@29.7.0': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.7 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 babel-plugin-istanbul: 6.1.1 @@ -12489,7 +11652,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 @@ -12501,14 +11664,14 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.12.14 - '@types/yargs': 17.0.32 + '@types/node': 20.16.11 + '@types/yargs': 17.0.33 chalk: 4.1.2 '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/resolve-uri@3.1.2': {} @@ -12520,16 +11683,32 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@jsdevtools/ono@7.1.3': {} - '@leichtgewicht/ip-codec@2.0.4': {} + '@leichtgewicht/ip-codec@2.0.5': {} + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.25.7 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.25.7 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 '@mdi/font@7.4.47': {} @@ -12559,129 +11738,153 @@ snapshots: '@pkgr/core@0.1.1': {} - '@polka/url@1.0.0-next.25': {} + '@polka/url@1.0.0-next.28': {} - '@rollup/plugin-babel@5.3.1(@babel/core@7.24.9)(@types/babel__core@7.20.5)(rollup@2.79.1)': + '@rollup/plugin-babel@5.3.1(@babel/core@7.25.7)(@types/babel__core@7.20.5)(rollup@2.79.2)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-module-imports': 7.24.7 - '@rollup/pluginutils': 3.1.0(rollup@2.79.1) - rollup: 2.79.1 + '@babel/core': 7.25.7 + '@babel/helper-module-imports': 7.25.7 + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + rollup: 2.79.2 optionalDependencies: '@types/babel__core': 7.20.5 transitivePeerDependencies: - supports-color - '@rollup/plugin-node-resolve@15.2.3(rollup@2.79.1)': + '@rollup/plugin-node-resolve@15.3.0(rollup@2.79.2)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@2.79.1) + '@rollup/pluginutils': 5.1.2(rollup@2.79.2) '@types/resolve': 1.20.2 deepmerge: 4.3.1 - is-builtin-module: 3.2.1 is-module: 1.0.0 resolve: 1.22.8 optionalDependencies: - rollup: 2.79.1 + rollup: 2.79.2 - '@rollup/plugin-replace@2.4.2(rollup@2.79.1)': + '@rollup/plugin-replace@2.4.2(rollup@2.79.2)': dependencies: - '@rollup/pluginutils': 3.1.0(rollup@2.79.1) + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) magic-string: 0.25.9 - rollup: 2.79.1 + rollup: 2.79.2 - '@rollup/plugin-terser@0.4.4(rollup@2.79.1)': + '@rollup/plugin-terser@0.4.4(rollup@2.79.2)': dependencies: serialize-javascript: 6.0.2 smob: 1.5.0 - terser: 5.31.3 + terser: 5.34.1 optionalDependencies: - rollup: 2.79.1 + rollup: 2.79.2 - '@rollup/plugin-typescript@11.1.6(rollup@4.18.0)(tslib@2.6.3)(typescript@5.4.5)': + '@rollup/plugin-typescript@11.1.6(rollup@4.24.0)(tslib@2.7.0)(typescript@5.4.5)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.18.0) + '@rollup/pluginutils': 5.1.2(rollup@4.24.0) resolve: 1.22.8 typescript: 5.4.5 optionalDependencies: - rollup: 4.18.0 - tslib: 2.6.3 + rollup: 4.24.0 + tslib: 2.7.0 - '@rollup/pluginutils@3.1.0(rollup@2.79.1)': + '@rollup/pluginutils@3.1.0(rollup@2.79.2)': dependencies: '@types/estree': 0.0.39 estree-walker: 1.0.1 picomatch: 2.3.1 - rollup: 2.79.1 + rollup: 2.79.2 - '@rollup/pluginutils@5.1.0(rollup@2.79.1)': + '@rollup/pluginutils@5.1.2(rollup@2.79.2)': dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 2.3.1 optionalDependencies: - rollup: 2.79.1 + rollup: 2.79.2 - '@rollup/pluginutils@5.1.0(rollup@4.18.0)': + '@rollup/pluginutils@5.1.2(rollup@4.24.0)': dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 2.3.1 optionalDependencies: - rollup: 4.18.0 + rollup: 4.24.0 - '@rollup/rollup-android-arm-eabi@4.18.0': + '@rollup/rollup-android-arm-eabi@4.24.0': optional: true - '@rollup/rollup-android-arm64@4.18.0': + '@rollup/rollup-android-arm64@4.24.0': optional: true - '@rollup/rollup-darwin-arm64@4.18.0': + '@rollup/rollup-darwin-arm64@4.24.0': optional: true - '@rollup/rollup-darwin-x64@4.18.0': + '@rollup/rollup-darwin-x64@4.24.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.18.0': + '@rollup/rollup-linux-arm-musleabihf@4.24.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.18.0': + '@rollup/rollup-linux-arm64-gnu@4.24.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.18.0': + '@rollup/rollup-linux-arm64-musl@4.24.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.18.0': + '@rollup/rollup-linux-riscv64-gnu@4.24.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.18.0': + '@rollup/rollup-linux-s390x-gnu@4.24.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.18.0': + '@rollup/rollup-linux-x64-gnu@4.24.0': optional: true - '@rollup/rollup-linux-x64-musl@4.18.0': + '@rollup/rollup-linux-x64-musl@4.24.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.18.0': + '@rollup/rollup-win32-arm64-msvc@4.24.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.18.0': + '@rollup/rollup-win32-ia32-msvc@4.24.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.18.0': + '@rollup/rollup-win32-x64-msvc@4.24.0': optional: true - '@shikijs/core@1.4.0': {} + '@shikijs/core@1.22.0': + dependencies: + '@shikijs/engine-javascript': 1.22.0 + '@shikijs/engine-oniguruma': 1.22.0 + '@shikijs/types': 1.22.0 + '@shikijs/vscode-textmate': 9.3.0 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.3 + + '@shikijs/engine-javascript@1.22.0': + dependencies: + '@shikijs/types': 1.22.0 + '@shikijs/vscode-textmate': 9.3.0 + oniguruma-to-js: 0.4.3 + + '@shikijs/engine-oniguruma@1.22.0': + dependencies: + '@shikijs/types': 1.22.0 + '@shikijs/vscode-textmate': 9.3.0 + + '@shikijs/transformers@1.22.0': + dependencies: + shiki: 1.22.0 - '@shikijs/transformers@1.4.0': + '@shikijs/types@1.22.0': dependencies: - shiki: 1.4.0 + '@shikijs/vscode-textmate': 9.3.0 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@9.3.0': {} '@sideway/address@4.1.5': dependencies: @@ -12716,12 +11919,12 @@ snapshots: dependencies: defer-to-connect: 2.0.1 - '@tanstack/virtual-core@3.8.1': {} + '@tanstack/virtual-core@3.10.8': {} - '@tanstack/vue-virtual@3.8.1(vue@3.4.31(typescript@5.4.5))': + '@tanstack/vue-virtual@3.10.8(vue@3.5.11(typescript@5.6.2))': dependencies: - '@tanstack/virtual-core': 3.8.1 - vue: 3.4.31(typescript@5.4.5) + '@tanstack/virtual-core': 3.10.8 + vue: 3.5.11(typescript@5.6.2) '@tootallnate/once@2.0.0': {} @@ -12729,33 +11932,33 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 + '@babel/parser': 7.25.7 + '@babel/types': 7.25.7 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.5 + '@types/babel__traverse': 7.20.6 '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.7 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 + '@babel/parser': 7.25.7 + '@babel/types': 7.25.7 - '@types/babel__traverse@7.20.5': + '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.7 '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 20.12.14 + '@types/node': 20.16.11 - '@types/bonjour@3.5.10': + '@types/bonjour@3.5.13': dependencies: - '@types/node': 20.14.7 + '@types/node': 20.16.11 '@types/braces@3.0.4': {} @@ -12763,33 +11966,37 @@ snapshots: dependencies: '@types/http-cache-semantics': 4.0.4 '@types/keyv': 3.1.4 - '@types/node': 20.12.14 + '@types/node': 20.16.11 '@types/responselike': 1.0.3 - '@types/connect-history-api-fallback@1.5.0': + '@types/connect-history-api-fallback@1.5.4': dependencies: - '@types/express-serve-static-core': 4.17.43 - '@types/node': 20.14.7 + '@types/express-serve-static-core': 5.0.0 + '@types/node': 20.16.11 '@types/connect@3.4.38': dependencies: - '@types/node': 20.12.14 + '@types/node': 20.16.11 '@types/cors@2.8.17': dependencies: - '@types/node': 20.12.14 + '@types/node': 20.16.11 - '@types/cytoscape@3.21.4': {} + '@types/cytoscape-fcose@2.2.4': + dependencies: + '@types/cytoscape': 3.21.8 + + '@types/cytoscape@3.21.8': {} '@types/d3-array@3.2.1': {} '@types/d3-axis@3.0.6': dependencies: - '@types/d3-selection': 3.0.10 + '@types/d3-selection': 3.0.11 '@types/d3-brush@3.0.6': dependencies: - '@types/d3-selection': 3.0.10 + '@types/d3-selection': 3.0.11 '@types/d3-chord@3.0.6': {} @@ -12806,7 +12013,7 @@ snapshots: '@types/d3-drag@3.0.7': dependencies: - '@types/d3-selection': 3.0.10 + '@types/d3-selection': 3.0.11 '@types/d3-dsv@3.0.7': {} @@ -12816,7 +12023,7 @@ snapshots: dependencies: '@types/d3-dsv': 3.0.7 - '@types/d3-force@3.0.9': {} + '@types/d3-force@3.0.10': {} '@types/d3-format@3.0.4': {} @@ -12824,7 +12031,7 @@ snapshots: dependencies: '@types/geojson': 7946.0.14 - '@types/d3-hierarchy@3.1.6': {} + '@types/d3-hierarchy@3.1.7': {} '@types/d3-interpolate@3.0.4': dependencies: @@ -12850,7 +12057,7 @@ snapshots: dependencies: '@types/d3-time': 3.0.3 - '@types/d3-selection@3.0.10': {} + '@types/d3-selection@3.0.11': {} '@types/d3-shape@1.3.12': dependencies: @@ -12866,14 +12073,14 @@ snapshots: '@types/d3-timer@3.0.2': {} - '@types/d3-transition@3.0.8': + '@types/d3-transition@3.0.9': dependencies: - '@types/d3-selection': 3.0.10 + '@types/d3-selection': 3.0.11 '@types/d3-zoom@3.0.8': dependencies: '@types/d3-interpolate': 3.0.4 - '@types/d3-selection': 3.0.10 + '@types/d3-selection': 3.0.11 '@types/d3@7.4.3': dependencies: @@ -12889,10 +12096,10 @@ snapshots: '@types/d3-dsv': 3.0.7 '@types/d3-ease': 3.0.2 '@types/d3-fetch': 3.0.7 - '@types/d3-force': 3.0.9 + '@types/d3-force': 3.0.10 '@types/d3-format': 3.0.4 '@types/d3-geo': 3.1.0 - '@types/d3-hierarchy': 3.1.6 + '@types/d3-hierarchy': 3.1.7 '@types/d3-interpolate': 3.0.4 '@types/d3-path': 3.1.0 '@types/d3-polygon': 3.0.2 @@ -12900,75 +12107,72 @@ snapshots: '@types/d3-random': 3.0.3 '@types/d3-scale': 4.0.8 '@types/d3-scale-chromatic': 3.0.3 - '@types/d3-selection': 3.0.10 + '@types/d3-selection': 3.0.11 '@types/d3-shape': 3.1.6 '@types/d3-time': 3.0.3 '@types/d3-time-format': 4.0.3 '@types/d3-timer': 3.0.2 - '@types/d3-transition': 3.0.8 + '@types/d3-transition': 3.0.9 '@types/d3-zoom': 3.0.8 '@types/debug@4.1.12': dependencies: '@types/ms': 0.7.34 - '@types/dompurify@3.0.5': - dependencies: - '@types/trusted-types': 2.0.7 + '@types/estree@0.0.39': {} - '@types/eslint-scope@3.7.7': - dependencies: - '@types/eslint': 8.56.10 - '@types/estree': 1.0.5 + '@types/estree@1.0.6': {} - '@types/eslint@8.56.10': + '@types/express-serve-static-core@4.19.6': dependencies: - '@types/estree': 1.0.5 - '@types/json-schema': 7.0.15 - - '@types/estree@0.0.39': {} - - '@types/estree@1.0.5': {} + '@types/node': 20.16.11 + '@types/qs': 6.9.16 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 - '@types/express-serve-static-core@4.17.43': + '@types/express-serve-static-core@5.0.0': dependencies: - '@types/node': 20.12.14 - '@types/qs': 6.9.12 + '@types/node': 20.16.11 + '@types/qs': 6.9.16 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 '@types/express@4.17.21': dependencies: '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.17.43 - '@types/qs': 6.9.12 - '@types/serve-static': 1.15.5 + '@types/express-serve-static-core': 4.19.6 + '@types/qs': 6.9.16 + '@types/serve-static': 1.15.7 - '@types/flexsearch@0.7.3': {} + '@types/flexsearch@0.7.6': {} '@types/geojson@7946.0.14': {} '@types/glob@7.2.0': dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.14.7 + '@types/node': 20.16.11 '@types/glob@8.1.0': dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.12.14 + '@types/node': 20.16.11 '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 20.12.14 + '@types/node': 20.16.11 + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 '@types/http-cache-semantics@4.0.4': {} '@types/http-errors@2.0.4': {} - '@types/http-proxy@1.17.11': + '@types/http-proxy@1.17.15': dependencies: - '@types/node': 20.14.7 + '@types/node': 20.16.11 '@types/istanbul-lib-coverage@2.0.6': {} @@ -12984,7 +12188,7 @@ snapshots: '@types/jsdom@21.1.7': dependencies: - '@types/node': 20.14.7 + '@types/node': 20.16.11 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 @@ -12994,41 +12198,35 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 20.12.14 + '@types/node': 20.16.11 - '@types/linkify-it@3.0.5': {} + '@types/linkify-it@5.0.0': {} '@types/lodash-es@4.17.12': dependencies: - '@types/lodash': 4.17.0 + '@types/lodash': 4.17.10 - '@types/lodash@4.17.0': {} - - '@types/lodash@4.17.6': {} + '@types/lodash@4.17.10': {} '@types/markdown-it@12.2.3': dependencies: - '@types/linkify-it': 3.0.5 - '@types/mdurl': 1.0.5 + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 - '@types/markdown-it@14.0.1': + '@types/markdown-it@14.1.2': dependencies: - '@types/linkify-it': 3.0.5 - '@types/mdurl': 1.0.5 + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 '@types/mdast@3.0.15': dependencies: - '@types/unist': 2.0.10 - - '@types/mdast@4.0.3': - dependencies: - '@types/unist': 3.0.2 + '@types/unist': 2.0.11 '@types/mdast@4.0.4': dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 - '@types/mdurl@1.0.5': {} + '@types/mdurl@2.0.0': {} '@types/micromatch@4.0.9': dependencies: @@ -13036,33 +12234,33 @@ snapshots: '@types/mime@1.3.5': {} - '@types/mime@3.0.4': {} - '@types/minimatch@5.1.2': {} '@types/ms@0.7.34': {} - '@types/node@18.19.38': + '@types/node-forge@1.3.11': dependencies: - undici-types: 5.26.5 + '@types/node': 20.16.11 + + '@types/node@12.20.55': {} - '@types/node@20.12.14': + '@types/node@18.19.55': dependencies: undici-types: 5.26.5 - '@types/node@20.14.7': + '@types/node@20.16.11': dependencies: - undici-types: 5.26.5 + undici-types: 6.19.8 - '@types/normalize-package-data@2.4.1': {} + '@types/normalize-package-data@2.4.4': {} '@types/prettier@2.7.3': {} '@types/prettier@3.0.0': dependencies: - prettier: 3.2.5 + prettier: 3.3.3 - '@types/qs@6.9.12': {} + '@types/qs@6.9.16': {} '@types/ramda@0.28.25': dependencies: @@ -13074,38 +12272,36 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 20.12.14 + '@types/node': 20.16.11 '@types/retry@0.12.0': {} '@types/rollup-plugin-visualizer@4.2.4': dependencies: - rollup: 2.79.1 - - '@types/semver@7.5.8': {} + rollup: 2.79.2 '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 20.12.14 + '@types/node': 20.16.11 - '@types/serve-index@1.9.1': + '@types/serve-index@1.9.4': dependencies: '@types/express': 4.17.21 - '@types/serve-static@1.15.5': + '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/mime': 3.0.4 - '@types/node': 20.14.7 + '@types/node': 20.16.11 + '@types/send': 0.17.4 '@types/sinonjs__fake-timers@8.1.1': {} '@types/sizzle@2.3.8': {} - '@types/sockjs@0.3.33': + '@types/sockjs@0.3.36': dependencies: - '@types/node': 20.14.7 + '@types/node': 20.16.11 '@types/stack-utils@2.0.3': {} @@ -13113,65 +12309,46 @@ snapshots: '@types/tough-cookie@4.0.5': {} - '@types/trusted-types@2.0.3': {} - '@types/trusted-types@2.0.7': {} - '@types/unist@2.0.10': {} + '@types/unist@2.0.11': {} - '@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.16.11 + '@types/ws@8.5.5': dependencies: - '@types/node': 20.14.7 + '@types/node': 20.16.11 '@types/yargs-parser@21.0.3': {} - '@types/yargs@17.0.32': + '@types/yargs@17.0.33': dependencies: '@types/yargs-parser': 21.0.3 '@types/yauzl@2.10.3': dependencies: - '@types/node': 20.12.14 - optional: true - - '@typescript-eslint/eslint-plugin@7.6.0(@typescript-eslint/parser@8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5))(eslint@9.6.0)(typescript@5.4.5)': - dependencies: - '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5) - '@typescript-eslint/scope-manager': 7.6.0 - '@typescript-eslint/type-utils': 7.6.0(eslint@9.6.0)(typescript@5.4.5) - '@typescript-eslint/utils': 7.6.0(eslint@9.6.0)(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 7.6.0 - debug: 4.3.5 - eslint: 9.6.0 - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare: 1.4.0 - semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.4.5) - optionalDependencies: - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color + '@types/node': 20.16.11 optional: true - '@typescript-eslint/eslint-plugin@8.0.0-alpha.44(@typescript-eslint/parser@8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5))(eslint@9.6.0)(typescript@5.4.5)': + '@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5)': dependencies: - '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5) - '@typescript-eslint/scope-manager': 8.0.0-alpha.44 - '@typescript-eslint/type-utils': 8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5) - '@typescript-eslint/utils': 8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 8.0.0-alpha.44 - eslint: 9.6.0 + '@eslint-community/regexpp': 4.11.1 + '@typescript-eslint/parser': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5) + '@typescript-eslint/scope-manager': 8.8.1 + '@typescript-eslint/type-utils': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5) + '@typescript-eslint/utils': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 8.8.1 + eslint: 9.12.0(jiti@1.21.6) 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: @@ -13179,47 +12356,29 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5)': + '@typescript-eslint/parser@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5)': dependencies: - '@typescript-eslint/scope-manager': 8.0.0-alpha.44 - '@typescript-eslint/types': 8.0.0-alpha.44 - '@typescript-eslint/typescript-estree': 8.0.0-alpha.44(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 8.0.0-alpha.44 - debug: 4.3.5 - eslint: 9.6.0 + '@typescript-eslint/scope-manager': 8.8.1 + '@typescript-eslint/types': 8.8.1 + '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 8.8.1 + debug: 4.3.7(supports-color@8.1.1) + eslint: 9.12.0(jiti@1.21.6) optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@7.6.0': - dependencies: - '@typescript-eslint/types': 7.6.0 - '@typescript-eslint/visitor-keys': 7.6.0 - - '@typescript-eslint/scope-manager@8.0.0-alpha.44': - dependencies: - '@typescript-eslint/types': 8.0.0-alpha.44 - '@typescript-eslint/visitor-keys': 8.0.0-alpha.44 - - '@typescript-eslint/type-utils@7.6.0(eslint@9.6.0)(typescript@5.4.5)': + '@typescript-eslint/scope-manager@8.8.1': dependencies: - '@typescript-eslint/typescript-estree': 7.6.0(typescript@5.4.5) - '@typescript-eslint/utils': 7.6.0(eslint@9.6.0)(typescript@5.4.5) - debug: 4.3.5 - eslint: 9.6.0 - ts-api-utils: 1.3.0(typescript@5.4.5) - optionalDependencies: - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - optional: true + '@typescript-eslint/types': 8.8.1 + '@typescript-eslint/visitor-keys': 8.8.1 - '@typescript-eslint/type-utils@8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5)': + '@typescript-eslint/type-utils@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5)': dependencies: - '@typescript-eslint/typescript-estree': 8.0.0-alpha.44(typescript@5.4.5) - '@typescript-eslint/utils': 8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5) - debug: 4.3.5 + '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.4.5) + '@typescript-eslint/utils': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5) + debug: 4.3.7(supports-color@8.1.1) ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 @@ -13227,33 +12386,31 @@ snapshots: - eslint - supports-color - '@typescript-eslint/types@7.15.0': {} - - '@typescript-eslint/types@7.6.0': {} + '@typescript-eslint/types@7.18.0': {} - '@typescript-eslint/types@8.0.0-alpha.44': {} + '@typescript-eslint/types@8.8.1': {} - '@typescript-eslint/typescript-estree@7.6.0(typescript@5.4.5)': + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.6.2)': dependencies: - '@typescript-eslint/types': 7.6.0 - '@typescript-eslint/visitor-keys': 7.6.0 - debug: 4.3.5 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.3.7(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.6.2 - ts-api-utils: 1.3.0(typescript@5.4.5) + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.6.2) optionalDependencies: - typescript: 5.4.5 + typescript: 5.6.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.0.0-alpha.44(typescript@5.4.5)': + '@typescript-eslint/typescript-estree@8.8.1(typescript@5.4.5)': dependencies: - '@typescript-eslint/types': 8.0.0-alpha.44 - '@typescript-eslint/visitor-keys': 8.0.0-alpha.44 - debug: 4.3.5 - globby: 11.1.0 + '@typescript-eslint/types': 8.8.1 + '@typescript-eslint/visitor-keys': 8.8.1 + debug: 4.3.7(supports-color@8.1.1) + fast-glob: 3.3.2 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.3 @@ -13263,55 +12420,43 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.6.0(eslint@9.6.0)(typescript@5.4.5)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.6.0 - '@typescript-eslint/types': 7.6.0 - '@typescript-eslint/typescript-estree': 7.6.0(typescript@5.4.5) - eslint: 9.6.0 - semver: 7.6.2 - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/utils@8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5)': + '@typescript-eslint/utils@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0) - '@typescript-eslint/scope-manager': 8.0.0-alpha.44 - '@typescript-eslint/types': 8.0.0-alpha.44 - '@typescript-eslint/typescript-estree': 8.0.0-alpha.44(typescript@5.4.5) - eslint: 9.6.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6)) + '@typescript-eslint/scope-manager': 8.8.1 + '@typescript-eslint/types': 8.8.1 + '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.4.5) + eslint: 9.12.0(jiti@1.21.6) transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/visitor-keys@7.6.0': + '@typescript-eslint/visitor-keys@7.18.0': dependencies: - '@typescript-eslint/types': 7.6.0 + '@typescript-eslint/types': 7.18.0 eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@8.0.0-alpha.44': + '@typescript-eslint/visitor-keys@8.8.1': dependencies: - '@typescript-eslint/types': 8.0.0-alpha.44 + '@typescript-eslint/types': 8.8.1 eslint-visitor-keys: 3.4.3 - '@unocss/astro@0.59.4(rollup@2.79.1)(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3))': + '@ungap/structured-clone@1.2.0': {} + + '@unocss/astro@0.59.4(rollup@2.79.2)(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1))': dependencies: '@unocss/core': 0.59.4 '@unocss/reset': 0.59.4 - '@unocss/vite': 0.59.4(rollup@2.79.1)(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3)) + '@unocss/vite': 0.59.4(rollup@2.79.2)(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1)) optionalDependencies: - vite: 5.2.13(@types/node@20.14.7)(terser@5.31.3) + vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) transitivePeerDependencies: - rollup - '@unocss/cli@0.59.4(rollup@2.79.1)': + '@unocss/cli@0.59.4(rollup@2.79.2)': dependencies: '@ampproject/remapping': 2.3.0 - '@rollup/pluginutils': 5.1.0(rollup@2.79.1) + '@rollup/pluginutils': 5.1.2(rollup@2.79.2) '@unocss/config': 0.59.4 '@unocss/core': 0.59.4 '@unocss/preset-uno': 0.59.4 @@ -13320,7 +12465,7 @@ snapshots: colorette: 2.0.20 consola: 3.2.3 fast-glob: 3.3.2 - magic-string: 0.30.10 + magic-string: 0.30.11 pathe: 1.1.2 perfect-debounce: 1.0.0 transitivePeerDependencies: @@ -13344,15 +12489,15 @@ snapshots: gzip-size: 6.0.0 sirv: 2.0.4 - '@unocss/postcss@0.59.4(postcss@8.4.39)': + '@unocss/postcss@0.59.4(postcss@8.4.47)': dependencies: '@unocss/config': 0.59.4 '@unocss/core': 0.59.4 '@unocss/rule-utils': 0.59.4 css-tree: 2.3.1 fast-glob: 3.3.2 - magic-string: 0.30.10 - postcss: 8.4.39 + magic-string: 0.30.11 + postcss: 8.4.47 '@unocss/preset-attributify@0.59.4': dependencies: @@ -13360,9 +12505,9 @@ snapshots: '@unocss/preset-icons@0.59.4': dependencies: - '@iconify/utils': 2.1.23 + '@iconify/utils': 2.1.33 '@unocss/core': 0.59.4 - ofetch: 1.3.4 + ofetch: 1.4.0 transitivePeerDependencies: - supports-color @@ -13391,7 +12536,7 @@ snapshots: '@unocss/preset-web-fonts@0.59.4': dependencies: '@unocss/core': 0.59.4 - ofetch: 1.3.4 + ofetch: 1.4.0 '@unocss/preset-wind@0.59.4': dependencies: @@ -13404,15 +12549,15 @@ snapshots: '@unocss/rule-utils@0.59.4': dependencies: '@unocss/core': 0.59.4 - magic-string: 0.30.10 + magic-string: 0.30.11 '@unocss/scope@0.59.4': {} '@unocss/transformer-attributify-jsx-babel@0.59.4': dependencies: - '@babel/core': 7.24.5 - '@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.5) - '@babel/preset-typescript': 7.24.1(@babel/core@7.24.5) + '@babel/core': 7.25.7 + '@babel/plugin-syntax-jsx': 7.25.7(@babel/core@7.25.7) + '@babel/preset-typescript': 7.25.7(@babel/core@7.25.7) '@unocss/core': 0.59.4 transitivePeerDependencies: - supports-color @@ -13435,10 +12580,10 @@ snapshots: dependencies: '@unocss/core': 0.59.4 - '@unocss/vite@0.59.4(rollup@2.79.1)(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3))': + '@unocss/vite@0.59.4(rollup@2.79.2)(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1))': dependencies: '@ampproject/remapping': 2.3.0 - '@rollup/pluginutils': 5.1.0(rollup@2.79.1) + '@rollup/pluginutils': 5.1.2(rollup@2.79.2) '@unocss/config': 0.59.4 '@unocss/core': 0.59.4 '@unocss/inspector': 0.59.4 @@ -13446,269 +12591,227 @@ snapshots: '@unocss/transformer-directives': 0.59.4 chokidar: 3.6.0 fast-glob: 3.3.2 - magic-string: 0.30.10 - vite: 5.2.13(@types/node@20.14.7)(terser@5.31.3) + magic-string: 0.30.11 + vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) transitivePeerDependencies: - rollup - '@vite-pwa/vitepress@0.4.0(vite-plugin-pwa@0.19.8(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.0.0))': - dependencies: - vite-plugin-pwa: 0.19.8(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.0.0) - - '@vitejs/plugin-vue@5.0.5(vite@5.2.13(@types/node@20.14.7)(terser@5.31.1))(vue@3.4.30(typescript@5.4.5))': + '@vite-pwa/vitepress@0.4.0(vite-plugin-pwa@0.19.8(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0))': dependencies: - vite: 5.2.13(@types/node@20.14.7)(terser@5.31.1) - vue: 3.4.30(typescript@5.4.5) + vite-plugin-pwa: 0.19.8(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0) - '@vitejs/plugin-vue@5.0.5(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3))(vue@3.4.30(typescript@5.4.5))': + '@vitejs/plugin-vue@5.1.4(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1))(vue@3.5.11(typescript@5.4.5))': dependencies: - vite: 5.2.13(@types/node@20.14.7)(terser@5.31.3) - vue: 3.4.30(typescript@5.4.5) + vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) + vue: 3.5.11(typescript@5.4.5) - '@vitejs/plugin-vue@5.0.5(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3))(vue@3.4.31(typescript@5.4.5))': + '@vitejs/plugin-vue@5.1.4(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1))(vue@3.5.11(typescript@5.6.2))': dependencies: - vite: 5.2.13(@types/node@20.14.7)(terser@5.31.3) - vue: 3.4.31(typescript@5.4.5) + vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) + vue: 3.5.11(typescript@5.6.2) - '@vitest/coverage-v8@1.5.3(vitest@1.5.3(@types/node@20.12.14)(@vitest/ui@1.5.3)(jsdom@24.0.0)(terser@5.31.3))': + '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.16.11)(@vitest/ui@1.6.0)(jsdom@24.1.3)(terser@5.34.1))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.4 + istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.1.7 - magic-string: 0.30.10 - magicast: 0.3.4 - picocolors: 1.0.0 + magic-string: 0.30.11 + magicast: 0.3.5 + picocolors: 1.1.0 std-env: 3.7.0 strip-literal: 2.1.0 test-exclude: 6.0.0 - vitest: 1.5.3(@types/node@20.12.14)(@vitest/ui@1.5.3)(jsdom@24.0.0)(terser@5.31.3) + vitest: 1.6.0(@types/node@20.16.11)(@vitest/ui@1.6.0)(jsdom@24.1.3)(terser@5.34.1) transitivePeerDependencies: - supports-color - '@vitest/expect@1.5.3': + '@vitest/expect@1.6.0': dependencies: - '@vitest/spy': 1.5.3 - '@vitest/utils': 1.5.3 - chai: 4.4.1 + '@vitest/spy': 1.6.0 + '@vitest/utils': 1.6.0 + chai: 4.5.0 - '@vitest/runner@1.5.3': + '@vitest/runner@1.6.0': dependencies: - '@vitest/utils': 1.5.3 + '@vitest/utils': 1.6.0 p-limit: 5.0.0 pathe: 1.1.2 - '@vitest/snapshot@1.5.3': + '@vitest/snapshot@1.6.0': dependencies: - magic-string: 0.30.10 + magic-string: 0.30.11 pathe: 1.1.2 pretty-format: 29.7.0 - '@vitest/spy@1.5.3': + '@vitest/spy@1.6.0': dependencies: tinyspy: 2.2.1 - '@vitest/ui@1.5.3(vitest@1.5.3)': + '@vitest/ui@1.6.0(vitest@1.6.0)': dependencies: - '@vitest/utils': 1.5.3 + '@vitest/utils': 1.6.0 fast-glob: 3.3.2 fflate: 0.8.2 flatted: 3.3.1 pathe: 1.1.2 - picocolors: 1.0.0 + picocolors: 1.1.0 sirv: 2.0.4 - vitest: 1.5.3(@types/node@20.12.14)(@vitest/ui@1.5.3)(jsdom@24.0.0)(terser@5.31.3) + vitest: 1.6.0(@types/node@20.16.11)(@vitest/ui@1.6.0)(jsdom@24.1.3)(terser@5.34.1) - '@vitest/utils@1.5.3': + '@vitest/utils@1.6.0': dependencies: diff-sequences: 29.6.3 estree-walker: 3.0.3 loupe: 2.3.7 pretty-format: 29.7.0 - '@vue/compat@3.4.31(vue@3.4.31(typescript@5.4.5))': + '@vue/compat@3.5.11(vue@3.5.11(typescript@5.6.2))': dependencies: - '@babel/parser': 7.24.7 + '@babel/parser': 7.25.7 estree-walker: 2.0.2 - source-map-js: 1.2.0 - vue: 3.4.31(typescript@5.4.5) + source-map-js: 1.2.1 + vue: 3.5.11(typescript@5.6.2) - '@vue/compiler-core@3.4.30': + '@vue/compiler-core@3.5.11': dependencies: - '@babel/parser': 7.24.7 - '@vue/shared': 3.4.30 + '@babel/parser': 7.25.7 + '@vue/shared': 3.5.11 entities: 4.5.0 estree-walker: 2.0.2 - source-map-js: 1.2.0 + source-map-js: 1.2.1 - '@vue/compiler-core@3.4.31': + '@vue/compiler-dom@3.5.11': dependencies: - '@babel/parser': 7.24.7 - '@vue/shared': 3.4.31 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.2.0 - - '@vue/compiler-dom@3.4.30': - dependencies: - '@vue/compiler-core': 3.4.30 - '@vue/shared': 3.4.30 - - '@vue/compiler-dom@3.4.31': - dependencies: - '@vue/compiler-core': 3.4.31 - '@vue/shared': 3.4.31 - - '@vue/compiler-sfc@3.4.30': - dependencies: - '@babel/parser': 7.24.7 - '@vue/compiler-core': 3.4.30 - '@vue/compiler-dom': 3.4.30 - '@vue/compiler-ssr': 3.4.30 - '@vue/shared': 3.4.30 - estree-walker: 2.0.2 - magic-string: 0.30.10 - postcss: 8.4.39 - source-map-js: 1.2.0 + '@vue/compiler-core': 3.5.11 + '@vue/shared': 3.5.11 - '@vue/compiler-sfc@3.4.31': + '@vue/compiler-sfc@3.5.11': dependencies: - '@babel/parser': 7.24.7 - '@vue/compiler-core': 3.4.31 - '@vue/compiler-dom': 3.4.31 - '@vue/compiler-ssr': 3.4.31 - '@vue/shared': 3.4.31 + '@babel/parser': 7.25.7 + '@vue/compiler-core': 3.5.11 + '@vue/compiler-dom': 3.5.11 + '@vue/compiler-ssr': 3.5.11 + '@vue/shared': 3.5.11 estree-walker: 2.0.2 - magic-string: 0.30.10 - postcss: 8.4.39 - source-map-js: 1.2.0 - - '@vue/compiler-ssr@3.4.30': - dependencies: - '@vue/compiler-dom': 3.4.30 - '@vue/shared': 3.4.30 + magic-string: 0.30.11 + postcss: 8.4.47 + source-map-js: 1.2.1 - '@vue/compiler-ssr@3.4.31': + '@vue/compiler-ssr@3.5.11': dependencies: - '@vue/compiler-dom': 3.4.31 - '@vue/shared': 3.4.31 + '@vue/compiler-dom': 3.5.11 + '@vue/shared': 3.5.11 - '@vue/devtools-api@6.6.3': {} + '@vue/devtools-api@6.6.4': {} - '@vue/devtools-api@7.1.3(vue@3.4.30(typescript@5.4.5))': + '@vue/devtools-api@7.4.6': dependencies: - '@vue/devtools-kit': 7.1.3(vue@3.4.30(typescript@5.4.5)) - transitivePeerDependencies: - - vue + '@vue/devtools-kit': 7.4.6 - '@vue/devtools-kit@7.1.3(vue@3.4.30(typescript@5.4.5))': + '@vue/devtools-kit@7.4.6': dependencies: - '@vue/devtools-shared': 7.1.3 + '@vue/devtools-shared': 7.4.6 + birpc: 0.2.17 hookable: 5.5.3 mitt: 3.0.1 perfect-debounce: 1.0.0 speakingurl: 14.0.1 - vue: 3.4.30(typescript@5.4.5) + superjson: 2.2.1 - '@vue/devtools-shared@7.1.3': + '@vue/devtools-shared@7.4.6': dependencies: rfdc: 1.4.1 - '@vue/reactivity@3.4.30': - dependencies: - '@vue/shared': 3.4.30 - - '@vue/reactivity@3.4.31': - dependencies: - '@vue/shared': 3.4.31 - - '@vue/runtime-core@3.4.30': - dependencies: - '@vue/reactivity': 3.4.30 - '@vue/shared': 3.4.30 - - '@vue/runtime-core@3.4.31': + '@vue/reactivity@3.5.11': dependencies: - '@vue/reactivity': 3.4.31 - '@vue/shared': 3.4.31 + '@vue/shared': 3.5.11 - '@vue/runtime-dom@3.4.30': + '@vue/runtime-core@3.5.11': dependencies: - '@vue/reactivity': 3.4.30 - '@vue/runtime-core': 3.4.30 - '@vue/shared': 3.4.30 - csstype: 3.1.3 + '@vue/reactivity': 3.5.11 + '@vue/shared': 3.5.11 - '@vue/runtime-dom@3.4.31': + '@vue/runtime-dom@3.5.11': dependencies: - '@vue/reactivity': 3.4.31 - '@vue/runtime-core': 3.4.31 - '@vue/shared': 3.4.31 + '@vue/reactivity': 3.5.11 + '@vue/runtime-core': 3.5.11 + '@vue/shared': 3.5.11 csstype: 3.1.3 - '@vue/server-renderer@3.4.30(vue@3.4.30(typescript@5.4.5))': + '@vue/server-renderer@3.5.11(vue@3.5.11(typescript@5.4.5))': dependencies: - '@vue/compiler-ssr': 3.4.30 - '@vue/shared': 3.4.30 - vue: 3.4.30(typescript@5.4.5) + '@vue/compiler-ssr': 3.5.11 + '@vue/shared': 3.5.11 + vue: 3.5.11(typescript@5.4.5) - '@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.4.5))': + '@vue/server-renderer@3.5.11(vue@3.5.11(typescript@5.6.2))': dependencies: - '@vue/compiler-ssr': 3.4.31 - '@vue/shared': 3.4.31 - vue: 3.4.31(typescript@5.4.5) + '@vue/compiler-ssr': 3.5.11 + '@vue/shared': 3.5.11 + vue: 3.5.11(typescript@5.6.2) - '@vue/shared@3.4.30': {} + '@vue/shared@3.5.11': {} - '@vue/shared@3.4.31': {} - - '@vueuse/core@10.9.0(vue@3.4.30(typescript@5.4.5))': + '@vueuse/core@10.11.1(vue@3.5.11(typescript@5.4.5))': dependencies: '@types/web-bluetooth': 0.0.20 - '@vueuse/metadata': 10.9.0 - '@vueuse/shared': 10.9.0(vue@3.4.30(typescript@5.4.5)) - vue-demi: 0.14.7(vue@3.4.30(typescript@5.4.5)) + '@vueuse/metadata': 10.11.1 + '@vueuse/shared': 10.11.1(vue@3.5.11(typescript@5.4.5)) + vue-demi: 0.14.10(vue@3.5.11(typescript@5.4.5)) transitivePeerDependencies: - '@vue/composition-api' - vue - '@vueuse/core@10.9.0(vue@3.4.31(typescript@5.4.5))': + '@vueuse/core@10.11.1(vue@3.5.11(typescript@5.6.2))': dependencies: '@types/web-bluetooth': 0.0.20 - '@vueuse/metadata': 10.9.0 - '@vueuse/shared': 10.9.0(vue@3.4.31(typescript@5.4.5)) - vue-demi: 0.14.7(vue@3.4.31(typescript@5.4.5)) + '@vueuse/metadata': 10.11.1 + '@vueuse/shared': 10.11.1(vue@3.5.11(typescript@5.6.2)) + vue-demi: 0.14.10(vue@3.5.11(typescript@5.6.2)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/integrations@10.11.1(axios@1.7.7)(focus-trap@7.6.0)(vue@3.5.11(typescript@5.4.5))': + dependencies: + '@vueuse/core': 10.11.1(vue@3.5.11(typescript@5.4.5)) + '@vueuse/shared': 10.11.1(vue@3.5.11(typescript@5.4.5)) + vue-demi: 0.14.10(vue@3.5.11(typescript@5.4.5)) + optionalDependencies: + axios: 1.7.7(debug@4.3.7) + focus-trap: 7.6.0 transitivePeerDependencies: - '@vue/composition-api' - vue - '@vueuse/integrations@10.9.0(axios@1.7.2)(focus-trap@7.5.4)(vue@3.4.30(typescript@5.4.5))': + '@vueuse/integrations@10.11.1(axios@1.7.7)(focus-trap@7.6.0)(vue@3.5.11(typescript@5.6.2))': dependencies: - '@vueuse/core': 10.9.0(vue@3.4.30(typescript@5.4.5)) - '@vueuse/shared': 10.9.0(vue@3.4.30(typescript@5.4.5)) - vue-demi: 0.14.7(vue@3.4.30(typescript@5.4.5)) + '@vueuse/core': 10.11.1(vue@3.5.11(typescript@5.6.2)) + '@vueuse/shared': 10.11.1(vue@3.5.11(typescript@5.6.2)) + vue-demi: 0.14.10(vue@3.5.11(typescript@5.6.2)) optionalDependencies: - axios: 1.7.2(debug@4.3.5) - focus-trap: 7.5.4 + axios: 1.7.7(debug@4.3.7) + focus-trap: 7.6.0 transitivePeerDependencies: - '@vue/composition-api' - vue - '@vueuse/metadata@10.9.0': {} + '@vueuse/metadata@10.11.1': {} - '@vueuse/shared@10.9.0(vue@3.4.30(typescript@5.4.5))': + '@vueuse/shared@10.11.1(vue@3.5.11(typescript@5.4.5))': dependencies: - vue-demi: 0.14.7(vue@3.4.30(typescript@5.4.5)) + vue-demi: 0.14.10(vue@3.5.11(typescript@5.4.5)) transitivePeerDependencies: - '@vue/composition-api' - vue - '@vueuse/shared@10.9.0(vue@3.4.31(typescript@5.4.5))': + '@vueuse/shared@10.11.1(vue@3.5.11(typescript@5.6.2))': dependencies: - vue-demi: 0.14.7(vue@3.4.31(typescript@5.4.5)) + vue-demi: 0.14.10(vue@3.5.11(typescript@5.6.2)) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -13727,7 +12830,7 @@ snapshots: '@wdio/logger@7.26.0': dependencies: chalk: 4.1.2 - loglevel: 1.9.1 + loglevel: 1.9.2 loglevel-plugin-prefix: 0.8.4 strip-ansi: 6.0.1 @@ -13735,7 +12838,7 @@ snapshots: '@wdio/types@7.30.2(typescript@5.4.5)': dependencies: - '@types/node': 18.19.38 + '@types/node': 18.19.55 got: 11.8.6 optionalDependencies: typescript: 5.4.5 @@ -13824,21 +12927,21 @@ snapshots: '@webassemblyjs/ast': 1.12.1 '@xtuc/long': 4.2.2 - '@webpack-cli/configtest@1.2.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0))(webpack@5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0))': + '@webpack-cli/configtest@1.2.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0))(webpack@5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0))': dependencies: - webpack: 5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0) - webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0) + webpack: 5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0) + webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0) - '@webpack-cli/info@1.5.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0))': + '@webpack-cli/info@1.5.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0))': dependencies: - envinfo: 7.10.0 - webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0) + envinfo: 7.14.0 + webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0) - '@webpack-cli/serve@1.7.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0))(webpack-dev-server@4.15.2(webpack-cli@4.10.0)(webpack@5.91.0))': + '@webpack-cli/serve@1.7.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0))(webpack-dev-server@4.15.2(webpack-cli@4.10.0)(webpack@5.95.0))': dependencies: - webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0) + webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0) optionalDependencies: - webpack-dev-server: 4.15.2(webpack-cli@4.10.0)(webpack@5.91.0) + webpack-dev-server: 4.15.2(webpack-cli@4.10.0)(webpack@5.95.0) '@xmldom/xmldom@0.8.10': {} @@ -13846,29 +12949,29 @@ snapshots: '@xtuc/long@4.2.2': {} - '@zenuml/core@3.23.28(typescript@5.4.5)': + '@zenuml/core@3.24.12(typescript@5.6.2)': dependencies: - '@headlessui-float/vue': 0.14.0(@headlessui/vue@1.7.22(vue@3.4.31(typescript@5.4.5)))(vue@3.4.31(typescript@5.4.5)) - '@headlessui/tailwindcss': 0.2.1(tailwindcss@3.4.4) - '@headlessui/vue': 1.7.22(vue@3.4.31(typescript@5.4.5)) + '@headlessui-float/vue': 0.14.4(@headlessui/vue@1.7.23(vue@3.5.11(typescript@5.6.2)))(vue@3.5.11(typescript@5.6.2)) + '@headlessui/tailwindcss': 0.2.1(tailwindcss@3.4.13) + '@headlessui/vue': 1.7.23(vue@3.5.11(typescript@5.6.2)) '@types/assert': 1.5.10 '@types/ramda': 0.28.25 - '@vue/compat': 3.4.31(vue@3.4.31(typescript@5.4.5)) + '@vue/compat': 3.5.11(vue@3.5.11(typescript@5.6.2)) antlr4: 4.11.0 color-string: 1.9.1 dom-to-image-more: 2.16.0 - dompurify: 3.1.6 + dompurify: 3.2.1 file-saver: 2.0.5 highlight.js: 10.7.3 html-to-image: 1.11.11 lodash: 4.17.21 marked: 4.3.0 pino: 8.21.0 - postcss: 8.4.39 + postcss: 8.4.47 ramda: 0.28.0 - tailwindcss: 3.4.4 - vue: 3.4.31(typescript@5.4.5) - vuex: 4.1.0(vue@3.4.31(typescript@5.4.5)) + tailwindcss: 3.4.13 + vue: 3.5.11(typescript@5.6.2) + vuex: 4.1.0(vue@3.5.11(typescript@5.6.2)) transitivePeerDependencies: - '@vue/composition-api' - ts-node @@ -13889,31 +12992,29 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 - acorn-import-assertions@1.9.0(acorn@8.11.3): + acorn-import-attributes@1.9.5(acorn@8.12.1): dependencies: - acorn: 8.11.3 + acorn: 8.12.1 - acorn-jsx@5.3.2(acorn@8.12.0): + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: - acorn: 8.12.0 - - acorn-walk@8.3.2: {} - - acorn@8.11.3: {} + acorn: 8.12.1 - acorn@8.12.0: {} + acorn-walk@8.3.4: + dependencies: + acorn: 8.12.1 acorn@8.12.1: {} agent-base@6.0.2: dependencies: - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color - agent-base@7.1.0: + agent-base@7.1.1: dependencies: - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -13927,17 +13028,21 @@ snapshots: clean-stack: 4.2.0 indent-string: 5.0.0 - ajv-formats@2.1.1(ajv@8.12.0): + ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: - ajv: 8.12.0 + ajv: 8.17.1 + + ajv-formats@3.0.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 ajv-keywords@3.5.2(ajv@6.12.6): dependencies: ajv: 6.12.6 - ajv-keywords@5.1.0(ajv@8.12.0): + ajv-keywords@5.1.0(ajv@8.17.1): dependencies: - ajv: 8.12.0 + ajv: 8.17.1 fast-deep-equal: 3.1.3 ajv@6.12.6: @@ -13954,37 +13059,30 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 - ajv@8.16.0: - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.0.1 + fast-uri: 3.0.2 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - algoliasearch@4.23.3: - dependencies: - '@algolia/cache-browser-local-storage': 4.23.3 - '@algolia/cache-common': 4.23.3 - '@algolia/cache-in-memory': 4.23.3 - '@algolia/client-account': 4.23.3 - '@algolia/client-analytics': 4.23.3 - '@algolia/client-common': 4.23.3 - '@algolia/client-personalization': 4.23.3 - '@algolia/client-search': 4.23.3 - '@algolia/logger-common': 4.23.3 - '@algolia/logger-console': 4.23.3 - '@algolia/recommend': 4.23.3 - '@algolia/requester-browser-xhr': 4.23.3 - '@algolia/requester-common': 4.23.3 - '@algolia/requester-node-http': 4.23.3 - '@algolia/transporter': 4.23.3 + algoliasearch@4.24.0: + dependencies: + '@algolia/cache-browser-local-storage': 4.24.0 + '@algolia/cache-common': 4.24.0 + '@algolia/cache-in-memory': 4.24.0 + '@algolia/client-account': 4.24.0 + '@algolia/client-analytics': 4.24.0 + '@algolia/client-common': 4.24.0 + '@algolia/client-personalization': 4.24.0 + '@algolia/client-search': 4.24.0 + '@algolia/logger-common': 4.24.0 + '@algolia/logger-console': 4.24.0 + '@algolia/recommend': 4.24.0 + '@algolia/requester-browser-xhr': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/requester-node-http': 4.24.0 + '@algolia/transporter': 4.24.0 amdefine@1.0.1: optional: true @@ -13999,7 +13097,9 @@ snapshots: dependencies: type-fest: 0.21.3 - ansi-escapes@6.2.1: {} + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 ansi-html-community@0.0.8: {} @@ -14007,7 +13107,7 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.0.1: {} + ansi-regex@6.1.0: {} ansi-sequence-parser@1.1.1: {} @@ -14034,6 +13134,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + app-module-path@2.2.0: {} + app-path@3.3.0: dependencies: execa: 1.0.0 @@ -14065,8 +13167,6 @@ snapshots: array-flatten@1.1.1: {} - array-flatten@2.1.2: {} - array-timsort@1.0.3: {} array-union@2.1.0: {} @@ -14092,9 +13192,11 @@ snapshots: assertion-error@1.1.0: {} + ast-module-types@6.0.0: {} + astral-regex@2.0.0: {} - async@3.2.5: {} + async@3.2.6: {} asynckit@0.4.0: {} @@ -14106,50 +13208,46 @@ snapshots: dependencies: possible-typed-array-names: 1.0.0 - avvio@7.2.5: + avvio@8.4.0: dependencies: - archy: 1.0.0 - debug: 4.3.5 + '@fastify/error': 3.4.1 fastq: 1.17.1 - queue-microtask: 1.2.3 - transitivePeerDependencies: - - supports-color aws-sign2@0.7.0: {} - aws4@1.12.0: {} + aws4@1.13.2: {} - axios@1.7.2(debug@4.3.5): + axios@1.7.7(debug@4.3.7): dependencies: - follow-redirects: 1.15.6(debug@4.3.5) + follow-redirects: 1.15.9(debug@4.3.7) form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - babel-jest@29.7.0(@babel/core@7.24.7): + babel-jest@29.7.0(@babel/core@7.25.7): dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.7 '@jest/transform': 29.7.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.24.7) + babel-preset-jest: 29.6.3(@babel/core@7.25.7) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 transitivePeerDependencies: - supports-color - babel-loader@9.1.3(@babel/core@7.24.7)(webpack@5.91.0(esbuild@0.21.5)): + babel-loader@9.2.1(@babel/core@7.25.7)(webpack@5.95.0(esbuild@0.21.5)): dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.7 find-cache-dir: 4.0.0 schema-utils: 4.2.0 - webpack: 5.91.0(esbuild@0.21.5) + webpack: 5.95.0(esbuild@0.21.5) babel-plugin-istanbul@6.1.1: dependencies: - '@babel/helper-plugin-utils': 7.24.5 + '@babel/helper-plugin-utils': 7.25.7 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 5.2.1 @@ -14159,80 +13257,59 @@ snapshots: babel-plugin-jest-hoist@29.6.3: dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.7 + '@babel/template': 7.25.7 + '@babel/types': 7.25.7 '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.20.5 - - babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.7): - dependencies: - '@babel/compat-data': 7.24.9 - '@babel/core': 7.24.7 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) - semver: 6.3.1 - transitivePeerDependencies: - - supports-color + '@types/babel__traverse': 7.20.6 - babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.9): + babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.25.7): dependencies: - '@babel/compat-data': 7.24.9 - '@babel/core': 7.24.9 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.9) + '@babel/compat-data': 7.25.7 + '@babel/core': 7.25.7 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.7) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.7): - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) - core-js-compat: 3.37.1 - transitivePeerDependencies: - - supports-color - - babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.9): - dependencies: - '@babel/core': 7.24.9 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.9) - core-js-compat: 3.37.1 - transitivePeerDependencies: - - supports-color - - babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.7): + babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.25.7): dependencies: - '@babel/core': 7.24.7 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) + '@babel/core': 7.25.7 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.7) + core-js-compat: 3.38.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.9): + babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.25.7): dependencies: - '@babel/core': 7.24.9 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.9) + '@babel/core': 7.25.7 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.7) transitivePeerDependencies: - supports-color - babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.7): - dependencies: - '@babel/core': 7.24.7 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7) - - babel-preset-jest@29.6.3(@babel/core@7.24.7): - dependencies: - '@babel/core': 7.24.7 + babel-preset-current-node-syntax@1.1.0(@babel/core@7.25.7): + dependencies: + '@babel/core': 7.25.7 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.7) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.25.7) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.7) + '@babel/plugin-syntax-import-attributes': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.25.7) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.7) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.7) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.7) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.7) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.25.7) + + babel-preset-jest@29.6.3(@babel/core@7.25.7): + dependencies: + '@babel/core': 7.25.7 babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7) + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.25.7) bail@2.0.2: {} @@ -14246,7 +13323,11 @@ snapshots: dependencies: tweetnacl: 0.14.5 - binary-extensions@2.2.0: {} + better-path-resolve@1.0.0: + dependencies: + is-windows: 1.0.2 + + binary-extensions@2.3.0: {} binary-searching@2.0.5: {} @@ -14255,6 +13336,8 @@ snapshots: buffers: 0.1.1 chainsaw: 0.1.0 + birpc@0.2.17: {} + blob-util@2.0.2: {} bluebird@3.7.1: {} @@ -14263,7 +13346,7 @@ snapshots: bmpimagejs@1.0.4: {} - body-parser@1.20.2: + body-parser@1.20.3: dependencies: bytes: 3.1.2 content-type: 1.0.5 @@ -14273,17 +13356,15 @@ snapshots: http-errors: 2.0.0 iconv-lite: 0.4.24 on-finished: 2.4.1 - qs: 6.11.0 + qs: 6.13.0 raw-body: 2.5.2 type-is: 1.6.18 unpipe: 1.0.0 transitivePeerDependencies: - supports-color - bonjour-service@1.1.1: + bonjour-service@1.2.1: dependencies: - array-flatten: 2.1.2 - dns-equal: 1.0.0 fast-deep-equal: 3.1.3 multicast-dns: 7.2.5 @@ -14307,27 +13388,16 @@ snapshots: dependencies: balanced-match: 1.0.2 - braces@3.0.2: - dependencies: - fill-range: 7.0.1 - braces@3.0.3: dependencies: fill-range: 7.1.1 - browserslist@4.23.0: - dependencies: - caniuse-lite: 1.0.30001594 - electron-to-chromium: 1.4.692 - node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.23.0) - - browserslist@4.23.1: + browserslist@4.24.0: dependencies: - caniuse-lite: 1.0.30001636 - electron-to-chromium: 1.4.808 - node-releases: 2.0.14 - update-browserslist-db: 1.0.16(browserslist@4.23.1) + caniuse-lite: 1.0.30001667 + electron-to-chromium: 1.5.33 + node-releases: 2.0.18 + update-browserslist-db: 1.1.1(browserslist@4.24.0) bser@2.1.1: dependencies: @@ -14396,23 +13466,21 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001594: {} - - caniuse-lite@1.0.30001636: {} + caniuse-lite@1.0.30001667: {} caseless@0.12.0: {} ccount@2.0.1: {} - chai@4.4.1: + chai@4.5.0: dependencies: assertion-error: 1.1.0 check-error: 1.0.3 - deep-eql: 4.1.3 + deep-eql: 4.1.4 get-func-name: 2.0.2 loupe: 2.3.7 pathval: 1.1.1 - type-detect: 4.0.8 + type-detect: 4.1.0 chainsaw@0.1.0: dependencies: @@ -14450,14 +13518,20 @@ snapshots: char-regex@1.0.2: {} + character-entities-html4@2.1.0: {} + character-entities-legacy@1.1.4: {} + character-entities-legacy@3.0.0: {} + character-entities@1.2.4: {} character-entities@2.0.2: {} character-reference-invalid@1.1.4: {} + chardet@0.7.0: {} + check-error@1.0.3: dependencies: get-func-name: 2.0.2 @@ -14481,7 +13555,7 @@ snapshots: chokidar@3.6.0: dependencies: anymatch: 3.1.3 - braces: 3.0.2 + braces: 3.0.3 glob-parent: 5.1.2 is-binary-path: 2.1.0 is-glob: 4.0.3 @@ -14490,13 +13564,13 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chrome-trace-event@1.0.3: {} + chrome-trace-event@1.0.4: {} ci-info@3.9.0: {} ci-info@4.0.0: {} - cjs-module-lexer@1.2.3: {} + cjs-module-lexer@1.4.1: {} cjson@0.3.0: dependencies: @@ -14528,18 +13602,18 @@ snapshots: d: 1.0.2 es5-ext: 0.10.64 es6-iterator: 2.0.3 - memoizee: 0.4.15 - timers-ext: 0.1.7 + memoizee: 0.4.17 + timers-ext: 0.1.8 cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 - cli-cursor@4.0.0: + cli-cursor@5.0.0: dependencies: - restore-cursor: 4.0.0 + restore-cursor: 5.1.0 - cli-table3@0.6.4: + cli-table3@0.6.5: dependencies: string-width: 4.2.3 optionalDependencies: @@ -14553,7 +13627,7 @@ snapshots: cli-truncate@4.0.0: dependencies: slice-ansi: 5.0.0 - string-width: 7.1.0 + string-width: 7.2.0 cliui@6.0.0: dependencies: @@ -14577,6 +13651,9 @@ snapshots: dependencies: mimic-response: 1.0.1 + clone@1.0.4: + optional: true + co@4.6.0: {} collect-v8-coverage@1.0.2: {} @@ -14611,9 +13688,9 @@ snapshots: dependencies: delayed-stream: 1.0.0 - commander@11.0.0: {} + comma-separated-tokens@2.0.3: {} - commander@12.0.0: {} + commander@11.0.0: {} commander@12.1.0: {} @@ -14629,7 +13706,7 @@ snapshots: commander@8.3.0: {} - comment-json@4.2.3: + comment-json@4.2.5: dependencies: array-timsort: 1.0.3 core-util-is: 1.0.3 @@ -14647,7 +13724,7 @@ snapshots: compressible@2.0.18: dependencies: - mime-db: 1.52.0 + mime-db: 1.53.0 compression@1.7.4: dependencies: @@ -14675,20 +13752,16 @@ snapshots: tree-kill: 1.2.2 yargs: 17.7.2 - confbox@0.1.7: {} - - configstore@6.0.0: - dependencies: - dot-prop: 6.0.1 - graceful-fs: 4.2.11 - unique-string: 3.0.0 - write-file-atomic: 3.0.3 - xdg-basedir: 5.1.0 + confbox@0.1.8: {} connect-history-api-fallback@2.0.0: {} consola@3.2.3: {} + console.table@0.10.0: + dependencies: + easy-table: 1.1.0 + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -14706,13 +13779,17 @@ snapshots: cookie-signature@1.0.6: {} - cookie@0.5.0: {} - cookie@0.6.0: {} - core-js-compat@3.37.1: + cookie@0.7.2: {} + + copy-anything@3.0.5: + dependencies: + is-what: 4.1.16 + + core-js-compat@3.38.1: dependencies: - browserslist: 4.23.1 + browserslist: 4.24.0 core-util-is@1.0.2: {} @@ -14727,6 +13804,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 @@ -14744,18 +13825,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.12.14): + create-jest@29.7.0(@types/node@20.16.11): 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.12.14) + jest-config: 29.7.0(@types/node@20.16.11) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -14768,6 +13849,12 @@ snapshots: dependencies: cross-spawn: 7.0.3 + cross-spawn@5.1.0: + dependencies: + lru-cache: 4.1.5 + shebang-command: 1.2.0 + which: 1.3.1 + cross-spawn@6.0.5: dependencies: nice-try: 1.0.5 @@ -14784,191 +13871,148 @@ snapshots: crypto-random-string@2.0.0: {} - crypto-random-string@4.0.0: - dependencies: - type-fest: 1.4.0 - - cspell-config-lib@8.7.0: - dependencies: - '@cspell/cspell-types': 8.7.0 - comment-json: 4.2.3 - yaml: 2.4.5 - - cspell-config-lib@8.9.1: - dependencies: - '@cspell/cspell-types': 8.9.1 - comment-json: 4.2.3 - yaml: 2.4.5 - - cspell-dictionary@8.7.0: + cspell-config-lib@8.14.4: dependencies: - '@cspell/cspell-pipe': 8.7.0 - '@cspell/cspell-types': 8.7.0 - cspell-trie-lib: 8.7.0 - fast-equals: 5.0.1 - gensequence: 7.0.0 + '@cspell/cspell-types': 8.14.4 + comment-json: 4.2.5 + yaml: 2.5.1 - cspell-dictionary@8.9.1: + cspell-dictionary@8.14.4: dependencies: - '@cspell/cspell-pipe': 8.9.1 - '@cspell/cspell-types': 8.9.1 - cspell-trie-lib: 8.9.1 + '@cspell/cspell-pipe': 8.14.4 + '@cspell/cspell-types': 8.14.4 + cspell-trie-lib: 8.14.4 fast-equals: 5.0.1 - gensequence: 7.0.0 - cspell-gitignore@8.7.0: + cspell-gitignore@8.14.4: dependencies: - cspell-glob: 8.7.0 + '@cspell/url': 8.14.4 + cspell-glob: 8.14.4 + cspell-io: 8.14.4 find-up-simple: 1.0.0 - cspell-glob@8.7.0: + cspell-glob@8.14.4: dependencies: - micromatch: 4.0.7 + '@cspell/url': 8.14.4 + micromatch: 4.0.8 - cspell-glob@8.9.1: + cspell-grammar@8.14.4: dependencies: - micromatch: 4.0.7 + '@cspell/cspell-pipe': 8.14.4 + '@cspell/cspell-types': 8.14.4 - cspell-grammar@8.7.0: + cspell-io@8.14.4: dependencies: - '@cspell/cspell-pipe': 8.7.0 - '@cspell/cspell-types': 8.7.0 + '@cspell/cspell-service-bus': 8.14.4 + '@cspell/url': 8.14.4 - cspell-grammar@8.9.1: + cspell-lib@8.14.4: dependencies: - '@cspell/cspell-pipe': 8.9.1 - '@cspell/cspell-types': 8.9.1 - - cspell-io@8.7.0: - dependencies: - '@cspell/cspell-service-bus': 8.7.0 - - cspell-io@8.9.1: - dependencies: - '@cspell/cspell-service-bus': 8.9.1 - '@cspell/url': 8.9.1 - - cspell-lib@8.7.0: - dependencies: - '@cspell/cspell-bundled-dicts': 8.7.0 - '@cspell/cspell-pipe': 8.7.0 - '@cspell/cspell-resolver': 8.7.0 - '@cspell/cspell-types': 8.7.0 - '@cspell/dynamic-import': 8.7.0 - '@cspell/strong-weak-map': 8.7.0 - clear-module: 4.1.2 - comment-json: 4.2.3 - configstore: 6.0.0 - cspell-config-lib: 8.7.0 - cspell-dictionary: 8.7.0 - cspell-glob: 8.7.0 - cspell-grammar: 8.7.0 - cspell-io: 8.7.0 - cspell-trie-lib: 8.7.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.11 - vscode-uri: 3.0.8 - - cspell-lib@8.9.1: - dependencies: - '@cspell/cspell-bundled-dicts': 8.9.1 - '@cspell/cspell-pipe': 8.9.1 - '@cspell/cspell-resolver': 8.9.1 - '@cspell/cspell-types': 8.9.1 - '@cspell/dynamic-import': 8.9.1 - '@cspell/strong-weak-map': 8.9.1 - '@cspell/url': 8.9.1 + '@cspell/cspell-bundled-dicts': 8.14.4 + '@cspell/cspell-pipe': 8.14.4 + '@cspell/cspell-resolver': 8.14.4 + '@cspell/cspell-types': 8.14.4 + '@cspell/dynamic-import': 8.14.4 + '@cspell/filetypes': 8.14.4 + '@cspell/strong-weak-map': 8.14.4 + '@cspell/url': 8.14.4 clear-module: 4.1.2 - comment-json: 4.2.3 - cspell-config-lib: 8.9.1 - cspell-dictionary: 8.9.1 - cspell-glob: 8.9.1 - cspell-grammar: 8.9.1 - cspell-io: 8.9.1 - cspell-trie-lib: 8.9.1 + comment-json: 4.2.5 + cspell-config-lib: 8.14.4 + cspell-dictionary: 8.14.4 + cspell-glob: 8.14.4 + cspell-grammar: 8.14.4 + cspell-io: 8.14.4 + cspell-trie-lib: 8.14.4 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.11 + vscode-languageserver-textdocument: 1.0.12 vscode-uri: 3.0.8 xdg-basedir: 5.1.0 - cspell-trie-lib@8.7.0: - dependencies: - '@cspell/cspell-pipe': 8.7.0 - '@cspell/cspell-types': 8.7.0 - gensequence: 7.0.0 - - cspell-trie-lib@8.9.1: + cspell-trie-lib@8.14.4: dependencies: - '@cspell/cspell-pipe': 8.9.1 - '@cspell/cspell-types': 8.9.1 + '@cspell/cspell-pipe': 8.14.4 + '@cspell/cspell-types': 8.14.4 gensequence: 7.0.0 - cspell@8.7.0: + cspell@8.14.4: dependencies: - '@cspell/cspell-json-reporter': 8.7.0 - '@cspell/cspell-pipe': 8.7.0 - '@cspell/cspell-types': 8.7.0 - '@cspell/dynamic-import': 8.7.0 + '@cspell/cspell-json-reporter': 8.14.4 + '@cspell/cspell-pipe': 8.14.4 + '@cspell/cspell-types': 8.14.4 + '@cspell/dynamic-import': 8.14.4 + '@cspell/url': 8.14.4 chalk: 5.3.0 chalk-template: 1.1.0 - commander: 12.0.0 - cspell-gitignore: 8.7.0 - cspell-glob: 8.7.0 - cspell-io: 8.7.0 - cspell-lib: 8.7.0 + commander: 12.1.0 + cspell-dictionary: 8.14.4 + cspell-gitignore: 8.14.4 + cspell-glob: 8.14.4 + cspell-io: 8.14.4 + cspell-lib: 8.14.4 fast-glob: 3.3.2 fast-json-stable-stringify: 2.1.0 - file-entry-cache: 8.0.0 + file-entry-cache: 9.1.0 get-stdin: 9.0.0 - semver: 7.6.0 + semver: 7.6.3 strip-ansi: 7.1.0 - vscode-uri: 3.0.8 css-tree@2.3.1: dependencies: mdn-data: 2.0.30 - source-map-js: 1.0.2 + source-map-js: 1.2.1 cssesc@3.0.0: {} - cssstyle@4.0.1: + cssstyle@4.1.0: dependencies: - rrweb-cssom: 0.6.0 + rrweb-cssom: 0.7.1 csstree-validator@3.0.0: dependencies: clap: 3.1.1 css-tree: 2.3.1 - resolve: 1.22.4 + resolve: 1.22.8 csstype@3.1.3: {} cuint@0.2.2: {} - cypress-image-snapshot@4.0.1(cypress@13.7.3)(jest@29.7.0(@types/node@20.12.14)): + cypress-image-snapshot@4.0.1(cypress@13.15.0)(jest@29.7.0(@types/node@20.16.11)): dependencies: chalk: 2.4.2 - cypress: 13.7.3 + cypress: 13.15.0 fs-extra: 7.0.1 glob: 7.2.3 - jest-image-snapshot: 4.2.0(jest@29.7.0(@types/node@20.12.14)) + jest-image-snapshot: 4.2.0(jest@29.7.0(@types/node@20.16.11)) pkg-dir: 3.0.0 term-img: 4.1.0 transitivePeerDependencies: - jest - cypress-wait-until@3.0.1: {} + cypress-split@1.24.0(@babel/core@7.25.7): + dependencies: + '@actions/core': 1.11.1 + arg: 5.0.2 + console.table: 0.10.0 + debug: 4.3.7(supports-color@8.1.1) + fast-shuffle: 6.1.0 + find-cypress-specs: 1.43.4(@babel/core@7.25.7) + globby: 11.1.0 + humanize-duration: 3.32.1 + transitivePeerDependencies: + - '@babel/core' + - supports-color + + cypress-wait-until@3.0.2: {} - cypress@13.7.3: + cypress@13.15.0: dependencies: - '@cypress/request': 3.0.1 + '@cypress/request': 3.0.5 '@cypress/xvfb': 1.2.4(supports-color@8.1.1) '@types/sinonjs__fake-timers': 8.1.1 '@types/sizzle': 2.3.8 @@ -14980,11 +14024,11 @@ snapshots: chalk: 4.1.2 check-more-types: 2.24.0 cli-cursor: 3.1.0 - cli-table3: 0.6.4 + cli-table3: 0.6.5 commander: 6.2.1 common-tags: 1.8.2 - dayjs: 1.11.11 - debug: 4.3.4(supports-color@8.1.1) + dayjs: 1.11.13 + debug: 4.3.7(supports-color@8.1.1) enquirer: 2.4.1 eventemitter2: 6.4.7 execa: 4.1.0 @@ -15005,18 +14049,23 @@ snapshots: process: 0.11.10 proxy-from-env: 1.0.0 request-progress: 3.0.0 - semver: 7.6.0 + semver: 7.6.3 supports-color: 8.1.1 tmp: 0.2.3 untildify: 4.0.0 yauzl: 2.10.0 - cytoscape-cose-bilkent@4.1.0(cytoscape@3.29.3): + cytoscape-cose-bilkent@4.1.0(cytoscape@3.30.2): dependencies: cose-base: 1.0.3 - cytoscape: 3.29.3 + cytoscape: 3.30.2 + + cytoscape-fcose@2.2.0(cytoscape@3.30.2): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.30.2 - cytoscape@3.29.3: {} + cytoscape@3.30.2: {} d3-array@2.12.1: dependencies: @@ -15077,7 +14126,7 @@ snapshots: d3-format@3.1.0: {} - d3-geo@3.1.0: + d3-geo@3.1.1: dependencies: d3-array: 3.2.4 @@ -15102,7 +14151,7 @@ snapshots: d3-array: 2.12.1 d3-shape: 1.3.7 - d3-scale-chromatic@3.0.0: + d3-scale-chromatic@3.1.0: dependencies: d3-color: 3.1.0 d3-interpolate: 3.0.1 @@ -15168,7 +14217,7 @@ snapshots: d3-fetch: 3.0.1 d3-force: 3.0.0 d3-format: 3.1.0 - d3-geo: 3.1.0 + d3-geo: 3.1.1 d3-hierarchy: 3.1.2 d3-interpolate: 3.0.1 d3-path: 3.1.0 @@ -15176,7 +14225,7 @@ snapshots: d3-quadtree: 3.0.1 d3-random: 3.0.1 d3-scale: 4.0.2 - d3-scale-chromatic: 3.0.0 + d3-scale-chromatic: 3.1.0 d3-selection: 3.0.0 d3-shape: 3.2.0 d3-time: 3.1.0 @@ -15188,9 +14237,9 @@ snapshots: d@1.0.2: dependencies: es5-ext: 0.10.64 - type: 2.7.2 + type: 2.7.3 - dagre-d3-es@7.0.10: + dagre-d3-es@7.0.11: dependencies: d3: 7.9.0 lodash-es: 4.17.21 @@ -15224,13 +14273,13 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.1 + dataloader@1.4.0: {} + date-fns@2.30.0: dependencies: - '@babel/runtime': 7.24.0 + '@babel/runtime': 7.25.7 - dayjs@1.11.10: {} - - dayjs@1.11.11: {} + dayjs@1.11.13: {} debug@2.6.9: dependencies: @@ -15246,15 +14295,15 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.3.4(supports-color@8.1.1): + debug@4.3.4: dependencies: ms: 2.1.2 - optionalDependencies: - supports-color: 8.1.1 - debug@4.3.5: + debug@4.3.7(supports-color@8.1.1): dependencies: - ms: 2.1.2 + ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 decamelize@1.2.0: {} @@ -15268,11 +14317,32 @@ snapshots: dependencies: mimic-response: 3.1.0 - dedent@1.5.1: {} + dedent@1.5.3: {} - deep-eql@4.1.3: + deep-eql@4.1.4: dependencies: - type-detect: 4.0.8 + type-detect: 4.1.0 + + deep-equal@2.2.3: + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + es-get-iterator: 1.1.3 + get-intrinsic: 1.2.4 + is-arguments: 1.1.1 + is-array-buffer: 3.0.4 + is-date-object: 1.0.5 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + isarray: 2.0.5 + object-is: 1.1.6 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.3 + side-channel: 1.0.6 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.2 + which-typed-array: 1.1.15 deep-is@0.1.4: {} @@ -15286,6 +14356,11 @@ snapshots: dependencies: strip-bom: 4.0.0 + defaults@1.0.4: + dependencies: + clone: 1.0.4 + optional: true + defer-to-connect@2.0.1: {} define-data-property@1.1.4: @@ -15314,18 +14389,84 @@ snapshots: depd@2.0.0: {} + dependency-tree@11.0.1: + dependencies: + commander: 12.1.0 + filing-cabinet: 5.0.2 + precinct: 12.1.2 + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + dequal@2.0.3: {} destr@2.0.3: {} destroy@1.2.0: {} + detect-indent@6.1.0: {} + detect-libc@2.0.3: {} detect-newline@3.1.0: {} detect-node@2.1.0: {} + detective-amd@6.0.0: + dependencies: + ast-module-types: 6.0.0 + escodegen: 2.1.0 + get-amd-module-type: 6.0.0 + node-source-walk: 7.0.0 + + detective-cjs@6.0.0: + dependencies: + ast-module-types: 6.0.0 + node-source-walk: 7.0.0 + + detective-es6@5.0.0: + dependencies: + node-source-walk: 7.0.0 + + detective-postcss@7.0.0(postcss@8.4.47): + dependencies: + is-url: 1.2.4 + postcss: 8.4.47 + postcss-values-parser: 6.0.2(postcss@8.4.47) + + detective-sass@6.0.0: + dependencies: + gonzales-pe: 4.3.0 + node-source-walk: 7.0.0 + + detective-scss@5.0.0: + dependencies: + gonzales-pe: 4.3.0 + node-source-walk: 7.0.0 + + detective-stylus@5.0.0: {} + + detective-typescript@13.0.0(typescript@5.6.2): + dependencies: + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2) + ast-module-types: 6.0.0 + node-source-walk: 7.0.0 + typescript: 5.6.2 + transitivePeerDependencies: + - supports-color + + detective-vue2@2.0.3(typescript@5.6.2): + dependencies: + '@vue/compiler-sfc': 3.5.11 + detective-es6: 5.0.0 + detective-sass: 6.0.0 + detective-scss: 5.0.0 + detective-stylus: 5.0.0 + detective-typescript: 13.0.0(typescript@5.6.2) + typescript: 5.6.2 + transitivePeerDependencies: + - supports-color + devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -15340,11 +14481,9 @@ snapshots: dlv@1.1.3: {} - dns-equal@1.0.0: {} - - dns-packet@5.6.0: + dns-packet@5.6.1: dependencies: - '@leichtgewicht/ip-codec': 2.0.4 + '@leichtgewicht/ip-codec': 2.0.5 dom-serializer@2.0.0: dependencies: @@ -15360,7 +14499,9 @@ snapshots: dependencies: domelementtype: 2.3.0 - dompurify@3.1.6: {} + dompurify@3.2.1: + optionalDependencies: + '@types/trusted-types': 2.0.7 domutils@3.1.0: dependencies: @@ -15368,16 +14509,18 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 - dot-prop@6.0.1: - dependencies: - is-obj: 2.0.0 - dotenv@16.4.5: {} + dotenv@8.6.0: {} + duplexer@0.1.2: {} eastasianwidth@0.2.0: {} + easy-table@1.1.0: + optionalDependencies: + wcwidth: 1.0.1 + ebnf-parser@0.1.10: {} ecc-jsbn@0.1.2: @@ -15389,17 +14532,15 @@ snapshots: ejs@3.1.10: dependencies: - jake: 10.9.1 - - electron-to-chromium@1.4.692: {} + jake: 10.9.2 - electron-to-chromium@1.4.808: {} + electron-to-chromium@1.5.33: {} elkjs@0.9.3: {} emittery@0.13.1: {} - emoji-regex@10.3.0: {} + emoji-regex@10.4.0: {} emoji-regex@8.0.0: {} @@ -15407,6 +14548,8 @@ snapshots: encodeurl@1.0.2: {} + encodeurl@2.0.0: {} + encoding@0.1.13: dependencies: iconv-lite: 0.6.3 @@ -15415,7 +14558,7 @@ snapshots: dependencies: once: 1.4.0 - enhanced-resolve@5.16.0: + enhanced-resolve@5.17.1: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 @@ -15431,7 +14574,9 @@ snapshots: env-paths@3.0.0: {} - envinfo@7.10.0: {} + envinfo@7.14.0: {} + + environment@1.1.0: {} error-ex@1.3.2: dependencies: @@ -15473,7 +14618,7 @@ snapshots: object-inspect: 1.13.2 object-keys: 1.1.1 object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 + regexp.prototype.flags: 1.5.3 safe-array-concat: 1.1.2 safe-regex-test: 1.0.3 string.prototype.trim: 1.2.9 @@ -15492,7 +14637,17 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@1.4.1: {} + es-get-iterator@1.1.3: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + is-arguments: 1.1.1 + is-map: 2.0.3 + is-set: 2.0.3 + is-string: 1.0.7 + isarray: 2.0.5 + stop-iteration-iterator: 1.0.0 es-module-lexer@1.5.4: {} @@ -15541,58 +14696,6 @@ snapshots: es6-iterator: 2.0.3 es6-symbol: 3.1.4 - esbuild@0.19.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.19.12 - '@esbuild/android-arm': 0.19.12 - '@esbuild/android-arm64': 0.19.12 - '@esbuild/android-x64': 0.19.12 - '@esbuild/darwin-arm64': 0.19.12 - '@esbuild/darwin-x64': 0.19.12 - '@esbuild/freebsd-arm64': 0.19.12 - '@esbuild/freebsd-x64': 0.19.12 - '@esbuild/linux-arm': 0.19.12 - '@esbuild/linux-arm64': 0.19.12 - '@esbuild/linux-ia32': 0.19.12 - '@esbuild/linux-loong64': 0.19.12 - '@esbuild/linux-mips64el': 0.19.12 - '@esbuild/linux-ppc64': 0.19.12 - '@esbuild/linux-riscv64': 0.19.12 - '@esbuild/linux-s390x': 0.19.12 - '@esbuild/linux-x64': 0.19.12 - '@esbuild/netbsd-x64': 0.19.12 - '@esbuild/openbsd-x64': 0.19.12 - '@esbuild/sunos-x64': 0.19.12 - '@esbuild/win32-arm64': 0.19.12 - '@esbuild/win32-ia32': 0.19.12 - '@esbuild/win32-x64': 0.19.12 - - esbuild@0.20.2: - optionalDependencies: - '@esbuild/aix-ppc64': 0.20.2 - '@esbuild/android-arm': 0.20.2 - '@esbuild/android-arm64': 0.20.2 - '@esbuild/android-x64': 0.20.2 - '@esbuild/darwin-arm64': 0.20.2 - '@esbuild/darwin-x64': 0.20.2 - '@esbuild/freebsd-arm64': 0.20.2 - '@esbuild/freebsd-x64': 0.20.2 - '@esbuild/linux-arm': 0.20.2 - '@esbuild/linux-arm64': 0.20.2 - '@esbuild/linux-ia32': 0.20.2 - '@esbuild/linux-loong64': 0.20.2 - '@esbuild/linux-mips64el': 0.20.2 - '@esbuild/linux-ppc64': 0.20.2 - '@esbuild/linux-riscv64': 0.20.2 - '@esbuild/linux-s390x': 0.20.2 - '@esbuild/linux-x64': 0.20.2 - '@esbuild/netbsd-x64': 0.20.2 - '@esbuild/openbsd-x64': 0.20.2 - '@esbuild/sunos-x64': 0.20.2 - '@esbuild/win32-arm64': 0.20.2 - '@esbuild/win32-ia32': 0.20.2 - '@esbuild/win32-x64': 0.20.2 - esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -15619,7 +14722,34 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - escalade@3.1.2: {} + 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.2.0: {} escape-html@1.0.3: {} @@ -15639,80 +14769,89 @@ snapshots: optionalDependencies: source-map: 0.1.43 - eslint-config-prettier@9.1.0(eslint@9.6.0): + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-config-prettier@9.1.0(eslint@9.12.0(jiti@1.21.6)): dependencies: - eslint: 9.6.0 + eslint: 9.12.0(jiti@1.21.6) - eslint-plugin-cypress@3.3.0(eslint@9.6.0): + eslint-plugin-cypress@3.5.0(eslint@9.12.0(jiti@1.21.6)): dependencies: - eslint: 9.6.0 + eslint: 9.12.0(jiti@1.21.6) globals: 13.24.0 - eslint-plugin-html@8.1.1: + eslint-plugin-html@8.1.2: dependencies: htmlparser2: 9.1.0 - eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.6.0(@typescript-eslint/parser@8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5))(eslint@9.6.0)(typescript@5.4.5))(eslint@9.6.0)(jest@29.7.0(@types/node@20.12.14))(typescript@5.4.5): + eslint-plugin-jest@28.8.3(@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.12.0(jiti@1.21.6))(jest@29.7.0(@types/node@20.16.11))(typescript@5.4.5): dependencies: - '@typescript-eslint/utils': 7.6.0(eslint@9.6.0)(typescript@5.4.5) - eslint: 9.6.0 + '@typescript-eslint/utils': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5) + eslint: 9.12.0(jiti@1.21.6) optionalDependencies: - '@typescript-eslint/eslint-plugin': 7.6.0(@typescript-eslint/parser@8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5))(eslint@9.6.0)(typescript@5.4.5) - jest: 29.7.0(@types/node@20.12.14) + '@typescript-eslint/eslint-plugin': 8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5) + jest: 29.7.0(@types/node@20.16.11) transitivePeerDependencies: - supports-color - typescript - eslint-plugin-jsdoc@48.5.2(eslint@9.6.0): + eslint-plugin-jsdoc@50.3.1(eslint@9.12.0(jiti@1.21.6)): dependencies: - '@es-joy/jsdoccomment': 0.43.1 + '@es-joy/jsdoccomment': 0.48.0 are-docs-informative: 0.0.2 comment-parser: 1.4.1 - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) escape-string-regexp: 4.0.0 - eslint: 9.6.0 - esquery: 1.5.0 - parse-imports: 2.1.1 - semver: 7.6.2 + eslint: 9.12.0(jiti@1.21.6) + espree: 10.2.0 + esquery: 1.6.0 + parse-imports: 2.2.1 + semver: 7.6.3 spdx-expression-parse: 4.0.0 - synckit: 0.9.0 + synckit: 0.9.2 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 - eslint-plugin-lodash@8.0.0(eslint@9.6.0): + eslint-plugin-lodash@8.0.0(eslint@9.12.0(jiti@1.21.6)): dependencies: - eslint: 9.6.0 + eslint: 9.12.0(jiti@1.21.6) lodash: 4.17.21 - eslint-plugin-markdown@5.0.0(eslint@9.6.0): + eslint-plugin-markdown@5.1.0(eslint@9.12.0(jiti@1.21.6)): dependencies: - eslint: 9.6.0 + eslint: 9.12.0(jiti@1.21.6) mdast-util-from-markdown: 0.8.5 transitivePeerDependencies: - supports-color - eslint-plugin-no-only-tests@3.1.0: {} + eslint-plugin-no-only-tests@3.3.0: {} eslint-plugin-tsdoc@0.3.0: dependencies: '@microsoft/tsdoc': 0.15.0 '@microsoft/tsdoc-config': 0.17.0 - eslint-plugin-unicorn@54.0.0(eslint@9.6.0): + eslint-plugin-unicorn@56.0.0(eslint@9.12.0(jiti@1.21.6)): dependencies: - '@babel/helper-validator-identifier': 7.24.7 - '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0) - '@eslint/eslintrc': 3.1.0 + '@babel/helper-validator-identifier': 7.25.7 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6)) ci-info: 4.0.0 clean-regexp: 1.0.0 - core-js-compat: 3.37.1 - eslint: 9.6.0 - esquery: 1.5.0 + core-js-compat: 3.38.1 + eslint: 9.12.0(jiti@1.21.6) + esquery: 1.6.0 + globals: 15.10.0 indent-string: 4.0.0 is-builtin-module: 3.2.1 jsesc: 3.0.2 @@ -15720,61 +14859,62 @@ snapshots: read-pkg-up: 7.0.1 regexp-tree: 0.1.27 regjsparser: 0.10.0 - semver: 7.6.2 + semver: 7.6.3 strip-indent: 3.0.0 - transitivePeerDependencies: - - supports-color eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 estraverse: 4.3.0 - eslint-scope@8.0.1: + eslint-scope@8.1.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.0.0: {} + eslint-visitor-keys@4.1.0: {} - eslint@9.6.0: + eslint@9.12.0(jiti@1.21.6): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0) - '@eslint-community/regexpp': 4.10.0 - '@eslint/config-array': 0.17.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6)) + '@eslint-community/regexpp': 4.11.1 + '@eslint/config-array': 0.18.0 + '@eslint/core': 0.6.0 '@eslint/eslintrc': 3.1.0 - '@eslint/js': 9.6.0 + '@eslint/js': 9.12.0 + '@eslint/plugin-kit': 0.2.0 + '@humanfs/node': 0.16.5 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.3.0 - '@nodelib/fs.walk': 1.2.8 + '@humanwhocodes/retry': 0.3.1 + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) escape-string-regexp: 4.0.0 - eslint-scope: 8.0.1 - eslint-visitor-keys: 4.0.0 - espree: 10.1.0 - esquery: 1.5.0 + eslint-scope: 8.1.0 + eslint-visitor-keys: 4.1.0 + espree: 10.2.0 + esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - ignore: 5.3.1 + ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 - is-path-inside: 3.0.3 json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 - optionator: 0.9.3 - strip-ansi: 6.0.1 + optionator: 0.9.4 text-table: 0.2.0 + optionalDependencies: + jiti: 1.21.6 transitivePeerDependencies: - supports-color @@ -15783,19 +14923,19 @@ snapshots: d: 1.0.2 es5-ext: 0.10.64 event-emitter: 0.3.5 - type: 2.7.2 + type: 2.7.3 - espree@10.1.0: + espree@10.2.0: dependencies: - acorn: 8.12.0 - acorn-jsx: 5.3.2(acorn@8.12.0) - eslint-visitor-keys: 4.0.0 + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + eslint-visitor-keys: 4.1.0 esprima@1.1.1: {} esprima@4.0.1: {} - esquery@1.5.0: + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -15815,7 +14955,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 esutils@1.0.0: {} @@ -15908,70 +15048,34 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - express@4.19.1: - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.2 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookie: 0.6.0 - cookie-signature: 1.0.6 - debug: 2.6.9 - depd: 2.0.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 1.2.0 - fresh: 0.5.2 - http-errors: 2.0.0 - merge-descriptors: 1.0.1 - methods: 1.1.2 - on-finished: 2.4.1 - parseurl: 1.3.3 - path-to-regexp: 0.1.7 - proxy-addr: 2.0.7 - qs: 6.11.0 - range-parser: 1.2.1 - safe-buffer: 5.2.1 - send: 0.18.0 - serve-static: 1.15.0 - setprototypeof: 1.2.0 - statuses: 2.0.1 - type-is: 1.6.18 - utils-merge: 1.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - - express@4.19.2: + express@4.21.0: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.2 + body-parser: 1.20.3 content-disposition: 0.5.4 content-type: 1.0.5 cookie: 0.6.0 cookie-signature: 1.0.6 debug: 2.6.9 depd: 2.0.0 - encodeurl: 1.0.2 + encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 1.2.0 + finalhandler: 1.3.1 fresh: 0.5.2 http-errors: 2.0.0 - merge-descriptors: 1.0.1 + merge-descriptors: 1.0.3 methods: 1.1.2 on-finished: 2.4.1 parseurl: 1.3.3 - path-to-regexp: 0.1.7 + path-to-regexp: 0.1.10 proxy-addr: 2.0.7 - qs: 6.11.0 + qs: 6.13.0 range-parser: 1.2.1 safe-buffer: 5.2.1 - send: 0.18.0 - serve-static: 1.15.0 + send: 0.19.0 + serve-static: 1.16.2 setprototypeof: 1.2.0 statuses: 2.0.1 type-is: 1.6.18 @@ -15982,13 +15086,21 @@ snapshots: ext@1.7.0: dependencies: - type: 2.7.2 + type: 2.7.3 extend@3.0.2: {} + extendable-error@0.1.7: {} + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + extract-zip@2.0.1(supports-color@8.1.1): dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -16012,24 +15124,35 @@ 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: {} - fast-json-stringify@2.7.13: + fast-json-stringify@5.16.1: dependencies: - ajv: 6.12.6 - deepmerge: 4.3.1 + '@fastify/merge-json-schemas': 0.1.1 + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + fast-deep-equal: 3.1.3 + fast-uri: 2.4.0 + json-schema-ref-resolver: 1.0.1 rfdc: 1.4.1 - string-similarity: 4.0.4 fast-levenshtein@2.0.6: {} + fast-querystring@1.1.2: + dependencies: + fast-decode-uri-component: 1.0.1 + fast-redact@3.5.0: {} - fast-safe-stringify@2.1.1: {} + fast-shuffle@6.1.0: + dependencies: + pcg: 1.0.0 + + fast-uri@2.4.0: {} - fast-uri@3.0.1: {} + fast-uri@3.0.2: {} fastest-levenshtein@1.0.16: {} @@ -16037,26 +15160,24 @@ snapshots: fastify-plugin@3.0.1: {} - fastify@3.29.5: + fastify@4.28.1: dependencies: - '@fastify/ajv-compiler': 1.1.0 - '@fastify/error': 2.0.0 + '@fastify/ajv-compiler': 3.6.0 + '@fastify/error': 3.4.1 + '@fastify/fast-json-stringify-compiler': 4.3.0 abstract-logging: 2.0.1 - avvio: 7.2.5 + avvio: 8.4.0 fast-content-type-parse: 1.1.0 - fast-json-stringify: 2.7.13 - find-my-way: 4.5.1 - flatstr: 1.0.12 - light-my-request: 4.12.0 - pino: 6.14.0 - process-warning: 1.0.0 + fast-json-stringify: 5.16.1 + find-my-way: 8.2.2 + light-my-request: 5.14.0 + pino: 9.4.0 + process-warning: 3.0.0 proxy-addr: 2.0.7 rfdc: 1.4.1 secure-json-parse: 2.7.0 - semver: 7.5.4 - tiny-lru: 8.0.2 - transitivePeerDependencies: - - supports-color + semver: 7.6.3 + toad-cache: 3.7.0 fastq@1.17.1: dependencies: @@ -16078,6 +15199,10 @@ snapshots: dependencies: pend: 1.2.0 + fdir@6.4.0(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + ferrum@1.9.4: dependencies: fastestsmallesttextencoderdecoder: 1.0.22 @@ -16099,24 +15224,38 @@ snapshots: dependencies: flat-cache: 4.0.1 + file-entry-cache@9.1.0: + dependencies: + flat-cache: 5.0.0 + file-saver@2.0.5: {} filelist@1.0.4: dependencies: minimatch: 5.1.6 - fill-range@7.0.1: + filing-cabinet@5.0.2: dependencies: - to-regex-range: 5.0.1 + app-module-path: 2.2.0 + commander: 12.1.0 + enhanced-resolve: 5.17.1 + module-definition: 6.0.0 + module-lookup-amd: 9.0.2 + resolve: 1.22.8 + resolve-dependency-path: 4.0.0 + sass-lookup: 6.0.1 + stylus-lookup: 6.0.0 + tsconfig-paths: 4.2.0 + typescript: 5.4.5 fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - finalhandler@1.2.0: + finalhandler@1.3.1: dependencies: debug: 2.6.9 - encodeurl: 1.0.2 + encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 parseurl: 1.3.3 @@ -16136,19 +15275,48 @@ snapshots: common-path-prefix: 3.0.0 pkg-dir: 7.0.0 - find-my-way@4.5.1: + find-cypress-specs@1.43.4(@babel/core@7.25.7): + dependencies: + '@actions/core': 1.11.1 + arg: 5.0.2 + console.table: 0.10.0 + debug: 4.3.7(supports-color@8.1.1) + find-test-names: 1.28.18(@babel/core@7.25.7) + globby: 11.1.0 + minimatch: 3.1.2 + pluralize: 8.0.0 + require-and-forget: 1.0.1 + shelljs: 0.8.5 + spec-change: 1.11.11 + tsx: 4.19.1 + transitivePeerDependencies: + - '@babel/core' + - supports-color + + find-my-way@8.2.2: dependencies: - fast-decode-uri-component: 1.0.1 fast-deep-equal: 3.1.3 - safe-regex2: 2.0.0 - semver-store: 0.3.0 + fast-querystring: 1.1.2 + safe-regex2: 3.1.0 find-process@1.4.7: dependencies: chalk: 4.1.2 commander: 5.1.0 - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + find-test-names@1.28.18(@babel/core@7.25.7): + dependencies: + '@babel/parser': 7.25.7 + '@babel/plugin-syntax-jsx': 7.25.7(@babel/core@7.25.7) + acorn-walk: 8.3.4 + debug: 4.3.7(supports-color@8.1.1) + globby: 11.1.0 + simple-bin-help: 1.8.0 transitivePeerDependencies: + - '@babel/core' - supports-color find-up-simple@1.0.0: {} @@ -16177,21 +15345,24 @@ snapshots: flatted: 3.3.1 keyv: 4.5.4 - flatstr@1.0.12: {} + flat-cache@5.0.0: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + + flat@5.0.2: {} flatted@3.3.1: {} flexsearch@0.7.43: {} - focus-trap@7.5.4: + focus-trap@7.6.0: dependencies: tabbable: 6.2.0 - follow-redirects@1.15.5: {} - - follow-redirects@1.15.6(debug@4.3.5): + follow-redirects@1.15.9(debug@4.3.7): optionalDependencies: - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) font-awesome@4.7.0: {} @@ -16204,19 +15375,13 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 3.0.7 - foreground-child@3.2.1: + foreground-child@3.3.0: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 forever-agent@0.6.1: {} - form-data@2.3.3: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - form-data@4.0.0: dependencies: asynckit: 0.4.0 @@ -16255,6 +15420,12 @@ snapshots: jsonfile: 4.0.0 universalify: 0.1.2 + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + fs-extra@9.1.0: dependencies: at-least-node: 1.0.0 @@ -16262,7 +15433,7 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 - fs-monkey@1.0.4: {} + fs-monkey@1.0.6: {} fs.realpath@1.0.0: {} @@ -16284,6 +15455,11 @@ snapshots: gensync@1.0.0-beta.2: {} + get-amd-module-type@6.0.0: + dependencies: + ast-module-types: 6.0.0 + node-source-walk: 7.0.0 + get-caller-file@2.0.5: {} get-east-asian-width@1.2.0: {} @@ -16310,11 +15486,11 @@ snapshots: get-stream@4.1.0: dependencies: - pump: 3.0.0 + pump: 3.0.2 get-stream@5.2.0: dependencies: - pump: 3.0.0 + pump: 3.0.2 get-stream@6.0.1: {} @@ -16326,13 +15502,13 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.2.4 - get-tsconfig@4.7.3: + get-tsconfig@4.8.1: dependencies: resolve-pkg-maps: 1.0.0 getos@3.2.1: dependencies: - async: 3.2.5 + async: 3.2.6 getpass@0.1.7: dependencies: @@ -16355,13 +15531,13 @@ snapshots: glob-to-regexp@0.4.1: {} - glob@10.4.3: + glob@10.4.5: dependencies: - foreground-child: 3.2.1 - jackspeak: 3.4.1 + foreground-child: 3.3.0 + jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 - package-json-from-dist: 1.0.0 + package-json-from-dist: 1.0.1 path-scurry: 1.11.1 glob@7.2.3: @@ -16397,7 +15573,7 @@ snapshots: globals@14.0.0: {} - globals@15.6.0: {} + globals@15.10.0: {} globalthis@1.0.4: dependencies: @@ -16409,7 +15585,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 @@ -16417,7 +15593,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 @@ -16425,13 +15601,17 @@ snapshots: dependencies: '@sindresorhus/merge-streams': 2.3.0 fast-glob: 3.3.2 - ignore: 5.3.1 + ignore: 5.3.2 path-type: 5.0.0 slash: 5.1.0 unicorn-magic: 0.1.0 glur@1.1.2: {} + gonzales-pe@4.3.0: + dependencies: + minimist: 1.2.8 + gopd@1.0.1: dependencies: get-intrinsic: 1.2.4 @@ -16458,6 +15638,8 @@ snapshots: dependencies: duplexer: 0.1.2 + hachure-fill@0.5.2: {} + handle-thing@2.0.1: {} handlebars@4.7.8: @@ -16467,7 +15649,7 @@ snapshots: source-map: 0.6.1 wordwrap: 1.0.0 optionalDependencies: - uglify-js: 3.17.4 + uglify-js: 3.19.3 has-ansi@2.0.0: dependencies: @@ -16493,10 +15675,6 @@ snapshots: dependencies: has-symbols: 1.0.3 - has@1.0.3: - dependencies: - function-bind: 1.1.2 - hasha@5.2.2: dependencies: is-stream: 2.0.1 @@ -16506,6 +15684,24 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-html@9.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + highlight.js@10.7.3: {} hookable@5.5.3: {} @@ -16523,12 +15719,14 @@ snapshots: dependencies: whatwg-encoding: 3.1.1 - html-entities@2.4.0: {} + html-entities@2.5.2: {} html-escaper@2.0.2: {} html-to-image@1.11.11: {} + html-void-elements@3.0.0: {} + htmlparser2@9.1.0: dependencies: domelementtype: 2.3.0 @@ -16561,24 +15759,24 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color http-proxy-agent@7.0.2: dependencies: - agent-base: 7.1.0 - debug: 4.3.5 + agent-base: 7.1.1 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color - http-proxy-middleware@2.0.6(@types/express@4.17.21): + http-proxy-middleware@2.0.7(@types/express@4.17.21): dependencies: - '@types/http-proxy': 1.17.11 + '@types/http-proxy': 1.17.15 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: @@ -16587,12 +15785,12 @@ snapshots: http-proxy@1.18.1: dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.15.5 + follow-redirects: 1.15.9(debug@4.3.7) requires-port: 1.0.0 transitivePeerDependencies: - debug - http-signature@1.3.6: + http-signature@1.4.0: dependencies: assert-plus: 1.0.0 jsprim: 2.0.2 @@ -16608,33 +15806,37 @@ snapshots: appdata-path: 1.0.0 compression: 1.7.4 cors: 2.8.5 - express: 4.19.2 + express: 4.21.0 spdy: 4.0.2 - uglify-js: 3.17.4 + uglify-js: 3.19.3 transitivePeerDependencies: - supports-color https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.4: + https-proxy-agent@7.0.5: dependencies: - agent-base: 7.1.0 - debug: 4.3.5 + agent-base: 7.1.1 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color + human-id@1.0.2: {} + human-signals@1.1.1: {} human-signals@2.1.0: {} human-signals@5.0.0: {} - husky@9.0.11: {} + humanize-duration@3.32.1: {} + + husky@9.1.6: {} iconv-lite@0.4.24: dependencies: @@ -16648,20 +15850,18 @@ snapshots: ieee754@1.2.1: {} - ignore@5.3.1: {} + ignore@5.3.2: {} import-fresh@3.3.0: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - import-local@3.1.0: + import-local@3.2.0: dependencies: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 - import-meta-resolve@4.0.0: {} - import-meta-resolve@4.1.0: {} imurmurhash@0.1.4: {} @@ -16695,11 +15895,13 @@ snapshots: internmap@2.0.3: {} + interpret@1.4.0: {} + interpret@2.2.0: {} ipaddr.js@1.9.1: {} - ipaddr.js@2.1.0: {} + ipaddr.js@2.2.0: {} is-alphabetical@1.0.4: {} @@ -16708,6 +15910,11 @@ snapshots: is-alphabetical: 1.0.4 is-decimal: 1.0.4 + is-arguments@1.1.1: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 @@ -16723,7 +15930,7 @@ snapshots: is-binary-path@2.1.0: dependencies: - binary-extensions: 2.2.0 + binary-extensions: 2.3.0 is-boolean-object@1.1.2: dependencies: @@ -16740,11 +15947,7 @@ snapshots: dependencies: ci-info: 3.9.0 - is-core-module@2.13.0: - dependencies: - has: 1.0.3 - - is-core-module@2.13.1: + is-core-module@2.15.1: dependencies: hasown: 2.0.2 @@ -16785,6 +15988,8 @@ snapshots: is-localhost-ip@2.0.0: {} + is-map@2.0.3: {} + is-module@1.0.0: {} is-negative-zero@2.0.3: {} @@ -16797,8 +16002,6 @@ snapshots: is-obj@1.0.1: {} - is-obj@2.0.0: {} - is-path-inside@3.0.3: {} is-plain-obj@3.0.0: {} @@ -16820,6 +16023,8 @@ snapshots: is-regexp@1.0.0: {} + is-set@2.0.3: {} + is-shared-array-buffer@1.0.3: dependencies: call-bind: 1.0.7 @@ -16834,6 +16039,10 @@ snapshots: dependencies: has-tostringtag: 1.0.2 + is-subdir@1.2.0: + dependencies: + better-path-resolve: 1.0.0 + is-symbol@1.0.4: dependencies: has-symbols: 1.0.3 @@ -16846,10 +16055,23 @@ snapshots: is-unicode-supported@0.1.0: {} + is-url-superb@4.0.0: {} + + is-url@1.2.4: {} + + is-weakmap@2.0.2: {} + is-weakref@1.0.2: dependencies: call-bind: 1.0.7 + is-weakset@2.0.3: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + + is-what@4.1.16: {} + is-windows@1.0.2: {} is-wsl@2.2.0: @@ -16866,8 +16088,6 @@ snapshots: isstream@0.1.2: {} - istanbul-lib-coverage@3.2.0: {} - istanbul-lib-coverage@3.2.2: {} istanbul-lib-hook@3.0.0: @@ -16876,30 +16096,30 @@ snapshots: istanbul-lib-instrument@4.0.3: dependencies: - '@babel/core': 7.24.4 + '@babel/core': 7.25.7 '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 semver: 6.3.1 transitivePeerDependencies: - supports-color istanbul-lib-instrument@5.2.1: dependencies: - '@babel/core': 7.24.7 - '@babel/parser': 7.24.7 + '@babel/core': 7.25.7 + '@babel/parser': 7.25.7 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 transitivePeerDependencies: - supports-color - istanbul-lib-instrument@6.0.2: + istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.24.7 - '@babel/parser': 7.24.7 + '@babel/core': 7.25.7 + '@babel/parser': 7.25.7 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.6.2 + semver: 7.6.3 transitivePeerDependencies: - supports-color @@ -16907,7 +16127,7 @@ snapshots: dependencies: archy: 1.0.0 cross-spawn: 7.0.3 - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 p-map: 3.0.0 rimraf: 3.0.2 uuid: 8.3.2 @@ -16920,25 +16140,20 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.3.5 - istanbul-lib-coverage: 3.2.0 + debug: 4.3.7(supports-color@8.1.1) + istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: - supports-color - istanbul-lib-source-maps@5.0.4: + istanbul-lib-source-maps@5.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.25 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color - istanbul-reports@3.1.6: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - istanbul-reports@3.1.7: dependencies: html-escaper: 2.0.2 @@ -16949,15 +16164,15 @@ snapshots: app-path: 3.3.0 plist: 3.1.0 - jackspeak@3.4.1: + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jake@10.9.1: + 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 @@ -16974,10 +16189,10 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.14 + '@types/node': 20.16.11 chalk: 4.1.2 co: 4.6.0 - dedent: 1.5.1 + dedent: 1.5.3 is-generator-fn: 2.1.0 jest-each: 29.7.0 jest-matcher-utils: 29.7.0 @@ -16987,23 +16202,23 @@ snapshots: jest-util: 29.7.0 p-limit: 3.1.0 pretty-format: 29.7.0 - pure-rand: 6.0.4 + pure-rand: 6.1.0 slash: 3.0.0 stack-utils: 2.0.6 transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@20.12.14): + jest-cli@29.7.0(@types/node@20.16.11): 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.12.14) + create-jest: 29.7.0(@types/node@20.16.11) exit: 0.1.2 - import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.12.14) + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@20.16.11) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -17013,12 +16228,12 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@20.12.14): + jest-config@29.7.0(@types/node@20.16.11): dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.7 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.24.7) + babel-jest: 29.7.0(@babel/core@7.25.7) chalk: 4.1.2 ci-info: 3.9.0 deepmerge: 4.3.1 @@ -17032,13 +16247,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.12.14 + '@types/node': 20.16.11 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -17067,7 +16282,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.14 + '@types/node': 20.16.11 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -17077,24 +16292,24 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 20.12.14 + '@types/node': 20.16.11 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.12.14)): + jest-image-snapshot@4.2.0(jest@29.7.0(@types/node@20.16.11)): dependencies: chalk: 1.1.3 get-stdin: 5.0.1 glur: 1.1.2 - jest: 29.7.0(@types/node@20.12.14) + jest: 29.7.0(@types/node@20.16.11) lodash: 4.17.21 mkdirp: 0.5.6 pixelmatch: 5.3.0 @@ -17116,12 +16331,12 @@ snapshots: jest-message-util@29.7.0: dependencies: - '@babel/code-frame': 7.24.2 + '@babel/code-frame': 7.25.7 '@jest/types': 29.6.3 '@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 @@ -17129,7 +16344,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.12.14 + '@types/node': 20.16.11 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -17164,7 +16379,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.14 + '@types/node': 20.16.11 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -17192,9 +16407,9 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.14 + '@types/node': 20.16.11 chalk: 4.1.2 - cjs-module-lexer: 1.2.3 + cjs-module-lexer: 1.4.1 collect-v8-coverage: 1.0.2 glob: 7.2.3 graceful-fs: 4.2.11 @@ -17212,15 +16427,15 @@ snapshots: jest-snapshot@29.7.0: dependencies: - '@babel/core': 7.24.7 - '@babel/generator': 7.24.5 - '@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.7) - '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.7) - '@babel/types': 7.24.5 + '@babel/core': 7.25.7 + '@babel/generator': 7.25.7 + '@babel/plugin-syntax-jsx': 7.25.7(@babel/core@7.25.7) + '@babel/plugin-syntax-typescript': 7.25.7(@babel/core@7.25.7) + '@babel/types': 7.25.7 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7) + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.25.7) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -17231,14 +16446,14 @@ snapshots: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.6.2 + semver: 7.6.3 transitivePeerDependencies: - supports-color jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.12.14 + '@types/node': 20.16.11 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -17257,7 +16472,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.14 + '@types/node': 20.16.11 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -17266,23 +16481,23 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 20.14.7 + '@types/node': 20.16.11 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 20.12.14 + '@types/node': 20.16.11 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@20.12.14): + jest@29.7.0(@types/node@20.16.11): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 - import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.12.14) + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@20.16.11) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -17336,30 +16551,30 @@ snapshots: jsbn@0.1.1: {} - jsdoc-type-pratt-parser@4.0.0: {} + jsdoc-type-pratt-parser@4.1.0: {} - jsdom@24.0.0: + jsdom@24.1.3: dependencies: - cssstyle: 4.0.1 + cssstyle: 4.1.0 data-urls: 5.0.0 decimal.js: 10.4.3 form-data: 4.0.0 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.4 + https-proxy-agent: 7.0.5 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.7 + nwsapi: 2.2.13 parse5: 7.1.2 - rrweb-cssom: 0.6.0 + rrweb-cssom: 0.7.1 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 4.1.3 + tough-cookie: 4.1.4 w3c-xmlserializer: 5.0.0 webidl-conversions: 7.0.0 whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.0.0 - ws: 8.16.0 + ws: 8.18.0 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -17368,19 +16583,21 @@ snapshots: jsesc@0.5.0: {} - jsesc@2.5.2: {} - jsesc@3.0.2: {} json-buffer@3.0.1: {} json-parse-even-better-errors@2.3.1: {} + json-schema-ref-resolver@1.0.1: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-to-typescript@13.1.2: dependencies: '@bcherny/json-schema-ref-parser': 10.0.5-fork '@types/json-schema': 7.0.15 - '@types/lodash': 4.17.0 + '@types/lodash': 4.17.10 '@types/prettier': 2.7.3 cli-color: 2.0.4 get-stdin: 8.0.0 @@ -17405,7 +16622,7 @@ snapshots: json5@2.2.3: {} - jsonc-parser@3.2.1: {} + jsonc-parser@3.3.1: {} jsonfile@4.0.0: optionalDependencies: @@ -17473,18 +16690,22 @@ snapshots: chevrotain: 11.0.3 chevrotain-allstar: 0.3.1(chevrotain@11.0.3) vscode-languageserver: 9.0.1 - vscode-languageserver-textdocument: 1.0.11 + vscode-languageserver-textdocument: 1.0.12 vscode-uri: 3.0.8 - launch-editor@2.6.1: + launch-editor@2.9.1: dependencies: - picocolors: 1.0.0 + picocolors: 1.1.0 shell-quote: 1.8.1 layout-base@1.0.2: {} + layout-base@2.0.1: {} + lazy-ass@1.6.0: {} + lazy-ass@2.0.3: {} + leven@3.1.0: {} levn@0.4.1: @@ -17494,12 +16715,11 @@ snapshots: lex-parser@0.1.4: {} - light-my-request@4.12.0: + light-my-request@5.14.0: dependencies: - ajv: 8.16.0 - cookie: 0.5.0 - process-warning: 1.0.0 - set-cookie-parser: 2.6.0 + cookie: 0.7.2 + process-warning: 3.0.0 + set-cookie-parser: 2.7.0 lilconfig@2.1.0: {} @@ -17511,18 +16731,18 @@ snapshots: dependencies: uc.micro: 1.0.6 - lint-staged@15.2.7: + lint-staged@15.2.10: dependencies: chalk: 5.3.0 commander: 12.1.0 - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) execa: 8.0.1 lilconfig: 3.1.2 - listr2: 8.2.2 - micromatch: 4.0.7 + listr2: 8.2.5 + micromatch: 4.0.8 pidtree: 0.6.0 string-argv: 0.3.2 - yaml: 2.4.5 + yaml: 2.5.1 transitivePeerDependencies: - supports-color @@ -17532,19 +16752,19 @@ snapshots: colorette: 2.0.20 log-update: 4.0.0 p-map: 4.0.0 - rfdc: 1.3.1 + rfdc: 1.4.1 rxjs: 7.8.1 through: 2.3.8 wrap-ansi: 7.0.0 optionalDependencies: enquirer: 2.4.1 - listr2@8.2.2: + listr2@8.2.5: dependencies: cli-truncate: 4.0.0 colorette: 2.0.20 eventemitter3: 5.0.1 - log-update: 6.0.0 + log-update: 6.1.0 rfdc: 1.4.1 wrap-ansi: 9.0.0 @@ -17554,8 +16774,8 @@ snapshots: local-pkg@0.5.0: dependencies: - mlly: 1.6.1 - pkg-types: 1.1.0 + mlly: 1.7.2 + pkg-types: 1.2.0 locate-path@3.0.0: dependencies: @@ -17590,6 +16810,8 @@ snapshots: lodash.sortby@4.7.0: {} + lodash.startcase@4.4.0: {} + lodash@4.17.21: {} log-symbols@4.1.0: @@ -17604,17 +16826,19 @@ snapshots: slice-ansi: 4.0.0 wrap-ansi: 6.2.0 - log-update@6.0.0: + log-update@6.1.0: dependencies: - ansi-escapes: 6.2.1 - cli-cursor: 4.0.0 + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 slice-ansi: 7.1.0 strip-ansi: 7.1.0 wrap-ansi: 9.0.0 loglevel-plugin-prefix@0.8.4: {} - loglevel@1.9.1: {} + loglevel@1.9.2: {} + + long@5.2.3: {} longest-streak@3.1.0: {} @@ -17624,15 +16848,16 @@ snapshots: lowercase-keys@2.0.0: {} - lru-cache@10.4.0: {} + lru-cache@10.4.3: {} - lru-cache@5.1.1: + lru-cache@4.1.5: dependencies: - yallist: 3.1.1 + pseudomap: 1.0.2 + yallist: 2.1.2 - lru-cache@6.0.0: + lru-cache@5.1.1: dependencies: - yallist: 4.0.0 + yallist: 3.1.1 lru-queue@0.1.0: dependencies: @@ -17644,19 +16869,15 @@ snapshots: dependencies: sourcemap-codec: 1.4.8 - magic-string@0.30.10: - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - - magic-string@0.30.5: + magic-string@0.30.11: dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 - magicast@0.3.4: + magicast@0.3.5: dependencies: - '@babel/parser': 7.24.5 - '@babel/types': 7.24.5 - source-map-js: 1.2.0 + '@babel/parser': 7.25.7 + '@babel/types': 7.25.7 + source-map-js: 1.2.1 make-dir@3.1.0: dependencies: @@ -17664,7 +16885,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.6.2 + semver: 7.6.3 makeerror@1.0.12: dependencies: @@ -17674,7 +16895,7 @@ snapshots: mark.js@8.11.1: {} - markdown-it@13.0.1: + markdown-it@13.0.2: dependencies: argparse: 2.0.1 entities: 3.0.1 @@ -17684,17 +16905,17 @@ snapshots: markdown-table@3.0.3: {} - marked@13.0.2: {} + marked@13.0.3: {} marked@4.3.0: {} mdast-builder@1.1.1: dependencies: - '@types/unist': 2.0.10 + '@types/unist': 2.0.11 mdast-util-find-and-replace@3.0.1: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 escape-string-regexp: 5.0.0 unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 @@ -17712,7 +16933,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 @@ -17728,7 +16949,7 @@ snapshots: mdast-util-frontmatter@2.0.1: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 devlop: 1.1.0 escape-string-regexp: 5.0.0 mdast-util-from-markdown: 2.0.1 @@ -17737,9 +16958,9 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-gfm-autolink-literal@2.0.0: + mdast-util-gfm-autolink-literal@2.0.1: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 ccount: 2.0.1 devlop: 1.1.0 mdast-util-find-and-replace: 3.0.1 @@ -17747,7 +16968,7 @@ snapshots: mdast-util-gfm-footnote@2.0.0: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.1 mdast-util-to-markdown: 2.1.0 @@ -17757,7 +16978,7 @@ snapshots: mdast-util-gfm-strikethrough@2.0.0: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.0.1 mdast-util-to-markdown: 2.1.0 transitivePeerDependencies: @@ -17765,7 +16986,7 @@ snapshots: mdast-util-gfm-table@2.0.0: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 devlop: 1.1.0 markdown-table: 3.0.3 mdast-util-from-markdown: 2.0.1 @@ -17775,7 +16996,7 @@ snapshots: mdast-util-gfm-task-list-item@2.0.0: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.1 mdast-util-to-markdown: 2.1.0 @@ -17785,7 +17006,7 @@ snapshots: mdast-util-gfm@3.0.0: dependencies: mdast-util-from-markdown: 2.0.1 - mdast-util-gfm-autolink-literal: 2.0.0 + mdast-util-gfm-autolink-literal: 2.0.1 mdast-util-gfm-footnote: 2.0.0 mdast-util-gfm-strikethrough: 2.0.0 mdast-util-gfm-table: 2.0.0 @@ -17796,13 +17017,25 @@ snapshots: mdast-util-phrasing@4.1.0: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 unist-util-is: 6.0.0 + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.2.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.0 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + mdast-util-to-markdown@2.1.0: dependencies: - '@types/mdast': 4.0.3 - '@types/unist': 3.0.2 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 longest-streak: 3.1.0 mdast-util-phrasing: 4.1.0 mdast-util-to-string: 4.0.0 @@ -17826,9 +17059,9 @@ snapshots: memfs@3.5.3: dependencies: - fs-monkey: 1.0.4 + fs-monkey: 1.0.6 - memoizee@0.4.15: + memoizee@0.4.17: dependencies: d: 1.0.2 es5-ext: 0.10.64 @@ -17837,11 +17070,11 @@ snapshots: is-promise: 2.2.2 lru-queue: 0.1.0 next-tick: 1.1.0 - timers-ext: 0.1.7 + timers-ext: 0.1.8 meow@12.1.1: {} - merge-descriptors@1.0.1: {} + merge-descriptors@1.0.3: {} merge-stream@2.0.0: {} @@ -17875,14 +17108,14 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - micromark-extension-gfm-autolink-literal@2.0.0: + micromark-extension-gfm-autolink-literal@2.1.0: dependencies: micromark-util-character: 2.1.0 micromark-util-sanitize-uri: 2.0.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - micromark-extension-gfm-footnote@2.0.0: + micromark-extension-gfm-footnote@2.1.0: dependencies: devlop: 1.1.0 micromark-core-commonmark: 2.0.1 @@ -17893,7 +17126,7 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - micromark-extension-gfm-strikethrough@2.0.0: + micromark-extension-gfm-strikethrough@2.1.0: dependencies: devlop: 1.1.0 micromark-util-chunked: 2.0.0 @@ -17902,7 +17135,7 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - micromark-extension-gfm-table@2.0.0: + micromark-extension-gfm-table@2.1.0: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.0 @@ -17914,7 +17147,7 @@ snapshots: dependencies: micromark-util-types: 2.0.0 - micromark-extension-gfm-task-list-item@2.0.1: + micromark-extension-gfm-task-list-item@2.1.0: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.0 @@ -17924,12 +17157,12 @@ snapshots: micromark-extension-gfm@3.0.0: dependencies: - micromark-extension-gfm-autolink-literal: 2.0.0 - micromark-extension-gfm-footnote: 2.0.0 - micromark-extension-gfm-strikethrough: 2.0.0 - micromark-extension-gfm-table: 2.0.0 + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.0 micromark-extension-gfm-tagfilter: 2.0.0 - micromark-extension-gfm-task-list-item: 2.0.1 + micromark-extension-gfm-task-list-item: 2.1.0 micromark-util-combine-extensions: 2.0.0 micromark-util-types: 2.0.0 @@ -18027,7 +17260,7 @@ snapshots: micromark@2.11.4: dependencies: - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) parse-entities: 2.0.0 transitivePeerDependencies: - supports-color @@ -18035,7 +17268,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.1 @@ -18054,13 +17287,15 @@ snapshots: transitivePeerDependencies: - supports-color - micromatch@4.0.7: + micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.1 mime-db@1.52.0: {} + mime-db@1.53.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 @@ -18071,6 +17306,8 @@ snapshots: mimic-fn@4.0.0: {} + mimic-function@5.0.1: {} + mimic-response@1.0.1: {} mimic-response@3.1.0: {} @@ -18087,14 +17324,6 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimatch@9.0.3: - dependencies: - brace-expansion: 2.0.1 - - minimatch@9.0.4: - dependencies: - brace-expansion: 2.0.1 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -18113,12 +17342,26 @@ snapshots: mkdirp@1.0.4: {} - mlly@1.6.1: + mlly@1.7.2: dependencies: - acorn: 8.12.0 + acorn: 8.12.1 pathe: 1.1.2 - pkg-types: 1.1.0 - ufo: 1.5.3 + pkg-types: 1.2.0 + ufo: 1.5.4 + + module-definition@6.0.0: + dependencies: + ast-module-types: 6.0.0 + node-source-walk: 7.0.0 + + module-lookup-amd@9.0.2: + dependencies: + commander: 12.1.0 + glob: 7.2.3 + requirejs: 2.3.7 + requirejs-config-file: 4.0.0 + + mri@1.2.0: {} mrmime@2.0.0: {} @@ -18130,7 +17373,7 @@ snapshots: multicast-dns@7.2.5: dependencies: - dns-packet: 5.6.0 + dns-packet: 5.6.1 thunky: 1.1.0 mz@2.7.0: @@ -18165,6 +17408,12 @@ snapshots: optionalDependencies: encoding: 0.1.13 + node-fetch@2.7.0(encoding@0.1.13): + dependencies: + whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 + node-fetch@3.3.1: dependencies: data-uri-to-buffer: 4.0.1 @@ -18179,7 +17428,11 @@ snapshots: dependencies: process-on-spawn: 1.0.0 - node-releases@2.0.14: {} + node-releases@2.0.18: {} + + node-source-walk@7.0.0: + dependencies: + '@babel/parser': 7.25.7 nomnom@1.5.2: dependencies: @@ -18209,7 +17462,7 @@ snapshots: dependencies: path-key: 4.0.0 - nwsapi@2.2.7: {} + nwsapi@2.2.13: {} nyc@15.1.0: dependencies: @@ -18223,13 +17476,13 @@ snapshots: foreground-child: 2.0.0 get-package-type: 0.1.0 glob: 7.2.3 - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 istanbul-lib-hook: 3.0.0 istanbul-lib-instrument: 4.0.3 istanbul-lib-processinfo: 2.0.3 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.6 + istanbul-reports: 3.1.7 make-dir: 3.1.0 node-preload: 0.2.1 p-map: 3.0.0 @@ -18247,10 +17500,13 @@ snapshots: object-hash@3.0.0: {} - object-inspect@1.13.1: {} - object-inspect@1.13.2: {} + object-is@1.1.6: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + object-keys@1.1.1: {} object.assign@4.1.5: @@ -18262,11 +17518,11 @@ snapshots: obuf@1.1.2: {} - ofetch@1.3.4: + ofetch@1.4.0: dependencies: destr: 2.0.3 node-fetch-native: 1.6.4 - ufo: 1.5.3 + ufo: 1.5.4 omggif@1.0.10: {} @@ -18290,29 +17546,51 @@ snapshots: dependencies: mimic-fn: 4.0.0 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + oniguruma-to-js@0.4.3: + dependencies: + regex: 4.3.3 + open@8.4.2: dependencies: define-lazy-prop: 2.0.0 is-docker: 2.2.1 is-wsl: 2.2.0 - optionator@0.9.3: + openapi-fetch@0.11.3: + dependencies: + openapi-typescript-helpers: 0.0.13 + + openapi-typescript-helpers@0.0.13: {} + + optionator@0.9.4: dependencies: - '@aashutoshrathi/word-wrap': 1.2.6 deep-is: 0.1.4 fast-levenshtein: 2.0.6 levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 + word-wrap: 1.2.5 + + os-tmpdir@1.0.2: {} ospath@1.2.2: {} + outdent@0.5.0: {} + p-cancelable@2.1.1: {} p-event@5.0.1: dependencies: p-timeout: 5.1.0 + p-filter@2.1.0: + dependencies: + p-map: 2.1.0 + p-filter@3.0.0: dependencies: p-map: 5.5.0 @@ -18335,7 +17613,7 @@ snapshots: p-limit@5.0.0: dependencies: - yocto-queue: 1.0.0 + yocto-queue: 1.1.1 p-locate@3.0.0: dependencies: @@ -18353,6 +17631,8 @@ snapshots: dependencies: p-limit: 4.0.0 + p-map@2.1.0: {} + p-map@3.0.0: dependencies: aggregate-error: 3.1.0 @@ -18383,7 +17663,9 @@ snapshots: lodash.flattendeep: 4.4.0 release-zalgo: 1.0.0 - package-json-from-dist@1.0.0: {} + package-json-from-dist@1.0.1: {} + + package-manager-detector@0.2.1: {} pako@1.0.11: {} @@ -18404,14 +17686,14 @@ snapshots: is-decimal: 1.0.4 is-hexadecimal: 1.0.4 - parse-imports@2.1.1: + parse-imports@2.2.1: dependencies: es-module-lexer: 1.5.4 slashes: 3.0.12 parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.25.7 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -18424,6 +17706,8 @@ snapshots: path-browserify@1.0.1: {} + path-data-parser@0.1.0: {} + path-exists@3.0.0: {} path-exists@4.0.0: {} @@ -18442,10 +17726,10 @@ snapshots: path-scurry@1.11.1: dependencies: - lru-cache: 10.4.0 + lru-cache: 10.4.3 minipass: 7.1.2 - path-to-regexp@0.1.7: {} + path-to-regexp@0.1.10: {} path-type@4.0.0: {} @@ -18459,40 +17743,37 @@ snapshots: dependencies: through: 2.3.8 + pcg@1.0.0: + dependencies: + long: 5.2.3 + ramda: 0.29.0 + pend@1.2.0: {} perfect-debounce@1.0.0: {} performance-now@2.1.0: {} - picocolors@1.0.0: {} - - picocolors@1.0.1: {} + picocolors@1.1.0: {} picomatch@2.3.1: {} + picomatch@4.0.2: {} + pidtree@0.6.0: {} pify@2.3.0: {} + pify@4.0.1: {} + pino-abstract-transport@1.2.0: dependencies: readable-stream: 4.5.2 split2: 4.2.0 - pino-std-serializers@3.2.0: {} - pino-std-serializers@6.2.2: {} - pino@6.14.0: - dependencies: - fast-redact: 3.5.0 - fast-safe-stringify: 2.1.1 - flatstr: 1.0.12 - pino-std-serializers: 3.2.0 - process-warning: 1.0.0 - quick-format-unescaped: 4.0.4 - sonic-boom: 1.4.1 + pino-std-serializers@7.0.0: {} pino@8.21.0: dependencies: @@ -18504,10 +17785,24 @@ 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 + pino@9.4.0: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 1.2.0 + pino-std-serializers: 7.0.0 + process-warning: 4.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.1.0 + thread-stream: 3.1.0 + pirates@4.0.6: {} pixelmatch@5.3.0: @@ -18526,10 +17821,10 @@ snapshots: dependencies: find-up: 6.3.0 - pkg-types@1.1.0: + pkg-types@1.2.0: dependencies: - confbox: 0.1.7 - mlly: 1.6.1 + confbox: 0.1.8 + mlly: 1.7.2 pathe: 1.1.2 plist@3.1.0: @@ -18546,69 +17841,95 @@ snapshots: pngjs@6.0.0: {} - pnpm@8.15.8: {} + points-on-curve@0.2.0: {} + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 possible-typed-array-names@1.0.0: {} - postcss-import@15.1.0(postcss@8.4.39): + postcss-import@15.1.0(postcss@8.4.47): dependencies: - postcss: 8.4.39 + postcss: 8.4.47 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.8 - postcss-js@4.0.1(postcss@8.4.39): + postcss-js@4.0.1(postcss@8.4.47): dependencies: camelcase-css: 2.0.1 - postcss: 8.4.39 + postcss: 8.4.47 - postcss-load-config@4.0.2(postcss@8.4.39): + postcss-load-config@4.0.2(postcss@8.4.47): dependencies: lilconfig: 3.1.2 - yaml: 2.4.5 + yaml: 2.5.1 optionalDependencies: - postcss: 8.4.39 + postcss: 8.4.47 - postcss-nested@6.0.1(postcss@8.4.39): + postcss-nested@6.2.0(postcss@8.4.47): dependencies: - postcss: 8.4.39 - postcss-selector-parser: 6.1.0 + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - postcss-selector-parser@6.1.0: + 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.38: + postcss-values-parser@6.0.2(postcss@8.4.47): dependencies: - nanoid: 3.3.7 - picocolors: 1.0.1 - source-map-js: 1.2.0 + color-name: 1.1.4 + is-url-superb: 4.0.0 + postcss: 8.4.47 + quote-unquote: 1.0.0 - postcss@8.4.39: + postcss@8.4.47: dependencies: nanoid: 3.3.7 - picocolors: 1.0.1 - source-map-js: 1.2.0 + picocolors: 1.1.0 + source-map-js: 1.2.1 - preact@10.21.0: {} + preact@10.24.2: {} + + precinct@12.1.2: + dependencies: + '@dependents/detective-less': 5.0.0 + commander: 12.1.0 + detective-amd: 6.0.0 + detective-cjs: 6.0.0 + detective-es6: 5.0.0 + detective-postcss: 7.0.0(postcss@8.4.47) + detective-sass: 6.0.0 + detective-scss: 5.0.0 + detective-stylus: 5.0.0 + detective-typescript: 13.0.0(typescript@5.6.2) + detective-vue2: 2.0.3(typescript@5.6.2) + module-definition: 6.0.0 + node-source-walk: 7.0.0 + postcss: 8.4.47 + typescript: 5.6.2 + transitivePeerDependencies: + - supports-color prelude-ls@1.2.1: {} - prettier-plugin-jsdoc@1.3.0(prettier@3.2.5): + prettier-plugin-jsdoc@1.3.0(prettier@3.3.3): dependencies: binary-searching: 2.0.5 comment-parser: 1.4.1 mdast-util-from-markdown: 2.0.1 - prettier: 3.2.5 + prettier: 3.3.3 transitivePeerDependencies: - supports-color prettier@2.8.8: {} - prettier@3.2.5: {} + prettier@3.3.3: {} pretty-bytes@5.6.0: {} @@ -18618,7 +17939,7 @@ snapshots: dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 - react-is: 18.2.0 + react-is: 18.3.1 process-nextick-args@2.0.1: {} @@ -18626,10 +17947,10 @@ snapshots: dependencies: fromentries: 1.3.2 - process-warning@1.0.0: {} - process-warning@3.0.0: {} + process-warning@4.0.0: {} + process@0.11.10: {} prompts@2.4.2: @@ -18637,6 +17958,8 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 + property-information@6.5.0: {} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -18650,22 +17973,20 @@ snapshots: dependencies: event-stream: 3.3.4 + pseudomap@1.0.2: {} + psl@1.9.0: {} - pump@3.0.0: + pump@3.0.2: dependencies: end-of-stream: 1.4.4 once: 1.4.0 punycode@2.3.1: {} - pure-rand@6.0.4: {} - - qs@6.10.4: - dependencies: - side-channel: 1.0.6 + pure-rand@6.1.0: {} - qs@6.11.0: + qs@6.13.0: dependencies: side-channel: 1.0.6 @@ -18677,10 +17998,14 @@ snapshots: quick-lru@5.1.1: {} + quote-unquote@1.0.0: {} + railroad-diagrams@1.0.0: {} ramda@0.28.0: {} + ramda@0.29.0: {} + randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 @@ -18694,7 +18019,7 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 - react-is@18.2.0: {} + react-is@18.3.1: {} read-cache@1.0.0: dependencies: @@ -18708,11 +18033,18 @@ snapshots: read-pkg@5.2.0: dependencies: - '@types/normalize-package-data': 2.4.1 + '@types/normalize-package-data': 2.4.4 normalize-package-data: 2.5.0 parse-json: 5.2.0 type-fest: 0.6.0 + read-yaml-file@1.1.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -18743,11 +18075,15 @@ snapshots: real-require@0.2.0: {} + rechoir@0.6.2: + dependencies: + resolve: 1.22.8 + rechoir@0.7.1: dependencies: resolve: 1.22.8 - regenerate-unicode-properties@10.1.1: + regenerate-unicode-properties@10.2.0: dependencies: regenerate: 1.4.2 @@ -18757,33 +18093,37 @@ snapshots: regenerator-transform@0.15.2: dependencies: - '@babel/runtime': 7.24.8 + '@babel/runtime': 7.25.7 + + regex@4.3.3: {} regexp-tree@0.1.27: {} - regexp.prototype.flags@1.5.2: + regexp.prototype.flags@1.5.3: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-errors: 1.3.0 set-function-name: 2.0.2 - regexpu-core@5.3.2: + regexpu-core@6.1.1: dependencies: - '@babel/regjsgen': 0.8.0 regenerate: 1.4.2 - regenerate-unicode-properties: 10.1.1 - regjsparser: 0.9.1 + regenerate-unicode-properties: 10.2.0 + regjsgen: 0.8.0 + regjsparser: 0.11.1 unicode-match-property-ecmascript: 2.0.0 - unicode-match-property-value-ecmascript: 2.1.0 + unicode-match-property-value-ecmascript: 2.2.0 + + regjsgen@0.8.0: {} regjsparser@0.10.0: dependencies: jsesc: 0.5.0 - regjsparser@0.9.1: + regjsparser@0.11.1: dependencies: - jsesc: 0.5.0 + jsesc: 3.0.2 release-zalgo@1.0.0: dependencies: @@ -18791,27 +18131,27 @@ snapshots: remark-frontmatter@5.0.0: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-frontmatter: 2.0.1 micromark-extension-frontmatter: 2.0.0 - unified: 11.0.4 + unified: 11.0.5 transitivePeerDependencies: - supports-color remark-gfm@4.0.0: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-gfm: 3.0.0 micromark-extension-gfm: 3.0.0 remark-parse: 11.0.0 remark-stringify: 11.0.0 - unified: 11.0.4 + unified: 11.0.5 transitivePeerDependencies: - supports-color remark-parse@11.0.0: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.0.1 micromark-util-types: 2.0.0 unified: 11.0.4 @@ -18820,16 +18160,16 @@ snapshots: remark-stringify@11.0.0: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-to-markdown: 2.1.0 unified: 11.0.4 remark@15.0.1: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 remark-parse: 11.0.0 remark-stringify: 11.0.0 - unified: 11.0.4 + unified: 11.0.5 transitivePeerDependencies: - supports-color @@ -18839,12 +18179,25 @@ snapshots: dependencies: throttleit: 1.0.1 + require-and-forget@1.0.1: + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + require-directory@2.1.1: {} require-from-string@2.0.2: {} require-main-filename@2.0.0: {} + requirejs-config-file@4.0.0: + dependencies: + esprima: 4.0.1 + stringify-object: 3.3.0 + + requirejs@2.3.7: {} + requires-port@1.0.0: {} resolve-alpn@1.2.1: {} @@ -18853,6 +18206,8 @@ snapshots: dependencies: resolve-from: 5.0.0 + resolve-dependency-path@4.0.0: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -18861,15 +18216,9 @@ snapshots: resolve.exports@2.0.2: {} - resolve@1.22.4: - dependencies: - is-core-module: 2.13.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - resolve@1.22.8: dependencies: - is-core-module: 2.13.1 + is-core-module: 2.15.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -18882,19 +18231,17 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 - restore-cursor@4.0.0: + restore-cursor@5.1.0: dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 + onetime: 7.0.0 + signal-exit: 4.1.0 - ret@0.2.2: {} + ret@0.4.3: {} retry@0.13.1: {} reusify@1.0.4: {} - rfdc@1.3.1: {} - rfdc@1.4.1: {} rimraf@2.7.1: @@ -18905,48 +18252,55 @@ snapshots: dependencies: glob: 7.2.3 - rimraf@5.0.8: + rimraf@5.0.10: dependencies: - glob: 10.4.3 + glob: 10.4.5 robust-predicates@3.0.2: {} - rollup-plugin-visualizer@5.12.0(rollup@4.18.0): + rollup-plugin-visualizer@5.12.0(rollup@4.24.0): dependencies: open: 8.4.2 picomatch: 2.3.1 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 4.18.0 + rollup: 4.24.0 - rollup@2.79.1: + rollup@2.79.2: optionalDependencies: fsevents: 2.3.3 - rollup@4.18.0: + rollup@4.24.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.18.0 - '@rollup/rollup-android-arm64': 4.18.0 - '@rollup/rollup-darwin-arm64': 4.18.0 - '@rollup/rollup-darwin-x64': 4.18.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.18.0 - '@rollup/rollup-linux-arm-musleabihf': 4.18.0 - '@rollup/rollup-linux-arm64-gnu': 4.18.0 - '@rollup/rollup-linux-arm64-musl': 4.18.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0 - '@rollup/rollup-linux-riscv64-gnu': 4.18.0 - '@rollup/rollup-linux-s390x-gnu': 4.18.0 - '@rollup/rollup-linux-x64-gnu': 4.18.0 - '@rollup/rollup-linux-x64-musl': 4.18.0 - '@rollup/rollup-win32-arm64-msvc': 4.18.0 - '@rollup/rollup-win32-ia32-msvc': 4.18.0 - '@rollup/rollup-win32-x64-msvc': 4.18.0 + '@rollup/rollup-android-arm-eabi': 4.24.0 + '@rollup/rollup-android-arm64': 4.24.0 + '@rollup/rollup-darwin-arm64': 4.24.0 + '@rollup/rollup-darwin-x64': 4.24.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.24.0 + '@rollup/rollup-linux-arm-musleabihf': 4.24.0 + '@rollup/rollup-linux-arm64-gnu': 4.24.0 + '@rollup/rollup-linux-arm64-musl': 4.24.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.24.0 + '@rollup/rollup-linux-riscv64-gnu': 4.24.0 + '@rollup/rollup-linux-s390x-gnu': 4.24.0 + '@rollup/rollup-linux-x64-gnu': 4.24.0 + '@rollup/rollup-linux-x64-musl': 4.24.0 + '@rollup/rollup-win32-arm64-msvc': 4.24.0 + '@rollup/rollup-win32-ia32-msvc': 4.24.0 + '@rollup/rollup-win32-x64-msvc': 4.24.0 fsevents: 2.3.3 - rrweb-cssom@0.6.0: {} + roughjs@4.6.6(patch_hash=vxb6t6fqvzyhwhtjiliqr25jyq): + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + + rrweb-cssom@0.7.1: {} run-parallel@1.2.0: dependencies: @@ -18956,7 +18310,7 @@ snapshots: rxjs@7.8.1: dependencies: - tslib: 2.6.2 + tslib: 2.7.0 safe-array-concat@1.1.2: dependencies: @@ -18975,14 +18329,18 @@ snapshots: es-errors: 1.3.0 is-regex: 1.1.4 - safe-regex2@2.0.0: + safe-regex2@3.1.0: dependencies: - ret: 0.2.2 + ret: 0.4.3 - safe-stable-stringify@2.4.3: {} + safe-stable-stringify@2.5.0: {} safer-buffer@2.1.2: {} + sass-lookup@6.0.1: + dependencies: + commander: 12.1.0 + saxes@6.0.0: dependencies: xmlchars: 2.2.0 @@ -18996,39 +18354,30 @@ snapshots: schema-utils@4.2.0: dependencies: '@types/json-schema': 7.0.15 - ajv: 8.12.0 - ajv-formats: 2.1.1(ajv@8.12.0) - ajv-keywords: 5.1.0(ajv@8.12.0) + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) - search-insights@2.13.0: {} + search-insights@2.17.2: {} secure-json-parse@2.7.0: {} select-hose@2.0.0: {} - selfsigned@2.1.1: + selfsigned@2.4.1: dependencies: + '@types/node-forge': 1.3.11 node-forge: 1.3.1 - semver-store@0.3.0: {} - semver@5.7.2: {} semver@6.3.1: {} - semver@7.5.4: - dependencies: - lru-cache: 6.0.0 - - semver@7.6.0: - dependencies: - lru-cache: 6.0.0 - semver@7.6.2: {} semver@7.6.3: {} - send@0.18.0: + send@0.19.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -19062,18 +18411,18 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@1.15.0: + serve-static@1.16.2: dependencies: - encodeurl: 1.0.2 + encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.18.0 + send: 0.19.0 transitivePeerDependencies: - supports-color set-blocking@2.0.0: {} - set-cookie-parser@2.6.0: {} + set-cookie-parser@2.7.0: {} set-function-length@1.2.2: dependencies: @@ -19099,31 +18448,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.2 + 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: @@ -19139,23 +18488,34 @@ snapshots: shell-quote@1.8.1: {} + shelljs@0.8.5: + dependencies: + glob: 7.2.3 + interpret: 1.4.0 + rechoir: 0.6.2 + shiki@0.14.7: dependencies: ansi-sequence-parser: 1.1.1 - jsonc-parser: 3.2.1 + jsonc-parser: 3.3.1 vscode-oniguruma: 1.7.0 vscode-textmate: 8.0.0 - shiki@1.4.0: + shiki@1.22.0: dependencies: - '@shikijs/core': 1.4.0 + '@shikijs/core': 1.22.0 + '@shikijs/engine-javascript': 1.22.0 + '@shikijs/engine-oniguruma': 1.22.0 + '@shikijs/types': 1.22.0 + '@shikijs/vscode-textmate': 9.3.0 + '@types/hast': 3.0.4 side-channel@1.0.6: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - object-inspect: 1.13.1 + object-inspect: 1.13.2 siginfo@2.0.0: {} @@ -19163,13 +18523,15 @@ snapshots: signal-exit@4.1.0: {} + simple-bin-help@1.8.0: {} + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 sirv@2.0.4: dependencies: - '@polka/url': 1.0.0-next.25 + '@polka/url': 1.0.0-next.28 mrmime: 2.0.0 totalist: 3.0.1 @@ -19213,20 +18575,17 @@ snapshots: uuid: 8.3.2 websocket-driver: 0.7.4 - sonic-boom@1.4.1: + sonic-boom@3.8.1: dependencies: atomic-sleep: 1.0.0 - flatstr: 1.0.12 - sonic-boom@3.8.1: + sonic-boom@4.1.0: dependencies: atomic-sleep: 1.0.0 source-map-js@1.0.1: {} - source-map-js@1.0.2: {} - - source-map-js@1.2.0: {} + source-map-js@1.2.1: {} source-map-support@0.5.13: dependencies: @@ -19253,6 +18612,8 @@ snapshots: sourcemap-codec@1.4.8: {} + space-separated-tokens@2.0.2: {} + spawn-command@0.0.2: {} spawn-wrap@2.0.0: @@ -19264,28 +18625,33 @@ snapshots: signal-exit: 3.0.7 which: 2.0.2 + spawndamnit@2.0.0: + dependencies: + cross-spawn: 5.1.0 + signal-exit: 3.0.7 + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.18 + spdx-license-ids: 3.0.20 spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.18 + spdx-license-ids: 3.0.20 spdx-expression-parse@4.0.0: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.18 + spdx-license-ids: 3.0.20 - spdx-license-ids@3.0.18: {} + spdx-license-ids@3.0.20: {} spdy-transport@3.0.0: dependencies: - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 @@ -19296,7 +18662,7 @@ snapshots: spdy@4.0.2: dependencies: - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 @@ -19306,6 +18672,17 @@ snapshots: speakingurl@14.0.1: {} + spec-change@1.11.11: + dependencies: + arg: 5.0.2 + debug: 4.3.7(supports-color@8.1.1) + deep-equal: 2.2.3 + dependency-tree: 11.0.1 + lazy-ass: 2.0.3 + tinyglobby: 0.2.9 + transitivePeerDependencies: + - supports-color + split2@4.2.0: {} split@0.3.3: @@ -19334,16 +18711,16 @@ snapshots: stackback@0.0.2: {} - start-server-and-test@2.0.4: + start-server-and-test@2.0.8: dependencies: arg: 5.0.2 bluebird: 3.7.2 check-more-types: 2.24.0 - debug: 4.3.5 + debug: 4.3.7(supports-color@8.1.1) execa: 5.1.1 lazy-ass: 1.6.0 ps-tree: 1.2.0 - wait-on: 7.2.0(debug@4.3.5) + wait-on: 8.0.1(debug@4.3.7) transitivePeerDependencies: - supports-color @@ -19353,6 +18730,10 @@ snapshots: std-env@3.7.0: {} + stop-iteration-iterator@1.0.0: + dependencies: + internal-slot: 1.0.7 + stream-combiner@0.0.4: dependencies: duplexer: 0.1.2 @@ -19364,8 +18745,6 @@ snapshots: char-regex: 1.0.2 strip-ansi: 6.0.1 - string-similarity@4.0.4: {} - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -19378,9 +18757,9 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - string-width@7.1.0: + string-width@7.2.0: dependencies: - emoji-regex: 10.3.0 + emoji-regex: 10.4.0 get-east-asian-width: 1.2.0 strip-ansi: 7.1.0 @@ -19395,7 +18774,7 @@ snapshots: gopd: 1.0.1 has-symbols: 1.0.3 internal-slot: 1.0.7 - regexp.prototype.flags: 1.5.2 + regexp.prototype.flags: 1.5.3 set-function-name: 2.0.2 side-channel: 1.0.6 @@ -19426,6 +18805,11 @@ snapshots: dependencies: safe-buffer: 5.2.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + stringify-object@3.3.0: dependencies: get-own-enumerable-property-symbols: 3.0.2 @@ -19442,7 +18826,9 @@ snapshots: strip-ansi@7.1.0: dependencies: - ansi-regex: 6.0.1 + ansi-regex: 6.1.0 + + strip-bom@3.0.0: {} strip-bom@4.0.0: {} @@ -19464,18 +18850,26 @@ snapshots: dependencies: js-tokens: 9.0.0 - stylis@4.3.2: {} + stylis@4.3.4: {} + + stylus-lookup@6.0.0: + dependencies: + commander: 12.1.0 sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.5 commander: 4.1.1 - glob: 10.4.3 + glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.6 ts-interface-checker: 0.1.13 + superjson@2.2.1: + dependencies: + copy-anything: 3.0.5 + supports-color@2.0.0: {} supports-color@5.5.0: @@ -19494,14 +18888,14 @@ snapshots: symbol-tree@3.2.4: {} - synckit@0.9.0: + synckit@0.9.2: dependencies: '@pkgr/core': 0.1.1 - tslib: 2.6.3 + tslib: 2.7.0 tabbable@6.2.0: {} - tailwindcss@3.4.4: + tailwindcss@3.4.13: dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -19513,16 +18907,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.39 - postcss-import: 15.1.0(postcss@8.4.39) - postcss-js: 4.0.1(postcss@8.4.39) - postcss-load-config: 4.0.2(postcss@8.4.39) - postcss-nested: 6.0.1(postcss@8.4.39) - postcss-selector-parser: 6.1.0 + picocolors: 1.1.0 + postcss: 8.4.47 + postcss-import: 15.1.0(postcss@8.4.47) + postcss-js: 4.0.1(postcss@8.4.47) + postcss-load-config: 4.0.2(postcss@8.4.47) + postcss-nested: 6.2.0(postcss@8.4.47) + postcss-selector-parser: 6.1.2 resolve: 1.22.8 sucrase: 3.35.0 transitivePeerDependencies: @@ -19532,7 +18926,7 @@ snapshots: teen_process@1.16.0: dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.25.7 bluebird: 3.7.2 lodash: 4.17.21 shell-quote: 1.8.1 @@ -19553,44 +18947,31 @@ snapshots: ansi-escapes: 4.3.2 iterm2-version: 4.2.0 - terser-webpack-plugin@5.3.10(esbuild@0.21.5)(webpack@5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0)): + term-size@2.2.1: {} + + terser-webpack-plugin@5.3.10(esbuild@0.21.5)(webpack@5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 - terser: 5.29.2 - webpack: 5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0) + terser: 5.34.1 + webpack: 5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0) optionalDependencies: esbuild: 0.21.5 - terser-webpack-plugin@5.3.10(esbuild@0.21.5)(webpack@5.91.0(esbuild@0.21.5)): + terser-webpack-plugin@5.3.10(esbuild@0.21.5)(webpack@5.95.0(esbuild@0.21.5)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 - terser: 5.29.2 - webpack: 5.91.0(esbuild@0.21.5) + terser: 5.34.1 + webpack: 5.95.0(esbuild@0.21.5) optionalDependencies: esbuild: 0.21.5 - terser@5.29.2: - dependencies: - '@jridgewell/source-map': 0.3.6 - acorn: 8.11.3 - commander: 2.20.3 - source-map-support: 0.5.21 - - terser@5.31.1: - dependencies: - '@jridgewell/source-map': 0.3.6 - acorn: 8.12.1 - commander: 2.20.3 - source-map-support: 0.5.21 - optional: true - - terser@5.31.3: + terser@5.34.1: dependencies: '@jridgewell/source-map': 0.3.6 acorn: 8.12.1 @@ -19617,6 +18998,10 @@ snapshots: dependencies: real-require: 0.2.0 + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + throat@6.0.2: {} throttleit@1.0.1: {} @@ -19625,19 +19010,28 @@ snapshots: thunky@1.1.0: {} - timers-ext@0.1.7: + timers-ext@0.1.8: dependencies: es5-ext: 0.10.64 next-tick: 1.1.0 - tiny-lru@8.0.2: {} + tinybench@2.9.0: {} + + tinyexec@0.3.0: {} - tinybench@2.8.0: {} + tinyglobby@0.2.9: + dependencies: + fdir: 6.4.0(picomatch@4.0.2) + picomatch: 4.0.2 tinypool@0.8.4: {} tinyspy@2.2.1: {} + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + tmp@0.2.3: {} tmpl@1.0.5: {} @@ -19648,11 +19042,13 @@ snapshots: dependencies: is-number: 7.0.0 + toad-cache@3.7.0: {} + toidentifier@1.0.1: {} totalist@3.0.1: {} - tough-cookie@4.1.3: + tough-cookie@4.1.4: dependencies: psl: 1.9.0 punycode: 2.3.1 @@ -19673,26 +19069,36 @@ snapshots: tree-kill@1.2.2: {} + trim-lines@3.0.1: {} + trough@2.2.0: {} ts-api-utils@1.3.0(typescript@5.4.5): dependencies: typescript: 5.4.5 + ts-api-utils@1.3.0(typescript@5.6.2): + dependencies: + typescript: 5.6.2 + ts-dedent@2.2.0: {} ts-interface-checker@0.1.13: {} ts-toolbelt@6.15.5: {} - tslib@2.6.2: {} + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 - tslib@2.6.3: {} + tslib@2.7.0: {} - tsx@4.7.3: + tsx@4.19.1: dependencies: - esbuild: 0.19.12 - get-tsconfig: 4.7.3 + esbuild: 0.23.1 + get-tsconfig: 4.8.1 optionalDependencies: fsevents: 2.3.3 @@ -19700,6 +19106,8 @@ snapshots: dependencies: safe-buffer: 5.2.1 + tunnel@0.0.6: {} + tweetnacl@0.14.5: {} type-check@0.4.0: @@ -19708,6 +19116,8 @@ snapshots: type-detect@4.0.8: {} + type-detect@4.1.0: {} + type-fest@0.16.0: {} type-fest@0.20.2: {} @@ -19718,16 +19128,14 @@ snapshots: type-fest@0.8.1: {} - type-fest@1.4.0: {} - - type-fest@4.15.0: {} + type-fest@4.26.1: {} type-is@1.6.18: dependencies: media-typer: 0.3.0 mime-types: 2.1.35 - type@2.7.2: {} + type@2.7.3: {} typed-array-buffer@1.0.2: dependencies: @@ -19774,15 +19182,15 @@ snapshots: dependencies: lunr: 2.3.9 marked: 4.3.0 - minimatch: 9.0.4 + minimatch: 9.0.5 shiki: 0.14.7 typescript: 5.4.5 - typescript-eslint@8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5): + typescript-eslint@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5): dependencies: - '@typescript-eslint/eslint-plugin': 8.0.0-alpha.44(@typescript-eslint/parser@8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5))(eslint@9.6.0)(typescript@5.4.5) - '@typescript-eslint/parser': 8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5) - '@typescript-eslint/utils': 8.0.0-alpha.44(eslint@9.6.0)(typescript@5.4.5) + '@typescript-eslint/eslint-plugin': 8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5) + '@typescript-eslint/parser': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5) + '@typescript-eslint/utils': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: @@ -19791,11 +19199,13 @@ snapshots: typescript@5.4.5: {} + typescript@5.6.2: {} + uc.micro@1.0.6: {} - ufo@1.5.3: {} + ufo@1.5.4: {} - uglify-js@3.17.4: {} + uglify-js@3.19.3: {} unbox-primitive@1.0.2: dependencies: @@ -19806,7 +19216,7 @@ snapshots: unconfig@0.3.13: dependencies: - '@antfu/utils': 0.7.7 + '@antfu/utils': 0.7.10 defu: 6.1.4 jiti: 1.21.6 @@ -19814,14 +19224,20 @@ snapshots: undici-types@5.26.5: {} - unicode-canonical-property-names-ecmascript@2.0.0: {} + undici-types@6.19.8: {} + + undici@5.28.4: + dependencies: + '@fastify/busboy': 2.1.1 + + unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-match-property-ecmascript@2.0.0: dependencies: - unicode-canonical-property-names-ecmascript: 2.0.0 + unicode-canonical-property-names-ecmascript: 2.0.1 unicode-property-aliases-ecmascript: 2.1.0 - unicode-match-property-value-ecmascript@2.1.0: {} + unicode-match-property-value-ecmascript@2.2.0: {} unicode-property-aliases-ecmascript@2.1.0: {} @@ -19829,48 +19245,58 @@ snapshots: unified@11.0.4: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 bail: 2.0.2 devlop: 1.1.0 extend: 3.0.2 is-plain-obj: 4.1.0 trough: 2.2.0 - vfile: 6.0.1 + vfile: 6.0.3 - unique-string@2.0.0: + unified@11.0.5: dependencies: - crypto-random-string: 2.0.0 + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 - unique-string@3.0.0: + unique-string@2.0.0: dependencies: - crypto-random-string: 4.0.0 + crypto-random-string: 2.0.0 unist-util-flatmap@1.0.0: {} unist-util-inspect@8.0.0: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-is@6.0.0: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 unist-util-stringify-position@2.0.3: dependencies: - '@types/unist': 2.0.10 + '@types/unist': 2.0.11 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: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-is: 6.0.0 unist-util-visit@5.0.0: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 @@ -19880,13 +19306,13 @@ snapshots: universalify@2.0.1: {} - unocss@0.59.4(postcss@8.4.39)(rollup@2.79.1)(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3)): + unocss@0.59.4(postcss@8.4.47)(rollup@2.79.2)(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1)): dependencies: - '@unocss/astro': 0.59.4(rollup@2.79.1)(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3)) - '@unocss/cli': 0.59.4(rollup@2.79.1) + '@unocss/astro': 0.59.4(rollup@2.79.2)(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1)) + '@unocss/cli': 0.59.4(rollup@2.79.2) '@unocss/core': 0.59.4 '@unocss/extractor-arbitrary-variants': 0.59.4 - '@unocss/postcss': 0.59.4(postcss@8.4.39) + '@unocss/postcss': 0.59.4(postcss@8.4.47) '@unocss/preset-attributify': 0.59.4 '@unocss/preset-icons': 0.59.4 '@unocss/preset-mini': 0.59.4 @@ -19901,9 +19327,9 @@ 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.2.13(@types/node@20.14.7)(terser@5.31.3)) + '@unocss/vite': 0.59.4(rollup@2.79.2)(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1)) optionalDependencies: - vite: 5.2.13(@types/node@20.14.7)(terser@5.31.3) + vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) transitivePeerDependencies: - postcss - rollup @@ -19911,47 +19337,42 @@ snapshots: unpipe@1.0.0: {} - unplugin-vue-components@0.26.0(@babel/parser@7.24.8)(rollup@2.79.1)(vue@3.4.31(typescript@5.4.5)): + unplugin-vue-components@0.26.0(@babel/parser@7.25.7)(rollup@2.79.2)(vue@3.5.11(typescript@5.6.2))(webpack-sources@3.2.3): dependencies: - '@antfu/utils': 0.7.6 - '@rollup/pluginutils': 5.1.0(rollup@2.79.1) + '@antfu/utils': 0.7.10 + '@rollup/pluginutils': 5.1.2(rollup@2.79.2) chokidar: 3.6.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) fast-glob: 3.3.2 local-pkg: 0.4.3 - magic-string: 0.30.5 - minimatch: 9.0.3 - resolve: 1.22.4 - unplugin: 1.4.0 - vue: 3.4.31(typescript@5.4.5) + magic-string: 0.30.11 + minimatch: 9.0.5 + resolve: 1.22.8 + unplugin: 1.14.1(webpack-sources@3.2.3) + vue: 3.5.11(typescript@5.6.2) optionalDependencies: - '@babel/parser': 7.24.8 + '@babel/parser': 7.25.7 transitivePeerDependencies: - rollup - supports-color + - webpack-sources - unplugin@1.4.0: + unplugin@1.14.1(webpack-sources@3.2.3): dependencies: - acorn: 8.11.3 - chokidar: 3.6.0 + acorn: 8.12.1 + webpack-virtual-modules: 0.6.2 + optionalDependencies: webpack-sources: 3.2.3 - webpack-virtual-modules: 0.5.0 untildify@4.0.0: {} upath@1.2.0: {} - update-browserslist-db@1.0.13(browserslist@4.23.0): - dependencies: - browserslist: 4.23.0 - escalade: 3.1.2 - picocolors: 1.0.1 - - update-browserslist-db@1.0.16(browserslist@4.23.1): + update-browserslist-db@1.1.1(browserslist@4.24.0): dependencies: - browserslist: 4.23.1 - escalade: 3.1.2 - picocolors: 1.0.1 + browserslist: 4.24.0 + escalade: 3.2.0 + picocolors: 1.1.0 uri-js@4.4.1: dependencies: @@ -19970,7 +19391,7 @@ snapshots: uuid@9.0.1: {} - v8-to-istanbul@9.2.0: + v8-to-istanbul@9.3.0: dependencies: '@jridgewell/trace-mapping': 0.3.25 '@types/istanbul-lib-coverage': 2.0.6 @@ -19991,114 +19412,94 @@ 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.1: + vfile@6.0.3: dependencies: - '@types/unist': 3.0.2 - unist-util-stringify-position: 4.0.0 + '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-node@1.5.3(@types/node@20.12.14)(terser@5.31.3): + vite-node@1.6.0(@types/node@20.16.11)(terser@5.34.1): dependencies: cac: 6.7.14 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) pathe: 1.1.2 - picocolors: 1.0.0 - vite: 5.2.13(@types/node@20.12.14)(terser@5.31.3) + picocolors: 1.1.0 + vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) transitivePeerDependencies: - '@types/node' - less - lightningcss - sass + - sass-embedded - stylus - sugarss - supports-color - terser - vite-plugin-istanbul@6.0.2(vite@5.2.13(@types/node@20.12.14)(terser@5.31.3)): + vite-plugin-istanbul@6.0.2(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1)): dependencies: '@istanbuljs/load-nyc-config': 1.1.0 - espree: 10.1.0 - istanbul-lib-instrument: 6.0.2 - picocolors: 1.0.1 + espree: 10.2.0 + istanbul-lib-instrument: 6.0.3 + picocolors: 1.1.0 source-map: 0.7.4 test-exclude: 6.0.0 - vite: 5.2.13(@types/node@20.12.14)(terser@5.31.3) + vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) transitivePeerDependencies: - supports-color - vite-plugin-pwa@0.19.8(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.0.0): + vite-plugin-pwa@0.19.8(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0): dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) fast-glob: 3.3.2 pretty-bytes: 6.1.1 - vite: 5.2.13(@types/node@20.14.7)(terser@5.31.3) - workbox-build: 7.1.0(@types/babel__core@7.20.5) - workbox-window: 7.0.0 + vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) + workbox-build: 7.1.1(@types/babel__core@7.20.5) + workbox-window: 7.1.0 transitivePeerDependencies: - supports-color - vite@5.2.13(@types/node@20.12.14)(terser@5.31.3): - dependencies: - esbuild: 0.20.2 - postcss: 8.4.38 - rollup: 4.18.0 - optionalDependencies: - '@types/node': 20.12.14 - fsevents: 2.3.3 - terser: 5.31.3 - - vite@5.2.13(@types/node@20.14.7)(terser@5.31.1): - dependencies: - esbuild: 0.20.2 - postcss: 8.4.38 - rollup: 4.18.0 - optionalDependencies: - '@types/node': 20.14.7 - fsevents: 2.3.3 - terser: 5.31.1 - - vite@5.2.13(@types/node@20.14.7)(terser@5.31.3): + vite@5.4.8(@types/node@20.16.11)(terser@5.34.1): dependencies: - esbuild: 0.20.2 - postcss: 8.4.38 - rollup: 4.18.0 + esbuild: 0.21.5 + postcss: 8.4.47 + rollup: 4.24.0 optionalDependencies: - '@types/node': 20.14.7 + '@types/node': 20.16.11 fsevents: 2.3.3 - terser: 5.31.3 + terser: 5.34.1 - vitepress-plugin-search@1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.1.4(@algolia/client-search@4.23.3)(@types/node@20.14.7)(axios@1.7.2)(postcss@8.4.39)(search-insights@2.13.0)(terser@5.31.1)(typescript@5.4.5))(vue@3.4.30(typescript@5.4.5)): + 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.16.11)(axios@1.7.7)(postcss@8.4.47)(search-insights@2.17.2)(terser@5.34.1)(typescript@5.4.5))(vue@3.5.11(typescript@5.4.5)): dependencies: - '@types/flexsearch': 0.7.3 + '@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.1 - vitepress: 1.1.4(@algolia/client-search@4.23.3)(@types/node@20.14.7)(axios@1.7.2)(postcss@8.4.39)(search-insights@2.13.0)(terser@5.31.1)(typescript@5.4.5) - vue: 3.4.30(typescript@5.4.5) - - vitepress@1.1.4(@algolia/client-search@4.23.3)(@types/node@20.14.7)(axios@1.7.2)(postcss@8.4.39)(search-insights@2.13.0)(terser@5.31.1)(typescript@5.4.5): - dependencies: - '@docsearch/css': 3.6.0 - '@docsearch/js': 3.6.0(@algolia/client-search@4.23.3)(search-insights@2.13.0) - '@shikijs/core': 1.4.0 - '@shikijs/transformers': 1.4.0 - '@types/markdown-it': 14.0.1 - '@vitejs/plugin-vue': 5.0.5(vite@5.2.13(@types/node@20.14.7)(terser@5.31.1))(vue@3.4.30(typescript@5.4.5)) - '@vue/devtools-api': 7.1.3(vue@3.4.30(typescript@5.4.5)) - '@vueuse/core': 10.9.0(vue@3.4.30(typescript@5.4.5)) - '@vueuse/integrations': 10.9.0(axios@1.7.2)(focus-trap@7.5.4)(vue@3.4.30(typescript@5.4.5)) - focus-trap: 7.5.4 + markdown-it: 13.0.2 + vitepress: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.16.11)(axios@1.7.7)(postcss@8.4.47)(search-insights@2.17.2)(terser@5.34.1)(typescript@5.4.5) + vue: 3.5.11(typescript@5.4.5) + + vitepress@1.1.4(@algolia/client-search@4.24.0)(@types/node@20.16.11)(axios@1.7.7)(postcss@8.4.47)(search-insights@2.17.2)(terser@5.34.1)(typescript@5.4.5): + dependencies: + '@docsearch/css': 3.6.2 + '@docsearch/js': 3.6.2(@algolia/client-search@4.24.0)(search-insights@2.17.2) + '@shikijs/core': 1.22.0 + '@shikijs/transformers': 1.22.0 + '@types/markdown-it': 14.1.2 + '@vitejs/plugin-vue': 5.1.4(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1))(vue@3.5.11(typescript@5.4.5)) + '@vue/devtools-api': 7.4.6 + '@vueuse/core': 10.11.1(vue@3.5.11(typescript@5.4.5)) + '@vueuse/integrations': 10.11.1(axios@1.7.7)(focus-trap@7.6.0)(vue@3.5.11(typescript@5.4.5)) + focus-trap: 7.6.0 mark.js: 8.11.1 minisearch: 6.3.0 - shiki: 1.4.0 - vite: 5.2.13(@types/node@20.14.7)(terser@5.31.1) - vue: 3.4.30(typescript@5.4.5) + shiki: 1.22.0 + vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) + vue: 3.5.11(typescript@5.4.5) optionalDependencies: - postcss: 8.4.39 + postcss: 8.4.47 transitivePeerDependencies: - '@algolia/client-search' - '@types/node' @@ -20118,6 +19519,7 @@ snapshots: - react - react-dom - sass + - sass-embedded - search-insights - sortablejs - stylus @@ -20126,25 +19528,25 @@ snapshots: - typescript - universal-cookie - vitepress@1.1.4(@algolia/client-search@4.23.3)(@types/node@20.14.7)(axios@1.7.2)(postcss@8.4.39)(search-insights@2.13.0)(terser@5.31.3)(typescript@5.4.5): - dependencies: - '@docsearch/css': 3.6.0 - '@docsearch/js': 3.6.0(@algolia/client-search@4.23.3)(search-insights@2.13.0) - '@shikijs/core': 1.4.0 - '@shikijs/transformers': 1.4.0 - '@types/markdown-it': 14.0.1 - '@vitejs/plugin-vue': 5.0.5(vite@5.2.13(@types/node@20.14.7)(terser@5.31.3))(vue@3.4.30(typescript@5.4.5)) - '@vue/devtools-api': 7.1.3(vue@3.4.30(typescript@5.4.5)) - '@vueuse/core': 10.9.0(vue@3.4.30(typescript@5.4.5)) - '@vueuse/integrations': 10.9.0(axios@1.7.2)(focus-trap@7.5.4)(vue@3.4.30(typescript@5.4.5)) - focus-trap: 7.5.4 + vitepress@1.1.4(@algolia/client-search@4.24.0)(@types/node@20.16.11)(axios@1.7.7)(postcss@8.4.47)(search-insights@2.17.2)(terser@5.34.1)(typescript@5.6.2): + dependencies: + '@docsearch/css': 3.6.2 + '@docsearch/js': 3.6.2(@algolia/client-search@4.24.0)(search-insights@2.17.2) + '@shikijs/core': 1.22.0 + '@shikijs/transformers': 1.22.0 + '@types/markdown-it': 14.1.2 + '@vitejs/plugin-vue': 5.1.4(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1))(vue@3.5.11(typescript@5.6.2)) + '@vue/devtools-api': 7.4.6 + '@vueuse/core': 10.11.1(vue@3.5.11(typescript@5.6.2)) + '@vueuse/integrations': 10.11.1(axios@1.7.7)(focus-trap@7.6.0)(vue@3.5.11(typescript@5.6.2)) + focus-trap: 7.6.0 mark.js: 8.11.1 minisearch: 6.3.0 - shiki: 1.4.0 - vite: 5.2.13(@types/node@20.14.7)(terser@5.31.3) - vue: 3.4.30(typescript@5.4.5) + shiki: 1.22.0 + vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) + vue: 3.5.11(typescript@5.6.2) optionalDependencies: - postcss: 8.4.39 + postcss: 8.4.47 transitivePeerDependencies: - '@algolia/client-search' - '@types/node' @@ -20164,6 +19566,7 @@ snapshots: - react - react-dom - sass + - sass-embedded - search-insights - sortablejs - stylus @@ -20172,36 +19575,37 @@ snapshots: - typescript - universal-cookie - vitest@1.5.3(@types/node@20.12.14)(@vitest/ui@1.5.3)(jsdom@24.0.0)(terser@5.31.3): + vitest@1.6.0(@types/node@20.16.11)(@vitest/ui@1.6.0)(jsdom@24.1.3)(terser@5.34.1): dependencies: - '@vitest/expect': 1.5.3 - '@vitest/runner': 1.5.3 - '@vitest/snapshot': 1.5.3 - '@vitest/spy': 1.5.3 - '@vitest/utils': 1.5.3 - acorn-walk: 8.3.2 - chai: 4.4.1 - debug: 4.3.4(supports-color@8.1.1) + '@vitest/expect': 1.6.0 + '@vitest/runner': 1.6.0 + '@vitest/snapshot': 1.6.0 + '@vitest/spy': 1.6.0 + '@vitest/utils': 1.6.0 + acorn-walk: 8.3.4 + chai: 4.5.0 + debug: 4.3.7(supports-color@8.1.1) execa: 8.0.1 local-pkg: 0.5.0 - magic-string: 0.30.10 + magic-string: 0.30.11 pathe: 1.1.2 - picocolors: 1.0.0 + picocolors: 1.1.0 std-env: 3.7.0 strip-literal: 2.1.0 - tinybench: 2.8.0 + tinybench: 2.9.0 tinypool: 0.8.4 - vite: 5.2.13(@types/node@20.12.14)(terser@5.31.3) - vite-node: 1.5.3(@types/node@20.12.14)(terser@5.31.3) - why-is-node-running: 2.2.2 + vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) + vite-node: 1.6.0(@types/node@20.16.11)(terser@5.34.1) + why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 20.12.14 - '@vitest/ui': 1.5.3(vitest@1.5.3) - jsdom: 24.0.0 + '@types/node': 20.16.11 + '@vitest/ui': 1.6.0(vitest@1.6.0) + jsdom: 24.1.3 transitivePeerDependencies: - less - lightningcss - sass + - sass-embedded - stylus - sugarss - supports-color @@ -20209,8 +19613,8 @@ snapshots: vscode-json-languageservice@4.2.1: dependencies: - jsonc-parser: 3.2.1 - vscode-languageserver-textdocument: 1.0.11 + jsonc-parser: 3.3.1 + vscode-languageserver-textdocument: 1.0.12 vscode-languageserver-types: 3.17.5 vscode-nls: 5.2.0 vscode-uri: 3.0.8 @@ -20222,7 +19626,7 @@ snapshots: vscode-jsonrpc: 8.2.0 vscode-languageserver-types: 3.17.5 - vscode-languageserver-textdocument@1.0.11: {} + vscode-languageserver-textdocument@1.0.12: {} vscode-languageserver-types@3.17.5: {} @@ -20238,50 +19642,46 @@ snapshots: vscode-uri@3.0.8: {} - vue-demi@0.14.7(vue@3.4.30(typescript@5.4.5)): - dependencies: - vue: 3.4.30(typescript@5.4.5) - - vue-demi@0.14.7(vue@3.4.31(typescript@5.4.5)): + vue-demi@0.14.10(vue@3.5.11(typescript@5.4.5)): dependencies: - vue: 3.4.31(typescript@5.4.5) + vue: 3.5.11(typescript@5.4.5) - vue-demi@0.14.8(vue@3.4.31(typescript@5.4.5)): + vue-demi@0.14.10(vue@3.5.11(typescript@5.6.2)): dependencies: - vue: 3.4.31(typescript@5.4.5) + vue: 3.5.11(typescript@5.6.2) - vue@3.4.30(typescript@5.4.5): + vue@3.5.11(typescript@5.4.5): dependencies: - '@vue/compiler-dom': 3.4.30 - '@vue/compiler-sfc': 3.4.30 - '@vue/runtime-dom': 3.4.30 - '@vue/server-renderer': 3.4.30(vue@3.4.30(typescript@5.4.5)) - '@vue/shared': 3.4.30 + '@vue/compiler-dom': 3.5.11 + '@vue/compiler-sfc': 3.5.11 + '@vue/runtime-dom': 3.5.11 + '@vue/server-renderer': 3.5.11(vue@3.5.11(typescript@5.4.5)) + '@vue/shared': 3.5.11 optionalDependencies: typescript: 5.4.5 - vue@3.4.31(typescript@5.4.5): + vue@3.5.11(typescript@5.6.2): dependencies: - '@vue/compiler-dom': 3.4.31 - '@vue/compiler-sfc': 3.4.31 - '@vue/runtime-dom': 3.4.31 - '@vue/server-renderer': 3.4.31(vue@3.4.31(typescript@5.4.5)) - '@vue/shared': 3.4.31 + '@vue/compiler-dom': 3.5.11 + '@vue/compiler-sfc': 3.5.11 + '@vue/runtime-dom': 3.5.11 + '@vue/server-renderer': 3.5.11(vue@3.5.11(typescript@5.6.2)) + '@vue/shared': 3.5.11 optionalDependencies: - typescript: 5.4.5 + typescript: 5.6.2 - vuex@4.1.0(vue@3.4.31(typescript@5.4.5)): + vuex@4.1.0(vue@3.5.11(typescript@5.6.2)): dependencies: - '@vue/devtools-api': 6.6.3 - vue: 3.4.31(typescript@5.4.5) + '@vue/devtools-api': 6.6.4 + vue: 3.5.11(typescript@5.6.2) w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 - wait-on@7.2.0(debug@4.3.5): + wait-on@8.0.1(debug@4.3.7): dependencies: - axios: 1.7.2(debug@4.3.5) + axios: 1.7.7(debug@4.3.7) joi: 17.13.3 lodash: 4.17.21 minimist: 1.2.8 @@ -20293,7 +19693,7 @@ snapshots: dependencies: makeerror: 1.0.12 - watchpack@2.4.1: + watchpack@2.4.2: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 @@ -20302,11 +19702,16 @@ snapshots: dependencies: minimalistic-assert: 1.0.1 + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + optional: true + web-streams-polyfill@3.3.3: {} webdriver@7.31.1(typescript@5.4.5): dependencies: - '@types/node': 18.19.38 + '@types/node': 18.19.55 '@wdio/config': 7.31.1(typescript@5.4.5) '@wdio/logger': 7.26.0 '@wdio/protocols': 7.27.0 @@ -20324,96 +19729,96 @@ snapshots: webidl-conversions@7.0.0: {} - webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0): + webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0): dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 1.2.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0))(webpack@5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0)) - '@webpack-cli/info': 1.5.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0)) - '@webpack-cli/serve': 1.7.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0))(webpack-dev-server@4.15.2(webpack-cli@4.10.0)(webpack@5.91.0)) + '@webpack-cli/configtest': 1.2.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0))(webpack@5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0)) + '@webpack-cli/info': 1.5.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0)) + '@webpack-cli/serve': 1.7.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0))(webpack-dev-server@4.15.2(webpack-cli@4.10.0)(webpack@5.95.0)) colorette: 2.0.20 commander: 7.2.0 cross-spawn: 7.0.3 fastest-levenshtein: 1.0.16 - import-local: 3.1.0 + import-local: 3.2.0 interpret: 2.2.0 rechoir: 0.7.1 - webpack: 5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0) - webpack-merge: 5.9.0 + webpack: 5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0) + webpack-merge: 5.10.0 optionalDependencies: - webpack-dev-server: 4.15.2(webpack-cli@4.10.0)(webpack@5.91.0) + webpack-dev-server: 4.15.2(webpack-cli@4.10.0)(webpack@5.95.0) - webpack-dev-middleware@5.3.4(webpack@5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0)): + webpack-dev-middleware@5.3.4(webpack@5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0)): dependencies: colorette: 2.0.20 memfs: 3.5.3 mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.2.0 - webpack: 5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0) + webpack: 5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0) - webpack-dev-server@4.15.2(webpack-cli@4.10.0)(webpack@5.91.0): + webpack-dev-server@4.15.2(webpack-cli@4.10.0)(webpack@5.95.0): dependencies: - '@types/bonjour': 3.5.10 - '@types/connect-history-api-fallback': 1.5.0 + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 '@types/express': 4.17.21 - '@types/serve-index': 1.9.1 - '@types/serve-static': 1.15.5 - '@types/sockjs': 0.3.33 - '@types/ws': 8.5.5 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.7 + '@types/sockjs': 0.3.36 + '@types/ws': 8.5.12 ansi-html-community: 0.0.8 - bonjour-service: 1.1.1 + bonjour-service: 1.2.1 chokidar: 3.6.0 colorette: 2.0.20 compression: 1.7.4 connect-history-api-fallback: 2.0.0 default-gateway: 6.0.3 - express: 4.19.1 + express: 4.21.0 graceful-fs: 4.2.11 - html-entities: 2.4.0 - http-proxy-middleware: 2.0.6(@types/express@4.17.21) - ipaddr.js: 2.1.0 - launch-editor: 2.6.1 + html-entities: 2.5.2 + http-proxy-middleware: 2.0.7(@types/express@4.17.21) + ipaddr.js: 2.2.0 + launch-editor: 2.9.1 open: 8.4.2 p-retry: 4.6.2 rimraf: 3.0.2 schema-utils: 4.2.0 - selfsigned: 2.1.1 + selfsigned: 2.4.1 serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 5.3.4(webpack@5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0)) - ws: 8.16.0 + webpack-dev-middleware: 5.3.4(webpack@5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0)) + ws: 8.18.0 optionalDependencies: - webpack: 5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0) - webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0) + webpack: 5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0) + webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0) transitivePeerDependencies: - bufferutil - debug - supports-color - utf-8-validate - webpack-merge@5.9.0: + webpack-merge@5.10.0: dependencies: clone-deep: 4.0.1 + flat: 5.0.2 wildcard: 2.0.1 webpack-sources@3.2.3: {} - webpack-virtual-modules@0.5.0: {} + webpack-virtual-modules@0.6.2: {} - webpack@5.91.0(esbuild@0.21.5): + webpack@5.95.0(esbuild@0.21.5): dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@webassemblyjs/ast': 1.12.1 '@webassemblyjs/wasm-edit': 1.12.1 '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.11.3 - acorn-import-assertions: 1.9.0(acorn@8.11.3) - browserslist: 4.23.0 - chrome-trace-event: 1.0.3 - enhanced-resolve: 5.16.0 - es-module-lexer: 1.4.1 + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) + browserslist: 4.24.0 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.1 + es-module-lexer: 1.5.4 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -20424,27 +19829,26 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(esbuild@0.21.5)(webpack@5.91.0(esbuild@0.21.5)) - watchpack: 2.4.1 + terser-webpack-plugin: 5.3.10(esbuild@0.21.5)(webpack@5.95.0(esbuild@0.21.5)) + watchpack: 2.4.2 webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - webpack@5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0): + webpack@5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0): dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@webassemblyjs/ast': 1.12.1 '@webassemblyjs/wasm-edit': 1.12.1 '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.11.3 - acorn-import-assertions: 1.9.0(acorn@8.11.3) - browserslist: 4.23.0 - chrome-trace-event: 1.0.3 - enhanced-resolve: 5.16.0 - es-module-lexer: 1.4.1 + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) + browserslist: 4.24.0 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.1 + es-module-lexer: 1.5.4 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -20455,11 +19859,11 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(esbuild@0.21.5)(webpack@5.91.0(esbuild@0.21.5)(webpack-cli@4.10.0)) - watchpack: 2.4.1 + terser-webpack-plugin: 5.3.10(esbuild@0.21.5)(webpack@5.95.0(esbuild@0.21.5)(webpack-cli@4.10.0)) + watchpack: 2.4.2 webpack-sources: 3.2.3 optionalDependencies: - webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0) + webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.95.0) transitivePeerDependencies: - '@swc/core' - esbuild @@ -20503,6 +19907,13 @@ snapshots: is-string: 1.0.7 is-symbol: 1.0.4 + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.3 + which-module@2.0.1: {} which-typed-array@1.1.15: @@ -20521,7 +19932,7 @@ snapshots: dependencies: isexe: 2.0.0 - why-is-node-running@2.2.2: + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 @@ -20532,6 +19943,8 @@ snapshots: wildcard@2.0.1: {} + word-wrap@1.2.5: {} + wordwrap@1.0.0: {} workbox-background-sync@7.1.0: @@ -20543,16 +19956,16 @@ snapshots: dependencies: workbox-core: 7.1.0 - workbox-build@7.1.0(@types/babel__core@7.20.5): + workbox-build@7.1.1(@types/babel__core@7.20.5): dependencies: '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1) - '@babel/core': 7.24.9 - '@babel/preset-env': 7.24.8(@babel/core@7.24.9) - '@babel/runtime': 7.24.8 - '@rollup/plugin-babel': 5.3.1(@babel/core@7.24.9)(@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) - '@rollup/plugin-terser': 0.4.4(rollup@2.79.1) + '@babel/core': 7.25.7 + '@babel/preset-env': 7.25.7(@babel/core@7.25.7) + '@babel/runtime': 7.25.7 + '@rollup/plugin-babel': 5.3.1(@babel/core@7.25.7)(@types/babel__core@7.20.5)(rollup@2.79.2) + '@rollup/plugin-node-resolve': 15.3.0(rollup@2.79.2) + '@rollup/plugin-replace': 2.4.2(rollup@2.79.2) + '@rollup/plugin-terser': 0.4.4(rollup@2.79.2) '@surma/rollup-plugin-off-main-thread': 2.2.3 ajv: 8.17.1 common-tags: 1.8.2 @@ -20561,7 +19974,7 @@ snapshots: glob: 7.2.3 lodash: 4.17.21 pretty-bytes: 5.6.0 - rollup: 2.79.1 + rollup: 2.79.2 source-map: 0.8.0-beta.0 stringify-object: 3.3.0 strip-comments: 2.0.1 @@ -20590,8 +20003,6 @@ snapshots: dependencies: workbox-core: 7.1.0 - workbox-core@7.0.0: {} - workbox-core@7.1.0: {} workbox-expiration@7.1.0: @@ -20644,11 +20055,6 @@ snapshots: workbox-sw@7.1.0: {} - workbox-window@7.0.0: - dependencies: - '@types/trusted-types': 2.0.3 - workbox-core: 7.0.0 - workbox-window@7.1.0: dependencies: '@types/trusted-types': 2.0.7 @@ -20675,7 +20081,7 @@ snapshots: wrap-ansi@9.0.0: dependencies: ansi-styles: 6.2.1 - string-width: 7.1.0 + string-width: 7.2.0 strip-ansi: 7.1.0 wrappy@1.0.2: {} @@ -20692,11 +20098,9 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 - ws@8.13.0: {} + ws@8.17.1: {} - ws@8.16.0: {} - - ws@8.5.0: {} + ws@8.18.0: {} xdg-basedir@5.1.0: {} @@ -20714,11 +20118,11 @@ snapshots: y18n@5.0.8: {} - yallist@3.1.1: {} + yallist@2.1.2: {} - yallist@4.0.0: {} + yallist@3.1.1: {} - yaml@2.4.5: {} + yaml@2.5.1: {} yargs-parser@18.1.3: dependencies: @@ -20746,7 +20150,7 @@ snapshots: yargs@17.7.2: dependencies: cliui: 8.0.1 - escalade: 3.1.2 + escalade: 3.2.0 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 @@ -20760,8 +20164,6 @@ snapshots: yocto-queue@0.1.0: {} - yocto-queue@1.0.0: {} - yocto-queue@1.1.1: {} zwitch@2.0.4: {} diff --git a/renovate.json b/renovate.json index 859cd982c4..5e074f885f 100644 --- a/renovate.json +++ b/renovate.json @@ -35,6 +35,10 @@ { "groupName": "eslint", "matchPackagePatterns": ["eslint"] + }, + { + "groupName": "dompurify", + "matchPackagePatterns": ["dompurify"] } ], "dependencyDashboard": false, diff --git a/scripts/tsc-check.ts b/scripts/tsc-check.ts new file mode 100644 index 0000000000..2e5b3016d1 --- /dev/null +++ b/scripts/tsc-check.ts @@ -0,0 +1,174 @@ +/** + * Verify the as-built tarballs can be imported into a fresh, out-of-tree TypeScript project. + */ + +/* eslint-disable no-console */ +import { mkdtemp, mkdir, writeFile, readFile, readdir, copyFile, rm } from 'node:fs/promises'; +import { execFileSync } from 'child_process'; +import * as path from 'path'; +import { fileURLToPath } from 'url'; +import { tmpdir } from 'node:os'; + +const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file +const __dirname = path.dirname(__filename); // get the name of the directory + +/** + * Packages to build and import + */ +const PACKAGES = { + mermaid: 'mermaid', + '@mermaid-js/layout-elk': 'mermaid-layout-elk', + // TODO: these don't import cleanly yet due to exotic tsconfig.json requirements + // '@mermaid-js/mermaid-zenuml': 'mermaid-zenuml', + // '@mermaid-js/parser': 'parser', +}; + +/** + * Files to create in the temporary package. + */ +const SRC = { + // a minimal description of a buildable package + 'package.json': (tarballs: Record) => + JSON.stringify( + { + dependencies: tarballs, + scripts: { build: 'tsc -b --verbose' }, + devDependencies: { + // these are somewhat-unexpectedly required, and a downstream would need + // to match the real `package.json` values + 'type-fest': '*', + '@types/d3': '^7.4.3', + typescript: '*', + }, + }, + null, + 2 + ), + // a fairly strict TypeScript configuration + 'tsconfig.json': () => + JSON.stringify( + { + compilerOptions: { + allowSyntheticDefaultImports: true, + composite: true, + declaration: true, + esModuleInterop: true, + incremental: true, + lib: ['dom', 'es2020'], + module: 'esnext', + moduleResolution: 'node', + noEmitOnError: true, + noImplicitAny: true, + noUnusedLocals: true, + sourceMap: true, + target: 'es2020', + rootDir: './src', + outDir: './lib', + strict: true, + tsBuildInfoFile: 'lib/.tsbuildinfo', + }, + }, + null, + 2 + ), + // the simplest possible script: will everything even import? + 'src/index.ts': (tarballs) => { + const imports: string[] = []; + const outputs: string[] = []; + let i = 0; + for (const pkg of Object.keys(tarballs)) { + imports.push(`import * as pkg_${i} from '${pkg}';`); + outputs.push(`console.log(pkg_${i});`); + i++; + } + return [...imports, ...outputs].join('\n'); + }, +}; + +/** + * Commands to run after source files are created. + * + * `npm` is used to detect any unwanted `pnpm`-specific runtime "features". + */ +const COMMANDS = [ + ['npm', 'install'], + ['npm', 'run', 'build'], +]; + +/** + * Built files to expect after commands are executed. + */ +const LIB = ['lib/index.js', 'lib/index.js.map', 'lib/index.d.ts', 'lib/.tsbuildinfo']; + +/** + * Run a small out-of-tree build. + */ +async function main() { + console.warn('Checking out-of-tree TypeScript build using', Object.keys(PACKAGES).join('\n')); + const cwd = await mkdtemp(path.join(tmpdir(), 'mermaid-tsc-check-')); + console.warn('... creating temporary folder', cwd); + const tarballs = await buildTarballs(cwd); + + for (const [filename, generate] of Object.entries(SRC)) { + const dest = path.join(cwd, filename); + await mkdir(path.dirname(dest), { recursive: true }); + console.warn('... creating', dest); + const text = generate(tarballs); + await writeFile(dest, text); + console.info(text); + } + + for (const argv of COMMANDS) { + console.warn('... in', cwd); + console.warn('>>>', ...argv); + execFileSync(argv[0], argv.slice(1), { cwd }); + } + + for (const lib of LIB) { + const checkLib = path.join(cwd, lib); + console.warn('... checking built file', checkLib); + await readFile(checkLib, 'utf-8'); + } + + console.warn('... deleting', cwd); + await rm(cwd, { recursive: true, force: true }); + console.warn('... tsc-check OK for\n', Object.keys(PACKAGES).join('\n')); +} + +/** Build all the tarballs. */ +async function buildTarballs(tmp: string): Promise> { + const dist = path.join(tmp, 'dist'); + await mkdir(dist); + const promises: Promise[] = []; + const tarballs: Record = {}; + for (const [pkg, srcPath] of Object.entries(PACKAGES)) { + promises.push(buildOneTarball(pkg, srcPath, dist, tarballs)); + } + await Promise.all(promises); + return tarballs; +} + +/** Build a single tarball. */ +async function buildOneTarball( + pkg: string, + srcPath: string, + dist: string, + tarballs: Record +): Promise { + const cwd = await mkdtemp(path.join(dist, 'pack-')); + const pkgDir = path.join(__dirname, '../packages', srcPath); + const argv = ['pnpm', 'pack', '--pack-destination', cwd]; + console.warn('>>>', ...argv); + execFileSync(argv[0], argv.slice(1), { cwd: pkgDir }); + const built = await readdir(cwd); + const dest = path.join(dist, built[0]); + await copyFile(path.join(cwd, built[0]), dest); + await rm(cwd, { recursive: true, force: true }); + tarballs[pkg] = dest; +} + +void main().catch((err) => { + console.error(err); + console.error('!!! tsc-check FAIL: temp folder left in place. see logs above for failure notes'); + process.exit(1); +}); diff --git a/tests/webpack/package.json b/tests/webpack/package.json index 5d211ca1b4..12bb73195e 100644 --- a/tests/webpack/package.json +++ b/tests/webpack/package.json @@ -1,5 +1,5 @@ { - "name": "webpack", + "name": "@mermaid-js/webpack-test", "version": "1.0.0", "description": "", "private": true, diff --git a/tsconfig.json b/tsconfig.json index 8e044caff5..abc88759b0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -66,7 +66,7 @@ // "newLine": "crlf", /* Set the newline character for emitting files. */ // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + "noEmitOnError": false /* Disable emitting files if any type checking errors are reported. */, // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ @@ -75,7 +75,7 @@ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, - "preserveSymlinks": true /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */, + // "preserveSymlinks": true /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */, "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, /* Type Checking */ diff --git a/vite.config.ts b/vite.config.ts index 87124b9bf5..fb31841d6f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,7 +10,6 @@ export default defineConfig({ plugins: [ jison(), jsonSchemaPlugin(), // handles .schema.yaml JSON Schema files - // @ts-expect-error According to the type definitions, rollup plugins are incompatible with vite typescript({ compilerOptions: { declaration: false } }), ], test: {