diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..dbd559c --- /dev/null +++ b/.env.development @@ -0,0 +1 @@ +VITE_PETSTORE_URL=https://localhost diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..994acdd --- /dev/null +++ b/.env.production @@ -0,0 +1 @@ +VITE_PETSTORE_URL=https://petstore.production diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..9104501 --- /dev/null +++ b/.env.test @@ -0,0 +1 @@ +VITE_PETSTORE_URL=https://petstore.test diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..e5468b4 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,55 @@ +module.exports = { + env: { + browser: true, + es2020: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:import/recommended', + 'plugin:import/typescript', + 'plugin:tailwindcss/recommended', + ], + overrides: [ + { + env: { + node: true, + }, + files: ['.eslintrc.{js,cjs}'], + parserOptions: { + sourceType: 'script', + }, + }, + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + rules: { + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + destructuredArrayIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + 'linebreak-style': ['error', 'unix'], + 'no-constant-condition': ['error', { checkLoops: false }], + quotes: ['error', 'single', { avoidEscape: true }], + semi: ['error', 'always'], + '@typescript-eslint/consistent-type-imports': 'error', + 'no-param-reassign': 'error', + 'no-var': 'error', + 'prefer-const': 'error', + }, + settings: { + 'import/resolver': { + node: { + extensions: ['.cjs', '.d.ts', '.js', '.jsx', '.mjs', '.ts', '.tsx'], + }, + }, + }, +}; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3e2bf3e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,62 @@ +name: CI + +on: + push: + pull_request: + branches: + - master + schedule: + - cron: '0 0 * * *' + +jobs: + node16: + name: Node 16 + runs-on: ubuntu-22.04 + steps: + - name: checkout + uses: actions/checkout@v4 + - name: checkout node + uses: actions/setup-node@v4 + with: + node-version: '16' + - run: npm install -g pnpm@latest + - run: pnpm install + - run: pnpm run build + - run: TZ=Europe/Zurich pnpm test -- --coverage + node18: + name: Node 18 + runs-on: ubuntu-22.04 + steps: + - name: checkout + uses: actions/checkout@v4 + - name: checkout node + uses: actions/setup-node@v4 + with: + node-version: '18' + - run: npm install -g pnpm@latest + - run: pnpm install + - run: pnpm run build + - run: TZ=Europe/Zurich pnpm test -- --coverage + node20: + name: Node 20 + runs-on: ubuntu-22.04 + steps: + - name: checkout + uses: actions/checkout@v4 + - name: checkout node + uses: actions/setup-node@v4 + with: + node-version: '20' + - run: npm install -g pnpm@latest + - run: pnpm install + - run: pnpm run build + - run: TZ=Europe/Zurich pnpm test -- --coverage + - name: coveralls.io + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: sonarcloud.io + uses: sonarsource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..97bee46 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +coverage diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f71a527 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2024 Dominik Zogg + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5698193 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# vue-petstore + +[![CI](https://github.com/chubbyts/vue-petstore/actions/workflows/ci.yml/badge.svg)](https://github.com/chubbyts/vue-petstore/actions/workflows/ci.yml) +[![Coverage Status](https://coveralls.io/repos/github/chubbyts/vue-petstore/badge.svg?branch=master)](https://coveralls.io/github/chubbyts/vue-petstore?branch=master) + +[![bugs](https://sonarcloud.io/api/project_badges/measure?project=chubbyts_vue-petstore&metric=bugs)](https://sonarcloud.io/dashboard?id=chubbyts_vue-petstore) +[![code_smells](https://sonarcloud.io/api/project_badges/measure?project=chubbyts_vue-petstore&metric=code_smells)](https://sonarcloud.io/dashboard?id=chubbyts_vue-petstore) +[![coverage](https://sonarcloud.io/api/project_badges/measure?project=chubbyts_vue-petstore&metric=coverage)](https://sonarcloud.io/dashboard?id=chubbyts_vue-petstore) +[![duplicated_lines_density](https://sonarcloud.io/api/project_badges/measure?project=chubbyts_vue-petstore&metric=duplicated_lines_density)](https://sonarcloud.io/dashboard?id=chubbyts_vue-petstore) +[![ncloc](https://sonarcloud.io/api/project_badges/measure?project=chubbyts_vue-petstore&metric=ncloc)](https://sonarcloud.io/dashboard?id=chubbyts_vue-petstore) +[![sqale_rating](https://sonarcloud.io/api/project_badges/measure?project=chubbyts_vue-petstore&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=chubbyts_vue-petstore) +[![alert_status](https://sonarcloud.io/api/project_badges/measure?project=chubbyts_vue-petstore&metric=alert_status)](https://sonarcloud.io/dashboard?id=chubbyts_vue-petstore) +[![reliability_rating](https://sonarcloud.io/api/project_badges/measure?project=chubbyts_vue-petstore&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=chubbyts_vue-petstore) +[![security_rating](https://sonarcloud.io/api/project_badges/measure?project=chubbyts_vue-petstore&metric=security_rating)](https://sonarcloud.io/dashboard?id=chubbyts_vue-petstore) +[![sqale_index](https://sonarcloud.io/api/project_badges/measure?project=chubbyts_vue-petstore&metric=sqale_index)](https://sonarcloud.io/dashboard?id=chubbyts_vue-petstore) +[![vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=chubbyts_vue-petstore&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=chubbyts_vue-petstore) + +## Description + +This is a vuejs frontend for the petstore skeleton. + +## Scripts + +### Compiles and hot-reloads for development +``` +pnpm start +``` + +### Compiles and minifies for production +``` +pnpm build +``` + +### Run your unit tests +``` +pnpm test +``` + +## Copyright + +2024 Dominik Zogg diff --git a/index.html b/index.html new file mode 100644 index 0000000..ca3961f --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + + Petstore + + + +
+ + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..b9ecc65 --- /dev/null +++ b/package.json @@ -0,0 +1,69 @@ +{ + "name": "vuw-petstore", + "type": "module", + "version": "1.0.0", + "description": "A skeleton vuejs app for the petstore api.", + "keywords": [ + "chubbyts", + "skleton", + "vue" + ], + "author": "Dominik Zogg", + "license": "MIT", + "repository": "chubbyts/vue-petstore", + "scripts": { + "build": "vue-tsc && vite build", + "cs-fix": "prettier --write src *.cjs *.js *.ts", + "cs": "prettier --check src *.cjs *.js *.ts", + "develop": "vite", + "lint-fix": "eslint src *.cjs *.js *.ts --fix", + "lint": "eslint src *.cjs *.js *.ts", + "start": "pnpm i && pnpm cs-fix && pnpm develop", + "test": "vitest" + }, + "dependencies": { + "@chubbyts/chubbyts-throwable-to-error": "^1.2.1", + "@vitejs/plugin-vue-jsx": "^3.1.0", + "autoprefixer": "^10.4.18", + "cross-fetch": "^4.0.0", + "date-fns": "^3.3.1", + "postcss": "^8.4.35", + "qs": "^6.12.0", + "tailwindcss": "^3.4.1", + "vue": "^3.4.21", + "vue-router": "^4.3.0", + "zod": "^3.22.4" + }, + "devDependencies": { + "@chubbyts/chubbyts-function-mock": "^1.4.1", + "@testing-library/jest-dom": "^6.4.2", + "@testing-library/user-event": "^14.5.2", + "@testing-library/vue": "^8.0.2", + "@types/node": "^20.11.25", + "@types/prettier": "^2.7.3", + "@types/qs": "^6.9.12", + "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/parser": "^7.1.1", + "@vitejs/plugin-vue": "^5.0.4", + "@vitest/coverage-v8": "^1.3.1", + "eslint": "^8.57.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-tailwindcss": "^3.14.3", + "eslint-plugin-vue": "^9.23.0", + "jsdom": "^24.0.0", + "prettier": "^2.8.8", + "typescript": "^5.4.2", + "vite": "^5.1.5", + "vitest": "^1.3.1", + "vue-tsc": "^1.8.27" + }, + "engines": { + "node": ">=16" + }, + "prettier": { + "printWidth": 120, + "tabWidth": 2, + "singleQuote": true, + "trailingComma": "all" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..972600c --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4447 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@chubbyts/chubbyts-throwable-to-error': + specifier: ^1.2.1 + version: 1.2.1 + '@vitejs/plugin-vue-jsx': + specifier: ^3.1.0 + version: 3.1.0(vite@5.1.6)(vue@3.4.21) + autoprefixer: + specifier: ^10.4.18 + version: 10.4.18(postcss@8.4.35) + cross-fetch: + specifier: ^4.0.0 + version: 4.0.0 + date-fns: + specifier: ^3.3.1 + version: 3.5.0 + postcss: + specifier: ^8.4.35 + version: 8.4.35 + qs: + specifier: ^6.12.0 + version: 6.12.0 + tailwindcss: + specifier: ^3.4.1 + version: 3.4.1 + vue: + specifier: ^3.4.21 + version: 3.4.21(typescript@5.4.2) + vue-router: + specifier: ^4.3.0 + version: 4.3.0(vue@3.4.21) + zod: + specifier: ^3.22.4 + version: 3.22.4 + +devDependencies: + '@chubbyts/chubbyts-function-mock': + specifier: ^1.4.1 + version: 1.4.1 + '@testing-library/jest-dom': + specifier: ^6.4.2 + version: 6.4.2(vitest@1.4.0) + '@testing-library/user-event': + specifier: ^14.5.2 + version: 14.5.2(@testing-library/dom@9.3.4) + '@testing-library/vue': + specifier: ^8.0.2 + version: 8.0.2(vue@3.4.21) + '@types/node': + specifier: ^20.11.25 + version: 20.11.28 + '@types/prettier': + specifier: ^2.7.3 + version: 2.7.3 + '@types/qs': + specifier: ^6.9.12 + version: 6.9.12 + '@typescript-eslint/eslint-plugin': + specifier: ^7.1.1 + version: 7.2.0(@typescript-eslint/parser@7.2.0)(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/parser': + specifier: ^7.1.1 + version: 7.2.0(eslint@8.57.0)(typescript@5.4.2) + '@vitejs/plugin-vue': + specifier: ^5.0.4 + version: 5.0.4(vite@5.1.6)(vue@3.4.21) + '@vitest/coverage-v8': + specifier: ^1.3.1 + version: 1.4.0(vitest@1.4.0) + eslint: + specifier: ^8.57.0 + version: 8.57.0 + eslint-plugin-import: + specifier: ^2.29.1 + version: 2.29.1(@typescript-eslint/parser@7.2.0)(eslint@8.57.0) + eslint-plugin-tailwindcss: + specifier: ^3.14.3 + version: 3.15.1(tailwindcss@3.4.1) + eslint-plugin-vue: + specifier: ^9.23.0 + version: 9.23.0(eslint@8.57.0) + jsdom: + specifier: ^24.0.0 + version: 24.0.0 + prettier: + specifier: ^2.8.8 + version: 2.8.8 + typescript: + specifier: ^5.4.2 + version: 5.4.2 + vite: + specifier: ^5.1.5 + version: 5.1.6(@types/node@20.11.28) + vitest: + specifier: ^1.3.1 + version: 1.4.0(@types/node@20.11.28)(jsdom@24.0.0) + vue-tsc: + specifier: ^1.8.27 + version: 1.8.27(typescript@5.4.2) + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@adobe/css-tools@4.3.3: + resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==} + dev: true + + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + /@ampproject/remapping@2.3.0: + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + /@babel/code-frame@7.23.5: + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.23.4 + chalk: 2.4.2 + + /@babel/compat-data@7.23.5: + resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/core@7.24.0: + resolution: {integrity: sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) + '@babel/helpers': 7.24.0 + '@babel/parser': 7.24.0 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/generator@7.23.6: + resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + dev: false + + /@babel/helper-annotate-as-pure@7.22.5: + resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: false + + /@babel/helper-compilation-targets@7.23.6: + resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.23.0 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: false + + /@babel/helper-create-class-features-plugin@7.24.0(@babel/core@7.24.0): + resolution: {integrity: sha512-QAH+vfvts51BCsNZ2PhY6HAggnlS6omLLFTsIpeqZk/MmJ6cW7tgz5yRv0fMJThcr6FmbMrENh1RgrWPTYA76g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + semver: 6.3.1 + dev: false + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.24.0 + '@babel/types': 7.24.0 + dev: false + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: false + + /@babel/helper-member-expression-to-functions@7.23.0: + resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: false + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: false + + /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: false + + /@babel/helper-optimise-call-expression@7.22.5: + resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: false + + /@babel/helper-plugin-utils@7.24.0: + resolution: {integrity: sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-replace-supers@7.22.20(@babel/core@7.24.0): + resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + dev: false + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: false + + /@babel/helper-skip-transparent-expression-wrappers@7.22.5: + resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: false + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: false + + /@babel/helper-string-parser@7.23.4: + resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} + engines: {node: '>=6.9.0'} + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + + /@babel/helper-validator-option@7.23.5: + resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helpers@7.24.0: + resolution: {integrity: sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/highlight@7.23.4: + resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + + /@babel/parser@7.24.0: + resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.24.0 + + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.0): + resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.24.0 + dev: false + + /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.24.0): + resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.24.0 + dev: false + + /@babel/plugin-transform-typescript@7.23.6(@babel/core@7.24.0): + resolution: {integrity: sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.24.0(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.24.0) + dev: false + + /@babel/runtime@7.24.0: + resolution: {integrity: sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 + dev: true + + /@babel/template@7.24.0: + resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/parser': 7.24.0 + '@babel/types': 7.24.0 + dev: false + + /@babel/traverse@7.24.0: + resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.24.0 + '@babel/types': 7.24.0 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/types@7.24.0: + resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.23.4 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + + /@chubbyts/chubbyts-function-mock@1.4.1: + resolution: {integrity: sha512-igr6L85ytNcp5LGUXDnluOuSe+uE06Qg6+8VFYAWkdN8+7yom6SEPk+JlumfhAWDeK7xRMTMJJ7oANbueuZipw==} + engines: {node: '>=16'} + dev: true + + /@chubbyts/chubbyts-throwable-to-error@1.2.1: + resolution: {integrity: sha512-sqmz+niGqD2gujhK01//c3kQjhL3l0S+pBVWWGUORP1kgyjuevUBaMvP1pajv7gH3UwMoop5LyjakbRv5Berow==} + engines: {node: '>=16'} + dev: false + + /@esbuild/aix-ppc64@0.19.12: + resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + optional: true + + /@esbuild/android-arm64@0.19.12: + resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + optional: true + + /@esbuild/android-arm@0.19.12: + resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + optional: true + + /@esbuild/android-x64@0.19.12: + resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + optional: true + + /@esbuild/darwin-arm64@0.19.12: + resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optional: true + + /@esbuild/darwin-x64@0.19.12: + resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optional: true + + /@esbuild/freebsd-arm64@0.19.12: + resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + optional: true + + /@esbuild/freebsd-x64@0.19.12: + resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + optional: true + + /@esbuild/linux-arm64@0.19.12: + resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-arm@0.19.12: + resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-ia32@0.19.12: + resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-loong64@0.19.12: + resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-mips64el@0.19.12: + resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-ppc64@0.19.12: + resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-riscv64@0.19.12: + resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-s390x@0.19.12: + resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-x64@0.19.12: + resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/netbsd-x64@0.19.12: + resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + optional: true + + /@esbuild/openbsd-x64@0.19.12: + resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + optional: true + + /@esbuild/sunos-x64@0.19.12: + resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + optional: true + + /@esbuild/win32-arm64@0.19.12: + resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + optional: true + + /@esbuild/win32-ia32@0.19.12: + resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + optional: true + + /@esbuild/win32-x64@0.19.12: + resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.57.0: + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.14: + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.2 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.2: + resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + dev: true + + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + + /@istanbuljs/schema@0.1.3: + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + dev: true + + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jridgewell/gen-mapping@0.3.5: + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + + /@jridgewell/resolve-uri@3.1.2: + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + /@jridgewell/set-array@1.2.1: + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + /@jridgewell/trace-mapping@0.3.25: + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + /@one-ini/wasm@0.1.1: + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + dev: true + + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + optional: true + + /@rollup/rollup-android-arm-eabi@4.13.0: + resolution: {integrity: sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==} + cpu: [arm] + os: [android] + requiresBuild: true + optional: true + + /@rollup/rollup-android-arm64@4.13.0: + resolution: {integrity: sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==} + cpu: [arm64] + os: [android] + requiresBuild: true + optional: true + + /@rollup/rollup-darwin-arm64@4.13.0: + resolution: {integrity: sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optional: true + + /@rollup/rollup-darwin-x64@4.13.0: + resolution: {integrity: sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==} + cpu: [x64] + os: [darwin] + requiresBuild: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.13.0: + resolution: {integrity: sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==} + cpu: [arm] + os: [linux] + requiresBuild: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.13.0: + resolution: {integrity: sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.13.0: + resolution: {integrity: sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.13.0: + resolution: {integrity: sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.13.0: + resolution: {integrity: sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.13.0: + resolution: {integrity: sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.13.0: + resolution: {integrity: sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==} + cpu: [arm64] + os: [win32] + requiresBuild: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.13.0: + resolution: {integrity: sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==} + cpu: [ia32] + os: [win32] + requiresBuild: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.13.0: + resolution: {integrity: sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==} + cpu: [x64] + os: [win32] + requiresBuild: true + optional: true + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@testing-library/dom@9.3.4: + resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==} + engines: {node: '>=14'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/runtime': 7.24.0 + '@types/aria-query': 5.0.4 + aria-query: 5.1.3 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + dev: true + + /@testing-library/jest-dom@6.4.2(vitest@1.4.0): + resolution: {integrity: sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + peerDependencies: + '@jest/globals': '>= 28' + '@types/bun': latest + '@types/jest': '>= 28' + jest: '>= 28' + vitest: '>= 0.32' + peerDependenciesMeta: + '@jest/globals': + optional: true + '@types/bun': + optional: true + '@types/jest': + optional: true + jest: + optional: true + vitest: + optional: true + dependencies: + '@adobe/css-tools': 4.3.3 + '@babel/runtime': 7.24.0 + aria-query: 5.3.0 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash: 4.17.21 + redent: 3.0.0 + vitest: 1.4.0(@types/node@20.11.28)(jsdom@24.0.0) + dev: true + + /@testing-library/user-event@14.5.2(@testing-library/dom@9.3.4): + resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + dependencies: + '@testing-library/dom': 9.3.4 + dev: true + + /@testing-library/vue@8.0.2(vue@3.4.21): + resolution: {integrity: sha512-A8wWX+qQn0o0izpQWnGCpwQt8wAdpsVP8vPP2h5Q/jcGhZ5yKXz9PPUqhQv+45LTFaWlyRf8bArTVaB/KFFd5A==} + engines: {node: '>=14'} + peerDependencies: + '@vue/compiler-sfc': '>= 3' + vue: '>= 3' + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true + dependencies: + '@babel/runtime': 7.24.0 + '@testing-library/dom': 9.3.4 + '@vue/test-utils': 2.4.5 + vue: 3.4.21(typescript@5.4.2) + dev: true + + /@types/aria-query@5.0.4: + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + dev: true + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + /@types/istanbul-lib-coverage@2.0.6: + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + dev: true + + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + + /@types/json5@0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + + /@types/node@20.11.28: + resolution: {integrity: sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==} + dependencies: + undici-types: 5.26.5 + + /@types/prettier@2.7.3: + resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} + dev: true + + /@types/qs@6.9.12: + resolution: {integrity: sha512-bZcOkJ6uWrL0Qb2NAWKa7TBU+mJHPzhx9jjLL1KHF+XpzEcR7EXHvjbHlGtR/IsP1vyPrehuS6XqkmaePy//mg==} + dev: true + + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + dev: true + + /@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0)(eslint@8.57.0)(typescript@5.4.2): + resolution: {integrity: sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/scope-manager': 7.2.0 + '@typescript-eslint/type-utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/visitor-keys': 7.2.0 + debug: 4.3.4 + eslint: 8.57.0 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare: 1.4.0 + semver: 7.6.0 + ts-api-utils: 1.3.0(typescript@5.4.2) + typescript: 5.4.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.2): + resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 7.2.0 + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) + '@typescript-eslint/visitor-keys': 7.2.0 + debug: 4.3.4 + eslint: 8.57.0 + typescript: 5.4.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@7.2.0: + resolution: {integrity: sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/visitor-keys': 7.2.0 + dev: true + + /@typescript-eslint/type-utils@7.2.0(eslint@8.57.0)(typescript@5.4.2): + resolution: {integrity: sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) + '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) + debug: 4.3.4 + eslint: 8.57.0 + ts-api-utils: 1.3.0(typescript@5.4.2) + typescript: 5.4.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@7.2.0: + resolution: {integrity: sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@7.2.0(typescript@5.4.2): + resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/visitor-keys': 7.2.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.6.0 + ts-api-utils: 1.3.0(typescript@5.4.2) + typescript: 5.4.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@7.2.0(eslint@8.57.0)(typescript@5.4.2): + resolution: {integrity: sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^8.56.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 7.2.0 + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) + eslint: 8.57.0 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@7.2.0: + resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 7.2.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /@vitejs/plugin-vue-jsx@3.1.0(vite@5.1.6)(vue@3.4.21): + resolution: {integrity: sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.0.0 || ^5.0.0 + vue: ^3.0.0 + dependencies: + '@babel/core': 7.24.0 + '@babel/plugin-transform-typescript': 7.23.6(@babel/core@7.24.0) + '@vue/babel-plugin-jsx': 1.2.1(@babel/core@7.24.0) + vite: 5.1.6(@types/node@20.11.28) + vue: 3.4.21(typescript@5.4.2) + transitivePeerDependencies: + - supports-color + dev: false + + /@vitejs/plugin-vue@5.0.4(vite@5.1.6)(vue@3.4.21): + resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 + vue: ^3.2.25 + dependencies: + vite: 5.1.6(@types/node@20.11.28) + vue: 3.4.21(typescript@5.4.2) + dev: true + + /@vitest/coverage-v8@1.4.0(vitest@1.4.0): + resolution: {integrity: sha512-4hDGyH1SvKpgZnIByr9LhGgCEuF9DKM34IBLCC/fVfy24Z3+PZ+Ii9hsVBsHvY1umM1aGPEjceRkzxCfcQ10wg==} + peerDependencies: + vitest: 1.4.0 + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 0.2.3 + debug: 4.3.4 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.4 + istanbul-reports: 3.1.7 + magic-string: 0.30.8 + magicast: 0.3.3 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 2.0.0 + test-exclude: 6.0.0 + v8-to-istanbul: 9.2.0 + vitest: 1.4.0(@types/node@20.11.28)(jsdom@24.0.0) + transitivePeerDependencies: + - supports-color + dev: true + + /@vitest/expect@1.4.0: + resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==} + dependencies: + '@vitest/spy': 1.4.0 + '@vitest/utils': 1.4.0 + chai: 4.4.1 + dev: true + + /@vitest/runner@1.4.0: + resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==} + dependencies: + '@vitest/utils': 1.4.0 + p-limit: 5.0.0 + pathe: 1.1.2 + dev: true + + /@vitest/snapshot@1.4.0: + resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==} + dependencies: + magic-string: 0.30.8 + pathe: 1.1.2 + pretty-format: 29.7.0 + dev: true + + /@vitest/spy@1.4.0: + resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==} + dependencies: + tinyspy: 2.2.1 + dev: true + + /@vitest/utils@1.4.0: + resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==} + dependencies: + diff-sequences: 29.6.3 + estree-walker: 3.0.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: true + + /@volar/language-core@1.11.1: + resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==} + dependencies: + '@volar/source-map': 1.11.1 + dev: true + + /@volar/source-map@1.11.1: + resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==} + dependencies: + muggle-string: 0.3.1 + dev: true + + /@volar/typescript@1.11.1: + resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==} + dependencies: + '@volar/language-core': 1.11.1 + path-browserify: 1.0.1 + dev: true + + /@vue/babel-helper-vue-transform-on@1.2.1: + resolution: {integrity: sha512-jtEXim+pfyHWwvheYwUwSXm43KwQo8nhOBDyjrUITV6X2tB7lJm6n/+4sqR8137UVZZul5hBzWHdZ2uStYpyRQ==} + dev: false + + /@vue/babel-plugin-jsx@1.2.1(@babel/core@7.24.0): + resolution: {integrity: sha512-Yy9qGktktXhB39QE99So/BO2Uwm/ZG+gpL9vMg51ijRRbINvgbuhyJEi4WYmGRMx/MSTfK0xjgZ3/MyY+iLCEg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + peerDependenciesMeta: + '@babel/core': + optional: true + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0) + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 + '@vue/babel-helper-vue-transform-on': 1.2.1 + '@vue/babel-plugin-resolve-type': 1.2.1(@babel/core@7.24.0) + camelcase: 6.3.0 + html-tags: 3.3.1 + svg-tags: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@vue/babel-plugin-resolve-type@1.2.1(@babel/core@7.24.0): + resolution: {integrity: sha512-IOtnI7pHunUzHS/y+EG/yPABIAp0VN8QhQ0UCS09jeMVxgAnI9qdOzO85RXdQGxq+aWCdv8/+k3W0aYO6j/8fQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/core': 7.24.0 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/parser': 7.24.0 + '@vue/compiler-sfc': 3.4.21 + dev: false + + /@vue/compiler-core@3.4.21: + resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==} + dependencies: + '@babel/parser': 7.24.0 + '@vue/shared': 3.4.21 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.0.2 + + /@vue/compiler-dom@3.4.21: + resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==} + dependencies: + '@vue/compiler-core': 3.4.21 + '@vue/shared': 3.4.21 + + /@vue/compiler-sfc@3.4.21: + resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==} + dependencies: + '@babel/parser': 7.24.0 + '@vue/compiler-core': 3.4.21 + '@vue/compiler-dom': 3.4.21 + '@vue/compiler-ssr': 3.4.21 + '@vue/shared': 3.4.21 + estree-walker: 2.0.2 + magic-string: 0.30.8 + postcss: 8.4.35 + source-map-js: 1.0.2 + + /@vue/compiler-ssr@3.4.21: + resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==} + dependencies: + '@vue/compiler-dom': 3.4.21 + '@vue/shared': 3.4.21 + + /@vue/devtools-api@6.6.1: + resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==} + dev: false + + /@vue/language-core@1.8.27(typescript@5.4.2): + resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@volar/language-core': 1.11.1 + '@volar/source-map': 1.11.1 + '@vue/compiler-dom': 3.4.21 + '@vue/shared': 3.4.21 + computeds: 0.0.1 + minimatch: 9.0.3 + muggle-string: 0.3.1 + path-browserify: 1.0.1 + typescript: 5.4.2 + vue-template-compiler: 2.7.16 + dev: true + + /@vue/reactivity@3.4.21: + resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==} + dependencies: + '@vue/shared': 3.4.21 + + /@vue/runtime-core@3.4.21: + resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==} + dependencies: + '@vue/reactivity': 3.4.21 + '@vue/shared': 3.4.21 + + /@vue/runtime-dom@3.4.21: + resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==} + dependencies: + '@vue/runtime-core': 3.4.21 + '@vue/shared': 3.4.21 + csstype: 3.1.3 + + /@vue/server-renderer@3.4.21(vue@3.4.21): + resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==} + peerDependencies: + vue: 3.4.21 + dependencies: + '@vue/compiler-ssr': 3.4.21 + '@vue/shared': 3.4.21 + vue: 3.4.21(typescript@5.4.2) + + /@vue/shared@3.4.21: + resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==} + + /@vue/test-utils@2.4.5: + resolution: {integrity: sha512-oo2u7vktOyKUked36R93NB7mg2B+N7Plr8lxp2JBGwr18ch6EggFjixSCdIVVLkT6Qr0z359Xvnafc9dcKyDUg==} + dependencies: + js-beautify: 1.15.1 + vue-component-type-helpers: 2.0.6 + dev: true + + /abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + + /acorn-jsx@5.3.2(acorn@8.11.3): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.11.3 + dev: true + + /acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /agent-base@7.1.0: + resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} + engines: {node: '>= 14'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /aria-query@5.1.3: + resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + dependencies: + deep-equal: 2.2.3 + dev: true + + /aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: true + + /array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + is-array-buffer: 3.0.4 + dev: true + + /array-includes@3.1.7: + resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + get-intrinsic: 1.2.4 + is-string: 1.0.7 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.filter@1.0.3: + resolution: {integrity: sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-array-method-boxes-properly: 1.0.0 + is-string: 1.0.7 + dev: true + + /array.prototype.findlastindex@1.2.4: + resolution: {integrity: sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 + dev: true + + /arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 + dev: true + + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: true + + /autoprefixer@10.4.18(postcss@8.4.35): + resolution: {integrity: sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.23.0 + caniuse-lite: 1.0.30001597 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + dev: false + + /available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + dependencies: + possible-typed-array-names: 1.0.0 + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + /binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + /boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + + /browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001597 + electron-to-chromium: 1.4.708 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.23.0) + dev: false + + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + + /call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: false + + /caniuse-lite@1.0.30001597: + resolution: {integrity: sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==} + dev: false + + /chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.3 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + /chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: true + + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true + + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + /computeds@0.0.1: + resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: true + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + /cross-fetch@4.0.0: + resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + dev: false + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + /css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + dev: true + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + /cssstyle@4.0.1: + resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} + engines: {node: '>=18'} + dependencies: + rrweb-cssom: 0.6.0 + dev: true + + /csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + /data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + dev: true + + /date-fns@3.5.0: + resolution: {integrity: sha512-a+DwyXn7NOfdJireCzAA0B9p7jIXEu/Q9JKCyMYvH6+0vPUNbQceA0neXrdfJ/xzl3mhOh5vibQQ3936Tssm6A==} + dev: false + + /de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + dev: true + + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + + /decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + dev: true + + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + + /deep-equal@2.2.3: + resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} + engines: {node: '>= 0.4'} + 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.2 + side-channel: 1.0.6 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.2 + which-typed-array: 1.1.15 + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + dev: true + + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: true + + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: true + + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dev: true + + /dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dev: true + + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + /editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.6.0 + dev: true + + /electron-to-chromium@1.4.708: + resolution: {integrity: sha512-iWgEEvREL4GTXXHKohhh33+6Y8XkPI5eHihDmm8zUk5Zo7HICEW+wI/j5kJ2tbuNUCXJ/sNXa03ajW635DiJXA==} + dev: false + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + /es-abstract@1.22.5: + resolution: {integrity: sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.3 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.8 + string.prototype.trimend: 1.0.7 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.5 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.15 + dev: true + + /es-array-method-boxes-properly@1.0.0: + resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} + dev: true + + /es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + /es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + 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 + dev: true + + /es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + dev: true + + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + dependencies: + hasown: 2.0.2 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /esbuild@0.19.12: + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + 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 + + /escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + dev: false + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + dependencies: + debug: 3.2.7 + is-core-module: 2.13.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): + resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.2) + debug: 3.2.7 + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0)(eslint@8.57.0): + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.2) + array-includes: 3.1.7 + array.prototype.findlastindex: 1.2.4 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + hasown: 2.0.2 + is-core-module: 2.13.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.7 + object.groupby: 1.0.2 + object.values: 1.1.7 + semver: 6.3.1 + tsconfig-paths: 3.15.0 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-tailwindcss@3.15.1(tailwindcss@3.4.1): + resolution: {integrity: sha512-4RXRMIaMG07C2TBEW1k0VM4+dDazz1kxcZhkK4zirvmHGZTA4jnlSO2kq5mamuSPi+Wo17dh2SlC8IyFBuCd7Q==} + engines: {node: '>=12.13.0'} + peerDependencies: + tailwindcss: ^3.4.0 + dependencies: + fast-glob: 3.3.2 + postcss: 8.4.35 + tailwindcss: 3.4.1 + dev: true + + /eslint-plugin-vue@9.23.0(eslint@8.57.0): + resolution: {integrity: sha512-Bqd/b7hGYGrlV+wP/g77tjyFmp81lh5TMw0be9093X02SyelxRRfCI6/IsGq/J7Um0YwB9s0Ry0wlFyjPdmtUw==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + eslint: 8.57.0 + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 6.0.16 + semver: 7.6.0 + vue-eslint-parser: 9.4.2(eslint@8.57.0) + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) + eslint-visitor-keys: 3.4.3 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + /estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + dependencies: + '@types/estree': 1.0.5 + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + dependencies: + reusify: 1.0.4 + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.2.0 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + dev: true + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + + /fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + dev: false + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + optional: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: false + + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + dev: true + + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: true + + /get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.3 + minipass: 7.0.4 + path-scurry: 1.10.1 + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: false + + /globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.4 + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + dependencies: + es-define-property: 1.0.0 + + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + /has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + + /he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: true + + /html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + dependencies: + whatwg-encoding: 3.1.1 + dev: true + + /html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + dev: true + + /html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + dev: false + + /http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /https-proxy-agent@7.0.4: + resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: true + + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true + + /internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.0.6 + dev: true + + /is-arguments@1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + dev: true + + /is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.3.0 + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.2 + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + + /is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + dev: true + + /is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + dev: true + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + dev: true + + /is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + dev: true + + /is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + dev: true + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.15 + dev: true + + /is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.7 + dev: true + + /is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + dev: true + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + /istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + dev: true + + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + dev: true + + /istanbul-lib-source-maps@5.0.4: + resolution: {integrity: sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==} + engines: {node: '>=10'} + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + debug: 4.3.4 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + dev: true + + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + + /js-beautify@1.15.1: + resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==} + engines: {node: '>=14'} + hasBin: true + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.3.10 + js-cookie: 3.0.5 + nopt: 7.2.0 + dev: true + + /js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-tokens@8.0.3: + resolution: {integrity: sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==} + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsdom@24.0.0: + resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + cssstyle: 4.0.1 + 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 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.7 + parse5: 7.1.2 + rrweb-cssom: 0.6.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.3 + 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 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: false + + /jsonc-parser@3.2.1: + resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + /lilconfig@3.1.1: + resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} + engines: {node: '>=14'} + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + /local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + dependencies: + mlly: 1.6.1 + pkg-types: 1.0.3 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /lru-cache@10.2.0: + resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} + engines: {node: 14 || >=16.14} + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: false + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + dev: true + + /magic-string@0.30.8: + resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + + /magicast@0.3.3: + resolution: {integrity: sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==} + dependencies: + '@babel/parser': 7.24.0 + '@babel/types': 7.24.0 + source-map-js: 1.0.2 + dev: true + + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + dependencies: + semver: 7.6.0 + dev: true + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: true + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: true + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + engines: {node: '>=16 || 14 >=14.17'} + + /mlly@1.6.1: + resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==} + dependencies: + acorn: 8.11.3 + pathe: 1.1.2 + pkg-types: 1.0.3 + ufo: 1.5.1 + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /muggle-string@0.3.1: + resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} + dev: true + + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: 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 + dependencies: + whatwg-url: 5.0.0 + dev: false + + /node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + dev: false + + /nopt@7.2.0: + resolution: {integrity: sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + dependencies: + abbrev: 2.0.0 + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + /normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: false + + /npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + + /nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + dependencies: + boolbase: 1.0.0 + dev: true + + /nwsapi@2.2.7: + resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} + dev: true + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + + /object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.fromentries@2.0.7: + resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + dev: true + + /object.groupby@1.0.2: + resolution: {integrity: sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==} + dependencies: + array.prototype.filter: 1.0.3 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + dev: true + + /object.values@1.1.7: + resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + dependencies: + yocto-queue: 1.0.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.5.0 + dev: true + + /path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + /path-scurry@1.10.1: + resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.2.0 + minipass: 7.0.4 + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + dev: true + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.1 + mlly: 1.6.1 + pathe: 1.1.2 + dev: true + + /possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + dev: true + + /postcss-import@15.1.0(postcss@8.4.35): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.35 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + /postcss-js@4.0.1(postcss@8.4.35): + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.35 + + /postcss-load-config@4.0.2(postcss@8.4.35): + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 3.1.1 + postcss: 8.4.35 + yaml: 2.4.1 + + /postcss-nested@6.0.1(postcss@8.4.35): + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.35 + postcss-selector-parser: 6.0.16 + + /postcss-selector-parser@6.0.16: + resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + /postcss@8.4.35: + resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + dev: true + + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: true + + /psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + dev: true + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + + /qs@6.12.0: + resolution: {integrity: sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.6 + dev: false + + /querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + /react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + dev: true + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + + /redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + dev: true + + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + dev: true + + /regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 + dev: true + + /requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup@4.13.0: + resolution: {integrity: sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.13.0 + '@rollup/rollup-android-arm64': 4.13.0 + '@rollup/rollup-darwin-arm64': 4.13.0 + '@rollup/rollup-darwin-x64': 4.13.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.13.0 + '@rollup/rollup-linux-arm64-gnu': 4.13.0 + '@rollup/rollup-linux-arm64-musl': 4.13.0 + '@rollup/rollup-linux-riscv64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-musl': 4.13.0 + '@rollup/rollup-win32-arm64-msvc': 4.13.0 + '@rollup/rollup-win32-ia32-msvc': 4.13.0 + '@rollup/rollup-win32-x64-msvc': 4.13.0 + fsevents: 2.3.3 + + /rrweb-cssom@0.6.0: + resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + + /safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + + /safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-regex: 1.1.4 + dev: true + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true + + /saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + dependencies: + xmlchars: 2.2.0 + dev: true + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + /semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + /set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + /side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 + + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + + /std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + dev: true + + /stop-iteration-iterator@1.0.0: + resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} + engines: {node: '>= 0.4'} + dependencies: + internal-slot: 1.0.7 + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + /string.prototype.trim@1.2.8: + resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + dev: true + + /string.prototype.trimend@1.0.7: + resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + dev: true + + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /strip-literal@2.0.0: + resolution: {integrity: sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==} + dependencies: + js-tokens: 8.0.3 + dev: true + + /sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.3.10 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + /svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + dev: false + + /symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + dev: true + + /tailwindcss@3.4.1: + resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.0 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.35 + postcss-import: 15.1.0(postcss@8.4.35) + postcss-js: 4.0.1(postcss@8.4.35) + postcss-load-config: 4.0.2(postcss@8.4.35) + postcss-nested: 6.0.1(postcss@8.4.35) + postcss-selector-parser: 6.0.16 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + /test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + + /tinybench@2.6.0: + resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==} + dev: true + + /tinypool@0.8.2: + resolution: {integrity: sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + + /tough-cookie@4.1.3: + resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==} + engines: {node: '>=6'} + dependencies: + psl: 1.9.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + dev: true + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: false + + /tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + dependencies: + punycode: 2.3.1 + dev: true + + /ts-api-utils@1.3.0(typescript@5.4.2): + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.4.2 + dev: true + + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + /tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 + dev: true + + /typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + dev: true + + /typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + dev: true + + /typed-array-length@1.0.5: + resolution: {integrity: sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 + dev: true + + /typescript@5.4.2: + resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + engines: {node: '>=14.17'} + hasBin: true + + /ufo@1.5.1: + resolution: {integrity: sha512-HGyF79+/qZ4soRvM+nHERR2pJ3VXDZ/8sL1uLahdgEDf580NkgiWOxLk33FetExqOWp352JZRsgXbG/4MaGOSg==} + dev: true + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.7 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + /universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + dev: true + + /update-browserslist-db@1.0.13(browserslist@4.23.0): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.23.0 + escalade: 3.1.2 + picocolors: 1.0.0 + dev: false + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + + /url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + /v8-to-istanbul@9.2.0: + resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} + engines: {node: '>=10.12.0'} + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + dev: true + + /vite-node@1.4.0(@types/node@20.11.28): + resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.2 + picocolors: 1.0.0 + vite: 5.1.6(@types/node@20.11.28) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite@5.1.6(@types/node@20.11.28): + resolution: {integrity: sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.11.28 + esbuild: 0.19.12 + postcss: 8.4.35 + rollup: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + + /vitest@1.4.0(@types/node@20.11.28)(jsdom@24.0.0): + resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} + 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.4.0 + '@vitest/ui': 1.4.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/node': 20.11.28 + '@vitest/expect': 1.4.0 + '@vitest/runner': 1.4.0 + '@vitest/snapshot': 1.4.0 + '@vitest/spy': 1.4.0 + '@vitest/utils': 1.4.0 + acorn-walk: 8.3.2 + chai: 4.4.1 + debug: 4.3.4 + execa: 8.0.1 + jsdom: 24.0.0 + local-pkg: 0.5.0 + magic-string: 0.30.8 + pathe: 1.1.2 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 2.0.0 + tinybench: 2.6.0 + tinypool: 0.8.2 + vite: 5.1.6(@types/node@20.11.28) + vite-node: 1.4.0(@types/node@20.11.28) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vue-component-type-helpers@2.0.6: + resolution: {integrity: sha512-qdGXCtoBrwqk1BT6r2+1Wcvl583ZVkuSZ3or7Y1O2w5AvWtlvvxwjGhmz5DdPJS9xqRdDlgTJ/38ehWnEi0tFA==} + dev: true + + /vue-eslint-parser@9.4.2(eslint@8.57.0): + resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + debug: 4.3.4 + eslint: 8.57.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + lodash: 4.17.21 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + dev: true + + /vue-router@4.3.0(vue@3.4.21): + resolution: {integrity: sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==} + peerDependencies: + vue: ^3.2.0 + dependencies: + '@vue/devtools-api': 6.6.1 + vue: 3.4.21(typescript@5.4.2) + dev: false + + /vue-template-compiler@2.7.16: + resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + dev: true + + /vue-tsc@1.8.27(typescript@5.4.2): + resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==} + hasBin: true + peerDependencies: + typescript: '*' + dependencies: + '@volar/typescript': 1.11.1 + '@vue/language-core': 1.8.27(typescript@5.4.2) + semver: 7.6.0 + typescript: 5.4.2 + dev: true + + /vue@3.4.21(typescript@5.4.2): + resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@vue/compiler-dom': 3.4.21 + '@vue/compiler-sfc': 3.4.21 + '@vue/runtime-dom': 3.4.21 + '@vue/server-renderer': 3.4.21(vue@3.4.21) + '@vue/shared': 3.4.21 + typescript: 5.4.2 + + /w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + dependencies: + xml-name-validator: 5.0.0 + dev: true + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: false + + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: true + + /whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + dependencies: + iconv-lite: 0.6.3 + dev: true + + /whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + dev: true + + /whatwg-url@14.0.0: + resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} + engines: {node: '>=18'} + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + dev: true + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.3 + dev: true + + /which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.2 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /ws@8.16.0: + resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} + 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 + dev: true + + /xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + dev: true + + /xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + dev: true + + /xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + dev: true + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: false + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yaml@2.4.1: + resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==} + engines: {node: '>= 14'} + hasBin: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true + + /zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + dev: false diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..2562819 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,7 @@ +export default { + plugins: { + 'tailwindcss/nesting': {}, + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..fd3d759 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,10 @@ +sonar.organization=chubbyts +sonar.projectKey=chubbyts_vue-petstore +sonar.projectName=vue-petstore + +sonar.sources=src +sonar.exclusions=tests/** +sonar.tests=tests +sonar.language=typescript +sonar.sourceEncoding=UTF-8 +sonar.javascript.lcov.reportPaths=coverage/lcov.info diff --git a/src/app.tsx b/src/app.tsx new file mode 100644 index 0000000..2e455c1 --- /dev/null +++ b/src/app.tsx @@ -0,0 +1,51 @@ +import { defineComponent, ref } from 'vue'; +import { RouterLink, RouterView } from 'vue-router'; + +const App = defineComponent(() => { + const displayMenu = ref(false); + + const toggleMenu = () => { + displayMenu.value = !displayMenu.value; + }; + + return () => ( +
+ + +
+ +
+
+ ); +}); + +export default App; diff --git a/src/client/client.ts b/src/client/client.ts new file mode 100644 index 0000000..2c7e17a --- /dev/null +++ b/src/client/client.ts @@ -0,0 +1,213 @@ +import { throwableToError } from '@chubbyts/chubbyts-throwable-to-error/dist/throwable-to-error'; +import type { HttpError } from './error'; +import { BadRequest, InternalServerError, NetworkError, NotFound, UnprocessableEntity } from './error'; +import qs from 'qs'; +import type { z } from 'zod'; + +export type Fetch = (input: RequestInfo | URL, init?: RequestInit) => Promise; + +export type ListClient = ( + modelListRequest: ModelListRequest, +) => Promise; + +export const createListClient = ( + fetch: Fetch, + url: string, + modelListRequestSchema: ModelListRequestSchema, + modelListResponseSchema: ModelListResponseSchema, +): ListClient, z.infer> => { + return async ( + modelListRequest: z.infer, + ): Promise | HttpError> => { + try { + const response: Response = await fetch(`${url}?${qs.stringify(modelListRequestSchema.parse(modelListRequest))}`, { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }); + + const json = await response.json(); + + if (200 === response.status) { + return modelListResponseSchema.parse(json); + } + + if (400 === response.status) { + return new BadRequest({ ...json }); + } + + if (500 === response.status) { + return new InternalServerError({ ...json }); + } + } catch (error) { + return new NetworkError({ title: throwableToError(error).message }); + } + + throw new Error('Unknown response'); + }; +}; + +export type CreateClient = ( + modelRequest: ModelRequest, +) => Promise; + +export const createCreateClient = ( + fetch: Fetch, + url: string, + modelRequestSchema: ModelRequestSchema, + modelResponseSchema: ModelResponseSchema, +): CreateClient, z.infer> => { + return async ( + modelRequest: z.infer, + ): Promise | HttpError> => { + try { + const response: Response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(modelRequestSchema.parse(modelRequest)), + }); + + const json = await response.json(); + + if (201 === response.status) { + return modelResponseSchema.parse(json); + } + + if (400 === response.status) { + return new BadRequest({ ...json }); + } + + if (422 === response.status) { + return new UnprocessableEntity({ ...json }); + } + + if (500 === response.status) { + return new InternalServerError({ ...json }); + } + } catch (error) { + return new NetworkError({ title: throwableToError(error).message }); + } + }; +}; + +export type ReadClient = (id: string) => Promise; + +export const createReadClient = ( + fetch: Fetch, + url: string, + modelResponseSchema: ModelResponseSchema, +): ReadClient> => { + return async (id: string): Promise | HttpError> => { + try { + const response: Response = await fetch(`${url}/${id}`, { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }); + + const json = await response.json(); + + if (200 === response.status) { + return modelResponseSchema.parse(json); + } + + if (404 === response.status) { + return new NotFound({ ...json }); + } + + if (500 === response.status) { + return new InternalServerError({ ...json }); + } + } catch (error) { + return new NetworkError({ title: throwableToError(error).message }); + } + }; +}; + +export type UpdateClient = ( + id: string, + modelRequest: ModelRequest, +) => Promise; + +export const createUpdateClient = ( + fetch: Fetch, + url: string, + modelRequestSchema: ModelRequestSchema, + modelResponseSchema: ModelResponseSchema, +): UpdateClient, z.infer> => { + return async ( + id: string, + modelRequest: z.infer, + ): Promise | HttpError> => { + try { + const response: Response = await fetch(`${url}/${id}`, { + method: 'PUT', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(modelRequestSchema.parse(modelRequest)), + }); + + const json = await response.json(); + + if (200 === response.status) { + return modelResponseSchema.parse(json); + } + + if (400 === response.status) { + return new BadRequest({ ...json }); + } + + if (404 === response.status) { + return new NotFound({ ...json }); + } + + if (422 === response.status) { + return new UnprocessableEntity({ ...json }); + } + + if (500 === response.status) { + return new InternalServerError({ ...json }); + } + } catch (error) { + return new NetworkError({ title: throwableToError(error).message }); + } + }; +}; + +export type DeleteClient = (id: string) => Promise; + +export const createDeleteClient = (fetch: Fetch, url: string): DeleteClient => { + return async (id: string): Promise => { + try { + const response: Response = await fetch(`${url}/${id}`, { + method: 'DELETE', + headers: { + Accept: 'application/json', + }, + }); + + if (204 === response.status) { + return; + } + + const json = await response.json(); + + if (404 === response.status) { + return new NotFound({ ...json }); + } + + if (500 === response.status) { + return new InternalServerError({ ...json }); + } + } catch (error) { + return new NetworkError({ title: throwableToError(error).message }); + } + }; +}; diff --git a/src/client/error.ts b/src/client/error.ts new file mode 100644 index 0000000..0117dad --- /dev/null +++ b/src/client/error.ts @@ -0,0 +1,58 @@ +export class HttpError { + title: string; + detail?: string; + instance?: string; + constructor({ title, detail, instance }: { title: string; detail?: string; instance?: string }) { + this.title = title; + this.detail = detail; + this.instance = instance; + } +} + +export interface InvalidParameter { + name: string; + reason: string; + details?: { [key: string]: unknown }; +} + +export class BadRequestOrUnprocessableEntity extends HttpError { + invalidParameters?: Array; + constructor({ + title, + detail, + instance, + invalidParameters, + }: { + title: string; + detail?: string; + instance?: string; + invalidParameters?: Array; + }) { + super({ title, detail, instance }); + this.invalidParameters = invalidParameters; + } +} + +export class BadRequest extends BadRequestOrUnprocessableEntity {} + +export class InternalServerError extends HttpError {} + +export class NetworkError extends HttpError {} + +export class NotFound extends HttpError {} + +export class UnprocessableEntity extends BadRequestOrUnprocessableEntity {} + +export const createInvalidParametersByName = ( + httpErrorOrUndefined: HttpError | undefined, +): Map> => { + return ( + httpErrorOrUndefined && httpErrorOrUndefined instanceof BadRequestOrUnprocessableEntity + ? httpErrorOrUndefined.invalidParameters ?? [] + : [] + ).reduce((map: Map>, invalidParameter: InvalidParameter) => { + map.set(invalidParameter.name, [...(map.get(invalidParameter.name) ?? []), invalidParameter]); + + return map; + }, new Map>()); +}; diff --git a/src/client/pet.ts b/src/client/pet.ts new file mode 100644 index 0000000..4a78c6d --- /dev/null +++ b/src/client/pet.ts @@ -0,0 +1,17 @@ +import { petListRequestSchema, petListResponseSchema, petRequestSchema, petResponseSchema } from '../model/pet'; +import { + createCreateClient, + createDeleteClient, + createListClient, + createReadClient, + createUpdateClient, +} from './client'; +import { fetch } from 'cross-fetch'; + +const url = `${import.meta.env.VITE_PETSTORE_URL}/api/pets`; + +export const listPetsClient = createListClient(fetch, url, petListRequestSchema, petListResponseSchema); +export const createPetClient = createCreateClient(fetch, url, petRequestSchema, petResponseSchema); +export const readPetClient = createReadClient(fetch, url, petResponseSchema); +export const updatePetClient = createUpdateClient(fetch, url, petRequestSchema, petResponseSchema); +export const deletePetClient = createDeleteClient(fetch, url); diff --git a/src/component/button.tsx b/src/component/button.tsx new file mode 100644 index 0000000..b8d7def --- /dev/null +++ b/src/component/button.tsx @@ -0,0 +1,65 @@ +import type { + AllowedComponentProps, + ButtonHTMLAttributes, + ComponentCustomProps, + SlotsType, + VNode, + VNodeProps, +} from 'vue'; +import { defineComponent } from 'vue'; +import type { RouterLinkProps } from 'vue-router'; +import { _RouterLinkI, RouterLink } from 'vue-router'; + +type ColorTheme = 'blue' | 'gray' | 'green' | 'red'; + +const getColorThemeClasses = (colorTheme: ColorTheme) => { + switch (colorTheme) { + case 'blue': + return 'bg-blue-600 hover:bg-blue-700'; + case 'gray': + return 'bg-gray-600 hover:bg-gray-700'; + case 'green': + return 'bg-green-600 hover:bg-green-700'; + case 'red': + return 'bg-red-600 hover:bg-red-700'; + } +}; + +export const AnchorButton = defineComponent( + ( + props: AllowedComponentProps & ComponentCustomProps & VNodeProps & RouterLinkProps & { colorTheme: ColorTheme }, + { slots }, + ) => { + return () => ( + + {slots.default()} + + ); + }, + { + name: 'AnchorButton', + props: ['class', 'colorTheme'], + slots: Object as SlotsType<{ default: () => VNode[] }>, + }, +); + +export const Button = defineComponent( + (props: ButtonHTMLAttributes & { colorTheme: ColorTheme }, { slots }) => { + return () => ( + + ); + }, + { + name: 'Button', + props: ['class', 'colorTheme'], + slots: Object as SlotsType<{ default: () => VNode[] }>, + }, +); diff --git a/src/component/form/form.tsx b/src/component/form/form.tsx new file mode 100644 index 0000000..41e3880 --- /dev/null +++ b/src/component/form/form.tsx @@ -0,0 +1,65 @@ +import type { FieldsetHTMLAttributes, SlotsType, VNode } from 'vue'; +import { defineComponent } from 'vue'; +import type { InvalidParameter } from '../../client/error'; + +export const FieldSet = defineComponent( + (props: FieldsetHTMLAttributes, { slots }) => { + return () => ( +
+ {slots.default()} +
+ ); + }, + { + name: 'FieldSet', + props: ['class'], + slots: Object as SlotsType<{ default: () => VNode[] }>, + }, +); + +export const TextField = defineComponent( + (props: { + dataTestId: string; + label: string; + value: string; + setValue: (value: string) => void; + invalidParameters: Array; + }) => { + console.log(props); + + return () => ( + + ); + }, + { + name: 'TextField', + props: ['dataTestId', 'label', 'value', 'setValue', 'invalidParameters'], + }, +); diff --git a/src/component/form/pet-filters-form.tsx b/src/component/form/pet-filters-form.tsx new file mode 100644 index 0000000..8df6a03 --- /dev/null +++ b/src/component/form/pet-filters-form.tsx @@ -0,0 +1,49 @@ +import { computed, defineComponent, reactive } from 'vue'; +import type { PetFilters } from '../../model/pet'; +import type { HttpError } from '../../client/error'; +import { createInvalidParametersByName } from '../../client/error'; +import { FieldSet, TextField } from './form'; +import { Button } from '../button'; + +export const PetFiltersForm = defineComponent( + (props: { + httpError: HttpError | undefined; + initialPetFilters: PetFilters; + submitPetFilters: (petFilters: PetFilters) => void; + }) => { + const groupInvalidParametersByName = computed(() => createInvalidParametersByName(props.httpError)); + + const petFilters = reactive(props.initialPetFilters); + + const onSubmit = () => { + props.submitPetFilters({ ...petFilters }); + }; + + return () => ( +
{ + e.preventDefault(); + e.stopPropagation(); + onSubmit(); + }} + > +
+ (petFilters.name = value === '' ? undefined : value)} + invalidParameters={groupInvalidParametersByName.value.get('filters[name]') ?? []} + /> + +
+
+ ); + }, + { + name: 'PetFiltersForm', + props: ['httpError', 'initialPetFilters', 'submitPetFilters'], + }, +); diff --git a/src/component/form/pet-form.tsx b/src/component/form/pet-form.tsx new file mode 100644 index 0000000..152c7e1 --- /dev/null +++ b/src/component/form/pet-form.tsx @@ -0,0 +1,104 @@ +import { computed, defineComponent, reactive } from 'vue'; +import type { PetRequest } from '../../model/pet'; +import type { HttpError } from '../../client/error'; +import { createInvalidParametersByName } from '../../client/error'; +import { FieldSet, TextField } from './form'; +import { Button } from '../button'; + +export const PetForm = defineComponent( + (props: { httpError: HttpError | undefined; initialPet?: PetRequest; submitPet: (pet: PetRequest) => void }) => { + const groupInvalidParametersByName = computed(() => createInvalidParametersByName(props.httpError)); + + const pet = reactive(props.initialPet ?? { name: '', vaccinations: [] }); + + const onSubmit = () => { + props.submitPet({ ...pet }); + }; + + return () => ( +
{ + e.preventDefault(); + e.stopPropagation(); + onSubmit(); + }} + > +
+ (pet.name = value)} + invalidParameters={groupInvalidParametersByName.value.get('name') ?? []} + /> + (pet.tag = value === '' ? undefined : value)} + invalidParameters={groupInvalidParametersByName.value.get('tag') ?? []} + /> +
+
Vaccinations
+
+ {pet.vaccinations.map((vaccination, i) => { + return ( +
+ + (pet.vaccinations = [ + ...pet.vaccinations.map((currentVaccination, y) => { + if (y === i) { + return { ...currentVaccination, name: value }; + } + + return currentVaccination; + }), + ]) + } + invalidParameters={groupInvalidParametersByName.value.get(`vaccinations[${i}][name]`) ?? []} + /> + +
+ ); + })} + +
+
+ +
+
+ ); + }, + { + name: 'PetForm', + props: ['httpError', 'initialPet', 'submitPet'], + }, +); diff --git a/src/component/heading.tsx b/src/component/heading.tsx new file mode 100644 index 0000000..091b640 --- /dev/null +++ b/src/component/heading.tsx @@ -0,0 +1,17 @@ +import type { HTMLAttributes, SlotsType, VNode } from 'vue'; +import { defineComponent } from 'vue'; + +export const H1 = defineComponent( + (props: HTMLAttributes, { slots }) => { + return () => ( +

+ {slots.default()} +

+ ); + }, + { + name: 'H1', + props: ['class'], + slots: Object as SlotsType<{ default: () => VNode[] }>, + }, +); diff --git a/src/component/page/home.tsx b/src/component/page/home.tsx new file mode 100644 index 0000000..7864c86 --- /dev/null +++ b/src/component/page/home.tsx @@ -0,0 +1,27 @@ +import { defineComponent, onMounted, onUnmounted } from 'vue'; +import { H1 } from '../heading'; + +const pageTitle = 'Home'; + +const Home = defineComponent( + () => { + onMounted(() => { + document.title = pageTitle; + }); + + onUnmounted(() => { + document.title = ''; + }); + + return () => ( +
+

{pageTitle}

+
+ ); + }, + { + name: 'Home', + }, +); + +export default Home; diff --git a/src/component/page/not-found.tsx b/src/component/page/not-found.tsx new file mode 100644 index 0000000..c3c2742 --- /dev/null +++ b/src/component/page/not-found.tsx @@ -0,0 +1,27 @@ +import { defineComponent, onMounted, onUnmounted } from 'vue'; +import { H1 } from '../heading'; + +const pageTitle = 'Not Found'; + +const NotFound = defineComponent( + () => { + onMounted(() => { + document.title = pageTitle; + }); + + onUnmounted(() => { + document.title = ''; + }); + + return () => ( +
+

{pageTitle}

+
+ ); + }, + { + name: 'NotFound', + }, +); + +export default NotFound; diff --git a/src/component/page/pet/create.tsx b/src/component/page/pet/create.tsx new file mode 100644 index 0000000..11884b5 --- /dev/null +++ b/src/component/page/pet/create.tsx @@ -0,0 +1,49 @@ +import { defineComponent, onMounted, onUnmounted } from 'vue'; +import { H1 } from '../../heading'; +import { HttpError as HttpErrorPartial } from '../../partial/http-error'; +import { useRouter } from 'vue-router'; +import { createModelResource } from '../../../hook/create-model-resource'; +import { createPetClient as createClient } from '../../../client/pet'; +import type { PetRequest } from '../../../model/pet'; +import { PetForm } from '../../form/pet-form'; +import { AnchorButton } from '../../button'; + +const pageTitle = 'Pet Create'; + +const PetCreate = defineComponent( + () => { + const { push } = useRouter(); + + const { httpError, actions } = createModelResource({ createClient }); + + const submitPet = async (pet: PetRequest) => { + if (await actions.createModel(pet)) { + push('/pet'); + } + }; + + onMounted(() => { + document.title = pageTitle; + }); + + onUnmounted(() => { + document.title = ''; + }); + + return () => ( +
+ {httpError.value ? : null} +

{pageTitle}

+ + + List + +
+ ); + }, + { + name: 'PetCreate', + }, +); + +export default PetCreate; diff --git a/src/component/page/pet/list.tsx b/src/component/page/pet/list.tsx new file mode 100644 index 0000000..134f799 --- /dev/null +++ b/src/component/page/pet/list.tsx @@ -0,0 +1,199 @@ +import { computed, defineComponent, onMounted, onUnmounted, watch } from 'vue'; +import { H1 } from '../../heading'; +import { HttpError as HttpErrorPartial } from '../../partial/http-error'; +import { format } from 'date-fns'; +import { de } from 'date-fns/locale'; +import { useRoute, useRouter } from 'vue-router'; +import { createModelResource } from '../../../hook/create-model-resource'; +import { deletePetClient as deleteClient, listPetsClient as listClient } from '../../../client/pet'; +import { z } from 'zod'; +import { numberSchema } from '../../../model/model'; +import type { PetFilters, PetSort } from '../../../model/pet'; +import { petFiltersSchema, petSortSchema } from '../../../model/pet'; +import qs from 'qs'; +import { AnchorButton, Button } from '../../button'; +import { Table, Tbody, Td, Th, Thead, Tr } from '../../table'; +import { Pagination } from '../../partial/pagination'; +import { PetFiltersForm } from '../../form/pet-filters-form'; + +const pageTitle = 'Pet List'; + +const limit = 10; + +const querySchema = z.object({ + page: numberSchema.optional().default(1), + filters: petFiltersSchema.optional().default({}), + sort: petSortSchema.optional().default({}), +}); + +const PetList = defineComponent( + () => { + const { push } = useRouter(); + const route = useRoute(); + const query = computed(() => querySchema.parse(qs.parse(route.fullPath.substring(route.path.length + 1)))); + + const { + modelList: petList, + httpError, + actions, + } = createModelResource({ + listClient, + deleteClient, + }); + + const petListRequest = computed(() => ({ + offset: query.value.page * limit - limit, + limit, + filters: query.value.filters, + sort: query.value.sort, + })); + + const fetchPetList = async () => { + actions.listModel(petListRequest.value); + }; + + const deletePet = async (id: string) => { + if (await actions.deleteModel(id)) { + fetchPetList(); + } + }; + + const submitPage = (page: number): void => { + push(`/pet?${qs.stringify({ ...query.value, page })}`); + }; + + const submitPetFilters = (filters: PetFilters): void => { + push(`/pet?${qs.stringify({ ...query.value, page: 1, filters })}`); + }; + + const submitPetSort = (sort: PetSort): void => { + push(`/pet?${qs.stringify({ ...query.value, page: 1, sort })}`); + }; + + onMounted(() => { + document.title = pageTitle; + + fetchPetList(); + }); + + watch(query, () => { + fetchPetList(); + }); + + onUnmounted(() => { + document.title = ''; + }); + + return () => ( + <> + {petList.value || httpError.value ? ( +
+ {httpError.value ? : null} +

{pageTitle}

+ {petList.value ? ( +
+ {petList.value._links?.create ? ( + + Create + + ) : null} + +
+ + + + + + + + + + + + + {petList.value.items.map((pet, i) => ( + + + + + + + + + ))} + +
IdCreatedAtUpdatedAt + Name ( + + | + + | + + ) + TagActions
{pet.id}{format(Date.parse(pet.createdAt), 'dd.MM.yyyy - HH:mm:ss', { locale: de })} + {pet.updatedAt && + format(Date.parse(pet.updatedAt), 'dd.MM.yyyy - HH:mm:ss', { locale: de })} + {pet.name}{pet.tag} + {pet._links?.read ? ( + + Read + + ) : null} + {pet._links?.update ? ( + + Update + + ) : null} + {pet._links?.delete ? ( + + ) : null} +
+
+
+ +
+
+ ) : null} +
+ ) : null} + + ); + }, + { + name: 'PetList', + }, +); + +export default PetList; diff --git a/src/component/page/pet/read.tsx b/src/component/page/pet/read.tsx new file mode 100644 index 0000000..821b3c2 --- /dev/null +++ b/src/component/page/pet/read.tsx @@ -0,0 +1,81 @@ +import { defineComponent, onMounted, onUnmounted } from 'vue'; +import { H1 } from '../../heading'; +import { HttpError as HttpErrorPartial } from '../../partial/http-error'; +import { format } from 'date-fns'; +import { de } from 'date-fns/locale'; +import { useRoute } from 'vue-router'; +import { createModelResource } from '../../../hook/create-model-resource'; +import { readPetClient as readClient } from '../../../client/pet'; +import { AnchorButton } from '../../button'; + +const pageTitle = 'Pet Read'; + +const PetRead = defineComponent( + () => { + const route = useRoute(); + const id = route.params.id as string; + + const { model: pet, httpError, actions } = createModelResource({ readClient }); + + onMounted(() => { + document.title = pageTitle; + + actions.readModel(id); + }); + + onUnmounted(() => { + document.title = ''; + }); + + return () => ( + <> + {pet.value || httpError.value ? ( +
+ {httpError.value ? : null} +

{pageTitle}

+ {pet.value ? ( +
+
+
Id
+
{pet.value.id}
+
CreatedAt
+
+ {format(Date.parse(pet.value.createdAt), 'dd.MM.yyyy - HH:mm:ss', { locale: de })} +
+
UpdatedAt
+
+ {pet.value.updatedAt + ? format(Date.parse(pet.value.updatedAt), 'dd.MM.yyyy - HH:mm:ss', { locale: de }) + : null} +
+
Name
+
{pet.value.name}
+
Tag
+
{pet.value.tag}
+
Vaccinations
+
+ {pet.value.vaccinations.length > 0 ? ( +
    + {pet.value.vaccinations.map((vaccination) => ( +
  • {vaccination.name}
  • + ))} +
+ ) : null} +
+
+
+ ) : null} + + List + +
+ ) : null} + + ); + }, + { + name: 'PetRead', + }, +); + +export default PetRead; diff --git a/src/component/page/pet/update.tsx b/src/component/page/pet/update.tsx new file mode 100644 index 0000000..cc3e522 --- /dev/null +++ b/src/component/page/pet/update.tsx @@ -0,0 +1,57 @@ +import { defineComponent, onMounted, onUnmounted } from 'vue'; +import { H1 } from '../../heading'; +import { HttpError as HttpErrorPartial } from '../../partial/http-error'; +import { useRoute, useRouter } from 'vue-router'; +import { createModelResource } from '../../../hook/create-model-resource'; +import { readPetClient as readClient, updatePetClient as updateClient } from '../../../client/pet'; +import type { PetRequest } from '../../../model/pet'; +import { PetForm } from '../../form/pet-form'; +import { AnchorButton } from '../../button'; + +const pageTitle = 'Pet Update'; + +const PetUpdate = defineComponent( + () => { + const { push } = useRouter(); + const route = useRoute(); + const id = route.params.id as string; + + const { model: pet, httpError, actions } = createModelResource({ readClient, updateClient }); + + const submitPet = async (petRequest: PetRequest) => { + if (await actions.updateModel(id, petRequest)) { + push('/pet'); + } + }; + + onMounted(() => { + document.title = pageTitle; + + actions.readModel(id); + }); + + onUnmounted(() => { + document.title = ''; + }); + + return () => ( + <> + {pet.value || httpError.value ? ( +
+ {httpError.value ? : null} +

{pageTitle}

+ {pet.value ? : null} + + List + +
+ ) : null} + + ); + }, + { + name: 'PetUpdate', + }, +); + +export default PetUpdate; diff --git a/src/component/partial/http-error.tsx b/src/component/partial/http-error.tsx new file mode 100644 index 0000000..ba85ba0 --- /dev/null +++ b/src/component/partial/http-error.tsx @@ -0,0 +1,28 @@ +import { defineComponent } from 'vue'; +import type { HttpError as HttpErrorType } from '../../client/error'; +import { BadRequestOrUnprocessableEntity } from '../../client/error'; + +export const HttpError = defineComponent( + (props: { httpError: HttpErrorType }) => { + return () => ( +
+

{props.httpError.title}

+ {props.httpError.detail ?

{props.httpError.detail}

: null} + {props.httpError.instance ?

{props.httpError.instance}

: null} + {props.httpError instanceof BadRequestOrUnprocessableEntity && props.httpError.invalidParameters?.length ? ( +
    + {props.httpError.invalidParameters.map((invalidParameter, i) => ( +
  • + {invalidParameter.name}: {invalidParameter.reason} +
  • + ))} +
+ ) : null} +
+ ); + }, + { + name: 'HttpError', + props: ['httpError'], + }, +); diff --git a/src/component/partial/pagination.tsx b/src/component/partial/pagination.tsx new file mode 100644 index 0000000..93a5231 --- /dev/null +++ b/src/component/partial/pagination.tsx @@ -0,0 +1,110 @@ +import { computed, defineComponent } from 'vue'; + +const calculatePages = (currentPage: number, totalPages: number, maxPages: number) => { + if (totalPages <= 1 || maxPages <= 1 || currentPage > totalPages) { + return []; + } + + const pages = [currentPage]; + + for (let i = 1; ; i++) { + if (currentPage - i >= 1) { + pages.push(currentPage - i); + + if (pages.length === maxPages || pages.length === totalPages) { + break; + } + } + + if (currentPage + i <= totalPages) { + pages.push(currentPage + i); + + if (pages.length === maxPages || pages.length === totalPages) { + break; + } + } + } + + pages.sort((a, b) => a - b); + + return pages; +}; + +export const Pagination = defineComponent( + (props: { currentPage: number; totalPages: number; maxPages: number; submitPage: (page: number) => void }) => { + const pages = computed(() => calculatePages(props.currentPage, props.totalPages, props.maxPages)); + + return () => ( + <> + {pages.value.length > 0 ? ( +
    + {props.currentPage > 2 ? ( +
  • + +
  • + ) : null} + {props.currentPage > 1 ? ( +
  • + +
  • + ) : null} + {pages.value.map((page: number) => ( +
  • + +
  • + ))} + {props.currentPage < props.totalPages ? ( +
  • + +
  • + ) : null} + {props.currentPage < props.totalPages - 1 ? ( +
  • + +
  • + ) : null} +
+ ) : null} + + ); + }, + { + name: 'Pagination', + props: ['currentPage', 'totalPages', 'maxPages', 'submitPage'], + }, +); diff --git a/src/component/table.tsx b/src/component/table.tsx new file mode 100644 index 0000000..87eb89d --- /dev/null +++ b/src/component/table.tsx @@ -0,0 +1,102 @@ +import type { HTMLAttributes, SlotsType, TableHTMLAttributes, TdHTMLAttributes, ThHTMLAttributes, VNode } from 'vue'; +import { defineComponent } from 'vue'; + +export const Table = defineComponent( + (props: TableHTMLAttributes, { slots }) => { + return () => ( +
+ {slots.default()} +
+ ); + }, + { + name: 'Table', + props: ['class'], + slots: Object as SlotsType<{ default: () => VNode[] }>, + }, +); + +export const Thead = defineComponent( + (props: HTMLAttributes, { slots }) => { + return () => ( +
+ {slots.default()} +
+ ); + }, + { + name: 'Thead', + props: ['class'], + slots: Object as SlotsType<{ default: () => VNode[] }>, + }, +); + +export const Tbody = defineComponent( + (props: HTMLAttributes, { slots }) => { + return () => ( +
+ {slots.default()} +
+ ); + }, + { + name: 'Tbody', + props: ['class'], + slots: Object as SlotsType<{ default: () => VNode[] }>, + }, +); + +export const Tr = defineComponent( + (props: HTMLAttributes, { slots }) => { + return () => ( +
+ {slots.default()} +
+ ); + }, + { + name: 'Tr', + props: ['class'], + slots: Object as SlotsType<{ default: () => VNode[] }>, + }, +); + +export const Th = defineComponent( + (props: ThHTMLAttributes, { slots }) => { + return () => ( +
+ {slots.default()} +
+ ); + }, + { + name: 'Th', + props: ['class'], + slots: Object as SlotsType<{ default: () => VNode[] }>, + }, +); + +export const Td = defineComponent( + (props: TdHTMLAttributes, { slots }) => { + return () => ( +
+ {slots.default()} +
+ ); + }, + { + name: 'Td', + props: ['class'], + slots: Object as SlotsType<{ default: () => VNode[] }>, + }, +); diff --git a/src/hook/create-model-resource.ts b/src/hook/create-model-resource.ts new file mode 100644 index 0000000..46ca2e5 --- /dev/null +++ b/src/hook/create-model-resource.ts @@ -0,0 +1,161 @@ +import { HttpError } from '../client/error'; +import type { ModelListRequest, ModelListResponse, ModelRequest, ModelResponse } from '../model/model'; +import type { CreateClient, DeleteClient, ListClient, ReadClient, UpdateClient } from '../client/client'; +import { ref } from 'vue'; + +export const createModelResource = < + MLReq extends ModelListRequest, + MLRes extends ModelListResponse, + MReq extends ModelRequest, + MRes extends ModelResponse, +>({ + listClient, + createClient, + readClient, + updateClient, + deleteClient, +}: { + listClient?: ListClient; + createClient?: CreateClient; + readClient?: ReadClient; + updateClient?: UpdateClient; + deleteClient?: DeleteClient; +}) => { + const loading = ref<'list' | 'create' | 'read' | 'update' | 'delete' | undefined>(); + const modelList = ref(); + const model = ref(); + const httpError = ref(); + + const listModel = async (req: MLReq): Promise => { + if (!listClient) { + throw new Error('Missing listClient'); + } + + loading.value = 'list'; + + const response = await listClient(req); + + let success: boolean; + + if (response instanceof HttpError) { + httpError.value = response; + success = false; + } else { + httpError.value = undefined; + modelList.value = response; + success = true; + } + + loading.value = undefined; + + return success; + }; + + const createModel = async (req: MReq): Promise => { + if (!createClient) { + throw new Error('Missing createClient'); + } + + loading.value = 'create'; + + const response = await createClient(req); + + let success: boolean; + + if (response instanceof HttpError) { + httpError.value = response; + success = false; + } else { + httpError.value = undefined; + model.value = response; + success = true; + } + + loading.value = undefined; + + return success; + }; + + const readModel = async (id: string): Promise => { + if (!readClient) { + throw new Error('Missing readClient'); + } + + loading.value = 'read'; + + const response = await readClient(id); + + let success: boolean; + + if (response instanceof HttpError) { + httpError.value = response; + success = false; + } else { + httpError.value = undefined; + model.value = response; + success = true; + } + + loading.value = undefined; + + return success; + }; + + const updateModel = async (id: string, req: MReq): Promise => { + if (!updateClient) { + throw new Error('Missing updateClient'); + } + + loading.value = 'update'; + + const response = await updateClient(id, req); + + let success: boolean; + + if (response instanceof HttpError) { + httpError.value = response; + success = false; + } else { + httpError.value = undefined; + model.value = response; + success = true; + } + + loading.value = undefined; + + return success; + }; + + const deleteModel = async (id: string): Promise => { + if (!deleteClient) { + throw new Error('Missing deleteClient'); + } + + loading.value = 'delete'; + + const response = await deleteClient(id); + + let success: boolean; + + if (response instanceof HttpError) { + httpError.value = response; + success = false; + } else { + httpError.value = undefined; + model.value = undefined; + success = true; + } + + loading.value = undefined; + + return success; + }; + + return { + loading, + modelList, + model, + httpError, + actions: { listModel, createModel, readModel, updateModel, deleteModel }, + }; +}; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..dbfb140 --- /dev/null +++ b/src/index.css @@ -0,0 +1,10 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +html, +body, +#root { + @apply w-full; + @apply h-full; +} diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 0000000..eaf1b9d --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,14 @@ +import { createApp } from 'vue'; +import './index.css'; +import App from './app.tsx'; +import { routes } from './routes.ts'; +import { createWebHistory, createRouter } from 'vue-router'; + +createApp(App) + .use( + createRouter({ + history: createWebHistory(), + routes, + }), + ) + .mount('#root'); diff --git a/src/model/model.ts b/src/model/model.ts new file mode 100644 index 0000000..295b96c --- /dev/null +++ b/src/model/model.ts @@ -0,0 +1,63 @@ +import { z } from 'zod'; + +export const numberSchema = z.union([ + z + .string() + .refine((number) => !Number.isNaN(parseInt(number, 10))) + .transform((number) => parseInt(number, 10)), + z.number(), +]); + +export const sortSchema = z.union([z.literal('asc'), z.literal('desc')]); + +const linkSchema = z.object({ + href: z.string(), +}); + +export const modelRequestSchema = z.object({}).strict(); + +export type ModelRequest = z.infer; + +export const modelResponseSchema = z + .object({ + id: z.string(), + createdAt: z.string(), + updatedAt: z.string().nullish(), + _links: z + .object({ + read: linkSchema.nullish(), + update: linkSchema.nullish(), + delete: linkSchema.nullish(), + }) + .strict(), + }) + .strict(); + +export type ModelResponse = z.infer; + +export const modelListRequestSchema = z + .object({ + offset: numberSchema.optional(), + limit: numberSchema.optional(), + filters: z.object({}).strict().optional(), + sort: z.object({}).strict().optional(), + }) + .strict(); + +export type ModelListRequest = z.infer; + +export const modelListResponseSchema = z + .object({ + offset: numberSchema, + limit: numberSchema, + filters: z.object({}).strict(), + sort: z.object({}).strict(), + count: numberSchema, + items: z.array(modelResponseSchema), + _links: z.object({ + create: linkSchema.nullish(), + }), + }) + .strict(); + +export type ModelListResponse = z.infer; diff --git a/src/model/pet.ts b/src/model/pet.ts new file mode 100644 index 0000000..068c127 --- /dev/null +++ b/src/model/pet.ts @@ -0,0 +1,57 @@ +import { z } from 'zod'; +import { + modelRequestSchema, + modelResponseSchema, + modelListRequestSchema, + sortSchema, + modelListResponseSchema, +} from './model'; + +export const petRequestSchema = z.object({ + ...modelRequestSchema.shape, + name: z.string(), + tag: z.string().nullish(), + vaccinations: z.array( + z.object({ + name: z.string(), + }), + ), +}); + +export type PetRequest = z.infer; + +export const petResponseSchema = z.object({ + ...modelResponseSchema.shape, + ...petRequestSchema.shape, +}); + +export type PetResponse = z.infer; + +export const petFiltersSchema = z.object({ + name: z.string().nullish(), +}); + +export type PetFilters = z.infer; + +export const petSortSchema = z.object({ + name: sortSchema.nullish(), +}); + +export type PetSort = z.infer; + +export const petListRequestSchema = z.object({ + ...modelListRequestSchema.shape, + filters: petFiltersSchema.optional(), + sort: petSortSchema.optional(), +}); + +export type PetListRequest = z.infer; + +export const petListResponseSchema = z.object({ + ...modelListResponseSchema.shape, + filters: petFiltersSchema, + sort: petSortSchema, + items: z.array(petResponseSchema), +}); + +export type PetListResponse = z.infer; diff --git a/src/routes.ts b/src/routes.ts new file mode 100644 index 0000000..6bc6358 --- /dev/null +++ b/src/routes.ts @@ -0,0 +1,17 @@ +import type { RouteRecordRaw } from 'vue-router'; + +const Home = () => import('./component/page/home'); +const PetList = () => import('./component/page/pet/list'); +const PetCreate = () => import('./component/page/pet/create'); +const PetRead = () => import('./component/page/pet/read'); +const PetUpdate = () => import('./component/page/pet/update'); +const NotFound = () => import('./component/page/not-found'); + +export const routes: Array = [ + { path: '/', name: 'Home', component: Home }, + { path: '/pet', name: 'PetList', component: PetList }, + { path: '/pet/create', name: 'PetCreate', component: PetCreate }, + { path: '/pet/:id', name: 'PetRead', component: PetRead }, + { path: '/pet/:id/update', name: 'PetUpdate', component: PetUpdate }, + { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound }, +]; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..fc61fff --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./src/**/*.{jsx,tsx}'], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/tests/app.test.tsx b/tests/app.test.tsx new file mode 100644 index 0000000..29b2578 --- /dev/null +++ b/tests/app.test.tsx @@ -0,0 +1,422 @@ +/** @jsxImportSource vue */ + +import { test, expect, vi } from 'vitest'; +import { formatHtml } from './formatter'; +import { render, screen } from '@testing-library/vue'; +import App from '../src/app'; +import { routes } from '../src/routes'; +import { userEvent } from '@testing-library/user-event'; +import { createRouter, createWebHistory } from 'vue-router'; +import { defineComponent } from 'vue'; + +vi.mock('../src/component/page/home', () => { + return { + __esModule: true, + default: defineComponent(() => () =>
), + }; +}); + +vi.mock('../src/component/page/pet/list', () => { + return { + __esModule: true, + default: defineComponent(() => () =>
), + }; +}); + +vi.mock('../src/component/page/pet/create', () => { + return { + __esModule: true, + default: defineComponent(() => () =>
), + }; +}); + +vi.mock('../src/component/page/pet/read', () => { + return { + __esModule: true, + default: defineComponent(() => () =>
), + }; +}); + +vi.mock('../src/component/page/pet/update', () => { + return { + __esModule: true, + default: defineComponent(() => () =>
), + }; +}); + +vi.mock('../src/component/page/not-found', () => { + return { + __esModule: true, + default: defineComponent(() => () =>
), + }; +}); + +test('close navigation', () => { + const router = createRouter({ + history: createWebHistory(), + routes + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+ + +
+
+
+ " + `); +}); + +test('open navigation', async () => { + const router = createRouter({ + history: createWebHistory(), + routes + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + const navigationToggle = await screen.findByTestId('navigation-toggle'); + + await userEvent.click(navigationToggle); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+ + +
+
+
+
+
+ " + `); +}); + +test('not found', async () => { + const router = createRouter({ + history: createWebHistory(), + routes + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/unknown'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+ + +
+
+
+
+
+ " + `); +}); + +test('pet list', async () => { + const router = createRouter({ + history: createWebHistory(), + routes + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+ + +
+
+
+
+
+ " + `); +}); + +test('pet create', async () => { + const router = createRouter({ + history: createWebHistory(), + routes + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet/create'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+ + +
+
+
+
+
+ " + `); +}); + +test('pet read', async () => { + const router = createRouter({ + history: createWebHistory(), + routes + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet/4d783b77-eb09-4603-b99b-f590b605eaa9'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+ + +
+
+
+
+
+ " + `); +}); + +test('pet update', async () => { + const router = createRouter({ + history: createWebHistory(), + routes + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet/4d783b77-eb09-4603-b99b-f590b605eaa9/update'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+ + +
+
+
+
+
+ " + `); +}); diff --git a/tests/client/client.test.ts b/tests/client/client.test.ts new file mode 100644 index 0000000..7b0121d --- /dev/null +++ b/tests/client/client.test.ts @@ -0,0 +1,1239 @@ +import { describe, expect, test } from 'vitest'; +import type { Fetch } from '../../src/client/client'; +import { + createCreateClient, + createDeleteClient, + createListClient, + createReadClient, + createUpdateClient, +} from '../../src/client/client'; +import { + modelRequestSchema, + modelResponseSchema, + modelListRequestSchema, + modelListResponseSchema, +} from '../../src/model/model'; +import { useFunctionMock } from '@chubbyts/chubbyts-function-mock/dist/function-mock'; +import { z } from 'zod'; + +const dummyModelRequestSchema = z.object({ + ...modelRequestSchema.shape, + name: z.string(), +}); + +type DummyModelRequest = z.infer; + +const dummyModelResponseSchema = z.object({ + ...modelResponseSchema.shape, + ...dummyModelRequestSchema.shape, +}); + +type DummyModelResponse = z.infer; + +const dummyModelListRequestSchema = z.object({ + ...modelListRequestSchema.shape, + filters: z + .object({ + name: z.string().optional(), + }) + .strict() + .optional(), +}); + +type DummyModelListRequest = z.infer; + +const dummyModelListResponseSchema = z.object({ + ...modelListResponseSchema.shape, + filters: z + .object({ + name: z.string().optional(), + }) + .strict() + .optional(), + items: z.array(dummyModelResponseSchema), +}); + +type DummyModelListResponse = z.infer; + +describe('createListClient', () => { + test('success', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const dummyModelResponse: DummyModelResponse = { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2022-06-12T20:08:24.793Z', + ...dummyModelRequest, + _links: {}, + }; + + const dummyModelListRequest: DummyModelListRequest = { filters: { name: 'Dummy' } }; + + const dummyModelListResponse: DummyModelListResponse = { + offset: 0, + limit: 20, + filters: {}, + sort: {}, + count: 1, + items: [dummyModelResponse], + ...dummyModelListRequest, + _links: {}, + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models?filters%5Bname%5D=Dummy', + { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }, + ], + return: Promise.resolve({ + status: 200, + json: () => Promise.resolve(dummyModelListResponse), + } as Response), + }, + ]); + + const listClient = createListClient( + fetch, + 'https://petstore.test/api/models', + dummyModelListRequestSchema, + dummyModelListResponseSchema, + ); + + expect(await listClient({ filters: { name: 'Dummy' } })).toEqual(dummyModelListResponse); + + expect(fetchMocks.length).toBe(0); + }); + + test('bad request', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models?filters%5Bname%5D=Dummy', + { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }, + ], + return: Promise.resolve({ + status: 400, + json: () => + Promise.resolve({ + title: 'Bad Request', + detail: 'Sorting value', + instance: '0123456789abcdef', + invalidParameters: [{ name: 'name', reason: 'unknown field', details: { key: 'value1' } }], + }), + } as Response), + }, + ]); + + const listClient = createListClient( + fetch, + 'https://petstore.test/api/models', + dummyModelListRequestSchema, + dummyModelListResponseSchema, + ); + + expect(await listClient({ filters: { name: 'Dummy' } })).toMatchInlineSnapshot(` + BadRequest { + "detail": "Sorting value", + "instance": "0123456789abcdef", + "invalidParameters": [ + { + "details": { + "key": "value1", + }, + "name": "name", + "reason": "unknown field", + }, + ], + "title": "Bad Request", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('internal server error', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models?filters%5Bname%5D=Dummy', + { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }, + ], + return: Promise.resolve({ + status: 500, + json: () => + Promise.resolve({ + title: 'Internal Server Error', + instance: '0123456789abcdef', + }), + } as Response), + }, + ]); + + const listClient = createListClient( + fetch, + 'https://petstore.test/api/models', + dummyModelListRequestSchema, + dummyModelListResponseSchema, + ); + + expect(await listClient({ filters: { name: 'Dummy' } })).toMatchInlineSnapshot(` + InternalServerError { + "detail": undefined, + "instance": "0123456789abcdef", + "title": "Internal Server Error", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('network error', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models?filters%5Bname%5D=Dummy', + { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }, + ], + error: new Error('Failed to fetch'), + }, + ]); + + const listClient = createListClient( + fetch, + 'https://petstore.test/api/models', + dummyModelListRequestSchema, + dummyModelListResponseSchema, + ); + + expect(await listClient({ filters: { name: 'Dummy' } })).toMatchInlineSnapshot(` + NetworkError { + "detail": undefined, + "instance": undefined, + "title": "Failed to fetch", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('unknown response', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models?filters%5Bname%5D=Dummy', + { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }, + ], + return: Promise.resolve({ + status: 418, + json: () => Promise.resolve({}), + } as Response), + }, + ]); + + const listClient = createListClient( + fetch, + 'https://petstore.test/api/models', + dummyModelListRequestSchema, + dummyModelListResponseSchema, + ); + + try { + await listClient({ filters: { name: 'Dummy' } }); + throw new Error('expect fail'); + } catch (e) { + expect(e).toMatchInlineSnapshot('[Error: Unknown response]'); + } + + expect(fetchMocks.length).toBe(0); + }); +}); + +describe('createCreateClient', () => { + test('success', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const dummyModelResponse: DummyModelResponse = { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2022-06-12T20:08:24.793Z', + ...dummyModelRequest, + _links: {}, + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models', + { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + return: Promise.resolve({ + status: 201, + json: () => Promise.resolve(dummyModelResponse), + } as Response), + }, + ]); + + const createClient = createCreateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + expect(await createClient(dummyModelRequest)).toEqual(dummyModelResponse); + + expect(fetchMocks.length).toBe(0); + }); + + test('bad request', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models', + { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + return: Promise.resolve({ + status: 400, + json: () => + Promise.resolve({ + title: 'Bad Request', + detail: 'name', + instance: '0123456789abcdef', + invalidParameters: [{ name: 'name', reason: 'empty', details: { key: 'value1' } }], + }), + } as Response), + }, + ]); + + const createClient = createCreateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + expect(await createClient(dummyModelRequest)).toMatchInlineSnapshot(` + BadRequest { + "detail": "name", + "instance": "0123456789abcdef", + "invalidParameters": [ + { + "details": { + "key": "value1", + }, + "name": "name", + "reason": "empty", + }, + ], + "title": "Bad Request", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('unprocessable entity', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models', + { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + return: Promise.resolve({ + status: 422, + json: () => + Promise.resolve({ + title: 'Unprocessable Entity', + detail: 'name', + instance: '0123456789abcdef', + invalidParameters: [{ name: 'name', reason: 'empty', details: { key: 'value1' } }], + }), + } as Response), + }, + ]); + + const createClient = createCreateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + expect(await createClient(dummyModelRequest)).toMatchInlineSnapshot(` + UnprocessableEntity { + "detail": "name", + "instance": "0123456789abcdef", + "invalidParameters": [ + { + "details": { + "key": "value1", + }, + "name": "name", + "reason": "empty", + }, + ], + "title": "Unprocessable Entity", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('internal server error', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models', + { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + return: Promise.resolve({ + status: 500, + json: () => + Promise.resolve({ + title: 'Internal Server Error', + instance: '0123456789abcdef', + }), + } as Response), + }, + ]); + + const createClient = createCreateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + expect(await createClient(dummyModelRequest)).toMatchInlineSnapshot(` + InternalServerError { + "detail": undefined, + "instance": "0123456789abcdef", + "title": "Internal Server Error", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('network error', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models', + { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + error: new Error('Failed to fetch'), + }, + ]); + + const createClient = createCreateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + expect(await createClient(dummyModelRequest)).toMatchInlineSnapshot(` + NetworkError { + "detail": undefined, + "instance": undefined, + "title": "Failed to fetch", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('unknown response', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models', + { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + return: Promise.resolve({ + status: 418, + json: () => Promise.resolve({}), + } as Response), + }, + ]); + + const createClient = createCreateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + try { + await createClient(dummyModelRequest); + throw new Error('expect fail'); + } catch (e) { + expect(e).toMatchInlineSnapshot('[Error: expect fail]'); + } + + expect(fetchMocks.length).toBe(0); + }); +}); + +describe('createReadClient', () => { + test('success', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const dummyModelResponse: DummyModelResponse = { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2022-06-12T20:08:24.793Z', + ...dummyModelRequest, + _links: {}, + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + `https://petstore.test/api/models/${dummyModelResponse.id}`, + { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }, + ], + return: Promise.resolve({ + status: 200, + json: () => Promise.resolve(dummyModelResponse), + } as Response), + }, + ]); + + const readClient = createReadClient(fetch, 'https://petstore.test/api/models', dummyModelResponseSchema); + + expect(await readClient('4d783b77-eb09-4603-b99b-f590b605eaa9')).toEqual(dummyModelResponse); + + expect(fetchMocks.length).toBe(0); + }); + + test('not found', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }, + ], + return: Promise.resolve({ + status: 404, + json: () => + Promise.resolve({ + title: 'Not Found', + detail: 'There is no model with id "4d783b77-eb09-4603-b99b-f590b605eaa9"', + instance: '0123456789abcdef', + }), + } as Response), + }, + ]); + + const readClient = createReadClient(fetch, 'https://petstore.test/api/models', dummyModelResponseSchema); + + expect(await readClient('4d783b77-eb09-4603-b99b-f590b605eaa9')).toMatchInlineSnapshot(` + NotFound { + "detail": "There is no model with id "4d783b77-eb09-4603-b99b-f590b605eaa9"", + "instance": "0123456789abcdef", + "title": "Not Found", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('internal server error', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }, + ], + return: Promise.resolve({ + status: 500, + json: () => + Promise.resolve({ + title: 'Internal Server Error', + instance: '0123456789abcdef', + }), + } as Response), + }, + ]); + + const readClient = createReadClient(fetch, 'https://petstore.test/api/models', dummyModelResponseSchema); + + expect(await readClient('4d783b77-eb09-4603-b99b-f590b605eaa9')).toMatchInlineSnapshot(` + InternalServerError { + "detail": undefined, + "instance": "0123456789abcdef", + "title": "Internal Server Error", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('network error', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }, + ], + error: new Error('Failed to fetch'), + }, + ]); + + const readClient = createReadClient(fetch, 'https://petstore.test/api/models', dummyModelResponseSchema); + + expect(await readClient('4d783b77-eb09-4603-b99b-f590b605eaa9')).toMatchInlineSnapshot(` + NetworkError { + "detail": undefined, + "instance": undefined, + "title": "Failed to fetch", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('unknown response', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }, + ], + return: Promise.resolve({ + status: 418, + json: () => Promise.resolve({}), + } as Response), + }, + ]); + + const readClient = createReadClient(fetch, 'https://petstore.test/api/models', dummyModelResponseSchema); + + try { + await readClient('4d783b77-eb09-4603-b99b-f590b605eaa9'); + throw new Error('expect fail'); + } catch (e) { + expect(e).toMatchInlineSnapshot('[Error: expect fail]'); + } + + expect(fetchMocks.length).toBe(0); + }); +}); + +describe('createUpdateClient', () => { + test('success', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const dummyModelResponse: DummyModelResponse = { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2022-06-12T20:08:24.793Z', + ...dummyModelRequest, + _links: {}, + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + `https://petstore.test/api/models/${dummyModelResponse.id}`, + { + method: 'PUT', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + return: Promise.resolve({ + status: 200, + json: () => Promise.resolve(dummyModelResponse), + } as Response), + }, + ]); + + const updateClient = createUpdateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + expect(await updateClient('4d783b77-eb09-4603-b99b-f590b605eaa9', dummyModelRequest)).toEqual(dummyModelResponse); + + expect(fetchMocks.length).toBe(0); + }); + + test('bad request', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'PUT', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + return: Promise.resolve({ + status: 400, + json: () => + Promise.resolve({ + title: 'Bad Request', + detail: 'name', + instance: '0123456789abcdef', + invalidParameters: [{ name: 'name', reason: 'empty', details: { key: 'value1' } }], + }), + } as Response), + }, + ]); + + const updateClient = createUpdateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + expect(await updateClient('4d783b77-eb09-4603-b99b-f590b605eaa9', dummyModelRequest)).toMatchInlineSnapshot(` + BadRequest { + "detail": "name", + "instance": "0123456789abcdef", + "invalidParameters": [ + { + "details": { + "key": "value1", + }, + "name": "name", + "reason": "empty", + }, + ], + "title": "Bad Request", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('not found', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'PUT', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + return: Promise.resolve({ + status: 404, + json: () => + Promise.resolve({ + title: 'Not Found', + detail: 'There is no model with id "4d783b77-eb09-4603-b99b-f590b605eaa9"', + instance: '0123456789abcdef', + }), + } as Response), + }, + ]); + + const updateClient = createUpdateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + expect(await updateClient('4d783b77-eb09-4603-b99b-f590b605eaa9', dummyModelRequest)).toMatchInlineSnapshot(` + NotFound { + "detail": "There is no model with id "4d783b77-eb09-4603-b99b-f590b605eaa9"", + "instance": "0123456789abcdef", + "title": "Not Found", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('unprocessable entity', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'PUT', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + return: Promise.resolve({ + status: 422, + json: () => + Promise.resolve({ + title: 'Unprocessable Entity', + detail: 'name', + instance: '0123456789abcdef', + invalidParameters: [{ name: 'name', reason: 'empty', details: { key: 'value1' } }], + }), + } as Response), + }, + ]); + + const updateClient = createUpdateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + expect(await updateClient('4d783b77-eb09-4603-b99b-f590b605eaa9', dummyModelRequest)).toMatchInlineSnapshot(` + UnprocessableEntity { + "detail": "name", + "instance": "0123456789abcdef", + "invalidParameters": [ + { + "details": { + "key": "value1", + }, + "name": "name", + "reason": "empty", + }, + ], + "title": "Unprocessable Entity", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('internal server error', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'PUT', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + return: Promise.resolve({ + status: 500, + json: () => + Promise.resolve({ + title: 'Internal Server Error', + instance: '0123456789abcdef', + }), + } as Response), + }, + ]); + + const updateClient = createUpdateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + expect(await updateClient('4d783b77-eb09-4603-b99b-f590b605eaa9', dummyModelRequest)).toMatchInlineSnapshot(` + InternalServerError { + "detail": undefined, + "instance": "0123456789abcdef", + "title": "Internal Server Error", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('network error', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'PUT', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + error: new Error('Failed to fetch'), + }, + ]); + + const updateClient = createUpdateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + expect(await updateClient('4d783b77-eb09-4603-b99b-f590b605eaa9', dummyModelRequest)).toMatchInlineSnapshot(` + NetworkError { + "detail": undefined, + "instance": undefined, + "title": "Failed to fetch", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('unknown response', async () => { + const dummyModelRequest: DummyModelRequest = { + name: 'Dummy', + }; + + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'PUT', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dummyModelRequest), + }, + ], + return: Promise.resolve({ + status: 418, + json: () => Promise.resolve({}), + } as Response), + }, + ]); + + const updateClient = createUpdateClient( + fetch, + 'https://petstore.test/api/models', + dummyModelRequestSchema, + dummyModelResponseSchema, + ); + + try { + await updateClient('4d783b77-eb09-4603-b99b-f590b605eaa9', dummyModelRequest); + throw new Error('expect fail'); + } catch (e) { + expect(e).toMatchInlineSnapshot('[Error: expect fail]'); + } + + expect(fetchMocks.length).toBe(0); + }); +}); + +describe('createDeleteClient', () => { + test('success', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'DELETE', + headers: { + Accept: 'application/json', + }, + }, + ], + return: Promise.resolve({ + status: 204, + json: () => Promise.resolve(undefined), + } as Response), + }, + ]); + + const deleteClient = createDeleteClient(fetch, 'https://petstore.test/api/models'); + + expect(await deleteClient('4d783b77-eb09-4603-b99b-f590b605eaa9')).toBeUndefined(); + + expect(fetchMocks.length).toBe(0); + }); + + test('not found', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'DELETE', + headers: { + Accept: 'application/json', + }, + }, + ], + return: Promise.resolve({ + status: 404, + json: () => + Promise.resolve({ + title: 'Not Found', + detail: 'There is no model with id "4d783b77-eb09-4603-b99b-f590b605eaa9"', + instance: '0123456789abcdef', + }), + } as Response), + }, + ]); + + const deleteClient = createDeleteClient(fetch, 'https://petstore.test/api/models'); + + expect(await deleteClient('4d783b77-eb09-4603-b99b-f590b605eaa9')).toMatchInlineSnapshot(` + NotFound { + "detail": "There is no model with id "4d783b77-eb09-4603-b99b-f590b605eaa9"", + "instance": "0123456789abcdef", + "title": "Not Found", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('internal server error', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'DELETE', + headers: { + Accept: 'application/json', + }, + }, + ], + return: Promise.resolve({ + status: 500, + json: () => + Promise.resolve({ + title: 'Internal Server Error', + instance: '0123456789abcdef', + }), + } as Response), + }, + ]); + + const deleteClient = createDeleteClient(fetch, 'https://petstore.test/api/models'); + + expect(await deleteClient('4d783b77-eb09-4603-b99b-f590b605eaa9')).toMatchInlineSnapshot(` + InternalServerError { + "detail": undefined, + "instance": "0123456789abcdef", + "title": "Internal Server Error", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('network error', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'DELETE', + headers: { + Accept: 'application/json', + }, + }, + ], + error: new Error('Failed to fetch'), + }, + ]); + + const deleteClient = createDeleteClient(fetch, 'https://petstore.test/api/models'); + + expect(await deleteClient('4d783b77-eb09-4603-b99b-f590b605eaa9')).toMatchInlineSnapshot(` + NetworkError { + "detail": undefined, + "instance": undefined, + "title": "Failed to fetch", + } + `); + + expect(fetchMocks.length).toBe(0); + }); + + test('unknown response', async () => { + const [fetch, fetchMocks] = useFunctionMock([ + { + parameters: [ + 'https://petstore.test/api/models/4d783b77-eb09-4603-b99b-f590b605eaa9', + { + method: 'DELETE', + headers: { + Accept: 'application/json', + }, + }, + ], + return: Promise.resolve({ + status: 418, + json: () => Promise.resolve({}), + } as Response), + }, + ]); + + const deleteClient = createDeleteClient(fetch, 'https://petstore.test/api/models'); + + try { + await deleteClient('4d783b77-eb09-4603-b99b-f590b605eaa9'); + throw new Error('expect fail'); + } catch (e) { + expect(e).toMatchInlineSnapshot('[Error: expect fail]'); + } + + expect(fetchMocks.length).toBe(0); + }); +}); diff --git a/tests/client/error.test.ts b/tests/client/error.test.ts new file mode 100644 index 0000000..1c2fe96 --- /dev/null +++ b/tests/client/error.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, test } from 'vitest'; +import type { InvalidParameter } from '../../src/client/error'; +import { BadRequest, NetworkError, UnprocessableEntity, createInvalidParametersByName } from '../../src/client/error'; + +describe('createInvalidParametersByName', () => { + test('with network error', () => { + const invalidParametersByName = createInvalidParametersByName(new NetworkError({ title: 'network error' })); + + expect(invalidParametersByName.size).toBe(0); + }); + + test('with bad request and without invalid parameters', () => { + const invalidParametersByName = createInvalidParametersByName(new BadRequest({ title: 'bad request' })); + + expect(invalidParametersByName.size).toBe(0); + }); + + test('with bad request and with invalid parameters', () => { + const invalidParameters: Array = [ + { name: 'name', reason: 'wrong type', details: { key: 'value1' } }, + { name: 'name', reason: 'not empty', details: { key: 'value2' } }, + { name: 'description', reason: 'to long', details: { key: 'value3' } }, + ]; + + const invalidParametersByName = createInvalidParametersByName( + new BadRequest({ title: 'bad request', invalidParameters }), + ); + + expect(invalidParametersByName.has('name')).toBeTruthy(); + + const InvalidParametersOfName = invalidParametersByName.get('name'); + + expect(InvalidParametersOfName).toBeInstanceOf(Array); + expect(InvalidParametersOfName).toHaveLength(2); + + expect(InvalidParametersOfName instanceof Array ? InvalidParametersOfName[0] : null).toBe(invalidParameters[0]); + expect(InvalidParametersOfName instanceof Array ? InvalidParametersOfName[1] : null).toBe(invalidParameters[1]); + + const InvalidParametersOfDescription = invalidParametersByName.get('description'); + + expect(InvalidParametersOfDescription).toBeInstanceOf(Array); + expect(InvalidParametersOfDescription).toHaveLength(1); + + expect(InvalidParametersOfDescription instanceof Array ? InvalidParametersOfDescription[0] : null).toBe( + invalidParameters[2], + ); + }); + + test('with unprocessable entity and with invalid parameters', () => { + const invalidParameters: Array = [ + { name: 'name', reason: 'wrong type', details: { key: 'value1' } }, + { name: 'name', reason: 'not empty', details: { key: 'value2' } }, + { name: 'description', reason: 'to long', details: { key: 'value3' } }, + ]; + + const invalidParametersByName = createInvalidParametersByName( + new UnprocessableEntity({ title: 'unprocessable entity', invalidParameters }), + ); + + expect(invalidParametersByName.has('name')).toBeTruthy(); + + const InvalidParametersOfName = invalidParametersByName.get('name'); + + expect(InvalidParametersOfName).toBeInstanceOf(Array); + expect(InvalidParametersOfName).toHaveLength(2); + + expect(InvalidParametersOfName instanceof Array ? InvalidParametersOfName[0] : null).toBe(invalidParameters[0]); + expect(InvalidParametersOfName instanceof Array ? InvalidParametersOfName[1] : null).toBe(invalidParameters[1]); + + const InvalidParametersOfDescription = invalidParametersByName.get('description'); + + expect(InvalidParametersOfDescription).toBeInstanceOf(Array); + expect(InvalidParametersOfDescription).toHaveLength(1); + + expect(InvalidParametersOfDescription instanceof Array ? InvalidParametersOfDescription[0] : null).toBe( + invalidParameters[2], + ); + }); +}); diff --git a/tests/client/pet.test.ts b/tests/client/pet.test.ts new file mode 100644 index 0000000..82d934b --- /dev/null +++ b/tests/client/pet.test.ts @@ -0,0 +1,10 @@ +import { test, expect } from 'vitest'; +import { listPetsClient, createPetClient, readPetClient, updatePetClient, deletePetClient } from '../../src/client/pet'; + +test('functions', () => { + expect(typeof listPetsClient).toBe('function'); + expect(typeof createPetClient).toBe('function'); + expect(typeof readPetClient).toBe('function'); + expect(typeof updatePetClient).toBe('function'); + expect(typeof deletePetClient).toBe('function'); +}); diff --git a/tests/component/form/pet-filters-form.test.tsx b/tests/component/form/pet-filters-form.test.tsx new file mode 100644 index 0000000..b02f2d8 --- /dev/null +++ b/tests/component/form/pet-filters-form.test.tsx @@ -0,0 +1,143 @@ +/** @jsxImportSource vue */ + +import { test, expect, vi } from 'vitest'; +import { formatHtml } from '../../formatter'; +import { PetFiltersForm } from '../../../src/component/form/pet-filters-form'; +import { BadRequest, NetworkError } from '../../../src/client/error'; +import { userEvent } from '@testing-library/user-event'; +import { render, screen } from '@testing-library/vue'; +import type { PetFilters } from '../../../src/model/pet'; + +test('default', () => { + const httpError = undefined; + const initialPetFilters = {}; + const submitPetFilters = () => { }; + + const { container } = render( + , + ); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+
+ +
+
+
+ " + `); +}); + +test('network error', () => { + const httpError = new NetworkError({ title: 'network error' }); + const initialPetFilters = {}; + const submitPetFilters = () => { }; + + render( + , + ); +}); + +test('bad request', () => { + const httpError = new BadRequest({ + title: 'bad request', + }); + const initialPetFilters = {}; + const submitPetFilters = () => { }; + + render( + , + ); +}); + +test('bad request - with query string name', () => { + const httpError = new BadRequest({ + title: 'bad request', + invalidParameters: [{ name: 'filters[name]', reason: 'reason' }], + }); + const initialPetFilters = {}; + const submitPetFilters = () => { }; + + const { container } = render( + , + ); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+
+ +
+
+
+ " + `); +}); + +test('submit with name', async () => { + const httpError = undefined; + const initialPetFilters = { name: 'Brown' }; + const submitPetFilters = vi.fn((petFilters: PetFilters) => { + expect(petFilters).toEqual({ name: 'Brownie' }); + }); + + render( + , + ); + + const nameField = await screen.findByTestId('pet-filters-form-name'); + + await userEvent.type(nameField, 'ie'); + + const submitButton = await screen.findByTestId('pet-filters-form-submit'); + + await userEvent.click(submitButton); + + expect(submitPetFilters).toHaveBeenCalledTimes(1); +}); + +test('submit without name', async () => { + const httpError = undefined; + const initialPetFilters = { name: '' }; + const submitPetFilters = vi.fn((petFilters: PetFilters) => { + expect(petFilters).toEqual({ name: undefined }); + }); + + render( + , + ); + + const nameField = await screen.findByTestId('pet-filters-form-name'); + + await userEvent.type(nameField, '{enter}'); + + expect(submitPetFilters).toHaveBeenCalledTimes(1); +}); diff --git a/tests/component/form/pet-form.test.tsx b/tests/component/form/pet-form.test.tsx new file mode 100644 index 0000000..14290af --- /dev/null +++ b/tests/component/form/pet-form.test.tsx @@ -0,0 +1,252 @@ +/** @jsxImportSource vue */ + +import { test, expect, vi } from 'vitest'; +import { formatHtml } from '../../formatter'; +import { PetForm } from '../../../src/component/form/pet-form'; +import { BadRequest, NetworkError } from '../../../src/client/error'; +import type { PetRequest } from '../../../src/model/pet'; +import { userEvent } from '@testing-library/user-event'; +import { render, screen } from '@testing-library/vue'; + +test('without initial pet', () => { + const httpError = undefined; + const initialPet = undefined; + const submitPet = () => { }; + + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+
+ +
+
Vaccinations
+
+ +
+
+ +
+
+
+ " + `); +}); + +test('with initial pet', () => { + const httpError = undefined; + const initialPet = { name: 'Brownie', tag: '0001-000', vaccinations: [{ name: 'rabies' }] }; + const submitPet = () => { }; + + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+
+ +
+
Vaccinations
+
+
+ +
+ +
+
+ +
+
+
+ " + `); +}); + +test('network error', () => { + const httpError = new NetworkError({ title: 'network error' }); + const initialPet = { name: 'Brownie', tag: '0001-000', vaccinations: [{ name: 'rabies' }] }; + const submitPet = () => { }; + + render(); +}); + +test('bad request', () => { + const httpError = new BadRequest({ + title: 'bad request', + }); + const initialPet = { name: 'Brownie', tag: '0001-000', vaccinations: [{ name: 'rabies' }] }; + const submitPet = () => { }; + + render(); +}); + +test('bad request - with query string name', () => { + const httpError = new BadRequest({ + title: 'bad request', + invalidParameters: [ + { name: 'name', reason: 'reason1' }, + { name: 'vaccinations[0][name]', reason: 'reason2' }, + ], + }); + const initialPet = { name: 'Brownie', tag: '0001-000', vaccinations: [{ name: 'rabies' }] }; + const submitPet = () => { }; + + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+
+ +
+
Vaccinations
+
+
+ +
+ +
+
+ +
+
+
+ " + `); +}); + +test('submit with name', async () => { + const httpError = undefined; + const initialPet = { name: 'Brown', vaccinations: [{ name: 'rabie' }, { name: 'cat cold' }] }; + const submitPet = vi.fn((pet: PetRequest) => { + expect(pet).toEqual({ name: 'Brownie', vaccinations: [{ name: 'rabies' }, { name: 'cat cold' }, { name: '' }] }); + }); + + render(); + + const nameField = await screen.findByTestId('pet-form-name'); + + await userEvent.type(nameField, 'ie'); + + const vaccinationNameField = await screen.findByTestId('pet-form-vaccinations-0-name'); + + await userEvent.type(vaccinationNameField, 's'); + + const addVaccination = await screen.findByTestId('pet-form-add-vaccination'); + + await userEvent.click(addVaccination); + await userEvent.click(addVaccination); + + const removeVaccination = await screen.findByTestId('pet-form-remove-vaccination-3'); + + await userEvent.click(removeVaccination); + + const submitButton = await screen.findByTestId('pet-form-submit'); + + await userEvent.click(submitButton); + + expect(submitPet).toHaveBeenCalledTimes(1); +}); diff --git a/tests/component/page/home.test.tsx b/tests/component/page/home.test.tsx new file mode 100644 index 0000000..f1a3883 --- /dev/null +++ b/tests/component/page/home.test.tsx @@ -0,0 +1,17 @@ +/** @jsxImportSource vue */ + +import { render } from '@testing-library/vue'; +import Home from '../../../src/component/page/home'; +import { test, expect } from 'vitest'; +import { formatHtml } from '../../formatter'; + +test('default', () => { + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+

Home

+
+ " + `); +}); diff --git a/tests/component/page/not-found.test.tsx b/tests/component/page/not-found.test.tsx new file mode 100644 index 0000000..dc2fd3b --- /dev/null +++ b/tests/component/page/not-found.test.tsx @@ -0,0 +1,17 @@ +/** @jsxImportSource vue */ + +import { render } from '@testing-library/vue'; +import NotFound from '../../../src/component/page/not-found'; +import { test, expect } from 'vitest'; +import { formatHtml } from '../../formatter'; + +test('default', () => { + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+

Not Found

+
+ " + `); +}); diff --git a/tests/component/page/pet/create.test.tsx b/tests/component/page/pet/create.test.tsx new file mode 100644 index 0000000..c2a8f17 --- /dev/null +++ b/tests/component/page/pet/create.test.tsx @@ -0,0 +1,178 @@ +/** @jsxImportSource vue */ + +import { vi, test, expect } from 'vitest'; +import type { PetRequest, PetResponse } from '../../../../src/model/pet'; +import { formatHtml } from '../../../formatter'; +import { UnprocessableEntity } from '../../../../src/client/error'; +import type { HttpError } from '../../../../src/client/error'; +import { render, screen } from '@testing-library/vue'; +import type { createPetClient } from '../../../../src/client/pet'; +import { defineComponent } from 'vue'; +import { createRouter, createWebHistory, RouterView } from 'vue-router'; +import { userEvent } from '@testing-library/user-event'; + +let mockCreatePetClient: typeof createPetClient; + +vi.mock('../../../../src/client/pet', () => { + return { + createPetClient: (pet: PetRequest) => { + return mockCreatePetClient(pet); + }, + }; +}); + +vi.mock('../../../../src/component/form/pet-form', () => { + return { + __esModule: true, + PetForm: defineComponent((props: { httpError: HttpError | undefined; initialPet?: PetRequest; submitPet: (pet: PetRequest) => void; }) => { + const onSubmit = () => { + props.submitPet({ name: 'Brownie', vaccinations: [] }); + }; + + return () => ( + List +
+
+ " + `); +}); + +test('unprocessable entity', async () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + mockCreatePetClient = async (_: PetRequest) => { + return new Promise((resolve) => + resolve(new UnprocessableEntity({ title: 'unprocessable entity' })), + ); + }; + + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'Home', component: defineComponent(() => () =>
) }, + { path: '/pet/create', name: 'PetCreate', component: () => import('../../../../src/component/page/pet/create') }, + ] + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet/create'); + + const testButton = await screen.findByTestId('pet-form-submit'); + + await userEvent.click(testButton); + + await screen.findByTestId('http-error'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+
+

unprocessable entity

+ +
+

Pet Create

+ List +
+
+ " + `); +}); + +test('successful', async () => { + mockCreatePetClient = async (petRequest: PetRequest) => { + const petResponse: PetResponse = { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2005-08-15T15:52:01+00:00', + ...petRequest, + _links: {}, + }; + return new Promise((resolve) => resolve(petResponse)); + }; + + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'Home', component: defineComponent(() => () =>
) }, + { path: '/pet', name: 'PetList', component: defineComponent(() => () =>
) }, + { path: '/pet/create', name: 'PetCreate', component: () => import('../../../../src/component/page/pet/create') }, + ] + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet/create'); + + const testButton = await screen.findByTestId('pet-form-submit'); + + await userEvent.click(testButton); + + await screen.findByTestId('page-pet-list-mock'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+ " + `); +}); diff --git a/tests/component/page/pet/list.test.tsx b/tests/component/page/pet/list.test.tsx new file mode 100644 index 0000000..110d8c6 --- /dev/null +++ b/tests/component/page/pet/list.test.tsx @@ -0,0 +1,806 @@ +/** @jsxImportSource vue */ + +import { vi, test, expect } from 'vitest'; +import type { PetFilters, PetListRequest, PetListResponse } from '../../../../src/model/pet'; +import { formatHtml } from '../../../formatter'; +import type { deletePetClient, listPetsClient } from '../../../../src/client/pet'; +import type { HttpError } from '../../../../src/client/error'; +import { BadRequest, NetworkError } from '../../../../src/client/error'; +import { userEvent } from '@testing-library/user-event'; +import { render, screen } from '@testing-library/vue'; +import { createRouter, createWebHistory, RouterView } from 'vue-router'; +import { defineComponent } from 'vue'; + +let mockDeletePetClient: typeof deletePetClient; +let mockListPetsClient: typeof listPetsClient; + +vi.mock('../../../../src/client/pet', () => { + return { + deletePetClient: (id: string) => { + return mockDeletePetClient(id); + }, + listPetsClient: (petListRequest: PetListRequest) => { + return mockListPetsClient(petListRequest); + }, + }; +}); + +vi.mock('../../../../src/component/form/pet-filters-form', () => { + return { + __esModule: true, + PetFiltersForm: defineComponent((props: { httpError: HttpError | undefined; initialPetFilters: PetFilters; submitPetFilters: (petFilters: PetFilters) => void; }) => { + const onClick = () => { + props.submitPetFilters({ name: 'Brownie' }); + }; + + return () => ( + +
+
+
+
+
+ Id +
+
+ CreatedAt +
+
+ UpdatedAt +
+
+ Name (||) +
+
+ Tag +
+
+ Actions +
+
+
+
+
+
+ 4d783b77-eb09-4603-b99b-f590b605eaa9 +
+
+ 15.08.2005 - 17:52:01 +
+
+ 15.08.2005 - 17:55:01 +
+
+ Brownie +
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+ " + `); +}); + +test('bad request', async () => { + mockListPetsClient = async (petListRequest: PetListRequest) => { + expect(petListRequest).toEqual({ + offset: 0, + limit: 10, + filters: {}, + sort: {}, + }); + + return new Promise((resolve) => resolve(new BadRequest({ title: 'bad request' }))); + }; + + mockDeletePetClient = async () => undefined; + + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'Home', component: defineComponent(() => () =>
) }, + { path: '/pet', name: 'PetList', component: () => import('../../../../src/component/page/pet/list') }, + ] + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet'); + + await screen.findByTestId('page-pet-list'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+
+

bad request

+ +
+

Pet List

+ +
+
+ " + `); +}); + +test('default maximal', async () => { + mockListPetsClient = async (petListRequest: PetListRequest) => { + expect(petListRequest).toEqual({ + offset: 0, + limit: 10, + filters: {}, + sort: {}, + }); + + return { + offset: 0, + limit: 10, + filters: {}, + sort: {}, + count: 1, + items: [ + { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2005-08-15T15:52:01+00:00', + updatedAt: '2005-08-15T15:55:01+00:00', + name: 'Brownie', + tag: '0001-000', + vaccinations: [{ name: 'Rabies' }], + _links: { + read: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + update: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + delete: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + }, + }, + ], + _links: { + create: { href: '/api/pets' }, + }, + }; + }; + + mockDeletePetClient = async () => undefined; + + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'Home', component: defineComponent(() => () =>
) }, + { path: '/pet', name: 'PetList', component: () => import('../../../../src/component/page/pet/list') }, + ] + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet'); + + await screen.findByTestId('page-pet-list'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+ +

Pet List

+
+ Create +
+
+
+
+
+ Id +
+
+ CreatedAt +
+
+ UpdatedAt +
+
+ Name (||) +
+
+ Tag +
+
+ Actions +
+
+
+
+
+
+ 4d783b77-eb09-4603-b99b-f590b605eaa9 +
+
+ 15.08.2005 - 17:52:01 +
+
+ 15.08.2005 - 17:55:01 +
+
+ Brownie +
+
+ 0001-000 +
+
+ ReadUpdate +
+
+
+
+
+
+ +
+
+
+
+ " + `); +}); + +test('delete error', async () => { + mockListPetsClient = async (petListRequest: PetListRequest) => { + expect(petListRequest).toEqual({ + offset: 0, + limit: 10, + filters: {}, + sort: {}, + }); + + return { + offset: 0, + limit: 10, + filters: {}, + sort: {}, + count: 1, + items: [ + { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2005-08-15T15:52:01+00:00', + updatedAt: '2005-08-15T15:55:01+00:00', + name: 'Brownie', + tag: '0001-000', + vaccinations: [{ name: 'Rabies' }], + _links: { + read: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + update: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + delete: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + }, + }, + ], + _links: { + create: { href: '/api/pets' }, + }, + }; + }; + + mockDeletePetClient = async (id: string) => { + expect(id).toBe('4d783b77-eb09-4603-b99b-f590b605eaa9'); + + return new NetworkError({ title: 'network error' }); + }; + + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'Home', component: defineComponent(() => () =>
) }, + { path: '/pet', name: 'PetList', component: () => import('../../../../src/component/page/pet/list') }, + ] + }); + + render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet'); + + await screen.findByTestId('page-pet-list'); + + const removeButton = await screen.findByTestId('remove-pet-0'); + + await userEvent.click(removeButton); + + await screen.findByTestId('http-error'); + + expect(formatHtml((await screen.findByTestId('http-error')).outerHTML)).toMatchInlineSnapshot(` + "
+

network error

+ +
+ " + `); +}); + +test('delete success', async () => { + const petListCalls: Array<{ parameters: [PetListRequest]; return: Promise; }> = [ + { + parameters: [ + { + offset: 0, + limit: 10, + filters: {}, + sort: {}, + }, + ], + return: Promise.resolve({ + offset: 0, + limit: 10, + filters: {}, + sort: {}, + count: 1, + items: [ + { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2005-08-15T15:52:01+00:00', + updatedAt: '2005-08-15T15:55:01+00:00', + name: 'Brownie', + tag: '0001-000', + vaccinations: [{ name: 'Rabies' }], + _links: { + read: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + update: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + delete: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + }, + }, + ], + _links: { + create: { href: '/api/pets' }, + }, + }), + }, + { + parameters: [ + { + offset: 0, + limit: 10, + filters: {}, + sort: {}, + }, + ], + return: Promise.resolve({ + offset: 0, + limit: 10, + filters: {}, + sort: {}, + count: 1, + items: [ + { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2005-08-15T15:52:01+00:00', + updatedAt: '2005-08-15T15:55:01+00:00', + name: 'Brownie', + tag: '0001-000', + vaccinations: [{ name: 'Rabies' }], + _links: { + read: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + update: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + delete: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + }, + }, + ], + _links: { + create: { href: '/api/pets' }, + }, + }), + }, + ]; + + mockListPetsClient = async (petListRequest: PetListRequest) => { + const petListCall = petListCalls.shift(); + if (!petListCall) { + throw new Error('Missing call'); + } + + expect(petListRequest).toEqual(petListCall.parameters[0]); + + return petListCall.return; + }; + + mockDeletePetClient = async (id: string) => { + expect(id).toBe('4d783b77-eb09-4603-b99b-f590b605eaa9'); + + return undefined; + }; + + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'Home', component: defineComponent(() => () =>
) }, + { path: '/pet', name: 'PetList', component: () => import('../../../../src/component/page/pet/list') }, + ] + }); + + render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet'); + + await screen.findByTestId('page-pet-list'); + + const removeButton = await screen.findByTestId('remove-pet-0'); + + await userEvent.click(removeButton); +}); + +test('submit', async () => { + const petListCalls: Array<{ parameters: [PetListRequest]; return: Promise; }> = [ + { + parameters: [ + { + offset: 0, + limit: 10, + filters: {}, + sort: {}, + }, + ], + return: Promise.resolve({ + offset: 0, + limit: 10, + filters: {}, + sort: {}, + count: 1, + items: [ + { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2005-08-15T15:52:01+00:00', + updatedAt: '2005-08-15T15:55:01+00:00', + name: 'Brownie', + tag: '0001-000', + vaccinations: [{ name: 'Rabies' }], + _links: { + read: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + update: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + delete: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + }, + }, + ], + _links: { + create: { href: '/api/pets' }, + }, + }), + }, + { + parameters: [ + { + offset: 0, + limit: 10, + filters: {}, + sort: { name: 'desc' }, + }, + ], + return: Promise.resolve({ + offset: 0, + limit: 10, + filters: {}, + sort: { name: 'desc' }, + count: 1, + items: [ + { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2005-08-15T15:52:01+00:00', + updatedAt: '2005-08-15T15:55:01+00:00', + name: 'Blacky', + tag: '0002-000', + vaccinations: [{ name: 'Rabies' }], + _links: { + read: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + update: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + delete: { href: '/api/pets/4d783b77-eb09-4603-b99b-f590b605eaa9' }, + }, + }, + ], + _links: { + create: { href: '/api/pets' }, + }, + }), + }, + { + parameters: [ + { + offset: 0, + limit: 10, + filters: { name: 'Brownie' }, + sort: { name: 'desc' }, + }, + ], + return: Promise.resolve({ + offset: 0, + limit: 10, + filters: { name: 'Brownie' }, + sort: { name: 'desc' }, + count: 0, + items: [], + _links: { + create: { href: '/api/pets' }, + }, + }), + }, + { + parameters: [ + { + offset: 10, + limit: 10, + filters: { name: 'Brownie' }, + sort: { name: 'desc' }, + }, + ], + return: Promise.resolve({ + offset: 10, + limit: 10, + filters: { name: 'Brownie' }, + sort: { name: 'desc' }, + count: 0, + items: [], + _links: { + create: { href: '/api/pets' }, + }, + }), + }, + ]; + + mockListPetsClient = async (petListRequest: PetListRequest) => { + const petListCall = petListCalls.shift(); + if (!petListCall) { + throw new Error('Missing call'); + } + + expect(petListRequest).toEqual(petListCall.parameters[0]); + + return petListCall.return; + }; + + mockDeletePetClient = async () => undefined; + + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'Home', component: defineComponent(() => () =>
) }, + { path: '/pet', name: 'PetList', component: () => import('../../../../src/component/page/pet/list') }, + ] + }); + + render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet'); + await screen.findByTestId('page-pet-list'); + + const petSortNameSubmitButton = await screen.findByTestId('pet-sort-name-desc'); + + await userEvent.click(petSortNameSubmitButton); + + await screen.findByTestId('page-pet-list'); + + const petFiltersFormSubmitButton = await screen.findByTestId('pet-filters-form-submit'); + + await userEvent.click(petFiltersFormSubmitButton); + + await screen.findByTestId('page-pet-list'); + + const paginationNextButton = await screen.findByTestId('pagination-next'); + + await userEvent.click(paginationNextButton); +}); diff --git a/tests/component/page/pet/read.test.tsx b/tests/component/page/pet/read.test.tsx new file mode 100644 index 0000000..c0a0ca2 --- /dev/null +++ b/tests/component/page/pet/read.test.tsx @@ -0,0 +1,205 @@ +/** @jsxImportSource vue */ + +import { vi, test, expect } from 'vitest'; +import { formatHtml } from '../../../formatter'; +import { NotFound } from '../../../../src/client/error'; +import { render, screen } from '@testing-library/vue'; +import type { readPetClient } from '../../../../src/client/pet'; +import { createRouter, createWebHistory, RouterView } from 'vue-router'; +import { defineComponent } from 'vue'; +import type { PetResponse } from '../../../../src/model/pet'; + +let mockReadPetClient: typeof readPetClient; + +vi.mock('../../../../src/client/pet', () => { + return { + readPetClient: (id: string) => { + return mockReadPetClient(id); + }, + }; +}); + +test('not found', async () => { + mockReadPetClient = async (id: string) => { + expect(id).toBe('4d783b77-eb09-4603-b99b-f590b605eaa9'); + + return new Promise((resolve) => resolve(new NotFound({ title: 'title' }))); + }; + + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'Home', component: defineComponent(() => () =>
) }, + { path: '/pet/:id', name: 'PetRead', component: () => import('../../../../src/component/page/pet/read') }, + ] + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet/4d783b77-eb09-4603-b99b-f590b605eaa9'); + + await screen.findByTestId('page-pet-read'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+
+

title

+ +
+

Pet Read

+ List +
+
+ " + `); +}); + +test('success without vaccinations', async () => { + const petResponse: PetResponse = { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2005-08-15T15:52:01+00:00', + updatedAt: '2005-08-15T15:55:01+00:00', + name: 'Brownie', + tag: '0001-000', + vaccinations: [], + _links: {}, + }; + + mockReadPetClient = async (id: string) => { + expect(id).toBe('4d783b77-eb09-4603-b99b-f590b605eaa9'); + + return new Promise((resolve) => resolve(petResponse)); + }; + + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'Home', component: defineComponent(() => () =>
) }, + { path: '/pet/:id', name: 'PetRead', component: () => import('../../../../src/component/page/pet/read') }, + ] + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet/4d783b77-eb09-4603-b99b-f590b605eaa9'); + + await screen.findByTestId('page-pet-read'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+ +

Pet Read

+
+
+
Id
+
4d783b77-eb09-4603-b99b-f590b605eaa9
+
CreatedAt
+
15.08.2005 - 17:52:01
+
UpdatedAt
+
15.08.2005 - 17:55:01
+
Name
+
Brownie
+
Tag
+
0001-000
+
Vaccinations
+
+
+
+ List +
+
+ " + `); +}); + +test('success with vaccinations', async () => { + const petResponse: PetResponse = { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2005-08-15T15:52:01+00:00', + updatedAt: '2005-08-15T15:55:01+00:00', + name: 'Brownie', + tag: '0001-000', + vaccinations: [{ name: 'Rabies' }], + _links: {}, + }; + + mockReadPetClient = async (id: string) => { + expect(id).toBe('4d783b77-eb09-4603-b99b-f590b605eaa9'); + + return new Promise((resolve) => resolve(petResponse)); + }; + + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'Home', component: defineComponent(() => () =>
) }, + { path: '/pet/:id', name: 'PetRead', component: () => import('../../../../src/component/page/pet/read') }, + ] + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet/4d783b77-eb09-4603-b99b-f590b605eaa9'); + + await screen.findByTestId('page-pet-read'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+ +

Pet Read

+
+
+
Id
+
4d783b77-eb09-4603-b99b-f590b605eaa9
+
CreatedAt
+
15.08.2005 - 17:52:01
+
UpdatedAt
+
15.08.2005 - 17:55:01
+
Name
+
Brownie
+
Tag
+
0001-000
+
Vaccinations
+
+
    +
  • Rabies
  • +
+
+
+
+ List +
+
+ " + `); +}); diff --git a/tests/component/page/pet/update.test.tsx b/tests/component/page/pet/update.test.tsx new file mode 100644 index 0000000..cad71a8 --- /dev/null +++ b/tests/component/page/pet/update.test.tsx @@ -0,0 +1,278 @@ +/** @jsxImportSource vue */ + +import { vi, test, expect } from 'vitest'; +import type { PetRequest, PetResponse } from '../../../../src/model/pet'; +import { formatHtml } from '../../../formatter'; +import type { HttpError } from '../../../../src/client/error'; +import { NotFound, UnprocessableEntity } from '../../../../src/client/error'; +import type { readPetClient, updatePetClient } from '../../../../src/client/pet'; +import { render, screen } from '@testing-library/vue'; +import { userEvent } from '@testing-library/user-event'; +import { defineComponent } from 'vue'; +import { createRouter, createWebHistory, RouterView } from 'vue-router'; + +let mockReadPetClient: typeof readPetClient; +let mockUpdatePetClient: typeof updatePetClient; + +vi.mock('../../../../src/client/pet', () => { + return { + readPetClient: (id: string) => { + return mockReadPetClient(id); + }, + updatePetClient: (id: string, pet: PetRequest) => { + return mockUpdatePetClient(id, pet); + }, + }; +}); + +vi.mock('../../../../src/component/form/pet-form', () => { + return { + __esModule: true, + PetForm: defineComponent((props: { httpError: HttpError | undefined; initialPet?: PetRequest; submitPet: (pet: PetRequest) => void; }) => { + const onSubmit = () => { + props.submitPet({ name: 'Brownie', vaccinations: [] }); + }; + + return () => ( + List +
+
+ " + `); +}); + +test('unprocessable entity', async () => { + const petResponse: PetResponse = { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2005-08-15T15:52:01+00:00', + updatedAt: '2005-08-15T15:55:01+00:00', + name: 'Brownie', + tag: '0001-000', + vaccinations: [{ name: 'Rabies' }], + _links: {}, + }; + + mockReadPetClient = async (id: string) => { + expect(id).toBe('4d783b77-eb09-4603-b99b-f590b605eaa9'); + + return new Promise((resolve) => resolve(petResponse)); + }; + + mockUpdatePetClient = async (id: string) => { + expect(id).toBe('4d783b77-eb09-4603-b99b-f590b605eaa9'); + + return new Promise((resolve) => + resolve(new UnprocessableEntity({ title: 'unprocessable entity' })), + ); + }; + + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'Home', component: defineComponent(() => () =>
) }, + { path: '/pet/:id/update', name: 'PetUpdate', component: () => import('../../../../src/component/page/pet/update') }, + ] + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet/4d783b77-eb09-4603-b99b-f590b605eaa9/update'); + + const testButton = await screen.findByTestId('pet-form-submit'); + + await userEvent.click(testButton); + + await screen.findByTestId('http-error'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+
+

unprocessable entity

+ +
+

Pet Update

+ List +
+
+ " + `); +}); + +test('successful', async () => { + const petRequest: PetRequest = { + name: 'Brownie', + tag: '0001-000', + vaccinations: [{ name: 'Rabies' }], + }; + + const petResponse: PetResponse = { + id: '4d783b77-eb09-4603-b99b-f590b605eaa9', + createdAt: '2005-08-15T15:52:01+00:00', + updatedAt: '2005-08-15T15:55:01+00:00', + ...petRequest, + _links: {}, + }; + + mockReadPetClient = async (id: string) => { + expect(id).toBe('4d783b77-eb09-4603-b99b-f590b605eaa9'); + + return new Promise((resolve) => resolve(petResponse)); + }; + + mockUpdatePetClient = async (id: string, petRequest: PetRequest) => { + expect(id).toBe('4d783b77-eb09-4603-b99b-f590b605eaa9'); + + expect(petRequest).toEqual(petRequest); + + return new Promise((resolve) => resolve(petResponse)); + }; + + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'Home', component: defineComponent(() => () =>
) }, + { path: '/pet', name: 'PetList', component: defineComponent(() => () =>
) }, + { path: '/pet/:id/update', name: 'PetUpdate', component: () => import('../../../../src/component/page/pet/update') }, + ] + }); + + const { container } = render(, { + global: { + plugins: [router], + }, + }); + + await router.push('/pet/4d783b77-eb09-4603-b99b-f590b605eaa9/update'); + + const testButton = await screen.findByTestId('pet-form-submit'); + + await userEvent.click(testButton); + + await screen.findByTestId('page-pet-list-mock'); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+ " + `); +}); diff --git a/tests/component/partial/http-error.test.tsx b/tests/component/partial/http-error.test.tsx new file mode 100644 index 0000000..f960e37 --- /dev/null +++ b/tests/component/partial/http-error.test.tsx @@ -0,0 +1,50 @@ +/** @jsxImportSource vue */ + +import { render } from '@testing-library/vue'; +import { HttpError as HttpErrorPartial } from '../../../src/component/partial/http-error'; +import { test, expect } from 'vitest'; +import { formatHtml } from '../../formatter'; +import { BadRequestOrUnprocessableEntity, HttpError } from '../../../src/client/error'; + +test('minimal', () => { + const httpError = new HttpError({ + title: 'This is the title', + }); + + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+

This is the title

+ +
+
+ " + `); +}); + +test('maximal', () => { + const httpError = new BadRequestOrUnprocessableEntity({ + title: 'This is the title', + detail: 'This is the detail', + instance: 'This is the instance', + invalidParameters: [{ name: 'Invalid Parameter Name', reason: 'Invalid Parameter Reason' }], + }); + + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
+

This is the title

+

This is the detail

+

This is the instance

+
    +
  • Invalid Parameter Name: Invalid Parameter Reason
  • +
+
+
+ " + `); +}); diff --git a/tests/component/partial/pagination.test.tsx b/tests/component/partial/pagination.test.tsx new file mode 100644 index 0000000..969290f --- /dev/null +++ b/tests/component/partial/pagination.test.tsx @@ -0,0 +1,223 @@ +/** @jsxImportSource vue */ + +import { render, screen } from '@testing-library/vue'; +import { userEvent } from '@testing-library/user-event'; +import { test, expect } from 'vitest'; +import { formatHtml } from '../../formatter'; +import { Pagination } from '../../../src/component/partial/pagination'; + +test('max pages 1', () => { + const submitPage = (): void => { }; + + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+ " + `); +}); + +test('total pages 1', () => { + const submitPage = (): void => { }; + + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+ " + `); +}); + +test('current 1', () => { + const submitPage = (): void => { }; + + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
    + +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ " + `); +}); + +test('current 4', () => { + const submitPage = (): void => { }; + + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ " + `); +}); + +test('current 7', () => { + const submitPage = (): void => { }; + + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ " + `); +}); + +test('current 10', () => { + const submitPage = (): void => { }; + + const { container } = render(); + + expect(formatHtml(container.outerHTML)).toMatchInlineSnapshot(` + "
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • + +
+
+ " + `); +}); + +test('buttons', async () => { + const pages: number[] = []; + + const submitPage = (page: number): void => { + pages.push(page); + }; + + render(); + + for await (const element of screen.getAllByRole('button')) { + await userEvent.click(element); + } + + expect(pages).toEqual([1, 6, 4, 5, 6, 7, 8, 9, 10, 8, 10]); +}); diff --git a/tests/formatter.ts b/tests/formatter.ts new file mode 100644 index 0000000..4d57bb9 --- /dev/null +++ b/tests/formatter.ts @@ -0,0 +1,5 @@ +import { format } from 'prettier'; + +export const formatHtml = (html: string) => { + return format(html, { parser: 'html' }); +}; diff --git a/tests/hook/create-model-resource.test.ts b/tests/hook/create-model-resource.test.ts new file mode 100644 index 0000000..5970728 --- /dev/null +++ b/tests/hook/create-model-resource.test.ts @@ -0,0 +1,291 @@ +import { describe, expect, test } from 'vitest'; +import { createModelResource } from '../../src/hook/create-model-resource'; +import { useFunctionMock } from '@chubbyts/chubbyts-function-mock/dist/function-mock'; +import type { CreateClient, ReadClient, DeleteClient, ListClient, UpdateClient } from '../../src/client/client'; +import type { ModelListRequest, ModelListResponse, ModelRequest, ModelResponse } from '../../src/model/model'; +import { BadRequest } from '../../src/client/error'; + +describe('createModelResource', () => { + describe('list', () => { + test('missing client', async () => { + const { actions } = createModelResource({}); + + try { + await actions.listModel({}); + throw new Error('expect failed'); + } catch (e) { + expect(e).toMatchInlineSnapshot('[Error: Missing listClient]'); + } + }); + + test('error', async () => { + const badRequest = new BadRequest({ title: 'bad request' }); + + const [listClient, listClientMocks] = useFunctionMock>([ + { parameters: [{}], return: Promise.resolve(badRequest) }, + ]); + + const { modelList, httpError, actions } = createModelResource({ listClient }); + + expect(modelList.value).toBeUndefined(); + expect(httpError.value).toBeUndefined(); + + expect(await actions.listModel({})).toBe(false); + + expect(modelList.value).toBeUndefined(); + expect(httpError.value).toEqual(badRequest); + + expect(listClientMocks.length).toBe(0); + }); + + test('success', async () => { + const modelListResponse: ModelListResponse = { + offset: 0, + limit: 10, + filters: {}, + sort: {}, + count: 0, + items: [], + _links: {}, + }; + + const [listClient, listClientMocks] = useFunctionMock>([ + { parameters: [{}], return: Promise.resolve(modelListResponse) }, + ]); + + const { modelList, httpError, actions } = createModelResource({ listClient }); + + expect(modelList.value).toBeUndefined(); + expect(httpError.value).toBeUndefined(); + + expect(await actions.listModel({})).toBe(true); + + expect(modelList.value).toEqual(modelListResponse); + expect(httpError.value).toBeUndefined(); + + expect(listClientMocks.length).toBe(0); + }); + }); + + describe('create', () => { + test('missing client', async () => { + const { actions } = createModelResource({}); + + try { + await actions.createModel({}); + throw new Error('expect failed'); + } catch (e) { + expect(e).toMatchInlineSnapshot('[Error: Missing createClient]'); + } + }); + + test('error', async () => { + const badRequest = new BadRequest({ title: 'bad request' }); + + const [createClient, createClientMocks] = useFunctionMock>([ + { parameters: [{}], return: Promise.resolve(badRequest) }, + ]); + + const { model, httpError, actions } = createModelResource({ createClient }); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toBeUndefined(); + + expect(await actions.createModel({})).toBe(false); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toEqual(badRequest); + + expect(createClientMocks.length).toBe(0); + }); + + test('success', async () => { + const modelResponse: ModelResponse = { + id: 'ddbb7edb-8c53-4586-9844-769e1c830719', + createdAt: '2022-06-12T20:08:24.793Z', + _links: {}, + }; + + const [createClient, createClientMocks] = useFunctionMock>([ + { parameters: [{}], return: Promise.resolve(modelResponse) }, + ]); + + const { model, httpError, actions } = createModelResource({ createClient }); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toBeUndefined(); + + expect(await actions.createModel({})).toBe(true); + + expect(model.value).toEqual(modelResponse); + expect(httpError.value).toBeUndefined(); + + expect(createClientMocks.length).toBe(0); + }); + }); + + describe('read', () => { + test('missing client', async () => { + const { actions } = createModelResource({}); + + try { + await actions.readModel('ddbb7edb-8c53-4586-9844-769e1c830719'); + throw new Error('expect failed'); + } catch (e) { + expect(e).toMatchInlineSnapshot('[Error: Missing readClient]'); + } + }); + + test('error', async () => { + const badRequest = new BadRequest({ title: 'bad request' }); + + const [readClient, readClientMocks] = useFunctionMock>([ + { parameters: ['ddbb7edb-8c53-4586-9844-769e1c830719'], return: Promise.resolve(badRequest) }, + ]); + + const { model, httpError, actions } = createModelResource({ readClient }); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toBeUndefined(); + + expect(await actions.readModel('ddbb7edb-8c53-4586-9844-769e1c830719')).toBe(false); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toEqual(badRequest); + + expect(readClientMocks.length).toBe(0); + }); + + test('success', async () => { + const modelResponse: ModelResponse = { + id: 'ddbb7edb-8c53-4586-9844-769e1c830719', + createdAt: '2022-06-12T20:08:24.793Z', + _links: {}, + }; + + const [readClient, readClientMocks] = useFunctionMock>([ + { parameters: ['ddbb7edb-8c53-4586-9844-769e1c830719'], return: Promise.resolve(modelResponse) }, + ]); + + const { model, httpError, actions } = createModelResource({ readClient }); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toBeUndefined(); + + expect(await actions.readModel('ddbb7edb-8c53-4586-9844-769e1c830719')).toBe(true); + + expect(model.value).toEqual(modelResponse); + expect(httpError.value).toBeUndefined(); + + expect(readClientMocks.length).toBe(0); + }); + }); + + describe('update', () => { + test('missing client', async () => { + const { actions } = createModelResource({}); + + try { + await actions.updateModel('ddbb7edb-8c53-4586-9844-769e1c830719', {}); + throw new Error('expect failed'); + } catch (e) { + expect(e).toMatchInlineSnapshot('[Error: Missing updateClient]'); + } + }); + + test('error', async () => { + const badRequest = new BadRequest({ title: 'bad request' }); + + const [updateClient, updateClientMocks] = useFunctionMock>([ + { parameters: ['ddbb7edb-8c53-4586-9844-769e1c830719', {}], return: Promise.resolve(badRequest) }, + ]); + + const { model, httpError, actions } = createModelResource({ updateClient }); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toBeUndefined(); + + expect(await actions.updateModel('ddbb7edb-8c53-4586-9844-769e1c830719', {})).toBe(false); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toEqual(badRequest); + + expect(updateClientMocks.length).toBe(0); + }); + + test('success', async () => { + const modelResponse: ModelResponse = { + id: 'ddbb7edb-8c53-4586-9844-769e1c830719', + createdAt: '2022-06-12T20:08:24.793Z', + _links: {}, + }; + + const [updateClient, updateClientMocks] = useFunctionMock>([ + { parameters: ['ddbb7edb-8c53-4586-9844-769e1c830719', {}], return: Promise.resolve(modelResponse) }, + ]); + + const { model, httpError, actions } = createModelResource({ updateClient }); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toBeUndefined(); + + expect(await actions.updateModel('ddbb7edb-8c53-4586-9844-769e1c830719', {})).toBe(true); + + expect(model.value).toEqual(modelResponse); + expect(httpError.value).toBeUndefined(); + + expect(updateClientMocks.length).toBe(0); + }); + }); + + describe('delete', () => { + test('missing client', async () => { + const { actions } = createModelResource({}); + + try { + await actions.deleteModel('ddbb7edb-8c53-4586-9844-769e1c830719'); + throw new Error('expect failed'); + } catch (e) { + expect(e).toMatchInlineSnapshot('[Error: Missing deleteClient]'); + } + }); + + test('error', async () => { + const badRequest = new BadRequest({ title: 'bad request' }); + + const [deleteClient, deleteClientMocks] = useFunctionMock([ + { parameters: ['ddbb7edb-8c53-4586-9844-769e1c830719'], return: Promise.resolve(badRequest) }, + ]); + + const { model, httpError, actions } = createModelResource({ deleteClient }); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toBeUndefined(); + + expect(await actions.deleteModel('ddbb7edb-8c53-4586-9844-769e1c830719')).toBe(false); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toEqual(badRequest); + + expect(deleteClientMocks.length).toBe(0); + }); + + test('success', async () => { + const [deleteClient, deleteClientMocks] = useFunctionMock([ + { parameters: ['ddbb7edb-8c53-4586-9844-769e1c830719'], return: Promise.resolve(undefined) }, + ]); + + const { model, httpError, actions } = createModelResource({ deleteClient }); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toBeUndefined(); + + expect(await actions.deleteModel('ddbb7edb-8c53-4586-9844-769e1c830719')).toBe(true); + + expect(model.value).toBeUndefined(); + expect(httpError.value).toBeUndefined(); + + expect(deleteClientMocks.length).toBe(0); + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ca4c892 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], + "skipLibCheck": true, + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + "jsxImportSource": "vue", + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.vue" + ], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..97ede7e --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..80bacec --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,24 @@ +/// +/// + +import { defineConfig } from 'vite'; +import vueJsx from '@vitejs/plugin-vue-jsx'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vueJsx()], + test: { + globals: true, + environment: 'jsdom', + setupFiles: './vitest.setup.ts', + include: ['tests/**/*.test.*'], + coverage: { + all: true, + clean: true, + reporter: ['text', 'html', 'lcov'], + provider: 'v8', + include: ['src'], + exclude: ['src/index.tsx', 'src/vite-env.d.ts'], + }, + }, +}); diff --git a/vitest.setup.ts b/vitest.setup.ts new file mode 100644 index 0000000..7b0828b --- /dev/null +++ b/vitest.setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom';