diff --git a/.changeset/config.json b/.changeset/config.json index bc4aa209e6..1ba0fb1daa 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -5,7 +5,7 @@ "fixed": [], "linked": [], "access": "public", - "baseBranch": "main", + "baseBranch": "dev", "updateInternalDependencies": "patch", - "ignore": ["@ballerine/docs", "@ballerine/backoffice-v2"] + "ignore": ["@ballerine/docs-site", "@ballerine/backoffice-v2"] } diff --git a/.changeset/smart-lobsters-turn.md b/.changeset/smart-lobsters-turn.md deleted file mode 100644 index 3f8c2599ec..0000000000 --- a/.changeset/smart-lobsters-turn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@ballerine/common': minor ---- - -added error with name validation diff --git a/.github/actions/integration-test-action/action.yml b/.github/actions/integration-test-action/action.yml new file mode 100644 index 0000000000..15804e9a59 --- /dev/null +++ b/.github/actions/integration-test-action/action.yml @@ -0,0 +1,39 @@ +name: Test +description: Test the project + +runs: + using: composite + steps: + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: 18.12.1 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + id: pnpm-install + with: + version: 8 + run_install: false + + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + shell: bash + run: pnpm install + + - name: Test + shell: bash + run: pnpm test:integration diff --git a/.github/actions/unit-test-action/action.yml b/.github/actions/unit-test-action/action.yml new file mode 100644 index 0000000000..389d139a08 --- /dev/null +++ b/.github/actions/unit-test-action/action.yml @@ -0,0 +1,39 @@ +name: Test +description: Test the project + +runs: + using: composite + steps: + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: 18.12.1 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + id: pnpm-install + with: + version: 8 + run_install: false + + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + shell: bash + run: pnpm install + + - name: Test + shell: bash + run: pnpm test:unit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21db13b0c6..264801ef7b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,29 +26,36 @@ jobs: - name: Format uses: ./.github/actions/format-action - - test: + build: strategy: matrix: os: [ubuntu-latest, windows-latest] - timeout-minutes: 60 runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Build + uses: ./.github/actions/build-action + + test_linux: + runs-on: ubuntu-latest + timeout-minutes: 60 steps: - name: Checkout uses: actions/checkout@v3 - name: Test uses: ./.github/actions/test-action - build: - strategy: - matrix: - os: [ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} + + test_windows: + runs-on: windows-latest + timeout-minutes: 60 steps: - name: Checkout uses: actions/checkout@v3 - - name: Build - uses: ./.github/actions/build-action + - name: Test + uses: ./.github/actions/unit-test-action diff --git a/.github/workflows/publish-images-staging.yml b/.github/workflows/publish-images-staging.yml new file mode 100644 index 0000000000..a42b30cea1 --- /dev/null +++ b/.github/workflows/publish-images-staging.yml @@ -0,0 +1,63 @@ +name: Publish workflow-service image - staging + +on: + push: + branches: + - staging + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository_owner }}/workflows-service + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install jq + run: sudo apt-get install jq + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get package version from package.json + id: get_version + run: | + PACKAGE_VERSION=$(jq -r '.version' services/workflows-service/package.json) + echo "::set-output name=version::$PACKAGE_VERSION" + - name: Print the version + run: echo "The version was ${{ steps.get_version.outputs.version }}" + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=staging + type=raw,value=commit-${{ github.sha }}-staging + type=raw,value=${{ steps.get_version.outputs.version }}-staging + - name: Build and push Docker image + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: services/workflows-service + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Install pnpm + uses: pnpm/action-setup@v2 + id: pnpm-install + with: + version: 8 + run_install: true diff --git a/.github/workflows/publish-images-test.yml b/.github/workflows/publish-images-test.yml new file mode 100644 index 0000000000..624a718a63 --- /dev/null +++ b/.github/workflows/publish-images-test.yml @@ -0,0 +1,63 @@ +name: Publish workflow-service image - test + +on: + push: + branches: + - test + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository_owner }}/workflows-service + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install jq + run: sudo apt-get install jq + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get package version from package.json + id: get_version + run: | + PACKAGE_VERSION=$(jq -r '.version' services/workflows-service/package.json) + echo "::set-output name=version::$PACKAGE_VERSION" + - name: Print the version + run: echo "The version was ${{ steps.get_version.outputs.version }}" + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=test + type=raw,value=commit-${{ github.sha }}-test + type=raw,value=${{ steps.get_version.outputs.version }}-test + - name: Build and push Docker image + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: services/workflows-service + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Install pnpm + uses: pnpm/action-setup@v2 + id: pnpm-install + with: + version: 8 + run_install: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0f2f568020..fd6d7941fb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: Release on: push: branches: - - main + - dev concurrency: ${{ github.workflow }}-${{ github.ref }} diff --git a/.vscode/launch.json b/.vscode/launch.json index 48b0718767..90d30a5cd7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,29 +1,34 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Attach", - "port": 9229, - "request": "attach", - "skipFiles": [ - "<node_internals>/**" - ], - "type": "node" - }, - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "skipFiles": [ - "<node_internals>/**" - ], - "program": "${workspaceFolder}/services/workflows-service/src/workflow/workflow-runtime-data.repository.ts", - "outFiles": [ - "${workspaceFolder}/**/*.js" - ] - } - ] -} \ No newline at end of file + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Attach", + "port": 9229, + "request": "attach", + "skipFiles": ["<node_internals>/**"], + "type": "node" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": ["<node_internals>/**"], + "program": "${workspaceFolder}/services/workflows-service/src/workflow/workflow-runtime-data.repository.ts", + "outFiles": ["${workspaceFolder}/**/*.js"] + }, + { + "type": "node", + "request": "launch", + "name": "Debug Current Test File", + "autoAttachChildProcesses": true, + "skipFiles": ["<node_internals>/**", "**/node_modules/**"], + "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs", + "args": ["run", "${relativeFile}"], + "smartStep": true, + "console": "integratedTerminal" + } + ] +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 17df8a690f..b9f27f702f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -oss@ballerine.io. +oss@ballerine.com. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b5c1e41749..1a540c76d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ We accept contributions in different sizes and varied difficulty, from documenta - Join our [Discord](https://discord.gg/e2rQE4YygA) or [Slack](https://join.slack.com/t/ballerine-oss/shared_invite/zt-1iu6otkok-OqBF3TrcpUmFd9oUjNs2iw) - Head to our [documentation](README.md) -- Contact us at [oss@ballerine.io](mailto:oss@ballerine.io) +- Contact us at [oss@ballerine.com](mailto:oss@ballerine.com) ## Prerequisites diff --git a/README.md b/README.md index ca6653995d..dd82e26510 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Ballerine is an open-source user risk decisioning infrastructure that helps comp ## Our vision for this project -Watch a brief video expalining what we're building. +Watch a brief video explaining what we're building. [Watch now](https://youtu.be/0SppYSZOatw) @@ -124,7 +124,7 @@ We believe in enabling companies to manage user identity and risk according to **Demo example video** -Watch a video of how the demo works, with explainations: +Watch a video of how the demo works, with explanations: [Watch now](https://youtu.be/EzBXhUM7gb8) <br/> diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..183da331d9 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 0.x.x | :white_check_mark: | + +## Reporting a Vulnerability + +Send an email to: dev@ballerine.com +Or contact us on slack diff --git a/apps/backoffice-v2/package.json b/apps/backoffice-v2/package.json index 89e345b063..4cc0451e1f 100644 --- a/apps/backoffice-v2/package.json +++ b/apps/backoffice-v2/package.json @@ -1,6 +1,6 @@ { "name": "@ballerine/backoffice-v2", - "version": "0.4.2", + "version": "0.4.4", "description": "Ballerine - Backoffice", "homepage": "https://github.com/ballerine-io/ballerine", "repository": { @@ -30,8 +30,8 @@ "not IE 11" ], "engines": { - "node": ">=14", - "yarn": ">=1.22.5" + "node": ">=18.0.0", + "pnpm": ">=8.0.0" }, "scripts": { "format": "prettier --plugin-search-dir=. --write .", @@ -41,21 +41,22 @@ "dev": "vite", "build": "vite build", "test": "vitest run --passWithNoTests", + "test:unit": "vitest run --passWithNoTests", "test:e2e": "playwright test", "typecheck": "tsc --noEmit", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" }, "dependencies": { - "@ballerine/common": "^0.4.3", - "@ballerine/workflow-browser-sdk": "^0.4.3", + "@ballerine/common": "0.5.6", + "@ballerine/workflow-browser-sdk": "^0.4.9", "@ballerine/workflow-node-sdk": "^0.4.3", "@formkit/auto-animate": "1.0.0-beta.5", "@hookform/resolvers": "^3.1.0", "@lukemorales/query-key-factory": "^1.0.3", "@radix-ui/react-checkbox": "^1.0.1", "@radix-ui/react-dialog": "^1.0.2", - "@radix-ui/react-dropdown-menu": "^2.0.4", + "@radix-ui/react-dropdown-menu": "^2.0.5", "@radix-ui/react-hover-card": "^1.0.2", "@radix-ui/react-label": "^2.0.1", "@radix-ui/react-scroll-area": "^1.0.2", @@ -72,6 +73,7 @@ "i18next": "^22.4.9", "i18next-browser-languagedetector": "^7.0.1", "i18next-http-backend": "^2.1.1", + "leaflet": "^1.9.4", "lucide-react": "^0.144.0", "match-sorter": "^6.3.1", "msw": "^1.0.0", @@ -82,7 +84,9 @@ "react-hot-toast": "^2.4.0", "react-i18next": "^12.1.4", "react-image-crop": "^10.0.9", + "react-leaflet": "^4.2.1", "react-router-dom": "^6.11.2", + "react-zoom-pan-pinch": "^3.0.8", "tailwind-merge": "^1.10.0", "tailwindcss-animate": "^1.0.5", "tesseract.js": "^4.0.1", @@ -103,6 +107,7 @@ "@tanstack/react-query-devtools": "4.22.0", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", + "@types/leaflet": "^1.9.3", "@types/node": "^18.11.13", "@types/qs": "^6.9.7", "@types/react": "^18.0.14", @@ -119,6 +124,7 @@ "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-storybook": "^0.6.6", + "eslint-plugin-unused-imports": "^2.0.0", "postcss": "^8.4.14", "prettier": "^2.8.0", "prettier-plugin-tailwindcss": "^0.2.1", diff --git a/apps/backoffice-v2/public/images/mock-documents/set_1_doc_pdf.pdf b/apps/backoffice-v2/public/images/mock-documents/set_1_doc_pdf.pdf new file mode 100644 index 0000000000..dbf091df9a Binary files /dev/null and b/apps/backoffice-v2/public/images/mock-documents/set_1_doc_pdf.pdf differ diff --git a/apps/backoffice-v2/src/Router/Router.tsx b/apps/backoffice-v2/src/Router/Router.tsx index 2ca262874f..7727ae9a85 100644 --- a/apps/backoffice-v2/src/Router/Router.tsx +++ b/apps/backoffice-v2/src/Router/Router.tsx @@ -9,10 +9,13 @@ import { Entities } from '../pages/Entities/Entities.page'; import { RouteError } from '../common/components/atoms/RouteError/RouteError'; import { CaseManagement } from '../pages/CaseManagement/CaseManagement.page'; import { rootLoader } from '../pages/Root/Root.loader'; -import { entityLoader } from '../pages/Entity/Entity.loader'; import { entitiesLoader } from '../pages/Entities/Entities.loader'; -import { localeLoader } from '../pages/Locale/Locale.loader'; +import { authenticatedLayoutLoader } from '../domains/auth/components/AuthenticatedLayout/AuthenticatedLayout.loader'; +import { entityLoader } from '../pages/Entity/Entity.loader'; +import { AuthenticatedLayout } from '../domains/auth/components/AuthenticatedLayout'; +import { UnauthenticatedLayout } from '../domains/auth/components/UnauthenticatedLayout'; import { Locale } from '../pages/Locale/Locale.page'; +import { unauthenticatedLayoutLoader } from '../domains/auth/components/UnauthenticatedLayout/UnauthenticatedLayout.loader'; const router = createBrowserRouter([ { @@ -21,35 +24,57 @@ const router = createBrowserRouter([ loader: rootLoader, errorElement: <RootError />, children: [ - ...(env.VITE_AUTH_ENABLED - ? [ - { - path: '/:locale/auth/sign-in', - element: <SignIn />, - }, - ] - : []), { - path: '/:locale', - element: <Locale />, - loader: localeLoader, + element: <UnauthenticatedLayout />, + loader: unauthenticatedLayoutLoader, + errorElement: <RouteError />, + children: [ + { + path: '/:locale', + element: <Locale />, + errorElement: <RouteError />, + children: [ + ...(env.VITE_AUTH_ENABLED + ? [ + { + path: '/:locale/auth/sign-in', + element: <SignIn />, + errorElement: <RouteError />, + }, + ] + : []), + ], + }, + ], + }, + { + element: <AuthenticatedLayout />, + loader: authenticatedLayoutLoader, errorElement: <RouteError />, children: [ { - path: '/:locale/case-management', - element: <CaseManagement />, + path: '/:locale', + element: <Locale />, + errorElement: <RouteError />, children: [ { - path: '/:locale/case-management/entities', - element: <Entities />, - loader: entitiesLoader, + path: '/:locale/case-management', + element: <CaseManagement />, errorElement: <RouteError />, children: [ { - path: '/:locale/case-management/entities/:entityId', - element: <Entity />, - loader: entityLoader, + path: '/:locale/case-management/entities', + element: <Entities />, + loader: entitiesLoader, errorElement: <RouteError />, + children: [ + { + path: '/:locale/case-management/entities/:entityId', + element: <Entity />, + loader: entityLoader, + errorElement: <RouteError />, + }, + ], }, ], }, diff --git a/apps/backoffice-v2/src/common/components/atoms/AssignButton/AssignButton.tsx b/apps/backoffice-v2/src/common/components/atoms/AssignButton/AssignButton.tsx index fe73954468..3ed7ff4497 100644 --- a/apps/backoffice-v2/src/common/components/atoms/AssignButton/AssignButton.tsx +++ b/apps/backoffice-v2/src/common/components/atoms/AssignButton/AssignButton.tsx @@ -29,12 +29,14 @@ interface IAssignButtonProps { assignees: Assignee[]; onAssigneeSelect: (id: string) => void; authenticatedUser: TAuthenticatedUser; + hasDecision: boolean; } export const AssignButton: React.FC<IAssignButtonProps> = ({ buttonType, assignees, onAssigneeSelect, caseState, + hasDecision, }) => { const isAssignButtonType = buttonType === 'Assign'; const isUnassignEnabled = caseState !== CaseState.UNASSIGNED; @@ -43,20 +45,20 @@ export const AssignButton: React.FC<IAssignButtonProps> = ({ return ( <div> {isAssignButtonType ? ( - <Button disabled={!caseState.assignToMeEnabled} onClick={onClick}> + <Button disabled={hasDecision || !caseState.assignToMeEnabled} onClick={onClick}> Assign Me </Button> ) : ( <DropdownMenu> <DropdownMenuTrigger asChild> - <Button variant={`outline`} disabled={!caseState.assignToOtherEnabled}> + <Button variant={`outline`} disabled={hasDecision || !caseState.assignToOtherEnabled}> {buttonType} </Button> </DropdownMenuTrigger> <DropdownMenuContent className={`min-w-[16rem]`} align={'start'}> {isUnassignEnabled ? ( <DropdownMenuItem - className={`border-b-2 text-cyan-950`} + className={`text-cyan-950 border-b-2`} onClick={() => onAssigneeSelect(null)} > Unassign diff --git a/apps/backoffice-v2/src/common/components/atoms/ErrorAlert/ErrorAlert.tsx b/apps/backoffice-v2/src/common/components/atoms/ErrorAlert/ErrorAlert.tsx new file mode 100644 index 0000000000..62a9a79c27 --- /dev/null +++ b/apps/backoffice-v2/src/common/components/atoms/ErrorAlert/ErrorAlert.tsx @@ -0,0 +1,17 @@ +import { FunctionComponentWithChildren } from '../../../types'; +import { Alert } from '../Alert/Alert'; +import { AlertCircle } from 'lucide-react'; +import { AlertTitle } from '../Alert/Alert.Title'; +import { AlertDescription } from '../Alert/Alert.Description'; + +export const ErrorAlert: FunctionComponentWithChildren = ({ children }) => { + return ( + <div className={`mt-3 p-1`}> + <Alert variant={`destructive`} className={`w-full max-w-lg`}> + <AlertCircle className="h-4 w-4" /> + <AlertTitle>Error</AlertTitle> + <AlertDescription>{children}</AlertDescription> + </Alert> + </div> + ); +}; diff --git a/apps/backoffice-v2/src/common/components/atoms/RouteError/RouteError.tsx b/apps/backoffice-v2/src/common/components/atoms/RouteError/RouteError.tsx index 58bdbec9d4..7eaa4dc25f 100644 --- a/apps/backoffice-v2/src/common/components/atoms/RouteError/RouteError.tsx +++ b/apps/backoffice-v2/src/common/components/atoms/RouteError/RouteError.tsx @@ -1,22 +1,11 @@ -import { Alert } from '../Alert/Alert'; -import { AlertCircle } from 'lucide-react'; -import { AlertTitle } from '../Alert/Alert.Title'; -import { AlertDescription } from '../Alert/Alert.Description'; import { useRouteError } from 'react-router-dom'; import { isErrorWithMessage } from '@ballerine/common'; +import { ErrorAlert } from '../ErrorAlert/ErrorAlert'; export const RouteError = () => { const error = useRouteError(); return ( - <div className={`mt-3 p-1`}> - <Alert variant={`destructive`} className={`w-full max-w-lg`}> - <AlertCircle className="h-4 w-4" /> - <AlertTitle>Error</AlertTitle> - <AlertDescription> - {isErrorWithMessage(error) ? error.message : 'Something went wrong.'} - </AlertDescription> - </Alert> - </div> + <ErrorAlert>{isErrorWithMessage(error) ? error.message : 'Something went wrong.'}</ErrorAlert> ); }; diff --git a/apps/backoffice-v2/src/common/components/atoms/Skeleton/Skeleton.tsx b/apps/backoffice-v2/src/common/components/atoms/Skeleton/Skeleton.tsx new file mode 100644 index 0000000000..e190f8ff84 --- /dev/null +++ b/apps/backoffice-v2/src/common/components/atoms/Skeleton/Skeleton.tsx @@ -0,0 +1,6 @@ +import { ComponentProps, FunctionComponent } from 'react'; +import { ctw } from '../../../utils/ctw/ctw'; + +export const Skeleton: FunctionComponent<ComponentProps<'div'>> = ({ className, ...props }) => ( + <div className={ctw('animate-pulse rounded-md bg-muted', className)} {...props} /> +); diff --git a/apps/backoffice-v2/src/common/components/molecules/FullScreenLoader/FullScreenLoader.tsx b/apps/backoffice-v2/src/common/components/molecules/FullScreenLoader/FullScreenLoader.tsx new file mode 100644 index 0000000000..3ad5784c0d --- /dev/null +++ b/apps/backoffice-v2/src/common/components/molecules/FullScreenLoader/FullScreenLoader.tsx @@ -0,0 +1,37 @@ +import React, { FunctionComponent } from 'react'; + +export const FullScreenLoader: FunctionComponent = () => { + return ( + <div className={`d-full mt-32 flex justify-center`}> + <svg + width="414" + height="608" + viewBox="-2 -2 32 38" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + className={`animated-path`} + fill={`transparent`} + strokeWidth={1} + stroke={`black`} + d="M15.0495 34.0001C11.7759 34.0001 9.24159 32.4364 7.99481 31.4764C7.16779 30.8416 7.02056 29.6619 7.66275 28.8444C8.30493 28.0269 9.49847 27.8813 10.3223 28.5161C11.2151 29.2036 13.0571 30.3431 15.3283 30.244C19.1689 30.0768 22.7996 26.5282 22.7996 22.9393C22.7996 21.1495 22.4644 19.9635 21.5622 18.5701L21.5278 18.5143C19.786 15.6005 16.0112 13.6404 12.1361 13.6404C11.1776 13.6404 10.2879 13.7642 9.48907 14.012C8.48976 14.3216 7.4278 13.7704 7.11454 12.7826C6.80127 11.7948 7.35888 10.742 8.35819 10.4354C9.52353 10.0763 10.7922 9.89355 12.133 9.89355C17.3708 9.89355 22.3266 12.5163 24.77 16.579C26.0575 18.5824 26.5838 20.428 26.5838 22.9393C26.5838 28.4728 21.2928 33.74 15.4849 33.9908C15.3346 33.997 15.1873 34.0001 15.0401 34.0001H15.0495Z" + /> + <path + className={`animated-path`} + fill={`transparent`} + strokeWidth={1} + stroke={`black`} + d="M6.81973 26.2184C2.93214 26.2184 0 23.3727 0 19.6011C0 18.1736 0.523149 16.7244 1.55692 15.2938L1.59451 15.2442C1.71668 15.0708 1.85138 14.9005 1.99548 14.7302C2.09573 14.6095 2.42152 14.2224 2.94467 13.7207C3.6965 12.9993 4.8963 13.0178 5.6262 13.761C6.3561 14.5042 6.33731 15.6901 5.58548 16.4116C5.17197 16.808 4.94015 17.0929 4.93702 17.096L4.89316 17.1486C4.82111 17.2291 4.75846 17.3096 4.70521 17.3871L4.64255 17.4769C4.43267 17.7679 3.79048 18.6535 3.79048 19.6042C3.79048 21.2918 5.03413 22.4716 6.81973 22.4716C8.80582 22.4716 12.6339 21.9328 15.9451 18.3191L15.967 18.2974L16.5183 17.7153C17.2326 16.9597 18.4324 16.9195 19.1999 17.6286C19.9642 18.3346 20.005 19.5206 19.2876 20.2792L18.7488 20.849C14.4602 25.5217 9.43548 26.2215 6.82286 26.2215L6.81973 26.2184Z" + /> + <path + stroke={`black`} + strokeWidth={1} + className={`animated-path`} + fill={`transparent`} + d="M9.12705 20.0439C8.19979 20.0439 7.38844 19.372 7.25373 18.4368C6.76818 15.1018 6.60841 12.0703 6.76504 9.16576C6.79324 4.10601 10.9659 0 16.0877 0C21.2096 0 25.4136 4.13388 25.4136 9.2184C25.4136 12.1849 24.5678 13.9375 23.2019 15.8047C22.5879 16.6439 21.4007 16.8297 20.5549 16.2228C19.7059 15.6158 19.518 14.4454 20.132 13.6062C21.1438 12.2251 21.6231 11.2157 21.6231 9.2184C21.6231 6.20237 19.1421 3.74991 16.0909 3.74991C13.0397 3.74991 10.5587 6.20237 10.5587 9.2184V9.32058C10.4083 11.9898 10.5555 14.7953 11.0066 17.9011C11.157 18.9261 10.4365 19.8767 9.39958 20.0253C9.30874 20.0377 9.21789 20.0439 9.12705 20.0439Z" + /> + </svg> + </div> + ); +}; diff --git a/apps/backoffice-v2/src/common/components/molecules/Map/Map.tsx b/apps/backoffice-v2/src/common/components/molecules/Map/Map.tsx new file mode 100644 index 0000000000..0f7f2d9d0c --- /dev/null +++ b/apps/backoffice-v2/src/common/components/molecules/Map/Map.tsx @@ -0,0 +1,23 @@ +import { FunctionComponent } from 'react'; +import { LatLngTuple } from 'leaflet'; +import { AlertDescription } from '../../atoms/Alert/Alert.Description'; +import { MapContainer, Marker, Popup, TileLayer } from 'react-leaflet'; +import { IMapProps } from './interfaces'; + +export const Map: FunctionComponent<IMapProps> = ({ latitude, longitude, popupContent }) => { + const position: LatLngTuple = [latitude, longitude]; + + if (!latitude || !longitude) { + return <AlertDescription>Invalid coordinates.</AlertDescription>; + } + + return ( + <MapContainer center={position} zoom={13} className={`mt-6 h-[600px] rounded-md`}> + <TileLayer + attribution='© <a href="https://stadiamaps.com/">Stadia Maps</a>, © <a href="https://openmaptiles.org/">OpenMapTiles</a> © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors' + url={`https://tiles.stadiamaps.com/tiles/alidade_smooth/{z}/{x}/{y}{r}.png`} + /> + <Marker position={position}>{popupContent && <Popup>{popupContent}</Popup>}</Marker> + </MapContainer> + ); +}; diff --git a/apps/backoffice-v2/src/common/components/molecules/Map/interfaces.ts b/apps/backoffice-v2/src/common/components/molecules/Map/interfaces.ts new file mode 100644 index 0000000000..448c30ff17 --- /dev/null +++ b/apps/backoffice-v2/src/common/components/molecules/Map/interfaces.ts @@ -0,0 +1,7 @@ +import { ReactNode } from 'react'; + +export interface IMapProps { + latitude: number; + longitude: number; + popupContent?: ReactNode; +} diff --git a/apps/backoffice-v2/src/common/components/organisms/Header/Header.Logo.tsx b/apps/backoffice-v2/src/common/components/organisms/Header/Header.Logo.tsx index 00bd1ce002..55d023d695 100644 --- a/apps/backoffice-v2/src/common/components/organisms/Header/Header.Logo.tsx +++ b/apps/backoffice-v2/src/common/components/organisms/Header/Header.Logo.tsx @@ -1,6 +1,7 @@ import { FunctionComponent } from 'react'; import { BallerineLogo } from '../../atoms/icons'; import { Link } from 'react-router-dom'; +import { env } from '../../../env/env'; /** * @description {@link BallerineLogo} with navigation to "/" on click. @@ -13,7 +14,11 @@ export const Logo: FunctionComponent = () => { to={`/en`} className={`btn-ghost btn flex gap-x-3 text-2xl normal-case focus:outline-primary`} > - <BallerineLogo /> + {!!env.VITE_IMAGE_LOGO_URL ? ( + <img className={`h-20 w-40`} src={env.VITE_IMAGE_LOGO_URL} /> + ) : ( + <BallerineLogo /> + )} </Link> </h1> ); diff --git a/apps/backoffice-v2/src/common/components/organisms/Header/Header.Navbar.tsx b/apps/backoffice-v2/src/common/components/organisms/Header/Header.Navbar.tsx index bbde4141dd..9edd2f5413 100644 --- a/apps/backoffice-v2/src/common/components/organisms/Header/Header.Navbar.tsx +++ b/apps/backoffice-v2/src/common/components/organisms/Header/Header.Navbar.tsx @@ -51,15 +51,18 @@ export const Navbar: FunctionComponent = () => { </ul> ))} <ul className={`menu menu-compact w-full space-y-2`}> - {filters?.map(({ id, name, entity }) => ( + {filters?.map(({ id, name }) => ( <NavItem - href={`/en/case-management/entities?entity=${entity}&filterId=${id}&filterName=${name}`} + href={`/en/case-management/entities?filterId=${id}`} key={id} className={ctw(`capitalize`, { - 'bg-muted font-bold': name === searchParams?.filterName, + 'bg-muted font-bold': id === searchParams?.filterId, })} > - <CheckSquare /> {name} + <span> + <CheckSquare className={`d-4`} /> + </span>{' '} + {name} </NavItem> ))} </ul> diff --git a/apps/backoffice-v2/src/common/components/organisms/ImageViewer/ImageViewer.tsx b/apps/backoffice-v2/src/common/components/organisms/ImageViewer/ImageViewer.tsx index ce9e252537..35774f9995 100644 --- a/apps/backoffice-v2/src/common/components/organisms/ImageViewer/ImageViewer.tsx +++ b/apps/backoffice-v2/src/common/components/organisms/ImageViewer/ImageViewer.tsx @@ -32,7 +32,7 @@ export const ImageViewer: FunctionComponent<IImageViewerProps> & IImageViewerChi ...rest }) => { return ( - <div className={ctw(`flex flex-col items-center gap-y-8`, className)} {...rest}> + <div className={ctw(`flex h-full flex-col items-center gap-y-8`, className)} {...rest}> <Provider selectedImage={selectedImage} onSelectImage={onSelectImage}> {children} </Provider> diff --git a/apps/backoffice-v2/src/common/components/organisms/Modal/Modal.tsx b/apps/backoffice-v2/src/common/components/organisms/Modal/Modal.tsx index 073b5225d2..152336a065 100644 --- a/apps/backoffice-v2/src/common/components/organisms/Modal/Modal.tsx +++ b/apps/backoffice-v2/src/common/components/organisms/Modal/Modal.tsx @@ -41,7 +41,7 @@ export const Modal: FunctionComponent<IModalProps> = ({ <Dialog.Content className={ctw(`modal-box w-full max-w-7xl`, className)} {...props}> <div className={`flex justify-end`}> <Dialog.Close - className={`btn-ghost btn-square btn-sm btn mb-4 focus:outline-primary`} + className={`btn-ghost btn-sm btn-square btn mb-4 focus:outline-primary`} aria-label={`Close`} > <XMarkSvg /> diff --git a/apps/backoffice-v2/src/common/components/templates/Layout/Layout.tsx b/apps/backoffice-v2/src/common/components/templates/Layout/Layout.tsx deleted file mode 100644 index 5746217174..0000000000 --- a/apps/backoffice-v2/src/common/components/templates/Layout/Layout.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { FunctionComponentWithChildren } from '../../../types'; -import { useIsAuthenticated } from '../../../../domains/auth/context/AuthProvider/hooks/useIsAuthenticated/useIsAuthenticated'; -import { UnauthenticatedLayout } from '../../../../domains/auth/components/UnauthenticatedLayout'; -import { AuthenticatedLayout } from '../../../../domains/auth/components/AuthenticatedLayout'; -import React from 'react'; -import { env } from '../../../env/env'; - -export const Layout: FunctionComponentWithChildren = ({ children }) => { - const isAuthenticated = useIsAuthenticated(); - - if (!isAuthenticated && env.VITE_AUTH_ENABLED) { - return <UnauthenticatedLayout>{children}</UnauthenticatedLayout>; - } - - return <AuthenticatedLayout>{children}</AuthenticatedLayout>; -}; diff --git a/apps/backoffice-v2/src/common/components/templates/Providers/Providers.tsx b/apps/backoffice-v2/src/common/components/templates/Providers/Providers.tsx index 720e1232af..f6afa08b9d 100644 --- a/apps/backoffice-v2/src/common/components/templates/Providers/Providers.tsx +++ b/apps/backoffice-v2/src/common/components/templates/Providers/Providers.tsx @@ -3,23 +3,21 @@ import { FunctionComponent, PropsWithChildren } from 'react'; import { queryClient } from '../../../../lib/react-query/query-client'; import { AuthProvider } from '../../../../domains/auth/context/AuthProvider/AuthProvider'; import { env } from '../../../env/env'; -// import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; +import { useLocation } from 'react-router-dom'; export const Providers: FunctionComponent<PropsWithChildren> = ({ children }) => { + const { state } = useLocation(); + return ( <QueryClientProvider client={queryClient}> <AuthProvider - protectedRoutes={ - [ - // Uses String.prototype.startsWith until authenticated layout is implemented. - '/en/case-management/', - ] as const - } redirectAuthenticatedTo={'/en/case-management/entities'} redirectUnauthenticatedTo={'/en/auth/sign-in'} signInOptions={{ redirect: env.VITE_AUTH_ENABLED, - callbackUrl: '/en/case-management/entities', + callbackUrl: state?.from + ? `${state?.from?.pathname}${state?.from?.search}` + : '/en/case-management/entities', }} signOutOptions={{ redirect: env.VITE_AUTH_ENABLED, diff --git a/apps/backoffice-v2/src/common/env/schema.ts b/apps/backoffice-v2/src/common/env/schema.ts index 6064642dd0..535e273a07 100644 --- a/apps/backoffice-v2/src/common/env/schema.ts +++ b/apps/backoffice-v2/src/common/env/schema.ts @@ -16,4 +16,5 @@ export const EnvSchema = z.object({ .transform(v => v * 1000) .or(z.literal(false)) .catch(undefined), + VITE_IMAGE_LOGO_URL: z.string().optional(), }); diff --git a/apps/backoffice-v2/src/common/hooks/useEntityType/useEntityType.ts b/apps/backoffice-v2/src/common/hooks/useEntityType/useEntityType.ts new file mode 100644 index 0000000000..a8568554e0 --- /dev/null +++ b/apps/backoffice-v2/src/common/hooks/useEntityType/useEntityType.ts @@ -0,0 +1,20 @@ +import { useMemo } from 'react'; +import { useFilterId } from '../useFilterId/useFilterId'; +import { useFiltersQuery } from '../../../domains/filters/hooks/queries/useFiltersQuery/useFiltersQuery'; + +export type TEntityType = 'individuals' | 'business'; + +export function useEntityType(defaultEntityType: TEntityType = 'individuals'): TEntityType { + const filterId = useFilterId(); + const { data: filters, isLoading } = useFiltersQuery(); + + const entityType = useMemo(() => { + if (isLoading || !Array.isArray(filters)) return null; + + const matchedFilter = filters.find(filter => filter.id === filterId); + + return matchedFilter ? (matchedFilter.entity as TEntityType) : null; + }, [filterId, filters, isLoading]); + + return entityType ? entityType : defaultEntityType; +} diff --git a/apps/backoffice-v2/src/common/hooks/useFilter/filter.ts b/apps/backoffice-v2/src/common/hooks/useFilter/filter.ts deleted file mode 100644 index 529d4ec9fd..0000000000 --- a/apps/backoffice-v2/src/common/hooks/useFilter/filter.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { AnyArray } from '../../types'; - -export const filter = <TArray extends AnyArray>({ - data, - filter, -}: { - data: TArray; - filter: string; -}) => { - // Avoid errors stemming from calling array methods on non-arrays. - if (!Array.isArray(data)) { - return []; - } - - // Don't filter when not needed. - if (!filter || !data?.length) { - return data; - } - - // Get filters - const terms = Object.keys(filter); - - if (!terms?.length) { - return data; - } - - // Filter - return data?.filter(item => { - return terms?.every(term => { - if (!filter?.[term]?.length) return true; - - return filter?.[term]?.some(termValue => { - if (typeof item?.[term]?.includes === 'function') { - return item?.[term]?.includes(termValue); - } - - return item?.[term] === termValue; - }); - }); - }); -}; diff --git a/apps/backoffice-v2/src/common/hooks/useFilter/useFilter.tsx b/apps/backoffice-v2/src/common/hooks/useFilter/useFilter.tsx deleted file mode 100644 index 9d5885ae13..0000000000 --- a/apps/backoffice-v2/src/common/hooks/useFilter/useFilter.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { useCallback, useMemo } from 'react'; -import { AnyArray, TKeyofArrayElement } from '../../types'; -import { filter as onFilter } from './filter'; -import { useSearchParamsByEntity } from '../useSearchParamsByEntity/useSearchParamsByEntity'; - -/** - * @description A hook to easily filter an array of objects by key using fuzzy search. - * @param props - * @param props.data - The data to filter. - * @param props.filter - The initial filter - defaults to empty string. - */ -export const useFilter = <TArray extends AnyArray>({ - data, - initialFilter, -}: { - data: TArray; - initialFilter?: Record<TKeyofArrayElement<TArray>, Array<string>>; -}) => { - const [{ filter = initialFilter }, setSearchParams] = useSearchParamsByEntity(); - const filtered = useMemo( - () => - onFilter({ - data, - filter, - }), - [data, filter], - ); - const onFilterChange = useCallback( - (value: { - [key in TKeyofArrayElement<TArray>]?: Array<string>; - }) => { - setSearchParams({ - filter: { - ...filter, - ...value, - }, - }); - }, - [filter, setSearchParams], - ); - - return { - filtered, - filter, - onFilter: onFilterChange, - }; -}; diff --git a/apps/backoffice-v2/src/common/hooks/useFilterId/useFilterId.tsx b/apps/backoffice-v2/src/common/hooks/useFilterId/useFilterId.tsx index 24ac924e7a..c068c76fb8 100644 --- a/apps/backoffice-v2/src/common/hooks/useFilterId/useFilterId.tsx +++ b/apps/backoffice-v2/src/common/hooks/useFilterId/useFilterId.tsx @@ -1,7 +1,8 @@ -import { useSearchParamsByEntity } from '../useSearchParamsByEntity/useSearchParamsByEntity'; +import { useSearchParams } from 'react-router-dom'; export const useFilterId = (): string | undefined => { - const [{ filterId }] = useSearchParamsByEntity(); + const [params] = useSearchParams(); + const filterId = params.get('filterId'); return filterId; }; diff --git a/apps/backoffice-v2/src/common/hooks/usePagination/generate-pagination-buttons.ts b/apps/backoffice-v2/src/common/hooks/usePagination/generate-pagination-buttons.ts deleted file mode 100644 index aa1dcb5be4..0000000000 --- a/apps/backoffice-v2/src/common/hooks/usePagination/generate-pagination-buttons.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const generatePaginationButtons = ({ page, siblings, totalPages }) => { - let min = Math.max(page - siblings, 1); - let max = Math.min(page + siblings, totalPages); - - if (min === 1) { - max = Math.min(siblings * 2 + 1, totalPages); - } - - if (max === totalPages) { - min = Math.max(totalPages - siblings * 2, 1); - } - - const length = Math.max(max - min + 1, 1); - - return Array.from({ length }, (_, i) => min + i); -}; diff --git a/apps/backoffice-v2/src/common/hooks/usePagination/pagination.ts b/apps/backoffice-v2/src/common/hooks/usePagination/pagination.ts deleted file mode 100644 index ead2ec3937..0000000000 --- a/apps/backoffice-v2/src/common/hooks/usePagination/pagination.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { AnyArray } from '../../types'; -import { generatePaginationButtons } from './generate-pagination-buttons'; - -export const pagination = <TArray extends AnyArray>({ data, pageSize, page, siblings }) => { - const totalItems = data?.length ?? 0; - const totalPages = Math.ceil(totalItems / pageSize); - const paginated = data?.slice((page - 1) * pageSize, page * pageSize); - const pages = generatePaginationButtons({ - page, - siblings, - totalPages, - }); - - return { - totalItems, - totalPages, - paginated, - pages, - }; -}; diff --git a/apps/backoffice-v2/src/common/hooks/usePagination/usePagination.tsx b/apps/backoffice-v2/src/common/hooks/usePagination/usePagination.tsx deleted file mode 100644 index abacd17dbb..0000000000 --- a/apps/backoffice-v2/src/common/hooks/usePagination/usePagination.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { useCallback, useMemo } from 'react'; -import { AnyArray } from '../../types'; -import { pagination } from './pagination'; -import { useSearchParamsByEntity } from '../useSearchParamsByEntity/useSearchParamsByEntity'; - -/** - * @description A hook to manage pagination state to be consumed by Mantine's Pagination component. Not using Mantine's usePagination hook since it doesn't support changing the number of items per page. - * @param props - * @param props.data - The data to paginate. - * @param props.initialPage - Defaults to 1. - * @param props.initialPageSize - The number of items per page. - */ -export const usePagination = <TArray extends AnyArray>({ - data, - initialPage = 1, - initialPageSize = 10, - siblings = 2, -}: { - data: TArray; - initialPage?: number; - initialPageSize?: number; - siblings?: number; -}) => { - const [{ page = initialPage, pageSize = initialPageSize }, setSearchParams] = - useSearchParamsByEntity(); - const { totalItems, totalPages, pages, paginated } = useMemo( - () => - pagination({ - data, - pageSize, - page, - siblings, - }), - [data, pageSize, page, siblings], - ); - const onPaginate = useCallback( - (page: number) => () => { - setSearchParams({ - page, - pageSize, - }); - }, - [pageSize, setSearchParams], - ); - - return { - paginated, - onPaginate, - totalItems, - totalPages, - pages, - page, - pageSize, - }; -}; diff --git a/apps/backoffice-v2/src/common/hooks/useSearch/useSearch.tsx b/apps/backoffice-v2/src/common/hooks/useSearch/useSearch.tsx index bd1c5a92d2..e35eb6ebb2 100644 --- a/apps/backoffice-v2/src/common/hooks/useSearch/useSearch.tsx +++ b/apps/backoffice-v2/src/common/hooks/useSearch/useSearch.tsx @@ -32,8 +32,9 @@ export const useSearch = <TArray extends AnyArray>({ useEffect(() => { setSearchParams({ search: debouncedSearch, + page: 1, }); - }, [debouncedSearch, setSearchParams]); + }, [debouncedSearch]); return { searched, diff --git a/apps/backoffice-v2/src/common/hooks/useSearchParamsByEntity/useSearchParamsByEntity.tsx b/apps/backoffice-v2/src/common/hooks/useSearchParamsByEntity/useSearchParamsByEntity.tsx index 24fd83e163..503208da14 100644 --- a/apps/backoffice-v2/src/common/hooks/useSearchParamsByEntity/useSearchParamsByEntity.tsx +++ b/apps/backoffice-v2/src/common/hooks/useSearchParamsByEntity/useSearchParamsByEntity.tsx @@ -1,17 +1,16 @@ -import { ZodSchema } from 'zod'; -import { useSearchParams } from 'react-router-dom'; +import { AnyZodObject } from 'zod'; import { useZodSearchParams } from '../useZodSearchParams/useZodSearchParams'; import { IUseZodSearchParams } from '../useZodSearchParams/interfaces'; import { BusinessesSearchSchema, IndividualsSearchSchema } from './validation-schemas'; import { useAuthenticatedUserQuery } from '../../../domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; import { useMemo } from 'react'; +import { useEntityType } from '../useEntityType/useEntityType'; -export const useSearchParamsByEntity = <TSchema extends ZodSchema>( +export const useSearchParamsByEntity = <TSchema extends AnyZodObject>( schema?: TSchema, options: IUseZodSearchParams = {}, ) => { - const [searchParams] = useSearchParams(); - const entity = searchParams.get('entity'); + const entity = useEntityType(); const { data: session } = useAuthenticatedUserQuery(); const EntitySearchSchema = useMemo( () => @@ -22,7 +21,6 @@ export const useSearchParamsByEntity = <TSchema extends ZodSchema>( ); return useZodSearchParams( - // @ts-ignore schema ? EntitySearchSchema.merge(schema) : EntitySearchSchema, options, ); diff --git a/apps/backoffice-v2/src/common/hooks/useSearchParamsByEntity/validation-schemas.ts b/apps/backoffice-v2/src/common/hooks/useSearchParamsByEntity/validation-schemas.ts index c8b43134d9..64a4c4457d 100644 --- a/apps/backoffice-v2/src/common/hooks/useSearchParamsByEntity/validation-schemas.ts +++ b/apps/backoffice-v2/src/common/hooks/useSearchParamsByEntity/validation-schemas.ts @@ -1,42 +1,38 @@ import { z } from 'zod'; -import { CaseStatus, CaseStatuses, States } from '../../enums'; +import { CaseStatus, CaseStatuses } from '../../enums'; export const SearchSchema = z.object({ sortDir: z.enum(['asc', 'desc']).catch('desc'), - pageSize: z.coerce.number().int().positive().catch(10), + pageSize: z.coerce.number().int().positive().catch(50), page: z.coerce.number().int().positive().catch(1), search: z.string().catch(''), filterId: z.string().catch(''), - filterName: z.string().catch(''), entity: z.string().catch(''), }); + export const IndividualsSearchSchema = (authenticatedUserId: string) => SearchSchema.extend({ - sortBy: z - .enum(['firstName', 'lastName', 'email', 'phone', 'caseCreatedAt', 'approvalState']) - .catch('caseCreatedAt'), + sortBy: z.enum(['firstName', 'lastName', 'email', 'createdAt']).catch('createdAt'), filter: z .object({ - approvalState: z.array(z.enum(States)).catch([]), assigneeId: z.array(z.string().nullable()).catch([authenticatedUserId, null]), - caseStatus: z.array(z.enum(CaseStatuses)).catch([CaseStatus.ACTIVE]), + status: z.array(z.enum(CaseStatuses)).catch([CaseStatus.ACTIVE]), }) .catch({ - approvalState: [], assigneeId: [authenticatedUserId, null], - caseStatus: [CaseStatus.ACTIVE], + status: [CaseStatus.ACTIVE], }), }); export const BusinessesSearchSchema = (authenticatedUserId: string) => SearchSchema.extend({ - sortBy: z.enum(['caseCreatedAt', 'companyName']).catch('caseCreatedAt'), + sortBy: z.enum(['createdAt', 'companyName']).catch('createdAt'), filter: z .object({ assigneeId: z.array(z.string().nullable()).catch([authenticatedUserId, null]), - caseStatus: z.array(z.enum(CaseStatuses)).catch([CaseStatus.ACTIVE]), + status: z.array(z.enum(CaseStatuses)).catch([CaseStatus.ACTIVE]), }) .catch({ assigneeId: [authenticatedUserId, null], - caseStatus: [CaseStatus.ACTIVE], + status: [CaseStatus.ACTIVE], }), }); diff --git a/apps/backoffice-v2/src/common/hooks/useSort/sort.ts b/apps/backoffice-v2/src/common/hooks/useSort/sort.ts deleted file mode 100644 index 2c2b494c5e..0000000000 --- a/apps/backoffice-v2/src/common/hooks/useSort/sort.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { AnyArray, TKeyofArrayElement } from '../../types'; - -export const sort = <TArray extends AnyArray>( - { - data, - sortBy, - sortDir, - }: { - data: TArray; - sortBy: TKeyofArrayElement<TArray>; - sortDir: 'asc' | 'desc'; - }, // Avoid errors stemming from calling array methods on non-arrays or empty arrays. -) => - !Array.isArray(data) || !data?.length - ? [] - : data?.slice().sort((a, b) => { - const aValue = String(a[sortBy]); - const bValue = String(b[sortBy]); - - if (sortDir === 'asc') { - return aValue.localeCompare(bValue); - } - - return bValue.localeCompare(aValue); - }); diff --git a/apps/backoffice-v2/src/common/hooks/useSort/useSort.tsx b/apps/backoffice-v2/src/common/hooks/useSort/useSort.tsx deleted file mode 100644 index f66db9b7a3..0000000000 --- a/apps/backoffice-v2/src/common/hooks/useSort/useSort.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { useCallback, useMemo } from 'react'; -import { AnyArray, TKeyofArrayElement } from '../../types'; -import { sort } from './sort'; -import { useSearchParamsByEntity } from '../useSearchParamsByEntity/useSearchParamsByEntity'; - -/** - * @description A hook to easily sort an array of objects by key, and change sort direction or the sort by key. - * @param props - * @param props.data - The data to sort. - * @param props.initialState - An object of the initial sorting direction and sort by state. - */ -export const useSort = <TArray extends AnyArray>({ - data, - initialState, -}: { - data: TArray; - initialState: { - sortDir?: 'asc' | 'desc'; - sortBy: TKeyofArrayElement<TArray>; - }; -}) => { - const [ - { sortBy = initialState?.sortBy, sortDir = initialState?.sortDir ?? 'asc' }, - setSearchParams, - ] = useSearchParamsByEntity(); - // Sort - const sorted = useMemo( - () => - sort({ - data, - sortBy, - sortDir, - }), - [data, sortDir, sortBy], - ); - const onSortDir = useCallback(() => { - setSearchParams({ - sortDir: sortDir === 'asc' ? 'desc' : 'asc', - }); - }, [setSearchParams, sortDir]); - const onSortBy = useCallback( - (sortBy: string) => { - setSearchParams({ - sortBy, - }); - }, - [setSearchParams], - ); - - return { - sortBy, - sortDir, - sorted, - onSortDir, - onSortBy, - }; -}; diff --git a/apps/backoffice-v2/src/common/hooks/useZodSearchParams/useZodSearchParams.tsx b/apps/backoffice-v2/src/common/hooks/useZodSearchParams/useZodSearchParams.tsx index b652ad2f23..4f896d8ad4 100644 --- a/apps/backoffice-v2/src/common/hooks/useZodSearchParams/useZodSearchParams.tsx +++ b/apps/backoffice-v2/src/common/hooks/useZodSearchParams/useZodSearchParams.tsx @@ -1,11 +1,11 @@ -import { z, ZodSchema } from 'zod'; +import { AnyZodObject, z } from 'zod'; import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'; import { useCallback, useEffect, useMemo } from 'react'; import { IUseZodSearchParams } from './interfaces'; import { defaultDeserializer } from './utils/default-deserializer'; import { defaultSerializer } from './utils/default-serializer'; -export const useZodSearchParams = <TSchema extends ZodSchema>( +export const useZodSearchParams = <TSchema extends AnyZodObject>( schema: TSchema, options: IUseZodSearchParams = {}, ) => { @@ -19,6 +19,7 @@ export const useZodSearchParams = <TSchema extends ZodSchema>( [schema, searchParamsAsObject], ); const navigate = useNavigate(); + const { state } = useLocation(); const onSetSearchParams = useCallback( (searchParams: Record<string, unknown>) => { navigate( @@ -26,6 +27,11 @@ export const useZodSearchParams = <TSchema extends ZodSchema>( ...parsedSearchParams, ...searchParams, })}`, + { + state: { + from: state?.from, + }, + }, ); }, [pathname, parsedSearchParams, setSearchParams], diff --git a/apps/backoffice-v2/src/common/hooks/useZodSearchParams/utils/default-deserializer.ts b/apps/backoffice-v2/src/common/hooks/useZodSearchParams/utils/default-deserializer.ts index 7a65ae3545..ca96c8e67c 100644 --- a/apps/backoffice-v2/src/common/hooks/useZodSearchParams/utils/default-deserializer.ts +++ b/apps/backoffice-v2/src/common/hooks/useZodSearchParams/utils/default-deserializer.ts @@ -1,7 +1,8 @@ import qs from 'qs'; import { parseNullAndEmptyArrayDeep } from './parse-null-and-empty-array-deep'; +import { IUseZodSearchParams } from '../interfaces'; -export const defaultDeserializer = (searchParams: string) => { +export const defaultDeserializer: IUseZodSearchParams['deserializer'] = (searchParams: string) => { const parsedSearchParams = qs.parse(searchParams, { ignoreQueryPrefix: true, strictNullHandling: true, diff --git a/apps/backoffice-v2/src/common/utils/fetcher/fetcher.ts b/apps/backoffice-v2/src/common/utils/fetcher/fetcher.ts index dc73346580..dbacf36be3 100644 --- a/apps/backoffice-v2/src/common/utils/fetcher/fetcher.ts +++ b/apps/backoffice-v2/src/common/utils/fetcher/fetcher.ts @@ -56,7 +56,7 @@ export const fetcher: IFetcher = async ({ const [data, jsonError] = isBlob ? await handlePromise(res.blob()) - : res.headers.get('content-length') > '0' + : !res.headers.get('content-length') || res.headers.get('content-length') > '0' ? await handlePromise(res.json()) : [undefined, undefined]; diff --git a/apps/backoffice-v2/src/common/utils/get-entity-type-by-filter-id/get-entity-type-by-filter-id.ts b/apps/backoffice-v2/src/common/utils/get-entity-type-by-filter-id/get-entity-type-by-filter-id.ts new file mode 100644 index 0000000000..10a32eaa9d --- /dev/null +++ b/apps/backoffice-v2/src/common/utils/get-entity-type-by-filter-id/get-entity-type-by-filter-id.ts @@ -0,0 +1,6 @@ +import { getFiltersFromQuery } from '../get-filters-from-query/get-filters-from-query'; +import { TEntityType } from 'src/domains/entities/types'; + +export function getEntityTypeByFilterId(filterId: string): null | TEntityType { + return getFiltersFromQuery().find(filter => filter.id === filterId)?.entity || null; +} diff --git a/apps/backoffice-v2/src/common/utils/get-filters-from-query/get-filters-from-query.ts b/apps/backoffice-v2/src/common/utils/get-filters-from-query/get-filters-from-query.ts new file mode 100644 index 0000000000..3402b88516 --- /dev/null +++ b/apps/backoffice-v2/src/common/utils/get-filters-from-query/get-filters-from-query.ts @@ -0,0 +1,9 @@ +import { queryClient } from '../../../lib/react-query/query-client'; +import { filtersQueryKeys } from '../../../domains/filters/query-keys'; +import { TFilter } from 'src/domains/filters/types'; + +export function getFiltersFromQuery(): TFilter[] { + const filters = queryClient.getQueryData<TFilter[]>(filtersQueryKeys.list().queryKey); + + return filters ? filters : []; +} diff --git a/apps/backoffice-v2/src/common/utils/key-factory/key-factory.ts b/apps/backoffice-v2/src/common/utils/key-factory/key-factory.ts new file mode 100644 index 0000000000..125e0953b8 --- /dev/null +++ b/apps/backoffice-v2/src/common/utils/key-factory/key-factory.ts @@ -0,0 +1,3 @@ +export const keyFactory = (...keys: Array<string>) => { + return keys?.join(':'); +}; diff --git a/apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/AuthenticatedLayout.layout.tsx b/apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/AuthenticatedLayout.layout.tsx index e2216a46b5..925c18c75c 100644 --- a/apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/AuthenticatedLayout.layout.tsx +++ b/apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/AuthenticatedLayout.layout.tsx @@ -1,19 +1,33 @@ import { Header } from '../../../../common/components/organisms/Header'; -import { FunctionComponentWithChildren } from '../../../../common/types'; -import { useSelectEntityFilterOnMount } from '../../../entities/hooks/useSelectEntityFilterOnMount/useSelectEntityFilterOnMount'; +import { useAuthenticatedLayoutLogic } from './hooks/useAuthenticatedLayoutLogic/useAuthenticatedLayoutLogic'; +import { Navigate, Outlet } from 'react-router-dom'; +import { FunctionComponent } from 'react'; +import { FullScreenLoader } from '../../../../common/components/molecules/FullScreenLoader/FullScreenLoader'; -export const AuthenticatedLayout: FunctionComponentWithChildren = ({ children }) => { - // Should only be uncommented once `useAuthRedirects` is no longer in use in `AuthProvider` - // useAuthenticatedLayout(); - useSelectEntityFilterOnMount(); +export const AuthenticatedLayout: FunctionComponent = () => { + const { shouldRedirect, isLoading, redirectUnauthenticatedTo, location } = + useAuthenticatedLayoutLogic(); + + if (isLoading) return <FullScreenLoader />; + + if (shouldRedirect) { + return ( + <Navigate + to={redirectUnauthenticatedTo} + replace + state={{ + from: location, + }} + /> + ); + } return ( <div className="drawer-mobile drawer"> <input id="app-drawer" type="checkbox" className="drawer-toggle" /> <div className={`drawer-content`}> <main className={`grid h-full grid-cols-[285px_1fr]`}> - {/*<Outlet />*/} - {children} + <Outlet /> </main> </div> <div className={`drawer-side w-56`}> diff --git a/apps/backoffice-v2/src/pages/Locale/Locale.loader.ts b/apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/AuthenticatedLayout.loader.ts similarity index 60% rename from apps/backoffice-v2/src/pages/Locale/Locale.loader.ts rename to apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/AuthenticatedLayout.loader.ts index d2ddef8469..a0fd6fc53f 100644 --- a/apps/backoffice-v2/src/pages/Locale/Locale.loader.ts +++ b/apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/AuthenticatedLayout.loader.ts @@ -1,10 +1,10 @@ -import { env } from '../../common/env/env'; -import { authQueryKeys } from '../../domains/auth/query-keys'; -import { queryClient } from '../../lib/react-query/query-client'; -import { filtersQueryKeys } from '../../domains/filters/query-keys'; +import { env } from '../../../../common/env/env'; +import { authQueryKeys } from '../../query-keys'; +import { queryClient } from '../../../../lib/react-query/query-client'; +import { filtersQueryKeys } from '../../../filters/query-keys'; import { LoaderFunction } from 'react-router-dom'; -export const localeLoader: LoaderFunction = async () => { +export const authenticatedLayoutLoader: LoaderFunction = async () => { if (!env.VITE_AUTH_ENABLED) return null; const authenticatedUser = authQueryKeys.authenticatedUser(); diff --git a/apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/hooks/useAuthenticatedLayout/useAuthenticatedLayout.tsx b/apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/hooks/useAuthenticatedLayout/useAuthenticatedLayout.tsx deleted file mode 100644 index a120c06467..0000000000 --- a/apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/hooks/useAuthenticatedLayout/useAuthenticatedLayout.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useAuthContext } from '../../../../context/AuthProvider/hooks/useAuthContext/useAuthContext'; -import { useAuthenticatedUserQuery } from '../../../../hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; -import { useIsAuthenticated } from '../../../../context/AuthProvider/hooks/useIsAuthenticated/useIsAuthenticated'; -import { useEffect } from 'react'; -import { env } from '../../../../../../common/env/env'; -import { useLocation, useNavigate } from 'react-router-dom'; - -export const useAuthenticatedLayout = () => { - const { redirectUnauthenticatedTo } = useAuthContext(); - const { isLoading } = useAuthenticatedUserQuery(); - const isAuthenticated = useIsAuthenticated(); - const navigate = useNavigate(); - const { pathname } = useLocation(); - const disableRedirect = true; - - useEffect(() => { - // When loading the user could be signed in or signed out. - if (isLoading || !env.VITE_AUTH_ENABLED) return; - - // Navigate to the sign-in page if the user is signed out. - if ( - disableRedirect || - isAuthenticated || - !redirectUnauthenticatedTo || - pathname === redirectUnauthenticatedTo - ) - return; - - void navigate(redirectUnauthenticatedTo, { - replace: true, - }); - }, [isLoading, isAuthenticated, navigate, pathname, redirectUnauthenticatedTo, disableRedirect]); -}; diff --git a/apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/hooks/useAuthenticatedLayoutLogic/useAuthenticatedLayoutLogic.tsx b/apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/hooks/useAuthenticatedLayoutLogic/useAuthenticatedLayoutLogic.tsx new file mode 100644 index 0000000000..4518a2fcfc --- /dev/null +++ b/apps/backoffice-v2/src/domains/auth/components/AuthenticatedLayout/hooks/useAuthenticatedLayoutLogic/useAuthenticatedLayoutLogic.tsx @@ -0,0 +1,27 @@ +import { useAuthContext } from '../../../../context/AuthProvider/hooks/useAuthContext/useAuthContext'; +import { useAuthenticatedUserQuery } from '../../../../hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; +import { useIsAuthenticated } from '../../../../context/AuthProvider/hooks/useIsAuthenticated/useIsAuthenticated'; +import { env } from '../../../../../../common/env/env'; +import { useLocation } from 'react-router-dom'; +import { useMemo } from 'react'; + +export const useAuthenticatedLayoutLogic = () => { + const { redirectUnauthenticatedTo } = useAuthContext(); + const { isLoading } = useAuthenticatedUserQuery(); + const isAuthenticated = useIsAuthenticated(); + const location = useLocation(); + const shouldRedirect = useMemo( + () => + [!isLoading, !isAuthenticated, !!redirectUnauthenticatedTo, env.VITE_AUTH_ENABLED].every( + Boolean, + ), + [isLoading, isAuthenticated, redirectUnauthenticatedTo], + ); + + return { + shouldRedirect, + isLoading, + redirectUnauthenticatedTo, + location, + }; +}; diff --git a/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/UnauthenticatedLayout.layout.tsx b/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/UnauthenticatedLayout.layout.tsx index 614d24731f..9c841502d5 100644 --- a/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/UnauthenticatedLayout.layout.tsx +++ b/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/UnauthenticatedLayout.layout.tsx @@ -1,13 +1,29 @@ -import { FunctionComponentWithChildren } from '../../../../common/types'; +import { useUnauthenticatedLayoutLogic } from './hooks/useUnauthenticatedLayoutLogic/useUnauthenticatedLayoutLogic'; +import { Navigate, Outlet } from 'react-router-dom'; +import { FunctionComponent } from 'react'; +import { FullScreenLoader } from '../../../../common/components/molecules/FullScreenLoader/FullScreenLoader'; -export const UnauthenticatedLayout: FunctionComponentWithChildren = ({ children }) => { - // Should only be uncommented once `useAuthRedirects` is no longer in use in `AuthProvider` - // useUnauthenticatedLayout(); +export const UnauthenticatedLayout: FunctionComponent = () => { + const { isLoading, shouldRedirect, redirectAuthenticatedTo, state } = + useUnauthenticatedLayoutLogic(); + + if (isLoading) return <FullScreenLoader />; + + if (shouldRedirect) { + return ( + <Navigate + to={redirectAuthenticatedTo} + replace + state={{ + from: state?.from, + }} + /> + ); + } return ( <main className={`h-full`}> - {/*<Outlet />*/} - {children} + <Outlet /> </main> ); }; diff --git a/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/UnauthenticatedLayout.loader.ts b/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/UnauthenticatedLayout.loader.ts new file mode 100644 index 0000000000..024fa73c5c --- /dev/null +++ b/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/UnauthenticatedLayout.loader.ts @@ -0,0 +1,9 @@ +import { LoaderFunction, redirect } from 'react-router-dom'; + +export const unauthenticatedLayoutLoader: LoaderFunction = async ({ request }) => { + const url = new URL(request.url); + + if (url.pathname === `/en/auth/sign-in`) return null; + + return redirect(`/en/auth/sign-in`); +}; diff --git a/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/hooks/useUnauthenticatedLayout/useUnauthenticatedLayout.tsx b/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/hooks/useUnauthenticatedLayout/useUnauthenticatedLayout.tsx deleted file mode 100644 index 89ba723444..0000000000 --- a/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/hooks/useUnauthenticatedLayout/useUnauthenticatedLayout.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useAuthenticatedUserQuery } from '../../../../hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; -import { useIsAuthenticated } from '../../../../context/AuthProvider/hooks/useIsAuthenticated/useIsAuthenticated'; -import { useAuthContext } from '../../../../context/AuthProvider/hooks/useAuthContext/useAuthContext'; -import { useEffect } from 'react'; -import { env } from '../../../../../../common/env/env'; -import { useLocation, useNavigate } from 'react-router-dom'; - -export const useUnauthenticatedLayout = () => { - const { isLoading } = useAuthenticatedUserQuery(); - const isAuthenticated = useIsAuthenticated(); - const navigate = useNavigate(); - const { pathname } = useLocation(); - const { redirectAuthenticatedTo } = useAuthContext(); - const disableRedirect = true; - - useEffect(() => { - // When loading the user could be signed in or signed out. - if (isLoading || !env.VITE_AUTH_ENABLED) return; - - // Navigate to the entities page if the user is signed in. - if ( - // Implement authenticated layout before removing disableRedirect. - disableRedirect || - !isAuthenticated || - !redirectAuthenticatedTo || - pathname.startsWith(redirectAuthenticatedTo) - ) - return; - - void navigate(redirectAuthenticatedTo, { - replace: true, - }); - }, [isLoading, isAuthenticated, navigate, pathname, redirectAuthenticatedTo, disableRedirect]); -}; diff --git a/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/hooks/useUnauthenticatedLayoutLogic/useUnauthenticatedLayoutLogic.tsx b/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/hooks/useUnauthenticatedLayoutLogic/useUnauthenticatedLayoutLogic.tsx new file mode 100644 index 0000000000..fa38b4dffc --- /dev/null +++ b/apps/backoffice-v2/src/domains/auth/components/UnauthenticatedLayout/hooks/useUnauthenticatedLayoutLogic/useUnauthenticatedLayoutLogic.tsx @@ -0,0 +1,23 @@ +import { useAuthenticatedUserQuery } from '../../../../hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; +import { useIsAuthenticated } from '../../../../context/AuthProvider/hooks/useIsAuthenticated/useIsAuthenticated'; +import { useAuthContext } from '../../../../context/AuthProvider/hooks/useAuthContext/useAuthContext'; +import { useLocation } from 'react-router-dom'; +import { useMemo } from 'react'; + +export const useUnauthenticatedLayoutLogic = () => { + const { isLoading } = useAuthenticatedUserQuery(); + const isAuthenticated = useIsAuthenticated(); + const { redirectAuthenticatedTo } = useAuthContext(); + const { state } = useLocation(); + const shouldRedirect = useMemo( + () => [!isLoading, isAuthenticated, redirectAuthenticatedTo].every(Boolean), + [isLoading, isAuthenticated, redirectAuthenticatedTo], + ); + + return { + isLoading, + shouldRedirect, + redirectAuthenticatedTo, + state, + }; +}; diff --git a/apps/backoffice-v2/src/domains/auth/context/AuthProvider/AuthProvider.tsx b/apps/backoffice-v2/src/domains/auth/context/AuthProvider/AuthProvider.tsx index 55f6c96bfd..f377f1bc68 100644 --- a/apps/backoffice-v2/src/domains/auth/context/AuthProvider/AuthProvider.tsx +++ b/apps/backoffice-v2/src/domains/auth/context/AuthProvider/AuthProvider.tsx @@ -1,8 +1,8 @@ import { FunctionComponentWithChildren } from '../../../../common/types'; -import { createContext, useMemo } from 'react'; +import React, { createContext, useMemo } from 'react'; import { env } from '../../../../common/env/env'; import { useAuthenticatedUserQuery } from '../../hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; -import { useAuthRedirects } from './hooks/useAuthRedirects/useAuthRedirects'; +import { FullScreenLoader } from '../../../../common/components/molecules/FullScreenLoader/FullScreenLoader'; export const AuthContext = createContext<{ redirectAuthenticatedTo?: string; @@ -18,7 +18,6 @@ export const AuthContext = createContext<{ }>(undefined); export const AuthProvider: FunctionComponentWithChildren<{ - protectedRoutes?: readonly string[]; redirectAuthenticatedTo?: string; redirectUnauthenticatedTo?: string; signInOptions?: { @@ -31,7 +30,6 @@ export const AuthProvider: FunctionComponentWithChildren<{ }; }> = ({ children, - protectedRoutes, redirectAuthenticatedTo, redirectUnauthenticatedTo, signInOptions, @@ -48,14 +46,8 @@ export const AuthProvider: FunctionComponentWithChildren<{ [redirectAuthenticatedTo, redirectUnauthenticatedTo, signInOptions, signOutOptions], ); - useAuthRedirects({ - protectedRoutes, - redirectAuthenticatedTo, - redirectUnauthenticatedTo, - }); - // Don't render the children to avoid a flash of wrong state (i.e. authenticated layout). - if (isLoading && env.VITE_AUTH_ENABLED) return null; + if (isLoading && env.VITE_AUTH_ENABLED) return <FullScreenLoader />; return <AuthContext.Provider value={contextValues}>{children}</AuthContext.Provider>; }; diff --git a/apps/backoffice-v2/src/domains/auth/context/AuthProvider/hooks/useAuthRedirects/useAuthRedirects.tsx b/apps/backoffice-v2/src/domains/auth/context/AuthProvider/hooks/useAuthRedirects/useAuthRedirects.tsx deleted file mode 100644 index 01ff939eb5..0000000000 --- a/apps/backoffice-v2/src/domains/auth/context/AuthProvider/hooks/useAuthRedirects/useAuthRedirects.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useAuthenticatedUserQuery } from '../../../../hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; -import { useEffect } from 'react'; -import { env } from '../../../../../../common/env/env'; -import { useIsAuthenticated } from '../useIsAuthenticated/useIsAuthenticated'; -import { useLocation, useNavigate } from 'react-router-dom'; - -export const useAuthRedirects = ({ - protectedRoutes, - redirectAuthenticatedTo, - redirectUnauthenticatedTo, -}: { - protectedRoutes: ReadonlyArray<string>; - redirectAuthenticatedTo?: string; - redirectUnauthenticatedTo?: string; -}) => { - const { isLoading } = useAuthenticatedUserQuery(); - const isAuthenticated = useIsAuthenticated(); - const navigate = useNavigate(); - const { pathname } = useLocation(); - - useEffect(() => { - const shouldRedirect = [ - // When loading the user could be signed in or signed out. - !isLoading, - env.VITE_AUTH_ENABLED, - !!redirectAuthenticatedTo, - isAuthenticated, - !pathname.startsWith(redirectAuthenticatedTo), - // Remove once authenticated layout is implemented. - !protectedRoutes?.every(route => pathname.startsWith(route)), - ].every(Boolean); - - if (!shouldRedirect) return; - - // Navigate to the entities page if the user is signed in. - - void navigate(redirectAuthenticatedTo, { - replace: true, - }); - }, [isLoading, isAuthenticated, navigate, pathname, redirectAuthenticatedTo, protectedRoutes]); - - useEffect(() => { - const shouldRedirect = [ - !isLoading, - env.VITE_AUTH_ENABLED, - !!redirectUnauthenticatedTo, - !isAuthenticated, - pathname !== redirectUnauthenticatedTo, - ].every(Boolean); - - if (!shouldRedirect) return; - - // Navigate to the sign-in page if the user is signed out. - navigate(redirectUnauthenticatedTo, { - replace: true, - }); - }, [isLoading, isAuthenticated, navigate, pathname, redirectUnauthenticatedTo]); -}; diff --git a/apps/backoffice-v2/src/domains/auth/fetchers.ts b/apps/backoffice-v2/src/domains/auth/fetchers.ts index 9d81f2d51d..2b8b2a1aca 100644 --- a/apps/backoffice-v2/src/domains/auth/fetchers.ts +++ b/apps/backoffice-v2/src/domains/auth/fetchers.ts @@ -4,6 +4,7 @@ import { z } from 'zod'; import { handleZodError } from '../../common/utils/handle-zod-error/handle-zod-error'; import { Method } from '../../common/enums'; import { AuthenticatedUserSchema } from './validation-schemas'; +import { sleep } from '@ballerine/common'; export const fetchSignOut = async ({ callbackUrl }: ISignInProps) => { const [session, error] = await apiClient({ @@ -51,6 +52,7 @@ export const fetchAuthenticatedUser = async () => { user: AuthenticatedUserSchema, }), }); + await sleep(2000); return handleZodError(error, session); }; diff --git a/apps/backoffice-v2/src/domains/auth/hooks/mutations/useSignInMutation/useSignInMutation.tsx b/apps/backoffice-v2/src/domains/auth/hooks/mutations/useSignInMutation/useSignInMutation.tsx index 41c64cb035..81fd8c2c0c 100644 --- a/apps/backoffice-v2/src/domains/auth/hooks/mutations/useSignInMutation/useSignInMutation.tsx +++ b/apps/backoffice-v2/src/domains/auth/hooks/mutations/useSignInMutation/useSignInMutation.tsx @@ -1,5 +1,5 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import { ISignInProps } from './interfaces'; import { fetchSignIn } from '../../../fetchers'; import { authQueryKeys } from '../../../query-keys'; @@ -8,6 +8,7 @@ export const useSignInMutation = () => { const queryClient = useQueryClient(); const navigate = useNavigate(); const getSession = authQueryKeys.authenticatedUser(); + const { state } = useLocation(); return useMutation({ mutationFn: ({ callbackUrl, body }: ISignInProps) => @@ -25,6 +26,9 @@ export const useSignInMutation = () => { void navigate(callbackUrl, { replace: true, + state: { + from: state?.from, + }, }); }, onSettled: () => { diff --git a/apps/backoffice-v2/src/domains/auth/hooks/mutations/useSignOutMutation/useSignOutMutation.tsx b/apps/backoffice-v2/src/domains/auth/hooks/mutations/useSignOutMutation/useSignOutMutation.tsx index e70f7ca416..59b9dd8f03 100644 --- a/apps/backoffice-v2/src/domains/auth/hooks/mutations/useSignOutMutation/useSignOutMutation.tsx +++ b/apps/backoffice-v2/src/domains/auth/hooks/mutations/useSignOutMutation/useSignOutMutation.tsx @@ -1,5 +1,5 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import { ISignInProps } from '../useSignInMutation/interfaces'; import { authQueryKeys } from '../../../query-keys'; import { fetchSignOut } from '../../../fetchers'; @@ -8,6 +8,7 @@ export const useSignOutMutation = () => { const queryClient = useQueryClient(); const navigate = useNavigate(); const authenticatedUser = authQueryKeys.authenticatedUser(); + const { state } = useLocation(); return useMutation({ mutationFn: ({ callbackUrl }: ISignInProps) => @@ -26,6 +27,9 @@ export const useSignOutMutation = () => { void navigate(callbackUrl, { replace: true, + state: { + from: state?.from, + }, }); }, onSettled: () => { diff --git a/apps/backoffice-v2/src/domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery.tsx b/apps/backoffice-v2/src/domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery.tsx index 53fcf71c0b..b7b676b253 100644 --- a/apps/backoffice-v2/src/domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery.tsx +++ b/apps/backoffice-v2/src/domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery.tsx @@ -2,5 +2,9 @@ import { useQuery } from '@tanstack/react-query'; import { authQueryKeys } from '../../../query-keys'; export const useAuthenticatedUserQuery = () => { - return useQuery(authQueryKeys.authenticatedUser()); + return useQuery({ + ...authQueryKeys.authenticatedUser(), + staleTime: 100_000, + refetchInterval: 100_000, + }); }; diff --git a/apps/backoffice-v2/src/domains/businesses/fetchers.ts b/apps/backoffice-v2/src/domains/businesses/fetchers.ts deleted file mode 100644 index 7f15ba4f21..0000000000 --- a/apps/backoffice-v2/src/domains/businesses/fetchers.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { apiClient } from '../../common/api-client/api-client'; -import { handleZodError } from '../../common/utils/handle-zod-error/handle-zod-error'; -import { Method } from '../../common/enums'; -import { BusinessByIdSchema, BusinessesListSchema } from './validation-schemas'; - -export const fetchBusinesses = async (filterId: string) => { - const [businesses, error] = await apiClient({ - endpoint: `businesses?filterId=${filterId ?? ''}`, - method: Method.GET, - schema: BusinessesListSchema, - }); - - return handleZodError(error, businesses); -}; - -export const fetchBusinessById = async ({ - businessId, - filterId, -}: { - businessId: string; - filterId: string; -}) => { - const [business, error] = await apiClient({ - endpoint: `businesses/${businessId}?filterId=${filterId ?? ''}`, - method: Method.GET, - schema: BusinessByIdSchema, - }); - - return handleZodError(error, business); -}; diff --git a/apps/backoffice-v2/src/domains/businesses/query-keys.ts b/apps/backoffice-v2/src/domains/businesses/query-keys.ts deleted file mode 100644 index 6a59098270..0000000000 --- a/apps/backoffice-v2/src/domains/businesses/query-keys.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createQueryKeys } from '@lukemorales/query-key-factory'; -import { fetchBusinessById, fetchBusinesses } from './fetchers'; - -export const businessesQueryKeys = createQueryKeys('businesses', { - list: (filterId: string) => ({ - queryKey: [{ filterId }], - queryFn: () => fetchBusinesses(filterId), - }), - byId: (businessId: string, filterId: string) => ({ - queryKey: [{ businessId, filterId }], - queryFn: () => - fetchBusinessById({ - businessId, - filterId, - }), - }), -}); diff --git a/apps/backoffice-v2/src/domains/businesses/types.ts b/apps/backoffice-v2/src/domains/businesses/types.ts deleted file mode 100644 index ca97b7eff5..0000000000 --- a/apps/backoffice-v2/src/domains/businesses/types.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type TBusiness = any; -export type TBusinesses = any; diff --git a/apps/backoffice-v2/src/domains/businesses/validation-schemas.ts b/apps/backoffice-v2/src/domains/businesses/validation-schemas.ts deleted file mode 100644 index 9928008b70..0000000000 --- a/apps/backoffice-v2/src/domains/businesses/validation-schemas.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { z } from 'zod'; -import { CaseStatuses, State, States } from '../../common/enums'; -import { ObjectWithIdSchema } from '../../lib/zod/utils/object-with-id/object-with-id'; - -export const BusinessesListSchema = z - .array( - ObjectWithIdSchema.extend({ - companyName: z.string().default(''), - registrationNumber: z.string().nullable().default(''), - legalForm: z.string().nullable().default(''), - countryOfIncorporation: z.string().nullable().default(''), - createdAt: z.string().default(''), - approvalState: z.enum(States).default(State.PROCESSING), - workflowRuntimeData: z.preprocess( - workflows => workflows?.[0], - ObjectWithIdSchema.extend({ - assigneeId: z.string().nullable().optional(), - createdAt: z.string().datetime(), - status: z.enum(CaseStatuses), - }).optional(), - ), - }), - ) - .default([]); - -export const BusinessByIdSchema = ObjectWithIdSchema.extend({ - companyName: z.string().default(''), - registrationNumber: z.string().nullable().default(''), - legalForm: z.string().nullable().default(''), - countryOfIncorporation: z.string().nullable().default(''), - dateOfIncorporation: z.string().nullable().default(''), - address: z.string().nullable().default(''), - phoneNumber: z.string().nullable().default(''), - email: z.string().nullable().default(''), - website: z.string().nullable().default(''), - industry: z.string().nullable().default(''), - taxIdentificationNumber: z.string().nullable().default(''), - vatNumber: z.string().nullable().default(''), - shareholderStructure: z.string().nullable().default(''), - numberOfEmployees: z.number().nullable().default(0), - businessPurpose: z.string().nullable().default(''), - // documents: z - // .array( - // z.object({ - // url: z.string(), - // doctype: z.string(), - // }), - // ) - // .default([]), - documents: z.any(), - approvalState: z.enum(States).default(State.PROCESSING), - createdAt: z.string().default(''), - updatedAt: z.string().default(''), -}).default({}); diff --git a/apps/backoffice-v2/src/domains/entities/hooks/mutations/useApproveEntityMutation/useApproveEntityMutation.tsx b/apps/backoffice-v2/src/domains/entities/hooks/mutations/useApproveEntityMutation/useApproveEntityMutation.tsx index 915a62b832..567152a07a 100644 --- a/apps/backoffice-v2/src/domains/entities/hooks/mutations/useApproveEntityMutation/useApproveEntityMutation.tsx +++ b/apps/backoffice-v2/src/domains/entities/hooks/mutations/useApproveEntityMutation/useApproveEntityMutation.tsx @@ -1,23 +1,18 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useFilterId } from '../../../../../common/hooks/useFilterId/useFilterId'; import toast from 'react-hot-toast'; import { t } from 'i18next'; -import { useFilterEntity } from '../../useFilterEntity/useFilterEntity'; -import { queryKeys } from '../../../query-keys'; import { fetchWorkflowEvent } from '../../../../workflows/fetchers'; +import { workflowsQueryKeys } from '../../../../workflows/query-keys'; +// @TODO: Refactor to be under cases/workflows domain export const useApproveEntityMutation = ({ - endUserId, workflowId, onSelectNextEntity, }: { - endUserId: string; workflowId: string; onSelectNextEntity: VoidFunction; }) => { const queryClient = useQueryClient(); - const filterId = useFilterId(); - const entity = useFilterEntity(); return useMutation({ mutationFn: () => @@ -28,12 +23,8 @@ export const useApproveEntityMutation = ({ }, }), onSuccess: () => { - void queryClient.invalidateQueries({ - queryKey: queryKeys[entity as keyof typeof queryKeys]?.list?.(filterId).queryKey, - }); - void queryClient.invalidateQueries({ - queryKey: queryKeys[entity as keyof typeof queryKeys]?.byId?.(endUserId, filterId).queryKey, - }); + // workflowsQueryKeys._def is the base key for all workflows queries + void queryClient.invalidateQueries(workflowsQueryKeys._def); toast.success(t('toast:approve_case.success')); diff --git a/apps/backoffice-v2/src/domains/entities/hooks/mutations/useRejectEntityMutation/useRejectEntityMutation.tsx b/apps/backoffice-v2/src/domains/entities/hooks/mutations/useRejectEntityMutation/useRejectEntityMutation.tsx index 6e91fa80b2..beb94f0ef3 100644 --- a/apps/backoffice-v2/src/domains/entities/hooks/mutations/useRejectEntityMutation/useRejectEntityMutation.tsx +++ b/apps/backoffice-v2/src/domains/entities/hooks/mutations/useRejectEntityMutation/useRejectEntityMutation.tsx @@ -1,24 +1,19 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { Action } from '../../../../../common/enums'; -import { useFilterId } from '../../../../../common/hooks/useFilterId/useFilterId'; import toast from 'react-hot-toast'; import { t } from 'i18next'; -import { useFilterEntity } from '../../useFilterEntity/useFilterEntity'; import { fetchWorkflowEvent } from '../../../../workflows/fetchers'; -import { queryKeys } from '../../../query-keys'; +import { workflowsQueryKeys } from '../../../../workflows/query-keys'; +// @TODO: Refactor to be under cases/workflows domain export const useRejectEntityMutation = ({ workflowId, - entityId, onSelectNextEntity, }: { workflowId: string; - entityId: string; onSelectNextEntity: VoidFunction; }) => { const queryClient = useQueryClient(); - const filterId = useFilterId(); - const entity = useFilterEntity(); return useMutation({ mutationFn: ( @@ -45,12 +40,8 @@ export const useRejectEntityMutation = ({ }, }), onSuccess: (data, payload) => { - void queryClient.invalidateQueries({ - queryKey: queryKeys[entity]?.list?.(filterId).queryKey, - }); - void queryClient.invalidateQueries({ - queryKey: queryKeys[entity].byId(entityId, filterId).queryKey, - }); + // workflowsQueryKeys._def is the base key for all workflows queries + void queryClient.invalidateQueries(workflowsQueryKeys._def); const action = payload.action === Action.REJECT ? 'reject_case' : 'ask_resubmit_case'; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/queries/useEntitiesQuery/useEntitiesQuery.tsx b/apps/backoffice-v2/src/domains/entities/hooks/queries/useEntitiesQuery/useEntitiesQuery.tsx deleted file mode 100644 index 26e23544c4..0000000000 --- a/apps/backoffice-v2/src/domains/entities/hooks/queries/useEntitiesQuery/useEntitiesQuery.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useQuery, UseQueryOptions } from '@tanstack/react-query'; -import { useFilterEntity } from '../../useFilterEntity/useFilterEntity'; -import { queryKeys } from '../../../query-keys'; -import { useFilterId } from '../../../../../common/hooks/useFilterId/useFilterId'; -import { TEntities } from '../../../types'; -import { useIsAuthenticated } from '../../../../auth/context/AuthProvider/hooks/useIsAuthenticated/useIsAuthenticated'; - -export const useEntitiesQuery = <TQueryFnData = TEntities,>( - options: UseQueryOptions<TQueryFnData> & { - select?: (entities: TEntities) => TQueryFnData; - }, -) => { - const entity = useFilterEntity(); - const filterId = useFilterId(); - const isAuthenticated = useIsAuthenticated(); - - return useQuery({ - ...queryKeys[entity as keyof typeof queryKeys]?.list?.(filterId), - enabled: !!filterId && isAuthenticated, - ...options, - }); -}; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/queries/useEntitiesWithWorkflowsQuery/useEntitiesWithWorkflowsQuery.tsx b/apps/backoffice-v2/src/domains/entities/hooks/queries/useEntitiesWithWorkflowsQuery/useEntitiesWithWorkflowsQuery.tsx deleted file mode 100644 index 28f37908c3..0000000000 --- a/apps/backoffice-v2/src/domains/entities/hooks/queries/useEntitiesWithWorkflowsQuery/useEntitiesWithWorkflowsQuery.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useEntitiesQuery } from '../useEntitiesQuery/useEntitiesQuery'; -import { TUsers } from '../../../../users/types'; -import { env } from '../../../../../common/env/env'; -import { useIsAuthenticated } from '../../../../auth/context/AuthProvider/hooks/useIsAuthenticated/useIsAuthenticated'; - -export const useEntitiesWithWorkflowsQuery = (users: TUsers) => { - const isAuthenticated = useIsAuthenticated(); - - return useEntitiesQuery({ - select: entities => - entities.map(entity => { - const assigneeId = entity?.workflowRuntimeData?.assigneeId; - - return { - ...entity, - assigneeId, - assigneeFullName: users?.find(user => user?.id === assigneeId)?.fullName, - caseCreatedAt: entity?.workflowRuntimeData?.createdAt, - caseStatus: entity?.workflowRuntimeData?.status, - }; - }), - enabled: isAuthenticated, - refetchInterval: env.VITE_ASSIGNMENT_POLLING_INTERVAL, - }); -}; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/queries/useEntityQuery/useEntityQuery.tsx b/apps/backoffice-v2/src/domains/entities/hooks/queries/useEntityQuery/useEntityQuery.tsx deleted file mode 100644 index 95956448e5..0000000000 --- a/apps/backoffice-v2/src/domains/entities/hooks/queries/useEntityQuery/useEntityQuery.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { isString } from '../../../../../common/utils/is-string/is-string'; -import { queryKeys } from '../../../query-keys'; -import { useFilterEntity } from '../../useFilterEntity/useFilterEntity'; -import { useFilterId } from '../../../../../common/hooks/useFilterId/useFilterId'; -import { TEntity } from '../../../types'; -import { useIsAuthenticated } from '../../../../auth/context/AuthProvider/hooks/useIsAuthenticated/useIsAuthenticated'; - -export const useEntityQuery = <TQueryFnData = TEntity,>({ - entityId, - select, -}: { - entityId: string; - select?: (data: TEntity) => TQueryFnData; -}) => { - const entity = useFilterEntity(); - const filterId = useFilterId(); - const isAuthenticated = useIsAuthenticated(); - - return useQuery({ - ...queryKeys[entity as keyof typeof queryKeys]?.byId?.(entityId, filterId), - enabled: isString(entityId) && !!entityId?.length && !!filterId && isAuthenticated, - select, - }); -}; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/queries/useEntityWithWorkflowQuery/useEntityWithWorkflowQuery.tsx b/apps/backoffice-v2/src/domains/entities/hooks/queries/useEntityWithWorkflowQuery/useEntityWithWorkflowQuery.tsx deleted file mode 100644 index 465d6795dc..0000000000 --- a/apps/backoffice-v2/src/domains/entities/hooks/queries/useEntityWithWorkflowQuery/useEntityWithWorkflowQuery.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useEntityQuery } from '../useEntityQuery/useEntityQuery'; -import { useWorkflowQuery } from '../../../../workflows/hooks/queries/useWorkflowQuery/useWorkflowQuery'; -import { useWorkflowsQuery } from '../../../../workflows/hooks/queries/useWorkflowsQuery/useWorkflowsQuery'; -import { useFilterEntity } from '../../useFilterEntity/useFilterEntity'; - -export const useEntityWithWorkflowQuery = (entityId: string) => { - const entity = useFilterEntity(); - const workflowId = useWorkflowsQuery()?.data?.find( - workflow => - (entity === 'individuals' && workflow.endUserId === entityId) || - (entity === 'businesses' && workflow.businessId === entityId), - )?.id; - const { data: workflow } = useWorkflowQuery({ workflowId }); - - return useEntityQuery({ - entityId, - select: entity => ({ - ...entity, - workflow, - }), - }); -}; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/queries/useFirstEntityIdQuery/useFirstEntityIdQuery.tsx b/apps/backoffice-v2/src/domains/entities/hooks/queries/useFirstEntityIdQuery/useFirstEntityIdQuery.tsx deleted file mode 100644 index b009edae4d..0000000000 --- a/apps/backoffice-v2/src/domains/entities/hooks/queries/useFirstEntityIdQuery/useFirstEntityIdQuery.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useCallback } from 'react'; -import { sort } from '../../../../../common/hooks/useSort/sort'; -import { useSelectEntitiesQuery } from '../useSelectEntitiesQuery/useSelectEntitiesQuery'; -import { TEntities } from '../../../types'; -import { useUsersQuery } from '../../../../users/hooks/queries/useUsersQuery/useUsersQuery'; -import { useSearchParamsByEntity } from '../../../../../common/hooks/useSearchParamsByEntity/useSearchParamsByEntity'; - -export const useFirstEntityIdQuery = ({ - initialState, -}: { - initialState: { - sortDir?: 'asc' | 'desc'; - sortBy: keyof TEntities[number]; - }; -}) => { - const [{ sortBy = initialState?.sortBy, sortDir = initialState?.sortDir ?? 'asc' }] = - useSearchParamsByEntity(); - const { data: users } = useUsersQuery(); - const selectFirstEntityId = useCallback( - (entities: TEntities) => { - return sort({ - data: entities.map(entity => { - const assigneeId = entity?.workflowRuntimeData?.assigneeId; - - return { - ...entity, - assigneeId, - assigneeFullName: users?.find(user => user?.id === assigneeId)?.fullName, - caseCreatedAt: entity?.workflowRuntimeData?.createdAt, - }; - }), - sortBy, - sortDir, - })?.[0]?.id; - }, - [sortDir, sortBy, users], - ); - - return useSelectEntitiesQuery(selectFirstEntityId); -}; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/queries/useNextEntityIdQuery/useNextEntityIdQuery.tsx b/apps/backoffice-v2/src/domains/entities/hooks/queries/useNextEntityIdQuery/useNextEntityIdQuery.tsx deleted file mode 100644 index c64abaa0e1..0000000000 --- a/apps/backoffice-v2/src/domains/entities/hooks/queries/useNextEntityIdQuery/useNextEntityIdQuery.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useParams } from 'react-router-dom'; -import { useCallback } from 'react'; -import { sort } from '../../../../../common/hooks/useSort/sort'; -import { useSelectEntitiesQuery } from '../useSelectEntitiesQuery/useSelectEntitiesQuery'; -import { TEntities } from '../../../types'; -import { useSearchParamsByEntity } from '../../../../../common/hooks/useSearchParamsByEntity/useSearchParamsByEntity'; - -export const useNextEntityIdQuery = ({ - initialState, -}: { - initialState: { - sortDir?: 'asc' | 'desc'; - sortBy: keyof TEntities[number]; - }; -}) => { - const { entityId } = useParams(); - const [{ sortBy = initialState?.sortBy, sortDir = initialState?.sortDir ?? 'asc' }] = - useSearchParamsByEntity(); - const selectNextEntityId = useCallback( - (data: TEntities) => { - if (!entityId) return; - - const sorted = sort({ data, sortBy, sortDir }); - const nextEntityIndex = sorted?.findIndex(entity => entity.id === entityId) + 1; - - return sorted?.[nextEntityIndex]?.id; - }, - [entityId, sortBy, sortDir], - ); - - return useSelectEntitiesQuery(selectNextEntityId); -}; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/queries/useSelectEntitiesQuery/useSelectEntitiesQuery.tsx b/apps/backoffice-v2/src/domains/entities/hooks/queries/useSelectEntitiesQuery/useSelectEntitiesQuery.tsx deleted file mode 100644 index 3ae52f9ad1..0000000000 --- a/apps/backoffice-v2/src/domains/entities/hooks/queries/useSelectEntitiesQuery/useSelectEntitiesQuery.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { queryKeys } from '../../../query-keys'; -import { useFilterEntity } from '../../useFilterEntity/useFilterEntity'; -import { useFilterId } from '../../../../../common/hooks/useFilterId/useFilterId'; -import { TEntities } from '../../../types'; -import { useIsAuthenticated } from '../../../../auth/context/AuthProvider/hooks/useIsAuthenticated/useIsAuthenticated'; - -export const useSelectEntitiesQuery = <TQueryFnData = TEntities,>( - select: (data: TEntities) => TQueryFnData, -) => { - const entity = useFilterEntity(); - const filterId = useFilterId(); - const isAuthenticated = useIsAuthenticated(); - - return useQuery({ - ...queryKeys[entity as keyof typeof queryKeys]?.list?.(filterId), - enabled: !!filterId && isAuthenticated, - select, - }); -}; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/useFilterEntity/useFilterEntity.tsx b/apps/backoffice-v2/src/domains/entities/hooks/useFilterEntity/useFilterEntity.tsx deleted file mode 100644 index be28a2e58b..0000000000 --- a/apps/backoffice-v2/src/domains/entities/hooks/useFilterEntity/useFilterEntity.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { useSearchParamsByEntity } from '../../../../common/hooks/useSearchParamsByEntity/useSearchParamsByEntity'; - -export const useFilterEntity = (): string | undefined => { - const [{ entity }] = useSearchParamsByEntity(); - - return entity; -}; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/useSelectEntity/useSelectEntity.tsx b/apps/backoffice-v2/src/domains/entities/hooks/useSelectEntity/useSelectEntity.tsx index bd10ad9b91..c738c8c756 100644 --- a/apps/backoffice-v2/src/domains/entities/hooks/useSelectEntity/useSelectEntity.tsx +++ b/apps/backoffice-v2/src/domains/entities/hooks/useSelectEntity/useSelectEntity.tsx @@ -4,7 +4,7 @@ import { useCallback } from 'react'; export const useSelectEntity = () => { const navigate = useNavigate(); const { locale = 'en' } = useParams(); - const { search } = useLocation(); + const { search, state } = useLocation(); return useCallback( (entityId: string) => () => { @@ -12,6 +12,9 @@ export const useSelectEntity = () => { void navigate(`/${locale}/case-management/entities/${entityId}${search}`, { replace: true, + state: { + from: state?.from, + }, }); }, [locale, navigate, search], diff --git a/apps/backoffice-v2/src/domains/entities/hooks/useSelectEntityFilterOnMount/useSelectEntityFilterOnMount.tsx b/apps/backoffice-v2/src/domains/entities/hooks/useSelectEntityFilterOnMount/useSelectEntityFilterOnMount.tsx index b0d1417340..cd45de51cc 100644 --- a/apps/backoffice-v2/src/domains/entities/hooks/useSelectEntityFilterOnMount/useSelectEntityFilterOnMount.tsx +++ b/apps/backoffice-v2/src/domains/entities/hooks/useSelectEntityFilterOnMount/useSelectEntityFilterOnMount.tsx @@ -1,20 +1,30 @@ import { useFiltersQuery } from '../../../filters/hooks/queries/useFiltersQuery/useFiltersQuery'; -import { useEffect } from 'react'; +import { useEffect, useMemo } from 'react'; import { useSearchParamsByEntity } from '../../../../common/hooks/useSearchParamsByEntity/useSearchParamsByEntity'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useLocation, useNavigate, useParams } from 'react-router-dom'; +import { useEntityType } from '../../../../common/hooks/useEntityType/useEntityType'; +import { searchParamsToObject } from '../../../../common/hooks/useZodSearchParams/utils/search-params-to-object'; export const useSelectEntityFilterOnMount = () => { const { data: filters } = useFiltersQuery(); const { locale } = useParams(); - const [{ entity, filterId, filterName }, setSearchParams] = useSearchParamsByEntity(); + const [{ filterId }, setSearchParams] = useSearchParamsByEntity(); + const entity = useEntityType(); const navigate = useNavigate(); const [firstFilter] = filters ?? []; + const { state } = useLocation(); + const prevFilterId = useMemo( + () => searchParamsToObject(new URLSearchParams(state?.from?.search))?.filterId, + [state?.from?.search], + ); useEffect(() => { - if ((entity && filterId && filterName) || !firstFilter) return; + if ((entity && filterId) || (!firstFilter && !prevFilterId)) return; - navigate( - `/${locale}/case-management/entities?entity=${firstFilter?.entity}&filterId=${firstFilter?.id}&filterName=${firstFilter?.name}`, - ); - }, [entity, filterId, filterName, firstFilter, setSearchParams]); + navigate(`/${locale}/case-management/entities?filterId=${prevFilterId || firstFilter?.id}`, { + state: { + from: state?.from, + }, + }); + }, [entity, filterId, firstFilter, locale, navigate, prevFilterId, state?.from]); }; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/useSelectEntityOnMount/useSelectEntityOnMount.tsx b/apps/backoffice-v2/src/domains/entities/hooks/useSelectEntityOnMount/useSelectEntityOnMount.tsx index 6152e13d72..8e2aedb909 100644 --- a/apps/backoffice-v2/src/domains/entities/hooks/useSelectEntityOnMount/useSelectEntityOnMount.tsx +++ b/apps/backoffice-v2/src/domains/entities/hooks/useSelectEntityOnMount/useSelectEntityOnMount.tsx @@ -1,25 +1,34 @@ -import { useEffect } from 'react'; -import { useFirstEntityIdQuery } from '../queries/useFirstEntityIdQuery/useFirstEntityIdQuery'; -import { useParams } from 'react-router-dom'; +import { useEffect, useMemo } from 'react'; +import { matchPath, useLocation, useParams } from 'react-router-dom'; import { useSelectEntity } from '../useSelectEntity/useSelectEntity'; -import { useFilterEntity } from '../useFilterEntity/useFilterEntity'; +import { useWorkflowsQuery } from '../../../workflows/hooks/queries/useWorkflowsQuery/useWorkflowsQuery'; +import { useSearchParamsByEntity } from '../../../../common/hooks/useSearchParamsByEntity/useSearchParamsByEntity'; +import { useEntityType } from '../../../../common/hooks/useEntityType/useEntityType'; /** * @description Sets the selected end user to the first end user in the array on mount if no user is currently selected. Returns the select end user handler. */ export const useSelectEntityOnMount = () => { - const { entityId } = useParams(); - const { data: firstEntityId } = useFirstEntityIdQuery({ - initialState: { - sortBy: 'caseCreatedAt', - }, - }); + const { entityId: caseId } = useParams(); + const [{ filterId, filter, sortBy, sortDir, page, pageSize }] = useSearchParamsByEntity(); + const { data } = useWorkflowsQuery({ filterId, filter, sortBy, sortDir, page, pageSize }); + const { data: workflows } = data || { data: [] }; const onSelectEntity = useSelectEntity(); - const entity = useFilterEntity(); + const entity = useEntityType(); + const firstCaseId = workflows?.[0]?.id; + const { state } = useLocation(); + const prevCaseId = useMemo(() => { + const match = matchPath( + '/:locale/case-management/entities/:entityId', + state?.from?.pathname ?? '', + ); + + return match?.params?.entityId; + }, [state?.from?.pathname]); useEffect(() => { - if (!firstEntityId || entityId) return; + if (caseId || (!firstCaseId && !prevCaseId)) return; - onSelectEntity(firstEntityId)(); - }, [entity, firstEntityId, entityId, onSelectEntity]); + onSelectEntity(prevCaseId || firstCaseId)(); + }, [entity, firstCaseId, caseId, onSelectEntity, prevCaseId]); }; diff --git a/apps/backoffice-v2/src/domains/entities/hooks/useSelectNextEntity/useSelectNextEntity.tsx b/apps/backoffice-v2/src/domains/entities/hooks/useSelectNextEntity/useSelectNextEntity.tsx index 62fe0966ac..a13c1d537d 100644 --- a/apps/backoffice-v2/src/domains/entities/hooks/useSelectNextEntity/useSelectNextEntity.tsx +++ b/apps/backoffice-v2/src/domains/entities/hooks/useSelectNextEntity/useSelectNextEntity.tsx @@ -1,14 +1,8 @@ -import { useSelectEntity } from '../useSelectEntity/useSelectEntity'; -import { useNextEntityIdQuery } from '../queries/useNextEntityIdQuery/useNextEntityIdQuery'; import { useCallback } from 'react'; export const useSelectNextEntity = () => { - const { data: nextId } = useNextEntityIdQuery({ - initialState: { - sortBy: 'caseCreatedAt', - }, - }); - const onSelectNextEntity = useSelectEntity(); - - return useCallback(() => onSelectNextEntity(nextId)(), [onSelectNextEntity, nextId]); + return useCallback(() => { + // @TODO: Implement + throw new Error('Not implemented'); + }, []); }; diff --git a/apps/backoffice-v2/src/domains/entities/query-keys.ts b/apps/backoffice-v2/src/domains/entities/query-keys.ts deleted file mode 100644 index 7dc8ac30e0..0000000000 --- a/apps/backoffice-v2/src/domains/entities/query-keys.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { individualsQueryKeys } from '../individuals/query-keys'; -import { businessesQueryKeys } from '../businesses/query-keys'; - -export const queryKeys = { - individuals: individualsQueryKeys, - businesses: businessesQueryKeys, -} as const; diff --git a/apps/backoffice-v2/src/domains/entities/types.ts b/apps/backoffice-v2/src/domains/entities/types.ts deleted file mode 100644 index ae37633730..0000000000 --- a/apps/backoffice-v2/src/domains/entities/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { TBusiness, TBusinesses } from '../businesses/types'; -import { TIndividual, TIndividuals } from '../individuals/types'; - -export type TEntity = TIndividual | TBusiness; -export type TEntities = TIndividuals | TBusinesses; diff --git a/apps/backoffice-v2/src/domains/filters/fetchers.ts b/apps/backoffice-v2/src/domains/filters/fetchers.ts index 136fcec82a..79c9409b1d 100644 --- a/apps/backoffice-v2/src/domains/filters/fetchers.ts +++ b/apps/backoffice-v2/src/domains/filters/fetchers.ts @@ -20,7 +20,7 @@ export const fetchFilters = async () => { method: Method.GET, schema: z.array( ObjectWithIdSchema.extend({ - entity: z.string(), + entity: z.enum(['individuals', 'businesses']), name: z.string(), }), ), diff --git a/apps/backoffice-v2/src/domains/filters/hooks/queries/useFiltersQuery/useFiltersQuery.tsx b/apps/backoffice-v2/src/domains/filters/hooks/queries/useFiltersQuery/useFiltersQuery.tsx index c9e7cc4ade..a7af38e13b 100644 --- a/apps/backoffice-v2/src/domains/filters/hooks/queries/useFiltersQuery/useFiltersQuery.tsx +++ b/apps/backoffice-v2/src/domains/filters/hooks/queries/useFiltersQuery/useFiltersQuery.tsx @@ -2,5 +2,9 @@ import { useQuery } from '@tanstack/react-query'; import { filtersQueryKeys } from '../../../query-keys'; export const useFiltersQuery = () => { - return useQuery(filtersQueryKeys.list()); + return useQuery({ + ...filtersQueryKeys.list(), + staleTime: 1_000_000, + refetchInterval: 1_000_000, + }); }; diff --git a/apps/backoffice-v2/src/domains/filters/types.ts b/apps/backoffice-v2/src/domains/filters/types.ts new file mode 100644 index 0000000000..e822fd1a76 --- /dev/null +++ b/apps/backoffice-v2/src/domains/filters/types.ts @@ -0,0 +1,7 @@ +import { TEntityType } from 'src/domains/entities/types'; + +export interface TFilter { + id: string; + name: string; + entity: TEntityType; +} diff --git a/apps/backoffice-v2/src/domains/individuals/fetchers.ts b/apps/backoffice-v2/src/domains/individuals/fetchers.ts deleted file mode 100644 index 18f1578ce2..0000000000 --- a/apps/backoffice-v2/src/domains/individuals/fetchers.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { apiClient } from '../../common/api-client/api-client'; -import { IndividualByIdSchema, IndividualsListSchema } from './validation-schemas'; -import { handleZodError } from '../../common/utils/handle-zod-error/handle-zod-error'; -import { Method } from '../../common/enums'; - -export const fetchIndividuals = async (filterId: string) => { - const [individuals, error] = await apiClient({ - endpoint: `end-users?filterId=${filterId ?? ''}`, - method: Method.GET, - schema: IndividualsListSchema, - }); - - return handleZodError(error, individuals); -}; - -export const fetchIndividualById = async (individualId: string, filterId: string) => { - const [individual, error] = await apiClient({ - endpoint: `end-users/${individualId}?filterId=${filterId ?? ''}`, - method: Method.GET, - schema: IndividualByIdSchema, - }); - - return handleZodError(error, individual); -}; - -export const fetchUpdateIndividualById = async ( - individualId: string, - body: Record<PropertyKey, unknown>, -) => { - const [individual, error] = await apiClient({ - endpoint: `end-users/${individualId}`, - method: Method.PATCH, - body, - schema: IndividualByIdSchema, - }); - - return handleZodError(error, individual); -}; diff --git a/apps/backoffice-v2/src/domains/individuals/query-keys.ts b/apps/backoffice-v2/src/domains/individuals/query-keys.ts deleted file mode 100644 index 083bd4ddbe..0000000000 --- a/apps/backoffice-v2/src/domains/individuals/query-keys.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createQueryKeys } from '@lukemorales/query-key-factory'; -import { fetchIndividualById, fetchIndividuals } from './fetchers'; - -export const individualsQueryKeys = createQueryKeys('individuals', { - list: (filterId: string) => ({ - queryKey: [{ filterId }], - queryFn: () => fetchIndividuals(filterId), - }), - byId: (individualId: string, filterId: string) => ({ - queryKey: [{ individualId, filterId }], - queryFn: () => fetchIndividualById(individualId, filterId), - }), -}); diff --git a/apps/backoffice-v2/src/domains/individuals/types.ts b/apps/backoffice-v2/src/domains/individuals/types.ts index 58147727b2..d555fd7328 100644 --- a/apps/backoffice-v2/src/domains/individuals/types.ts +++ b/apps/backoffice-v2/src/domains/individuals/types.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { IndividualByIdSchema, IndividualsListSchema } from './validation-schemas'; +import { IndividualByIdSchema } from './validation-schemas'; +// @TODO: Remove export type TIndividual = z.infer<typeof IndividualByIdSchema>; -export type TIndividuals = z.infer<typeof IndividualsListSchema>; diff --git a/apps/backoffice-v2/src/domains/individuals/validation-schemas.ts b/apps/backoffice-v2/src/domains/individuals/validation-schemas.ts index 89d1cd8b9d..c0b3d23236 100644 --- a/apps/backoffice-v2/src/domains/individuals/validation-schemas.ts +++ b/apps/backoffice-v2/src/domains/individuals/validation-schemas.ts @@ -2,36 +2,6 @@ import { z } from 'zod'; import { ObjectWithIdSchema } from '../../lib/zod/utils/object-with-id/object-with-id'; import { CaseStatuses, State, States } from '../../common/enums'; -export const IndividualsListSchema = z - .array( - ObjectWithIdSchema.extend({ - avatarUrl: z.string().nullable().default(''), - createdAt: z.string().default(''), - firstName: z.string().nullable().default(''), - middleName: z.string().default(''), - lastName: z.string().nullable().default(''), - fullName: z.string().default(''), - email: z.string().nullable().default(''), - phone: z.string().nullable().default(''), - approvalState: z.enum(States).default(State.PROCESSING), - endUserType: z.string().nullable().default(''), - workflowRuntimeData: z.preprocess( - workflows => workflows?.[0], - ObjectWithIdSchema.extend({ - assigneeId: z.string().nullable().optional(), - createdAt: z.string().datetime(), - status: z.enum(CaseStatuses), - }).optional(), - ), - }).transform(({ firstName, lastName, ...rest }) => ({ - ...rest, - firstName, - lastName, - fullName: `${firstName} ${lastName}`, - })), - ) - .default([]); - export const IndividualByIdSchema = ObjectWithIdSchema.extend({ avatarUrl: z.string().nullable().default(''), createdAt: z.string().default(''), diff --git a/apps/backoffice-v2/src/domains/storage/hooks/queries/useStorageFileQuery/useStorageFileQuery.tsx b/apps/backoffice-v2/src/domains/storage/hooks/queries/useStorageFileQuery/useStorageFileQuery.tsx deleted file mode 100644 index e2f1fe3dd4..0000000000 --- a/apps/backoffice-v2/src/domains/storage/hooks/queries/useStorageFileQuery/useStorageFileQuery.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { isString } from '../../../../../common/utils/is-string/is-string'; - -import { storageQueryKeys } from '../../../query-keys'; - -export const useStorageFileQuery = (fileId: string) => - useQuery({ - ...storageQueryKeys.fileById(fileId), - enabled: isString(fileId) && !!fileId.length, - }); diff --git a/apps/backoffice-v2/src/domains/storage/hooks/queries/useStorageFilesQuery/useStorageFilesQuery.tsx b/apps/backoffice-v2/src/domains/storage/hooks/queries/useStorageFilesQuery/useStorageFilesQuery.tsx index 314f329db4..1dea9ae5ca 100644 --- a/apps/backoffice-v2/src/domains/storage/hooks/queries/useStorageFilesQuery/useStorageFilesQuery.tsx +++ b/apps/backoffice-v2/src/domains/storage/hooks/queries/useStorageFilesQuery/useStorageFilesQuery.tsx @@ -11,6 +11,8 @@ export const useStorageFilesQuery = (fileIds: Array<string>) => { fileIds?.map(fileId => ({ ...storageQueryKeys.fileById(fileId), enabled: isString(fileId) && !!fileId?.length && isAuthenticated, + staleTime: 100_000, + refetchInterval: 100_000, })) ?? [], }); }; diff --git a/apps/backoffice-v2/src/domains/users/hooks/queries/useUsersQuery/useUsersQuery.tsx b/apps/backoffice-v2/src/domains/users/hooks/queries/useUsersQuery/useUsersQuery.tsx index b2a6943298..d6ec28a490 100644 --- a/apps/backoffice-v2/src/domains/users/hooks/queries/useUsersQuery/useUsersQuery.tsx +++ b/apps/backoffice-v2/src/domains/users/hooks/queries/useUsersQuery/useUsersQuery.tsx @@ -10,5 +10,6 @@ export const useUsersQuery = () => { ...usersQueryKeys.list(), enabled: isAuthenticated, refetchInterval: env.VITE_ASSIGNMENT_POLLING_INTERVAL, + staleTime: 1_000_000, }); }; diff --git a/apps/backoffice-v2/src/domains/workflows/fetchers.ts b/apps/backoffice-v2/src/domains/workflows/fetchers.ts index 1c01193299..daa858e7e1 100644 --- a/apps/backoffice-v2/src/domains/workflows/fetchers.ts +++ b/apps/backoffice-v2/src/domains/workflows/fetchers.ts @@ -2,31 +2,92 @@ import { apiClient } from '../../common/api-client/api-client'; import { z } from 'zod'; import { handleZodError } from '../../common/utils/handle-zod-error/handle-zod-error'; import { ObjectWithIdSchema } from '../../lib/zod/utils/object-with-id/object-with-id'; -import { Method } from '../../common/enums'; +import { Method, States } from '../../common/enums'; import { IWorkflowId } from './interfaces'; +import qs from 'qs'; +import { zPropertyKey } from '../../lib/zod/utils/z-property-key/z-property-key'; + +export const fetchWorkflows = async (params: { + filterId: string; + orderBy: string; + page: { + number: number; + size: number; + }; + filter: Record<string, unknown>; +}) => { + const queryParams = qs.stringify(params, { encode: false }); -export const fetchWorkflows = async () => { const [workflows, error] = await apiClient({ - endpoint: `workflows/active-states`, + endpoint: `workflows?${queryParams}`, method: Method.GET, - schema: z.array( - ObjectWithIdSchema.extend({ - state: z.string().nullable(), - endUserId: z.string().nullable(), - businessId: z.string().nullable(), - assigneeId: z.string().nullable(), + schema: z.object({ + data: z.array( + z.object({ + id: z.string(), + status: z.string(), + createdAt: z.string().datetime(), + entity: ObjectWithIdSchema.extend({ + name: z.string(), + avatarUrl: z.string().nullable(), + approvalState: z.enum(States), + }), + assignee: ObjectWithIdSchema.extend({ + firstName: z.string(), + lastName: z.string(), + }).nullable(), + }), + ), + meta: z.object({ + totalItems: z.number().nonnegative(), + totalPages: z.number().nonnegative(), }), - ), + }), }); return handleZodError(error, workflows); }; -export const fetchWorkflowById = async ({ workflowId }: IWorkflowId) => { +export const WorkflowByIdSchema = z.object({ + id: z.string(), + status: z.string(), + nextEvents: z.array(z.any()), + workflowDefinition: ObjectWithIdSchema.extend({ + name: z.string(), + contextSchema: z.record(z.any(), z.any()).nullable(), + config: z.record(z.any(), z.any()).nullable(), + }), + createdAt: z.string().datetime(), + context: z.object({ + documents: z.array(z.any()), + entity: z.record(z.any(), z.any()), + parentMachine: ObjectWithIdSchema.extend({ + status: z.union([z.literal('active'), z.literal('failed'), z.literal('completed')]), + }).optional(), + pluginsOutput: z.record(zPropertyKey, z.any()).optional(), + }), + entity: ObjectWithIdSchema.extend({ + name: z.string(), + avatarUrl: z.string().nullable(), + approvalState: z.enum(States), + }), + assignee: ObjectWithIdSchema.extend({ + firstName: z.string(), + lastName: z.string(), + }).nullable(), +}); + +export const fetchWorkflowById = async ({ + workflowId, + filterId, +}: { + workflowId: string; + filterId: string; +}) => { const [workflow, error] = await apiClient({ - endpoint: `workflows/${workflowId}`, + endpoint: `workflows/${workflowId}?filterId=${filterId}`, method: Method.GET, - schema: z.any(), + schema: WorkflowByIdSchema, }); return handleZodError(error, workflow); @@ -48,6 +109,22 @@ export const fetchUpdateWorkflowById = async ({ return handleZodError(error, workflow); }; +export const updateWorkflowSetAssignById = async ({ + workflowId, + body, +}: IWorkflowId & { + body: { assigneeId: string | null; isAssignedToMe?: boolean }; +}) => { + const [workflow, error] = await apiClient({ + endpoint: `workflows/assign/${workflowId}`, + method: Method.PATCH, + body, + schema: z.any(), + }); + + return handleZodError(error, workflow); +}; + export const fetchWorkflowEvent = async ({ workflowId, body, diff --git a/apps/backoffice-v2/src/domains/workflows/hooks/mutations/useAssignWorkflowMutation/useAssignWorkflowMutation.tsx b/apps/backoffice-v2/src/domains/workflows/hooks/mutations/useAssignWorkflowMutation/useAssignWorkflowMutation.tsx index ab23e230fc..9adabeffc0 100644 --- a/apps/backoffice-v2/src/domains/workflows/hooks/mutations/useAssignWorkflowMutation/useAssignWorkflowMutation.tsx +++ b/apps/backoffice-v2/src/domains/workflows/hooks/mutations/useAssignWorkflowMutation/useAssignWorkflowMutation.tsx @@ -1,7 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import toast from 'react-hot-toast'; import { t } from 'i18next'; -import { fetchUpdateWorkflowById } from '../../../fetchers'; +import { updateWorkflowSetAssignById } from '../../../fetchers'; import { useUsersQuery } from '../../../../users/hooks/queries/useUsersQuery/useUsersQuery'; const getToastActionAndContext = ({ @@ -29,7 +29,7 @@ export const useAssignWorkflowMutation = ({ workflowRuntimeId }: { workflowRunti return useMutation({ mutationFn: ({ assigneeId }: { assigneeId: string | null; isAssignedToMe: boolean }) => - fetchUpdateWorkflowById({ + updateWorkflowSetAssignById({ workflowId: workflowRuntimeId, body: { assigneeId, diff --git a/apps/backoffice-v2/src/domains/workflows/hooks/mutations/useUpdateWorkflowByIdMutation/useUpdateWorkflowByIdMutation.tsx b/apps/backoffice-v2/src/domains/workflows/hooks/mutations/useUpdateWorkflowByIdMutation/useUpdateWorkflowByIdMutation.tsx index 9b3b5e8975..ee60d89959 100644 --- a/apps/backoffice-v2/src/domains/workflows/hooks/mutations/useUpdateWorkflowByIdMutation/useUpdateWorkflowByIdMutation.tsx +++ b/apps/backoffice-v2/src/domains/workflows/hooks/mutations/useUpdateWorkflowByIdMutation/useUpdateWorkflowByIdMutation.tsx @@ -1,12 +1,15 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import toast from 'react-hot-toast'; import { t } from 'i18next'; +import { fetchUpdateWorkflowById, WorkflowByIdSchema } from '../../../fetchers'; +import { useFilterId } from '../../../../../common/hooks/useFilterId/useFilterId'; import { workflowsQueryKeys } from '../../../query-keys'; -import { fetchUpdateWorkflowById } from '../../../fetchers'; +import { z } from 'zod'; export const useUpdateWorkflowByIdMutation = ({ workflowId }: { workflowId: string }) => { const queryClient = useQueryClient(); - const workflowById = workflowsQueryKeys.byId({ workflowId }); + const filterId = useFilterId(); + const workflowById = workflowsQueryKeys.byId({ workflowId, filterId }); return useMutation({ mutationFn: ({ @@ -31,18 +34,18 @@ export const useUpdateWorkflowByIdMutation = ({ workflowId }: { workflowId: stri }); const previousWorkflow = queryClient.getQueryData(workflowById.queryKey); - queryClient.setQueryData(workflowById.queryKey, oldWorkflow => { - return { - ...oldWorkflow, - workflowRuntimeData: { - ...oldWorkflow?.workflowRuntimeData, + queryClient.setQueryData( + workflowById.queryKey, + (oldWorkflow: z.output<typeof WorkflowByIdSchema>) => { + return { + ...oldWorkflow, context: { - ...oldWorkflow?.workflowRuntimeData?.context, + ...oldWorkflow?.context, ...context, }, - }, - }; - }); + }; + }, + ); return { previousWorkflow }; }, diff --git a/apps/backoffice-v2/src/domains/workflows/hooks/queries/useWorkflowQuery/useWorkflowQuery.tsx b/apps/backoffice-v2/src/domains/workflows/hooks/queries/useWorkflowQuery/useWorkflowQuery.tsx index f9e097161e..6f54598d0e 100644 --- a/apps/backoffice-v2/src/domains/workflows/hooks/queries/useWorkflowQuery/useWorkflowQuery.tsx +++ b/apps/backoffice-v2/src/domains/workflows/hooks/queries/useWorkflowQuery/useWorkflowQuery.tsx @@ -1,45 +1,16 @@ import { useQuery } from '@tanstack/react-query'; -import { createWorkflow } from '@ballerine/workflow-browser-sdk'; -import { isString } from '../../../../../common/utils/is-string/is-string'; import { workflowsQueryKeys } from '../../../query-keys'; -import { useIsAuthenticated } from '../../../../auth/context/AuthProvider/hooks/useIsAuthenticated/useIsAuthenticated'; - -export const useWorkflowQuery = ({ workflowId }: { workflowId: string }) => { - const isAuthenticated = useIsAuthenticated(); +export const useWorkflowQuery = ({ + workflowId, + filterId, +}: { + workflowId: string; + filterId: string; +}) => { return useQuery({ - ...workflowsQueryKeys.byId({ workflowId }), - enabled: isString(workflowId) && !!workflowId.length && isAuthenticated, - select: ({ workflowDefinition, workflowRuntimeData }) => { - const { definition, definitionType, ...rest } = workflowDefinition; - const workflow = { - ...rest, - definitionType, - definition: { - ...definition, - initial: workflowRuntimeData?.state ?? definition?.initial, - context: workflowRuntimeData?.context, - }, - workflowContext: { - machineContext: { - ...workflowRuntimeData?.context, - documents: workflowRuntimeData?.context?.documents?.map(document => ({ - ...document, - id: `${document?.category}-${document?.type}-${document?.issuer?.country}`.toLowerCase(), - })), - }, - state: workflowRuntimeData?.state ?? definition?.initial, - }, - }; - const workflowService = createWorkflow(workflow); - const snapshot = workflowService.getSnapshot(); - - return { - ...workflow, - runtimeDataId: workflowRuntimeData?.id, - assigneeId: workflowRuntimeData?.assigneeId, - nextEvents: snapshot.nextEvents, - }; - }, + ...workflowsQueryKeys.byId({ workflowId, filterId }), + enabled: !!filterId && !!workflowId, + staleTime: 10_000, }); }; diff --git a/apps/backoffice-v2/src/domains/workflows/hooks/queries/useWorkflowsQuery/useWorkflowsQuery.tsx b/apps/backoffice-v2/src/domains/workflows/hooks/queries/useWorkflowsQuery/useWorkflowsQuery.tsx index 6069d3c0d5..5066cea712 100644 --- a/apps/backoffice-v2/src/domains/workflows/hooks/queries/useWorkflowsQuery/useWorkflowsQuery.tsx +++ b/apps/backoffice-v2/src/domains/workflows/hooks/queries/useWorkflowsQuery/useWorkflowsQuery.tsx @@ -3,8 +3,26 @@ import { useQuery } from '@tanstack/react-query'; import { workflowsQueryKeys } from '../../../query-keys'; import { useIsAuthenticated } from '../../../../auth/context/AuthProvider/hooks/useIsAuthenticated/useIsAuthenticated'; -export const useWorkflowsQuery = () => { +export const useWorkflowsQuery = ({ + filterId, + sortBy, + sortDir, + page, + pageSize, + filter, +}: { + filterId: string; + sortBy: string; + sortDir: string; + page: number; + pageSize: number; + filter: Record<string, unknown>; +}) => { const isAuthenticated = useIsAuthenticated(); - return useQuery({ ...workflowsQueryKeys.list(), enabled: isAuthenticated }); + return useQuery({ + ...workflowsQueryKeys.list({ filterId, filter, sortBy, sortDir, page, pageSize }), + enabled: !!filterId && isAuthenticated && !!sortBy && !!sortDir && !!page && !!pageSize, + staleTime: 100_000, + }); }; diff --git a/apps/backoffice-v2/src/domains/workflows/query-keys.ts b/apps/backoffice-v2/src/domains/workflows/query-keys.ts index 10dba81a7b..5a9aa29885 100644 --- a/apps/backoffice-v2/src/domains/workflows/query-keys.ts +++ b/apps/backoffice-v2/src/domains/workflows/query-keys.ts @@ -1,15 +1,37 @@ import { createQueryKeys } from '@lukemorales/query-key-factory'; import { fetchWorkflowById, fetchWorkflows } from './fetchers'; -import { IWorkflowId } from './interfaces'; - export const workflowsQueryKeys = createQueryKeys('workflows', { - list: () => ({ - queryKey: [{}], - queryFn: () => fetchWorkflows(), - }), - byId: ({ workflowId }: IWorkflowId) => ({ - queryKey: [{ workflowId }], - queryFn: () => fetchWorkflowById({ workflowId }), + list: ({ + sortBy, + sortDir, + page, + pageSize, + ...params + }: { + filterId: string; + sortBy: string; + sortDir: string; + page: number; + pageSize: number; + filter: Record<string, unknown>; + }) => { + const data = { + ...params, + orderBy: `${sortBy}:${sortDir}`, + page: { + number: Number(page), + size: Number(pageSize), + }, + }; + + return { + queryKey: [data], + queryFn: () => fetchWorkflows(data), + }; + }, + byId: ({ workflowId, filterId }: { workflowId: string; filterId: string }) => ({ + queryKey: [{ workflowId, filterId }], + queryFn: () => fetchWorkflowById({ workflowId, filterId }), }), }); diff --git a/apps/backoffice-v2/src/index.css b/apps/backoffice-v2/src/index.css index 60531b8b1c..d7e51f33d8 100644 --- a/apps/backoffice-v2/src/index.css +++ b/apps/backoffice-v2/src/index.css @@ -112,3 +112,20 @@ font-feature-settings: 'rlig' 1, 'calt' 1; } } + +@layer components { + .animated-path { + stroke-dasharray: 320; + stroke-dashoffset: 0; + animation: animate-stroke 5s linear alternate infinite; + } + + @keyframes animate-stroke { + from { + stroke-dashoffset: 320; + } + to { + stroke-dashoffset: 0; + } + } +} diff --git a/apps/backoffice-v2/src/pages/Entities/Entities.loader.ts b/apps/backoffice-v2/src/pages/Entities/Entities.loader.ts index 3f1ec90a5a..47ae3da2eb 100644 --- a/apps/backoffice-v2/src/pages/Entities/Entities.loader.ts +++ b/apps/backoffice-v2/src/pages/Entities/Entities.loader.ts @@ -1,25 +1,37 @@ -import { searchParamsToObject } from '../../common/hooks/useZodSearchParams/utils/search-params-to-object'; import { authQueryKeys } from '../../domains/auth/query-keys'; import { queryClient } from '../../lib/react-query/query-client'; -import { queryKeys } from '../../domains/entities/query-keys'; import { usersQueryKeys } from '../../domains/users/query-keys'; import { LoaderFunction } from 'react-router-dom'; +import { workflowsQueryKeys } from '../../domains/workflows/query-keys'; +import { defaultDeserializer } from '../../common/hooks/useZodSearchParams/utils/default-deserializer'; +import { getEntityTypeByFilterId } from '../../common/utils/get-entity-type-by-filter-id/get-entity-type-by-filter-id'; export const entitiesLoader: LoaderFunction = async ({ request }) => { - const url = new URL(request.url); - const { entity, filterId } = searchParamsToObject(url.searchParams); + const { filterId, filter, sortBy, sortDir, page, pageSize } = defaultDeserializer( + request.url.split('?')[1], + ); + const entity = getEntityTypeByFilterId(filterId); const authenticatedUser = authQueryKeys.authenticatedUser(); const session = await queryClient.ensureQueryData( authenticatedUser.queryKey, authenticatedUser.queryFn, ); - if (!entity || !filterId || !session?.user) return null; + if (!entity || !filterId || !session?.user || !sortBy || !sortDir || !page || !pageSize) { + return null; + } - const entityList = queryKeys[entity].list?.(filterId); const usersList = usersQueryKeys.list(); - await queryClient.ensureQueryData(entityList.queryKey, entityList.queryFn); await queryClient.ensureQueryData(usersList.queryKey, usersList.queryFn); + const workflowList = workflowsQueryKeys.list({ + filterId, + filter, + sortBy, + sortDir, + page, + pageSize, + }); + await queryClient.ensureQueryData(workflowList.queryKey, workflowList.queryFn); return null; }; diff --git a/apps/backoffice-v2/src/pages/Entities/Entities.page.tsx b/apps/backoffice-v2/src/pages/Entities/Entities.page.tsx index 582dac0877..a41e5db43b 100644 --- a/apps/backoffice-v2/src/pages/Entities/Entities.page.tsx +++ b/apps/backoffice-v2/src/pages/Entities/Entities.page.tsx @@ -12,7 +12,7 @@ export const Entities: FunctionComponent = () => { onSearch, onFilter, onSortBy, - onSortDir, + onSortDirToggle, search, cases, isLoading, @@ -28,7 +28,7 @@ export const Entities: FunctionComponent = () => { onSearch={onSearch} onFilter={onFilter} onSortBy={onSortBy} - onSortDir={onSortDir} + onSortDirToggle={onSortDirToggle} search={search} > <MotionScrollArea className={`h-[calc(100vh-210px)]`}> @@ -37,30 +37,18 @@ export const Entities: FunctionComponent = () => { ? skeletonEntities.map(index => ( <Cases.SkeletonItem key={`cases-list-skeleton-${index}`} /> )) - : cases?.map( - ({ - id, - firstName, - lastName, - caseCreatedAt, - companyName, - assigneeId, - assigneeFullName, - avatarUrl, - approvalState, - }) => ( - <Cases.Item - key={id} - id={id} - fullName={entity !== 'businesses' ? `${firstName} ${lastName}` : companyName} - avatarUrl={avatarUrl} - createdAt={caseCreatedAt} - assigneeId={assigneeId} - assigneeFullName={assigneeFullName} - status={approvalState} - /> - ), - )} + : cases?.map(case_ => ( + <Cases.Item + key={case_.id} + id={case_.id} + fullName={case_.entity.name} + avatarUrl={case_.entity.avatarUrl} + createdAt={case_.createdAt} + assigneeId={case_.assignee?.id} + assigneeFullName={`${case_.assignee?.firstName} ${case_.assignee?.lastName}`} + status={case_.entity.approvalState} + /> + ))} </Cases.List> </MotionScrollArea> <div className={`divider my-0 px-4`}></div> @@ -81,7 +69,7 @@ export const Entities: FunctionComponent = () => { </Case.Content> </Case> )} - {!cases?.length && !isLoading ? ( + {Array.isArray(cases) && !cases.length && !isLoading ? ( <div className={`p-2`}> <h2 className={`mt-4 text-6xl`}>No cases were found</h2> </div> diff --git a/apps/backoffice-v2/src/pages/Entities/components/Cases/Cases.tsx b/apps/backoffice-v2/src/pages/Entities/components/Cases/Cases.tsx index cd977e141a..e6262a14a8 100644 --- a/apps/backoffice-v2/src/pages/Entities/components/Cases/Cases.tsx +++ b/apps/backoffice-v2/src/pages/Entities/components/Cases/Cases.tsx @@ -23,7 +23,7 @@ import { TIndividual } from '../../../../domains/individuals/types'; * @param onSearch * @param onFilter * @param onSortBy - * @param onSortDir + * @param onSortDirToggle * @param props * @constructor */ @@ -32,7 +32,7 @@ export const Cases: FunctionComponent<ICasesProps> & ICasesChildren = ({ onSearch, onFilter, onSortBy, - onSortDir, + onSortDirToggle, search, ...props }) => { @@ -66,7 +66,7 @@ export const Cases: FunctionComponent<ICasesProps> & ICasesChildren = ({ </div> </div> <div className={`flex items-center justify-between`}> - <div className="dropdown dropdown-bottom dropdown-hover z-[60]"> + <div className="dropdown-hover dropdown dropdown-bottom z-[60]"> <button className={`btn-ghost btn-sm btn h-[2.125rem] gap-2 border-neutral/10 text-xs focus-visible:outline-primary theme-dark:border-neutral/50`} tabIndex={0} @@ -115,8 +115,8 @@ export const Cases: FunctionComponent<ICasesProps> & ICasesChildren = ({ > <div className={`input-group flex items-center`}> <button - className={`btn-ghost btn-square btn-sm btn !rounded-md focus-visible:border-none focus-visible:bg-neutral/10 focus-visible:outline-none focus-visible:ring-0 focus-visible:theme-dark:bg-neutral`} - onClick={onSortDir} + className={`btn-ghost btn-sm btn-square btn !rounded-md focus-visible:border-none focus-visible:bg-neutral/10 focus-visible:outline-none focus-visible:ring-0 focus-visible:theme-dark:bg-neutral`} + onClick={onSortDirToggle} ref={sortRef} > <SortSvg /> diff --git a/apps/backoffice-v2/src/pages/Entities/components/Cases/hooks/useCases/useCases.tsx b/apps/backoffice-v2/src/pages/Entities/components/Cases/hooks/useCases/useCases.tsx index 2e22eb74f2..056a2eb2cc 100644 --- a/apps/backoffice-v2/src/pages/Entities/components/Cases/hooks/useCases/useCases.tsx +++ b/apps/backoffice-v2/src/pages/Entities/components/Cases/hooks/useCases/useCases.tsx @@ -1,16 +1,16 @@ import { useDocumentListener } from '../../../../../../common/hooks/useDocumentListener/useDocumentListener'; import { useCallback, useRef } from 'react'; import { useUsersQuery } from '../../../../../../domains/users/hooks/queries/useUsersQuery/useUsersQuery'; -import { useFilterEntity } from '../../../../../../domains/entities/hooks/useFilterEntity/useFilterEntity'; +import { useEntityType } from '../../../../../../common/hooks/useEntityType/useEntityType'; import { useSearchParamsByEntity } from '../../../../../../common/hooks/useSearchParamsByEntity/useSearchParamsByEntity'; export const useCases = () => { const [{ filter, sortBy }] = useSearchParamsByEntity(); - const entity = useFilterEntity(); + const entity = useEntityType(); const sharedSortByOptions = [ { label: 'Created At', - value: 'caseCreatedAt', + value: 'createdAt', }, ]; const individualsSortByOptions = [ @@ -40,8 +40,8 @@ export const useCases = () => { entity === 'individuals' ? individualsSortByOptions : businessesSortByOptions; const filterByOptions = [ { - label: 'Case Status', - value: 'caseStatus', + label: 'Status', + value: 'status', options: [ { label: 'Active', diff --git a/apps/backoffice-v2/src/pages/Entities/components/Cases/interfaces.ts b/apps/backoffice-v2/src/pages/Entities/components/Cases/interfaces.ts index c85341a436..468f765a6b 100644 --- a/apps/backoffice-v2/src/pages/Entities/components/Cases/interfaces.ts +++ b/apps/backoffice-v2/src/pages/Entities/components/Cases/interfaces.ts @@ -15,6 +15,6 @@ export interface ICasesProps extends DivComponent { onSearch: ChangeEventHandler<HTMLInputElement>; onFilter: (filterBy: keyof TIndividual) => (filters: Array<string>) => void; onSortBy: ChangeEventHandler<HTMLSelectElement>; - onSortDir: () => void; + onSortDirToggle: () => void; search: string; } diff --git a/apps/backoffice-v2/src/pages/Entities/hooks/useEntities/useEntities.tsx b/apps/backoffice-v2/src/pages/Entities/hooks/useEntities/useEntities.tsx index d0fb72774a..8d53181fb2 100644 --- a/apps/backoffice-v2/src/pages/Entities/hooks/useEntities/useEntities.tsx +++ b/apps/backoffice-v2/src/pages/Entities/hooks/useEntities/useEntities.tsx @@ -1,44 +1,74 @@ import { useSearch } from '../../../../common/hooks/useSearch/useSearch'; -import { useFilter } from '../../../../common/hooks/useFilter/useFilter'; -import { usePagination } from '../../../../common/hooks/usePagination/usePagination'; import { ChangeEventHandler, useCallback } from 'react'; import { createArrayOfNumbers } from '../../../../common/utils/create-array-of-numbers/create-array-of-numbers'; -import { useUsersQuery } from '../../../../domains/users/hooks/queries/useUsersQuery/useUsersQuery'; -import { useSort } from '../../../../common/hooks/useSort/useSort'; -import { useEntitiesWithWorkflowsQuery } from '../../../../domains/entities/hooks/queries/useEntitiesWithWorkflowsQuery/useEntitiesWithWorkflowsQuery'; -import { TIndividual } from '../../../../domains/individuals/types'; -import { useFilterEntity } from '../../../../domains/entities/hooks/useFilterEntity/useFilterEntity'; import { useSelectEntityOnMount } from '../../../../domains/entities/hooks/useSelectEntityOnMount/useSelectEntityOnMount'; +import { useWorkflowsQuery } from '../../../../domains/workflows/hooks/queries/useWorkflowsQuery/useWorkflowsQuery'; +import { useSearchParamsByEntity } from '../../../../common/hooks/useSearchParamsByEntity/useSearchParamsByEntity'; +import { useEntityType } from '../../../../common/hooks/useEntityType/useEntityType'; export const useEntities = () => { - const { data: users } = useUsersQuery(); - const { data: cases, isLoading } = useEntitiesWithWorkflowsQuery(users); - const entity = useFilterEntity(); - const individualsSearchOptions = ['firstName', 'lastName', 'email', 'phone']; - const businessesSearchOptions = [ - 'companyName', - 'registrationNumber', - 'legalForm', - 'countryOfIncorporation', - ]; + const [{ filterId, filter, sortBy, sortDir, page, pageSize }, setSearchParams] = + useSearchParamsByEntity(); + const { data, isLoading } = useWorkflowsQuery({ + filterId, + filter, + sortBy, + sortDir, + page, + pageSize, + }); + const { + meta: { totalPages }, + data: cases, + } = data || { meta: { totalPages: 0 }, data: [] }; + const entity = useEntityType(); + const individualsSearchOptions = ['entity.name', 'entity.email']; + const businessesSearchOptions = ['entity.name']; const { searched, onSearch, search } = useSearch({ data: cases, searchBy: entity === 'individuals' ? individualsSearchOptions : businessesSearchOptions, }); - const { sorted, onSortBy, onSortDir } = useSort({ - data: searched, - initialState: { - sortBy: 'caseCreatedAt', + + const onSortDirToggle = useCallback(() => { + setSearchParams({ + sortDir: sortDir === 'asc' ? 'desc' : 'asc', + }); + }, [setSearchParams, sortDir]); + + const onSortBy = useCallback( + (sortBy: string) => { + setSearchParams({ + sortBy, + }); }, - }); - const { filtered, onFilter } = useFilter({ - data: sorted, - }); - const { paginated, page, pages, totalPages, onPaginate } = usePagination({ - data: filtered, - initialPageSize: 10, - initialPage: 1, - }); + [setSearchParams], + ); + + const onFilterChange = useCallback( + (key: string) => { + return (values: string[]) => { + setSearchParams({ + filter: { + ...filter, + [key]: values, + }, + page: 1, + }); + }; + }, + [filter, setSearchParams], + ); + + const onPaginate = useCallback( + (page: number) => () => { + setSearchParams({ + page, + pageSize, + }); + }, + [pageSize, setSearchParams], + ); + const onSearchChange: ChangeEventHandler<HTMLInputElement> = useCallback( event => { onSearch(event.target.value); @@ -51,14 +81,6 @@ export const useEntities = () => { }, [onSortBy], ); - const onFilterChange = useCallback( - (key: keyof TIndividual) => (values: Array<string>) => { - onFilter({ - [key]: values, - }); - }, - [onFilter], - ); const skeletonEntities = createArrayOfNumbers(3); useSelectEntityOnMount(); @@ -68,12 +90,11 @@ export const useEntities = () => { onSearch: onSearchChange, onFilter: onFilterChange, onSortBy: onSortByChange, - onSortDir, + onSortDirToggle, search, - cases: paginated, + cases: searched, isLoading, page, - pages, totalPages, skeletonEntities, entity, diff --git a/apps/backoffice-v2/src/pages/Entity/Entity.loader.ts b/apps/backoffice-v2/src/pages/Entity/Entity.loader.ts index f38a4569ad..235bab503d 100644 --- a/apps/backoffice-v2/src/pages/Entity/Entity.loader.ts +++ b/apps/backoffice-v2/src/pages/Entity/Entity.loader.ts @@ -1,28 +1,24 @@ -import { authQueryKeys } from '../../domains/auth/query-keys'; +import { searchParamsToObject } from '../../common/hooks/useZodSearchParams/utils/search-params-to-object'; import { queryClient } from '../../lib/react-query/query-client'; -import { queryKeys } from '../../domains/entities/query-keys'; import { LoaderFunction } from 'react-router-dom'; +import { workflowsQueryKeys } from '../../domains/workflows/query-keys'; +import { authQueryKeys } from '../../domains/auth/query-keys'; -export const entityLoader: LoaderFunction = async ({ params, request }) => { +export const entityLoader: LoaderFunction = async ({ request, params }) => { const url = new URL(request.url); - const { entityId } = params; - const entity = url?.searchParams?.get('entity'); - const filterId = url?.searchParams?.get('filterId'); + const { filterId } = searchParamsToObject(url.searchParams); const authenticatedUser = authQueryKeys.authenticatedUser(); const session = await queryClient.ensureQueryData( authenticatedUser.queryKey, authenticatedUser.queryFn, ); - if (entity || !filterId || !session?.user) return null; - - const entityById = queryKeys[entity].byId(entityId, filterId); - // TODO: Add workflowId to params/searchParams - // const workflowById = workflows.byId({ workflowId }); - - await queryClient.ensureQueryData(entityById.queryKey, entityById.queryFn); + if (!filterId || !session?.user) { + return null; + } - // await queryClient.ensureQueryData(workflowById.queryKey, workflowById.queryFn); + const workflowById = workflowsQueryKeys.byId({ workflowId: params.entityId, filterId }); + await queryClient.ensureQueryData(workflowById.queryKey, workflowById.queryFn); return null; }; diff --git a/apps/backoffice-v2/src/pages/Entity/Entity.page.tsx b/apps/backoffice-v2/src/pages/Entity/Entity.page.tsx index 78f60510b4..76ddb72e4e 100644 --- a/apps/backoffice-v2/src/pages/Entity/Entity.page.tsx +++ b/apps/backoffice-v2/src/pages/Entity/Entity.page.tsx @@ -5,36 +5,40 @@ import { Card } from '../../common/components/atoms/Card/Card'; import { CardContent } from '../../common/components/atoms/Card/Card.Content'; export const Entity = () => { - const { selectedEntity, tasks, components, isLoading } = useEntity(); + const { workflow, selectedEntity, tasks, cells, isLoading } = useEntity(); // Selected entity return ( - <Case> + <Case key={workflow?.id}> {/* Reject and approve header */} <Case.Actions - id={selectedEntity?.id} - fullName={selectedEntity?.fullName} + id={workflow?.id} + fullName={selectedEntity?.name} avatarUrl={selectedEntity?.avatarUrl} - showResolutionButtons={selectedEntity.workflow?.config?.workflowLevelResolution} + showResolutionButtons={workflow?.workflowDefinition?.config?.workflowLevelResolution} /> <Case.Content key={selectedEntity?.id}> {Array.isArray(tasks) && tasks?.length > 0 && - tasks?.map((task, index) => ( - <Card key={index} className={`me-4`}> - <CardContent - className={ctw('grid gap-2', { - 'grid-cols-2': task?.some(field => field?.type === 'multiDocuments'), - })} - > - {task?.map((field, index) => { - const Cell = components[field?.type]; + tasks?.map((task, index) => { + if (!Array.isArray(task) || !task?.length) return; - return <Cell key={index} {...field} />; - })} - </CardContent> - </Card> - ))} + return ( + <Card key={index} className={`me-4`}> + <CardContent + className={ctw('grid gap-2', { + 'grid-cols-2': task?.some(field => field?.type === 'multiDocuments'), + })} + > + {task?.map((field, index) => { + const Cell = cells[field?.type]; + + return <Cell key={index} {...field} />; + })} + </CardContent> + </Card> + ); + })} {!isLoading && !tasks?.length && ( <div className={`p-2`}> <h2 className={`mt-4 text-6xl`}>No tasks were found</h2> diff --git a/apps/backoffice-v2/src/pages/Entity/components/CallToAction/CallToAction.tsx b/apps/backoffice-v2/src/pages/Entity/components/CallToAction/CallToAction.tsx index 2575f18670..7648ba1e07 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/CallToAction/CallToAction.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/CallToAction/CallToAction.tsx @@ -1,186 +1,113 @@ -import React, { FunctionComponent, useCallback, useState } from 'react'; +import React, { FunctionComponent } from 'react'; import { Dialog } from '../../../../common/components/organisms/Dialog/Dialog'; -import { DropdownMenu } from '../../../../common/components/molecules/DropdownMenu/DropdownMenu'; -import { DropdownMenuTrigger } from '../../../../common/components/molecules/DropdownMenu/DropdownMenu.Trigger'; import { Button } from '../../../../common/components/atoms/Button/Button'; import { ctw } from '../../../../common/utils/ctw/ctw'; -import { DropdownMenuContent } from '../../../../common/components/molecules/DropdownMenu/DropdownMenu.Content'; -import { DropdownMenuLabel } from '../../../../common/components/molecules/DropdownMenu/DropdownMenu.Label'; -import { DropdownMenuSeparator } from '../../../../common/components/molecules/DropdownMenu/DropdownMenu.Separator'; -import { DialogTrigger } from '../../../../common/components/organisms/Dialog/Dialog.Trigger'; -import { DropdownMenuItem } from '../../../../common/components/molecules/DropdownMenu/DropdownMenu.Item'; -import { AlertTriangle, RotateCcw } from 'lucide-react'; import { DialogContent } from '../../../../common/components/organisms/Dialog/Dialog.Content'; -import { DialogHeader } from '../../../../common/components/organisms/Dialog/Dialog.Header'; -import { DialogTitle } from '../../../../common/components/organisms/Dialog/Dialog.Title'; -import { DialogDescription } from '../../../../common/components/organisms/Dialog/Dialog.Description'; import { Select } from '../../../../common/components/atoms/Select/Select'; import { DialogFooter } from '../../../../common/components/organisms/Dialog/Dialog.Footer'; import { DialogClose } from '@radix-ui/react-dialog'; -import { useParams } from 'react-router-dom'; import { ICallToActionProps } from './interfaces'; -import { useEntityWithWorkflowQuery } from '../../../../domains/entities/hooks/queries/useEntityWithWorkflowQuery/useEntityWithWorkflowQuery'; -import { useAuthenticatedUserQuery } from '../../../../domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; -import { useCaseState } from '../Case/hooks/useCaseState/useCaseState'; -import { useUpdateWorkflowByIdMutation } from '../../../../domains/workflows/hooks/mutations/useUpdateWorkflowByIdMutation/useUpdateWorkflowByIdMutation'; -import toast from 'react-hot-toast'; import { SelectItem } from '../../../../common/components/atoms/Select/Select.Item'; import { SelectContent } from '../../../../common/components/atoms/Select/Select.Content'; import { SelectTrigger } from '../../../../common/components/atoms/Select/Select.Trigger'; import { SelectValue } from '../../../../common/components/atoms/Select/Select.Value'; +import { Input } from '../../../../common/components/atoms/Input/Input'; +import { DialogTrigger } from '../../../../common/components/organisms/Dialog/Dialog.Trigger'; +import { useCallToActionLogic } from './hooks/useCallToActionLogic/useCallToActionLogic'; export const CallToAction: FunctionComponent<ICallToActionProps> = ({ value, data }) => { - const { entityId } = useParams(); - const { data: entity } = useEntityWithWorkflowQuery(entityId); - const [revisionReason, setRevisionReason] = useState(''); - const onRevisionReasonChange = useCallback( - (value: string) => setRevisionReason(value), - [setRevisionReason], - ); - const { data: session } = useAuthenticatedUserQuery(); - const caseState = useCaseState(session?.user, entity?.workflow); - const revisionReasons = - entity?.workflow?.contextSchema?.schema?.properties?.documents?.items?.properties?.decision?.properties?.revisionReason?.anyOf?.find( - ({ enum: enum_ }) => !!enum_, - )?.enum; - const { mutate: mutateUpdateWorkflowById, isLoading: isLoadingUpdateWorkflowById } = - useUpdateWorkflowByIdMutation({ - workflowId: entity?.workflow?.runtimeDataId, - }); - const onMutateUpdateWorkflowById = - ( - payload: - | { - id: string; - approvalStatus: 'rejected' | 'approved'; - } - | { - id: string; - approvalStatus: 'revision'; - revisionReason: string; - }, - ) => - () => { - if (!payload?.id) { - toast.error('Invalid task id'); - - return; - } - - const action = ( - { - approved: 'approve_document', - rejected: 'reject_document', - revision: 'ask_resubmit_document', - } as const - )[payload.approvalStatus]; - - const context = { - documents: entity?.workflow?.workflowContext?.machineContext?.documents?.map(document => { - if (document?.id !== payload?.id) return document; - - switch (payload?.approvalStatus) { - case 'approved': - return { - ...document, - decision: { - revisionReason: null, - rejectionReason: null, - status: payload?.approvalStatus, - }, - }; - case 'rejected': - return { - ...document, - decision: { - revisionReason: null, - // Change when rejection reason is implemented. - rejectionReason: document?.decision?.rejectionReason ?? '', - status: payload?.approvalStatus, - }, - }; - case 'revision': - return { - ...document, - decision: { - revisionReason: payload?.revisionReason, - rejectionReason: null, - status: payload?.approvalStatus, - }, - }; - default: - return document; - } - }), - }; - return mutateUpdateWorkflowById({ - context, - action, - }); - }; + const { + onMutateUpdateWorkflowById, + isLoadingUpdateWorkflowById, + caseState, + action, + actions, + onActionChange, + reasons, + reason, + onReasonChange, + comment, + onCommentChange, + noReasons, + } = useCallToActionLogic(); return value === 'Reject' ? ( <Dialog> - <DropdownMenu> - <DropdownMenuTrigger asChild> - <Button - variant={`destructive`} - className={ctw({ - // loading: debouncedIsLoadingRejectEntity, - })} - // disabled={isLoading || !canReject} - disabled={!caseState.actionButtonsEnabled || data?.disabled} - > - {value} - </Button> - </DropdownMenuTrigger> - <DropdownMenuContent className={`min-w-[16rem]`} align={`end`}> - <DropdownMenuLabel>{value}</DropdownMenuLabel> - <DropdownMenuSeparator /> - <DialogTrigger asChild> - <DropdownMenuItem className={`cursor-pointer gap-x-2`}> - <RotateCcw size={18} /> - Ask to re-submit - </DropdownMenuItem> - </DialogTrigger> - <DropdownMenuItem - className={`cursor-pointer gap-x-2 text-red-500`} - onClick={onMutateUpdateWorkflowById({ - id: data?.id, - approvalStatus: 'rejected', - })} - > - <AlertTriangle className={`text-red-500`} size={18} /> - Block - </DropdownMenuItem> - </DropdownMenuContent> - </DropdownMenu> + <DialogTrigger asChild> + <Button + variant={`destructive`} + className={ctw({ + // loading: debouncedIsLoadingRejectEntity, + })} + // disabled={isLoading || !canReject} + disabled={!caseState.actionButtonsEnabled || data?.disabled} + > + {value} + </Button> + </DialogTrigger> <DialogContent> - <DialogHeader> - <DialogTitle>Request document re-submission</DialogTitle> - <DialogDescription> - This action will send a request to the user to re-submit their document. State the - reason for requesting a document re-submission. - </DialogDescription> - </DialogHeader> - <Select onValueChange={onRevisionReasonChange}> - <SelectTrigger className="w-full"> - <SelectValue placeholder="Re-submission reason" /> - </SelectTrigger> - <SelectContent> - {revisionReasons?.map(reason => { - const reasonWithSpace = reason.replace(/_/g, ' ').toLowerCase(); - const capitalizedReason = - reasonWithSpace.charAt(0).toUpperCase() + reasonWithSpace.slice(1); + <div> + <label className={`mb-1 font-bold`} htmlFor={`action`}> + Action + </label> + <Select onValueChange={onActionChange} value={action} id={`action`}> + <SelectTrigger className="w-full"> + <SelectValue /> + </SelectTrigger> + <SelectContent> + {actions?.map(({ label, value }) => { + return ( + <SelectItem key={action} value={value}> + {label} + </SelectItem> + ); + })} + </SelectContent> + </Select> + </div> + {!noReasons && ( + <div> + <label className={`mb-1 font-bold`} htmlFor={`reason`}> + Reason + </label> + <Select onValueChange={onReasonChange} value={reason} id={`reason`}> + <SelectTrigger className="w-full"> + <SelectValue /> + </SelectTrigger> + <SelectContent> + {reasons?.map(reason => { + const reasonWithSpace = reason.replace(/_/g, ' ').toLowerCase(); + const capitalizedReason = + reasonWithSpace.charAt(0).toUpperCase() + reasonWithSpace.slice(1); + + return ( + <SelectItem key={`${action}${reason}`} value={reason} className={`capitalize`}> + {capitalizedReason} + </SelectItem> + ); + })} + </SelectContent> + </Select> + </div> + )} + <div> + <label className={`mb-1 font-bold`} htmlFor={`comment`}> + {noReasons ? 'Reason' : 'Comment'} + </label> + <Input + onChange={event => { + if (noReasons) { + onReasonChange(event.target.value); + + return; + } - return ( - <SelectItem key={reason} value={reason}> - {capitalizedReason} - </SelectItem> - ); - })} - </SelectContent> - </Select> + onCommentChange(event.target.value); + }} + value={noReasons ? reason : comment} + id={noReasons ? `reason` : `comment`} + /> + </div> <DialogFooter> <DialogClose asChild> <button @@ -194,8 +121,8 @@ export const CallToAction: FunctionComponent<ICallToActionProps> = ({ value, dat // disabled={!resubmissionReason} onClick={onMutateUpdateWorkflowById({ id: data?.id, - approvalStatus: 'revision', - revisionReason, + approvalStatus: action, + reason: comment ? `${reason} - ${comment}` : reason, })} > Confirm diff --git a/apps/backoffice-v2/src/pages/Entity/components/CallToAction/hooks/useCallToActionLogic/useCallToActionLogic.tsx b/apps/backoffice-v2/src/pages/Entity/components/CallToAction/hooks/useCallToActionLogic/useCallToActionLogic.tsx new file mode 100644 index 0000000000..3de2844f37 --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/CallToAction/hooks/useCallToActionLogic/useCallToActionLogic.tsx @@ -0,0 +1,132 @@ +import { useParams } from 'react-router-dom'; +import { useFilterId } from '../../../../../../common/hooks/useFilterId/useFilterId'; +import { useWorkflowQuery } from '../../../../../../domains/workflows/hooks/queries/useWorkflowQuery/useWorkflowQuery'; +import { useAuthenticatedUserQuery } from '../../../../../../domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; +import { useCaseState } from '../../../Case/hooks/useCaseState/useCaseState'; +import { useUpdateWorkflowByIdMutation } from '../../../../../../domains/workflows/hooks/mutations/useUpdateWorkflowByIdMutation/useUpdateWorkflowByIdMutation'; +import toast from 'react-hot-toast'; +import { useCallback, useState } from 'react'; + +export const useCallToActionLogic = () => { + const { entityId } = useParams(); + const filterId = useFilterId(); + const { data: workflow } = useWorkflowQuery({ workflowId: entityId, filterId }); + const { data: session } = useAuthenticatedUserQuery(); + const caseState = useCaseState(session?.user, workflow); + const { mutate: mutateUpdateWorkflowById, isLoading: isLoadingUpdateWorkflowById } = + useUpdateWorkflowByIdMutation({ + workflowId: workflow.id, + }); + const onMutateUpdateWorkflowById = + ( + payload: + | { + id: string; + approvalStatus: 'approved'; + } + | { + id: string; + approvalStatus: 'revision' | 'rejected'; + reason: string; + }, + ) => + () => { + if (!payload?.id) { + toast.error('Invalid task id'); + + return; + } + + const action = ( + { + approved: 'approve_document', + rejected: 'reject_document', + revision: 'ask_resubmit_document', + } as const + )[payload.approvalStatus]; + + const context = { + documents: workflow.context.documents?.map(document => { + if (document?.id !== payload?.id) return document; + + switch (payload?.approvalStatus) { + case 'approved': + return { + ...document, + decision: { + revisionReason: null, + rejectionReason: null, + status: payload?.approvalStatus, + }, + }; + case 'rejected': + return { + ...document, + decision: { + revisionReason: null, + // Change when rejection reason is implemented. + rejectionReason: payload?.reason, + status: payload?.approvalStatus, + }, + }; + case 'revision': + return { + ...document, + decision: { + revisionReason: payload?.reason, + rejectionReason: null, + status: payload?.approvalStatus, + }, + }; + default: + return document; + } + }), + }; + return mutateUpdateWorkflowById({ + context, + action, + }); + }; + const revisionReasons = + workflow.workflowDefinition.contextSchema?.schema?.properties?.documents?.items?.properties?.decision?.properties?.revisionReason?.anyOf?.find( + ({ enum: enum_ }) => !!enum_, + )?.enum; + const rejectionReasons = + workflow.workflowDefinition.contextSchema?.schema?.properties?.documents?.items?.properties?.decision?.properties?.rejectionReason?.anyOf?.find( + ({ enum: enum_ }) => !!enum_, + )?.enum; + const actions = [ + { + label: 'Ask to re-submit', + value: 'revision', + }, + { + label: 'Block', + value: 'rejected', + }, + ] as const; + const [action, setAction] = useState<(typeof actions)[number]['value']>(actions[0].value); + const reasons = action === 'revision' ? revisionReasons : rejectionReasons; + const noReasons = !reasons?.length; + const [reason, setReason] = useState(reasons?.[0] ?? ''); + const [comment, setComment] = useState(''); + const onReasonChange = useCallback((value: string) => setReason(value), [setReason]); + const onActionChange = useCallback((value: typeof action) => setAction(value), [setAction]); + const onCommentChange = useCallback((value: string) => setComment(value), [setComment]); + + return { + onMutateUpdateWorkflowById, + isLoadingUpdateWorkflowById, + caseState, + action, + actions, + reasons, + reason, + comment, + onReasonChange, + onActionChange, + onCommentChange, + noReasons, + }; +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Actions.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Actions.tsx index 29ec29c7c5..ee515b2cd7 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Actions.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Actions.tsx @@ -1,5 +1,4 @@ import React, { FunctionComponent } from 'react'; -import { Avatar } from '../../../../common/components/atoms/Avatar'; import { IActionsProps } from './interfaces'; import { ResubmissionReason, useActions } from './hooks/useActions/useActions'; import { ctw } from '../../../../common/utils/ctw/ctw'; @@ -71,7 +70,8 @@ export const Actions: FunctionComponent<IActionsProps> = ({ isActionButtonDisabled, onTriggerAssignToMe, isAssignedToMe, - } = useActions({ entityId: id, fullName }); + hasDecision, + } = useActions({ workflowId: id, fullName }); return ( <div className={`sticky top-0 z-50 col-span-2 bg-base-100 px-4 pt-4`}> @@ -89,6 +89,7 @@ export const Actions: FunctionComponent<IActionsProps> = ({ onMutateAssignWorkflow(id, onTriggerAssignToMe); }} buttonType={'Assign'} + hasDecision={hasDecision} /> <AssignButton assignees={assignees as Assignee[]} @@ -98,6 +99,7 @@ export const Actions: FunctionComponent<IActionsProps> = ({ onMutateAssignWorkflow(id, !onTriggerAssignToMe); }} buttonType={'Re-Assign'} + hasDecision={hasDecision} /> </div> <div className={`flex h-20 justify-between`}> @@ -112,7 +114,7 @@ export const Actions: FunctionComponent<IActionsProps> = ({ </h2> </div> {showResolutionButtons && ( - <div className={`flex items-center space-x-6 pe-[3.35rem]`}> + <div className={`pe-[3.35rem] flex items-center space-x-6`}> <Button className={ctw({ // loading: debouncedIsLoadingRejectEntity, diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Documents.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Documents.tsx index 5988cf2e33..eb088008be 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Documents.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Documents.tsx @@ -6,6 +6,8 @@ import { CheckSvg, XMarkSvg } from '../../../../common/components/atoms/icons'; import { useDocuments } from './hooks/useDocuments/useDocuments'; import { ctw } from '../../../../common/utils/ctw/ctw'; import ReactCrop from 'react-image-crop'; +import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch'; +import { FileText } from 'lucide-react'; /** * @description To be used by {@link Case}, and be wrapped by {@link Case.Content}. Displays a single entity's documents using {@link ImageViewer}. Displays documents[0].imageUrl if no document was selected yet. @@ -30,33 +32,70 @@ export const Documents: FunctionComponent<IDocumentsProps> = ({ documents, isLoa isLoadingOCR, selectedImage, onSelectImage, + documentRotation, + onRotateDocument, + onTransformed, + isRotatedOrTransformed, } = useDocuments(documents); return ( <ImageViewer selectedImage={selectedImage} onSelectImage={onSelectImage}> <div className={`flex min-h-[600px] w-full flex-col items-center`}> <div - className={` - d-full relative max-w-[441px] items-center rounded-md`} + className={ctw( + ` + d-full relative flex justify-center rounded-md`, + )} > - <ReactCrop - crop={crop} - onChange={onCrop} - disabled={!isCropping || selectedImage?.fileType === 'pdf'} - className={ctw({ - 'd-full [&>div]:d-full': selectedImage?.fileType === 'pdf', - })} - > - <ImageViewer.SelectedImage - key={initialImage?.imageUrl} - initialImage={initialImage} - ref={selectedImageRef} - isLoading={isLoading} - /> - </ReactCrop> + <TransformWrapper onTransformed={onTransformed}> + <TransformComponent + wrapperClass={`max-w-[441px]`} + contentClass={ctw(`overflow-x-auto`, { + 'hover:cursor-move': selectedImage?.fileType !== 'pdf', + })} + wrapperStyle={{ + width: '100%', + height: '100%', + }} + contentStyle={{ + width: '100%', + height: '100%', + }} + > + <ReactCrop + crop={crop} + onChange={onCrop} + disabled={ + !isCropping || selectedImage?.fileType === 'pdf' || isRotatedOrTransformed + } + className={ctw({ + 'd-full [&>div]:d-full': selectedImage?.fileType === 'pdf', + 'rotate-90': documentRotation === 90, + 'rotate-180': documentRotation === 180, + 'rotate-[270deg]': documentRotation === 270, + })} + > + <ImageViewer.SelectedImage + key={initialImage?.imageUrl} + initialImage={initialImage} + ref={selectedImageRef} + isLoading={isLoading} + /> + </ReactCrop> + </TransformComponent> + </TransformWrapper> <div className={`absolute z-50 flex space-x-2 bottom-right-6`}> {selectedImage?.fileType !== 'pdf' && ( <> + <button + type={`button`} + className={ctw( + `btn-ghost btn-sm btn-circle btn bg-base-300/70 text-[0.688rem] focus:outline-primary`, + )} + onClick={onRotateDocument} + > + <FileText className={`rotate-90 p-0.5`} /> + </button> <button className={ctw( 'btn-ghost btn-sm btn-circle btn bg-base-300/70 focus:outline-primary', @@ -68,18 +107,26 @@ export const Documents: FunctionComponent<IDocumentsProps> = ({ documents, isLoa > <XMarkSvg className={`p-0.5`} /> </button> - <button - type={`button`} - className={ctw( - `btn-ghost btn-sm btn-circle btn bg-base-300/70 text-[0.688rem] focus:outline-primary`, - { loading: isLoadingOCR }, - )} - onClick={onOcr} - disabled={isLoading} + <div + title={ + isRotatedOrTransformed + ? `Cannot OCR rotated, zoomed, panned, or pinched documents` + : undefined + } > - {isCropping && !isLoadingOCR && <CheckSvg className={`p-0.5`} />} - {!isCropping && !isLoadingOCR && <span className={`p-0.5`}>OCR</span>} - </button> + <button + type={`button`} + className={ctw( + `btn-ghost btn-sm btn-circle btn bg-base-300/70 text-[0.688rem] focus:outline-primary`, + { loading: isLoadingOCR }, + )} + onClick={onOcr} + disabled={isLoading || isRotatedOrTransformed} + > + {isCropping && !isLoadingOCR && <CheckSvg className={`p-0.5`} />} + {!isCropping && !isLoadingOCR && <span className={`p-0.5`}>OCR</span>} + </button> + </div> </> )} <ImageViewer.ZoomButton disabled={isLoading} /> diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useActions/interfaces.ts b/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useActions/interfaces.ts index 81598e7236..a443090367 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useActions/interfaces.ts +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useActions/interfaces.ts @@ -1,4 +1,4 @@ export interface IUseActions { - entityId: string; + workflowId: string; fullName: string; } diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useActions/useActions.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useActions/useActions.tsx index 87824e9939..c873b36709 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useActions/useActions.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useActions/useActions.tsx @@ -2,8 +2,6 @@ import { useCallback, useState } from 'react'; import { useApproveEntityMutation } from '../../../../../../domains/entities/hooks/mutations/useApproveEntityMutation/useApproveEntityMutation'; import { useDebounce } from '../../../../../../common/hooks/useDebounce/useDebounce'; import { createInitials } from '../../../../../../common/utils/create-initials/create-initials'; -import { Action } from '../../../../../../common/enums'; -import { useEntityWithWorkflowQuery } from '../../../../../../domains/entities/hooks/queries/useEntityWithWorkflowQuery/useEntityWithWorkflowQuery'; import { IUseActions } from './interfaces'; import { useAuthenticatedUserQuery } from '../../../../../../domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; import { useCaseState } from '../useCaseState/useCaseState'; @@ -11,6 +9,8 @@ import { useUsersQuery } from '../../../../../../domains/users/hooks/queries/use import { useAssignWorkflowMutation } from '../../../../../../domains/workflows/hooks/mutations/useAssignWorkflowMutation/useAssignWorkflowMutation'; import { useRejectEntityMutation } from '../../../../../../domains/entities/hooks/mutations/useRejectEntityMutation/useRejectEntityMutation'; import { useSelectNextEntity } from '../../../../../../domains/entities/hooks/useSelectNextEntity/useSelectNextEntity'; +import { useWorkflowQuery } from '../../../../../../domains/workflows/hooks/queries/useWorkflowQuery/useWorkflowQuery'; +import { useFilterId } from '../../../../../../common/hooks/useFilterId/useFilterId'; export const ResubmissionReason = { BLURRY_IMAGE: 'BLURRY_IMAGE', @@ -23,27 +23,24 @@ export const ResubmissionReason = { FACE_IS_NOT_MATCHING: 'FACE_IS_NOT_MATCHING', } as const; -export const useActions = ({ entityId, fullName }: IUseActions) => { +export const useActions = ({ workflowId, fullName }: IUseActions) => { const onSelectNextEntity = useSelectNextEntity(); - const { isLoading: isLoadingEntity, data: entity } = useEntityWithWorkflowQuery(entityId); - const { workflow } = entity ?? {}; + const filterId = useFilterId(); + const { data: workflow } = useWorkflowQuery({ workflowId, filterId }); const { mutate: mutateApproveEntity, isLoading: isLoadingApproveEntity } = useApproveEntityMutation({ - entityId, - workflowId: workflow?.runtimeDataId, + workflowId: workflowId, onSelectNextEntity, }); const { mutate: mutateRejectEntity, isLoading: isLoadingRejectEntity } = useRejectEntityMutation({ - entityId, - workflowId: workflow?.runtimeDataId, + workflowId: workflowId, onSelectNextEntity, }); const { mutate: mutateAssignWorkflow, isLoading: isLoadingAssignWorkflow } = - useAssignWorkflowMutation({ workflowRuntimeId: workflow?.runtimeDataId }); + useAssignWorkflowMutation({ workflowRuntimeId: workflowId }); - const isLoading = - isLoadingApproveEntity || isLoadingRejectEntity || isLoadingEntity || isLoadingAssignWorkflow; + const isLoading = isLoadingApproveEntity || isLoadingRejectEntity || isLoadingAssignWorkflow; // Create initials from the first character of the first name, middle name, and last name. const initials = createInitials(fullName); @@ -55,12 +52,8 @@ export const useActions = ({ entityId, fullName }: IUseActions) => { const assignees = users?.filter(assignee => assignee?.id !== authenticatedUser?.id); // Disable the reject/approve buttons if the end user is not ready to be rejected/approved. // Based on `workflowDefinition` - ['APPROVE', 'REJECT', 'RECOLLECT']. - const canReject = - (workflow?.nextEvents.includes(Action.REJECT.toLowerCase()) as boolean) && - caseState.actionButtonsEnabled; - const canApprove = - (workflow?.nextEvents.includes(Action.APPROVE.toLowerCase()) as boolean) && - caseState.actionButtonsEnabled; + const canReject = caseState.actionButtonsEnabled; + const canApprove = caseState.actionButtonsEnabled; // Only display the button spinners if the request is longer than 300ms const debouncedIsLoadingRejectEntity = useDebounce(isLoadingRejectEntity, 300); @@ -93,6 +86,9 @@ export const useActions = ({ entityId, fullName }: IUseActions) => { ); const isActionButtonDisabled = !caseState.actionButtonsEnabled; const onTriggerAssignToMe = true; + const hasDecision = + workflow?.context?.documents?.length && + workflow?.context?.documents?.every(document => !!document?.decision?.status); // useDocumentListener('keydown', event => { // if (!event.ctrlKey || document.activeElement !== document.body) return; @@ -128,7 +124,6 @@ export const useActions = ({ entityId, fullName }: IUseActions) => { debouncedIsLoadingApproveEntity, debouncedIsLoadingAssignEntity, isLoading, - isLoadingEntity, initials, canReject, canApprove, @@ -139,5 +134,6 @@ export const useActions = ({ entityId, fullName }: IUseActions) => { caseState, authenticatedUser, assignees, + hasDecision, }; }; diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useCaseState/useCaseState.ts b/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useCaseState/useCaseState.ts index 875aad7d91..8b1cfcf413 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useCaseState/useCaseState.ts +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useCaseState/useCaseState.ts @@ -2,9 +2,11 @@ import { CaseState } from '../../../../../../common/enums'; import { TAuthenticatedUser } from '../../../../../../domains/auth/types'; export const useCaseState = (authenticatedUser: TAuthenticatedUser, workflow) => { + const assigneeId = workflow?.assigneeId || workflow?.assignee?.id; + if (!workflow) return CaseState.UNKNOWN; - if (workflow?.assigneeId === authenticatedUser?.id) return CaseState.ASSIGNED_TO_ME; - if (!workflow?.assigneeId) return CaseState.UNASSIGNED; + if (assigneeId === authenticatedUser?.id) return CaseState.ASSIGNED_TO_ME; + if (!assigneeId) return CaseState.UNASSIGNED; return CaseState.ASSIGNED_TO_OTHER; }; diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useDocuments/useDocuments.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useDocuments/useDocuments.tsx index 5ca1a500f3..5e4918ad1b 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useDocuments/useDocuments.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/hooks/useDocuments/useDocuments.tsx @@ -6,6 +6,7 @@ import { createArrayOfNumbers } from '../../../../../../common/utils/create-arra import toast from 'react-hot-toast'; import { t } from 'i18next'; import { useToggle } from '../../../../../../common/hooks/useToggle/useToggle'; +import { ReactZoomPanPinchContentRef } from 'react-zoom-pan-pinch'; export const useDocuments = (documents: IDocumentsProps['documents']) => { const initialImage = documents?.[0]; @@ -70,6 +71,21 @@ export const useDocuments = (documents: IDocumentsProps['documents']) => { }, [], ); + const [documentRotation, setDocumentRotation] = useState(0); + const onRotateDocument = useCallback(() => { + setDocumentRotation(prevState => (prevState >= 270 ? 0 : prevState + 90)); + }, []); + const [isTransformed, setIsTransformed] = useState(false); + const isRotatedOrTransformed = documentRotation !== 0 || isTransformed; + const onTransformed = useCallback( + ( + ref: ReactZoomPanPinchContentRef, + state: ReactZoomPanPinchContentRef['instance']['transformState'], + ) => { + setIsTransformed(state?.scale !== 1 || state?.positionX !== 0 || state?.positionY !== 0); + }, + [], + ); return { crop, @@ -83,5 +99,9 @@ export const useDocuments = (documents: IDocumentsProps['documents']) => { isLoadingOCR, selectedImage, onSelectImage, + documentRotation, + onRotateDocument, + isRotatedOrTransformed, + onTransformed, }; }; diff --git a/apps/backoffice-v2/src/pages/Entity/components/Container/Container.tsx b/apps/backoffice-v2/src/pages/Entity/components/Container/Container.tsx index 3a4b6f1d96..e209f5605f 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Container/Container.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Container/Container.tsx @@ -1,7 +1,7 @@ import React, { FunctionComponent } from 'react'; import { ctw } from '../../../../common/utils/ctw/ctw'; import { IContainerProps } from './interfaces'; -import { components } from '../../hooks/useEntity/components'; +import { cells } from '../../hooks/useEntity/cells'; export const Container: FunctionComponent<IContainerProps> = ({ value, id }) => { return ( @@ -10,12 +10,12 @@ export const Container: FunctionComponent<IContainerProps> = ({ value, id }) => 'm-2 mt-6 flex justify-end space-x-2 rounded p-2 text-slate-50': id === 'actions', rounded: id === 'alerts', 'col-span-full': id === 'alerts' || id === 'header', - 'grid grid-cols-2': id === 'header', + 'grid grid-cols-2': id === 'header' || id === 'map-container', 'm-2 flex flex-col space-y-2 p-2': id === 'alerts', })} > {value?.map((cell, index) => { - const Cell = components[cell?.type]; + const Cell = cells[cell?.type]; return <Cell key={index} {...cell} />; })} diff --git a/apps/backoffice-v2/src/pages/Entity/components/Details/Details.tsx b/apps/backoffice-v2/src/pages/Entity/components/Details/Details.tsx index 20101dec8c..54e280ccfc 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Details/Details.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Details/Details.tsx @@ -4,11 +4,13 @@ import { Separator } from '../../../../common/components/atoms/Separator/Separat import React, { FunctionComponent } from 'react'; import { useParams } from 'react-router-dom'; import { IDetailsProps } from './interfaces'; -import { useEntityWithWorkflowQuery } from '../../../../domains/entities/hooks/queries/useEntityWithWorkflowQuery/useEntityWithWorkflowQuery'; +import { useWorkflowQuery } from '../../../../domains/workflows/hooks/queries/useWorkflowQuery/useWorkflowQuery'; +import { useFilterId } from '../../../../common/hooks/useFilterId/useFilterId'; export const Details: FunctionComponent<IDetailsProps> = ({ id, value }) => { const { entityId } = useParams(); - const { data: entity } = useEntityWithWorkflowQuery(entityId); + const filterId = useFilterId(); + const { data: workflow } = useWorkflowQuery({ workflowId: entityId, filterId }); if (!value.data?.length) return; @@ -19,10 +21,10 @@ export const Details: FunctionComponent<IDetailsProps> = ({ id, value }) => { })} > <EditableDetails - workflowId={entity?.workflow?.runtimeDataId} + workflowId={workflow.id} id={id} valueId={value?.id} - documents={entity?.workflow?.workflowContext?.machineContext?.documents} + documents={workflow.context.documents} title={value?.title} data={value?.data} /> diff --git a/apps/backoffice-v2/src/pages/Entity/components/Details/interfaces.ts b/apps/backoffice-v2/src/pages/Entity/components/Details/interfaces.ts index b4e3d52a19..2d1d29f8a0 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Details/interfaces.ts +++ b/apps/backoffice-v2/src/pages/Entity/components/Details/interfaces.ts @@ -10,6 +10,9 @@ export interface IDetailsProps { format?: string; pattern?: string; value: unknown; + dropdownOptions?: Array<{ label: string; value: string }>; + dependantOn?: string; + dependantValue?: string; }>; }; } diff --git a/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/EditableDetails.tsx b/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/EditableDetails.tsx index 5c4f39ba65..69678b3843 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/EditableDetails.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/EditableDetails.tsx @@ -5,7 +5,7 @@ import { toStartCase } from '../../../../common/utils/to-start-case/to-start-cas import { camelCaseToSpace } from '../../../../common/utils/camel-case-to-space/camel-case-to-space'; import { Input } from '../../../../common/components/atoms/Input/Input'; import { Button } from '../../../../common/components/atoms/Button/Button'; -import React, { FunctionComponent } from 'react'; +import React, { FunctionComponent, useEffect, useState } from 'react'; import { AnyRecord } from '../../../../common/types'; import { IEditableDetails } from './interfaces'; import { useUpdateWorkflowByIdMutation } from '../../../../domains/workflows/hooks/mutations/useUpdateWorkflowByIdMutation/useUpdateWorkflowByIdMutation'; @@ -14,6 +14,21 @@ import { FormItem } from '../../../../common/components/organisms/Form/Form.Item import { FormLabel } from '../../../../common/components/organisms/Form/Form.Label'; import { FormControl } from '../../../../common/components/organisms/Form/Form.Control'; import { FormMessage } from '../../../../common/components/organisms/Form/Form.Message'; +import { SelectItem } from '../../../../common/components/atoms/Select/Select.Item'; +import { SelectContent } from '../../../../common/components/atoms/Select/Select.Content'; +import { SelectTrigger } from '../../../../common/components/atoms/Select/Select.Trigger'; +import { SelectValue } from '../../../../common/components/atoms/Select/Select.Value'; +import { Select } from '../../../../common/components/atoms/Select/Select'; +import { useWatchDropdownOptions } from './hooks/useWatchDropdown'; +import { keyFactory } from '../../../../common/utils/key-factory/key-factory'; + +const useInitialCategorySetValue = ({ form, data }) => { + useEffect(() => { + const categoryValue = form.getValues('category'); + + form.setValue('category', categoryValue); + }, [form, data]); +}; export const EditableDetails: FunctionComponent<IEditableDetails> = ({ data, @@ -23,21 +38,32 @@ export const EditableDetails: FunctionComponent<IEditableDetails> = ({ title, workflowId, }) => { - const { mutate: mutateUpdateWorkflowById } = useUpdateWorkflowByIdMutation({ - workflowId, - }); + const [formData, setFormData] = useState(data); + const useInitialCategorySetValue = () => { + useEffect(() => { + const categoryValue = form.getValues('category'); + form.setValue('category', categoryValue); + }, [form, data, setFormData]); + }; const POSITIVE_VALUE_INDICATOR = ['approved']; const NEGATIVE_VALUE_INDICATOR = ['revision', 'rejected']; - const isDecisionPositive = (isDecisionComponent: boolean, value) => { - return ( - isDecisionComponent && value && POSITIVE_VALUE_INDICATOR.includes(String(value).toLowerCase()) - ); + const isDecisionPositive = (isDecisionComponent: boolean, value: string) => { + return isDecisionComponent && value && POSITIVE_VALUE_INDICATOR.includes(value.toLowerCase()); }; - const isDecisionNegative = (isDecisionComponent: boolean, value) => { - return ( - isDecisionComponent && value && NEGATIVE_VALUE_INDICATOR.includes(String(value).toLowerCase()) - ); + const isDecisionNegative = (isDecisionComponent: boolean, value: string) => { + return isDecisionComponent && value && NEGATIVE_VALUE_INDICATOR.includes(value.toLowerCase()); }; + const defaultValues = formData?.reduce((acc, curr) => { + acc[curr.title] = curr.value; + + return acc; + }, {}); + const form = useForm({ + defaultValues, + }); + const { mutate: mutateUpdateWorkflowById } = useUpdateWorkflowByIdMutation({ + workflowId, + }); const onMutateTaskDecisionById = ({ context, action, @@ -49,26 +75,26 @@ export const EditableDetails: FunctionComponent<IEditableDetails> = ({ context, action, }); - const defaultValues = data?.reduce((acc, curr) => { - acc[curr.title] = curr.value; - - return acc; - }, {}); - const form = useForm({ - defaultValues, - }); - const onSubmit: SubmitHandler<Record<PropertyKey, unknown>> = data => { + const onSubmit: SubmitHandler<Record<PropertyKey, unknown>> = formData => { const context = { documents: documents?.map(document => { if (document?.id !== valueId) return document; - return { - ...document, - properties: Object.keys(document?.properties).reduce((acc, curr) => { - acc[curr] = data?.[curr]; + const properties = Object.keys(document?.propertiesSchema?.properties ?? {}).reduce( + (acc, curr) => { + if (!formData?.[curr]) return acc; + acc[curr] = formData?.[curr]; return acc; - }, {}), + }, + {}, + ); + + return { + ...document, + type: formData.type, + category: formData.category, + properties: properties, }; }), }; @@ -80,16 +106,24 @@ export const EditableDetails: FunctionComponent<IEditableDetails> = ({ }; const isDecisionComponent = title === 'Decision'; + useWatchDropdownOptions({ form, data, setFormData }); + useInitialCategorySetValue({ + form, + data, + }); + return ( <Form {...form}> <form onSubmit={form.handleSubmit(onSubmit)} className={`flex h-full flex-col`}> - <legend className={`sr-only`}>{title}</legend> + <legend className={ctw({ 'sr-only': id !== 'visible-title' }, 'mb-2 font-bold')}> + {title} + </legend> <div className={ctw(`grid grid-cols-2 gap-4`, { 'grid-cols-3': id === 'entity-details', })} > - {data?.map(({ title, isEditable, type, format, pattern, value }) => + {formData?.map(({ title, isEditable, type, format, pattern, value, dropdownOptions }) => isDecisionComponent && !value ? null : ( <FormField key={title} @@ -98,28 +132,54 @@ export const EditableDetails: FunctionComponent<IEditableDetails> = ({ render={({ field }) => ( <FormItem> <FormLabel>{toStartCase(camelCaseToSpace(title))}</FormLabel> - <FormControl> - <Input - type={!format ? (type === 'string' ? 'text' : type) : format} + {dropdownOptions ? ( + <Select disabled={!isEditable} - className={ctw( - `p-1 disabled:cursor-auto disabled:border-none disabled:bg-background disabled:opacity-100`, - { - 'font-bold text-success': isDecisionPositive( - isDecisionComponent, - value, - ), - 'font-bold text-destructive': isDecisionNegative( - isDecisionComponent, - value, - ), - }, - )} - pattern={pattern} - autoComplete={'off'} - {...field} - /> - </FormControl> + onValueChange={field.onChange} + defaultValue={field.value} + > + <FormControl> + <SelectTrigger className="w-full"> + <SelectValue /> + </SelectTrigger> + </FormControl> + <SelectContent> + {dropdownOptions?.map(({ label, value }, index) => { + return ( + <SelectItem + key={keyFactory(id, valueId, label, index?.toString())} + value={value} + > + {label} + </SelectItem> + ); + })} + </SelectContent> + </Select> + ) : ( + <FormControl> + <Input + type={!format ? (type === 'string' ? 'text' : type) : format} + disabled={!isEditable} + className={ctw( + `p-1 disabled:cursor-auto disabled:border-none disabled:bg-background disabled:opacity-100`, + { + 'font-bold text-success': isDecisionPositive( + isDecisionComponent, + field.value, + ), + 'font-bold text-destructive': isDecisionNegative( + isDecisionComponent, + field.value, + ), + }, + )} + pattern={pattern} + autoComplete={'off'} + {...field} + /> + </FormControl> + )} <FormMessage /> </FormItem> )} diff --git a/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/hooks/useWatchDropdown.ts b/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/hooks/useWatchDropdown.ts new file mode 100644 index 0000000000..e89a636b5e --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/hooks/useWatchDropdown.ts @@ -0,0 +1,37 @@ +import { useEffect } from 'react'; +import { TDropdownOption } from '../types'; + +export const useWatchDropdownOptions = ({ form, data, setFormData }) => { + const watch = form.watch; + + useEffect(() => { + const subscription = watch((value, { name }) => { + if (!['category'].includes(name)) return subscription.unsubscribe(); + const newData = structuredClone(data); + + newData + .filter(item => !!item.dropdownOptions) + .filter(item => + item.dropdownOptions.find(dropdownOption => dropdownOption.dependantOn === name), + ) + .forEach(item => { + item.dropdownOptions = item.dropdownOptions.filter( + (dropdownOption: TDropdownOption) => dropdownOption.dependantValue === value[name], + ); + item.value = item.dropdownOptions.find( + dropDownOption => dropDownOption.value == value, + )?.value; + + if (item.value) { + form.setValue(item.title, `${item.value}`); + } + + return (newData[newData.indexOf(item)] = item); + }); + + setFormData(newData); + }); + + return () => subscription.unsubscribe(); + }, [watch, data, setFormData]); +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/interfaces.ts b/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/interfaces.ts index 0847c4cda6..fcf501020c 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/interfaces.ts +++ b/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/interfaces.ts @@ -1,3 +1,5 @@ +import { TDropdownOption } from './types'; + export interface IEditableDetails { data: Array<{ title: string; @@ -6,6 +8,7 @@ export interface IEditableDetails { type: string; format?: string; pattern?: string; + dropdownOptions?: Array<TDropdownOption>; }>; valueId: string; id: string; diff --git a/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/types.ts b/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/types.ts new file mode 100644 index 0000000000..0b918bdfa1 --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/EditableDetails/types.ts @@ -0,0 +1,6 @@ +export type TDropdownOption = { + value: string; + label: string; + dependantValue?: string; + dependantOn?: string; +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/Heading/Heading.tsx b/apps/backoffice-v2/src/pages/Entity/components/Heading/Heading.tsx index 9c5fd41bf3..fa5662349d 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Heading/Heading.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Heading/Heading.tsx @@ -1,6 +1,14 @@ import React, { FunctionComponent } from 'react'; import { IHeadingProps } from './interfaces'; +import { ctw } from '../../../../common/utils/ctw/ctw'; -export const Heading: FunctionComponent<IHeadingProps> = ({ value }) => ( - <h2 className={`ml-2 mt-6 p-2 text-2xl font-bold`}>{value}</h2> +export const Heading: FunctionComponent<IHeadingProps> = ({ id, value }) => ( + <h2 + className={ctw(`ml-2 mt-6 p-2 text-2xl font-bold`, { + 'text-lg text-slate-400': id === 'nested-details-heading', + 'col-span-full': id === 'map-header', + })} + > + {value} + </h2> ); diff --git a/apps/backoffice-v2/src/pages/Entity/components/Heading/interfaces.ts b/apps/backoffice-v2/src/pages/Entity/components/Heading/interfaces.ts index bf42255efd..37341b10fb 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Heading/interfaces.ts +++ b/apps/backoffice-v2/src/pages/Entity/components/Heading/interfaces.ts @@ -1,3 +1,4 @@ export interface IHeadingProps { + id?: string; value: string; } diff --git a/apps/backoffice-v2/src/pages/Entity/components/MapCell/MapCell.tsx b/apps/backoffice-v2/src/pages/Entity/components/MapCell/MapCell.tsx new file mode 100644 index 0000000000..3b927836cd --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/MapCell/MapCell.tsx @@ -0,0 +1,22 @@ +import { FunctionComponent } from 'react'; +import 'leaflet/dist/leaflet.css'; +import { useNominatimQuery } from './hooks/useNominatimQuery/useNominatimQuery'; +import { IMapCellProps } from './interfaces'; +import { Map } from '../../../../common/components/molecules/Map/Map'; +import { ErrorAlert } from '../../../../common/components/atoms/ErrorAlert/ErrorAlert'; + +export const MapCell: FunctionComponent<IMapCellProps> = ({ value }) => { + const { data } = useNominatimQuery(value); + + if (!data?.[0]?.lat || !data?.[0]?.lon) { + return <ErrorAlert>Invalid address.</ErrorAlert>; + } + + return ( + <Map + latitude={data?.[0]?.lat} + longitude={data?.[0]?.lon} + popupContent={`${value?.country}, ${value?.city}, ${value?.street}`} + /> + ); +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/MapCell/hooks/useNominatimQuery/fetcher.ts b/apps/backoffice-v2/src/pages/Entity/components/MapCell/hooks/useNominatimQuery/fetcher.ts new file mode 100644 index 0000000000..6ada63c4e6 --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/MapCell/hooks/useNominatimQuery/fetcher.ts @@ -0,0 +1,32 @@ +import { handlePromise } from '@ballerine/common'; +import { fetcher } from '../../../../../../common/utils/fetcher/fetcher'; +import { Method } from '../../../../../../common/enums'; +import { z } from 'zod'; +import { handleZodError } from '../../../../../../common/utils/handle-zod-error/handle-zod-error'; + +export const fetchNominatimSearch = async ({ + country, + city, + street, +}: { + country: string; + city: string; + street: string; +}) => { + const [coordinates, error] = await handlePromise( + fetcher({ + method: Method.GET, + url: `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent( + `${country} ${city} ${street}`, + )}&format=json`, + schema: z.array( + z.object({ + lat: z.coerce.number(), + lon: z.coerce.number(), + }), + ), + }), + ); + + return handleZodError(error, coordinates); +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/MapCell/hooks/useNominatimQuery/useNominatimQuery.tsx b/apps/backoffice-v2/src/pages/Entity/components/MapCell/hooks/useNominatimQuery/useNominatimQuery.tsx new file mode 100644 index 0000000000..5b11e71123 --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/MapCell/hooks/useNominatimQuery/useNominatimQuery.tsx @@ -0,0 +1,32 @@ +import { useQuery } from '@tanstack/react-query'; +import { isString } from '../../../../../../common/utils/is-string/is-string'; +import { fetchNominatimSearch } from './fetcher'; + +export const useNominatimQuery = ({ + country, + city, + street, +}: { + country: string; + city: string; + street: string; +}) => { + return useQuery({ + queryKey: ['nominatim', 'search', { country, city, street }], + queryFn: () => + fetchNominatimSearch({ + country, + city, + street, + }), + staleTime: Infinity, + refetchInterval: false, + enabled: + isString(country) && + !!country?.length && + isString(city) && + !!city?.length && + isString(street) && + !!street?.length, + }); +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/MapCell/interfaces.ts b/apps/backoffice-v2/src/pages/Entity/components/MapCell/interfaces.ts new file mode 100644 index 0000000000..f07fead647 --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/MapCell/interfaces.ts @@ -0,0 +1,8 @@ +export interface IMapCellProps { + id?: string; + value: { + country: string; + city: string; + street: string; + }; +} diff --git a/apps/backoffice-v2/src/pages/Entity/components/NestedComponent/NestedComponent.tsx b/apps/backoffice-v2/src/pages/Entity/components/NestedComponent/NestedComponent.tsx new file mode 100644 index 0000000000..6190844548 --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/NestedComponent/NestedComponent.tsx @@ -0,0 +1,53 @@ +import { FunctionComponentWithChildren } from '../../../../common/types'; +import { ctw } from '../../../../common/utils/ctw/ctw'; +import { convertSnakeCaseToTitleCase } from '../../hooks/useEntity/utils'; +import { isObject } from '@ballerine/common'; +import { FunctionComponent } from 'react'; +import { INestedComponentProps } from './interfaces'; + +export const NestedComponent: FunctionComponent<INestedComponentProps> = ({ + id, + value, + isNested = false, +}) => { + if (!value?.data?.length) return; + + const Container: FunctionComponentWithChildren = ({ children }) => { + if (!isNested) return <>{children}</>; + + return <div className={`mb-4 grid grid-cols-2 gap-4`}>{children}</div>; + }; + + return ( + <Container> + {value?.data?.map(({ title, value }) => { + return ( + <div key={title}> + <h4 + className={ctw(`mb-1 text-lg font-bold`, { + 'text-2xl': !isNested, + 'text-slate-400': isNested, + })} + > + {convertSnakeCaseToTitleCase(title)} + </h4> + {isObject(value) && ( + <NestedComponent + isNested + id={id} + value={{ + data: Object.entries(value)?.map(([title, value]) => ({ + title, + value, + })), + }} + /> + )} + {Array.isArray(value) && <p>{value.join(', ')}</p>} + {!isObject(value) && !Array.isArray(value) && <p>{value}</p>} + </div> + ); + })} + </Container> + ); +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/NestedComponent/interfaces.ts b/apps/backoffice-v2/src/pages/Entity/components/NestedComponent/interfaces.ts new file mode 100644 index 0000000000..661108273f --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/NestedComponent/interfaces.ts @@ -0,0 +1,10 @@ +export interface INestedComponentProps { + id?: string; + value: { + data: Array<{ + title: string; + value: unknown; + }>; + }; + isNested?: boolean; +} diff --git a/apps/backoffice-v2/src/pages/Entity/components/NestedDetails/NestedDetails.tsx b/apps/backoffice-v2/src/pages/Entity/components/NestedDetails/NestedDetails.tsx new file mode 100644 index 0000000000..e9bd22b379 --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/NestedDetails/NestedDetails.tsx @@ -0,0 +1,13 @@ +import { NestedComponent } from '../NestedComponent/NestedComponent'; +import { FunctionComponent } from 'react'; +import { INestedDetailsProps } from './interfaces'; + +export const NestedDetails: FunctionComponent<INestedDetailsProps> = ({ id, value }) => { + if (!value?.data?.length) return; + + return ( + <div className={`ml-3`}> + <NestedComponent id={id} value={value} /> + </div> + ); +}; diff --git a/apps/backoffice-v2/src/pages/Entity/components/NestedDetails/interfaces.ts b/apps/backoffice-v2/src/pages/Entity/components/NestedDetails/interfaces.ts new file mode 100644 index 0000000000..53f310e114 --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/components/NestedDetails/interfaces.ts @@ -0,0 +1,9 @@ +export interface INestedDetailsProps { + id?: string; + value: { + data: Array<{ + title: string; + value: unknown; + }>; + }; +} diff --git a/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/components.tsx b/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/cells.tsx similarity index 75% rename from apps/backoffice-v2/src/pages/Entity/hooks/useEntity/components.tsx rename to apps/backoffice-v2/src/pages/Entity/hooks/useEntity/cells.tsx index 903fc456b8..083965e8a0 100644 --- a/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/components.tsx +++ b/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/cells.tsx @@ -5,13 +5,17 @@ import { CallToAction } from '../../components/CallToAction/CallToAction'; import { FaceComparison } from '../../components/FaceComparison/FaceComparison'; import { Details } from '../../components/Details/Details'; import { MultiDocuments } from '../../components/MultiDocuments/MultiDocuments'; +import { NestedDetails } from '../../components/NestedDetails/NestedDetails'; +import { MapCell } from '../../components/MapCell/MapCell'; -export const components = { +export const cells = { heading: Heading, alert: Alert, container: Container, callToAction: CallToAction, faceComparison: FaceComparison, details: Details, + nestedDetails: NestedDetails, multiDocuments: MultiDocuments, + map: MapCell, }; diff --git a/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/useEntity.tsx b/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/useEntity.tsx index 9de049181a..f6f3927e24 100644 --- a/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/useEntity.tsx +++ b/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/useEntity.tsx @@ -1,30 +1,35 @@ import { useParams } from 'react-router-dom'; -import { useEntityWithWorkflowQuery } from '../../../../domains/entities/hooks/queries/useEntityWithWorkflowQuery/useEntityWithWorkflowQuery'; import { useStorageFilesQuery } from '../../../../domains/storage/hooks/queries/useStorageFilesQuery/useStorageFilesQuery'; -import { useFilterEntity } from '../../../../domains/entities/hooks/useFilterEntity/useFilterEntity'; -import { useUpdateWorkflowByIdMutation } from '../../../../domains/workflows/hooks/mutations/useUpdateWorkflowByIdMutation/useUpdateWorkflowByIdMutation'; import { useCaseState } from '../../components/Case/hooks/useCaseState/useCaseState'; import { useAuthenticatedUserQuery } from '../../../../domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery'; import { toStartCase } from '../../../../common/utils/to-start-case/to-start-case'; -import { components } from './components'; - -const convertSnakeCaseToTitleCase = (input: string): string => - input - .split('_') - .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); +import { cells } from './cells'; +import { useFilterId } from '../../../../common/hooks/useFilterId/useFilterId'; +import { useWorkflowQuery } from '../../../../domains/workflows/hooks/queries/useWorkflowQuery/useWorkflowQuery'; +import { + composePickableCategoryType, + convertSnakeCaseToTitleCase, + extractCountryCodeFromWorkflow, + getIsEditable, + isExistingSchemaForDocument, + omitPropsFromObject, +} from './utils'; +import { getDocumentsByCountry } from '@ballerine/common'; +import { getAddressDeep } from './utils/get-address-deep/get-address-deep'; export const useEntity = () => { const { entityId } = useParams(); - const { data: entity, isLoading } = useEntityWithWorkflowQuery(entityId); + const filterId = useFilterId(); + + const { data: workflow, isLoading } = useWorkflowQuery({ workflowId: entityId, filterId }); const docsData = useStorageFilesQuery( - entity?.workflow?.workflowContext?.machineContext?.documents?.flatMap(({ pages }) => + workflow?.context?.documents?.flatMap(({ pages }) => pages?.map(({ ballerineFileId }) => ballerineFileId), ), ); const results = []; - entity?.workflow?.workflowContext?.machineContext?.documents?.forEach((document, docIndex) => { + workflow?.context?.documents?.forEach((document, docIndex) => { document?.pages.forEach((page, pageIndex) => { if (!results[docIndex]) { results[docIndex] = []; @@ -32,30 +37,54 @@ export const useEntity = () => { results[docIndex][pageIndex] = docsData.shift().data; }); }); - const filterEntity = useFilterEntity(); - const selectedEntity = { - id: entityId, - fullName: filterEntity === 'individuals' ? entity?.fullName : entity?.companyName, - avatarUrl: entity?.avatarUrl, - workflow: entity?.workflow, - }; + const selectedEntity = workflow?.entity; + const issuerCountryCode = extractCountryCodeFromWorkflow(workflow); + const documentsSchemas = !!issuerCountryCode && getDocumentsByCountry(issuerCountryCode); const octetToFileType = (base64: string, fileType: string) => base64?.replace(/application\/octet-stream/gi, fileType); - const { mutate: mutateUpdateWorkflowById, isLoading: isLoadingUpdateWorkflowById } = - useUpdateWorkflowByIdMutation({ - workflowId: entity?.workflow?.runtimeDataId, - }); const { data: session } = useAuthenticatedUserQuery(); - const caseState = useCaseState(session?.user, entity?.workflow); - const contextEntity = entity?.workflow?.workflowContext?.machineContext?.entity; - const contextDocuments = entity?.workflow?.workflowContext?.machineContext?.documents; + const caseState = useCaseState(session?.user, workflow); + const { + documents: contextDocuments, + entity: contextEntity, + pluginsOutput, + } = workflow?.context ?? {}; + const pluginsOutputKeys = Object.keys(pluginsOutput ?? {}); + const address = getAddressDeep(pluginsOutput); const tasks = contextEntity ? [ + ...(Object.keys(pluginsOutput ?? {}).length === 0 + ? [] + : pluginsOutputKeys + ?.filter(key => !!Object.keys(pluginsOutput[key] ?? {})?.length) + ?.map(key => [ + { + id: 'nested-details-heading', + type: 'heading', + value: convertSnakeCaseToTitleCase(key), + }, + { + type: 'nestedDetails', + value: { + data: Object.entries(pluginsOutput[key] ?? {})?.map(([title, value]) => ({ + title, + value, + })), + }, + }, + ])), ...(contextDocuments?.map( ( { id, type: docType, category, issuer, properties, propertiesSchema, decision }, docIndex, ) => { + const additionProperties = + isExistingSchemaForDocument(documentsSchemas) && + composePickableCategoryType(category, docType, documentsSchemas); + const isDoneWithRevision = + decision?.status === 'revision' && + workflow?.context?.parentMachine?.status === 'completed'; + return [ { id: 'header', @@ -76,7 +105,7 @@ export const useEntity = () => { value: 'Reject', data: { id, - disabled: Boolean(decision), + disabled: !isDoneWithRevision && Boolean(decision?.status), approvalStatus: 'rejected', }, }, @@ -85,7 +114,7 @@ export const useEntity = () => { value: 'Approve', data: { id, - disabled: Boolean(decision), + disabled: !isDoneWithRevision && Boolean(decision?.status), approvalStatus: 'approved', }, }, @@ -102,15 +131,32 @@ export const useEntity = () => { value: { id, title: `${category} - ${docType}`, - data: Object.entries(propertiesSchema?.properties ?? {})?.map( - ([title, { type, format, pattern, isEditable = true }]) => ({ + data: Object.entries( + { + ...additionProperties, + ...propertiesSchema?.properties, + } ?? {}, + )?.map( + ([ title, - value: properties?.[title] ?? '', - type, - format, - pattern, - isEditable: caseState.writeEnabled && isEditable, - }), + { type, format, pattern, isEditable = true, dropdownOptions, value }, + ]) => { + const fieldValue = value || (properties?.[title] ?? ''); + const isEditableDecision = isDoneWithRevision || !decision?.status; + + return { + title, + value: fieldValue, + type, + format, + pattern, + isEditable: + isEditableDecision && + caseState.writeEnabled && + getIsEditable(isEditable, title), + dropdownOptions, + }; + }, ), }, }, @@ -134,12 +180,14 @@ export const useEntity = () => { data: contextDocuments?.[docIndex]?.pages?.map( ({ type, metadata, data }, pageIndex) => ({ - title: `${category} - ${docType}${ + title: `${convertSnakeCaseToTitleCase( + category, + )} - ${convertSnakeCaseToTitleCase(docType)}${ metadata?.side ? ` - ${metadata?.side}` : '' }`, imageUrl: type === 'pdf' - ? octetToFileType(results[docIndex][pageIndex], type) + ? octetToFileType(results[docIndex][pageIndex], `application/${type}`) : results[docIndex][pageIndex], fileType: type, }), @@ -149,28 +197,70 @@ export const useEntity = () => { ]; }, ) ?? []), - [ - { - id: 'entity-details', - type: 'details', - value: { - title: `${toStartCase(contextEntity?.type)} Information`, - data: Object.entries(contextEntity?.data ?? {})?.map(([title, value]) => ({ - title, - value, - type: 'string', - isEditable: false, - })), - }, - }, - ], + Object.keys(contextEntity?.data ?? {}).length === 0 + ? [] + : [ + { + type: 'heading', + value: `${toStartCase(contextEntity?.type)} Information`, + }, + { + id: 'entity-details', + type: 'details', + value: { + title: `${toStartCase(contextEntity?.type)} Information`, + data: [ + ...Object.entries( + omitPropsFromObject(contextEntity?.data, 'additionalInfo', 'address') ?? {}, + ), + ...Object.entries(contextEntity?.data?.additionalInfo ?? {}), + ]?.map(([title, value]) => ({ + title, + value, + type: 'string', + isEditable: false, + })), + }, + }, + ], + Object.keys(address ?? {})?.length === 0 + ? [] + : [ + { + id: 'map-container', + type: 'container', + value: [ + { + id: 'map-header', + type: 'heading', + value: `${toStartCase(contextEntity?.type)} Address`, + }, + { + type: 'details', + value: { + title: `${toStartCase(contextEntity?.type)} Address`, + data: Object.entries(address ?? {})?.map(([title, value]) => ({ + title, + value, + isEditable: false, + })), + }, + }, + { + type: 'map', + value: address, + }, + ], + }, + ], ] : []; return { selectedEntity, - components, + cells, tasks, + workflow, isLoading, }; }; diff --git a/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/utils.ts b/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/utils.ts new file mode 100644 index 0000000000..c4b43eb0df --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/utils.ts @@ -0,0 +1,85 @@ +import { TDropdownOption } from '../../components/EditableDetails/types'; +import { AnyArray } from '../../../../common/types'; +import { TDocument } from '@ballerine/common'; + +export const convertSnakeCaseToTitleCase = (input: string): string => + input + .split('_') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +const composeDataFormCell = ( + cellName: string, + categoryDropdownOptions: TDropdownOption[], + value: string, +) => { + return { + [cellName]: { + title: cellName, + type: 'string', + dropdownOptions: categoryDropdownOptions, + value: value, + }, + }; +}; +const uniqueArrayByKey = (array: AnyArray, key: PropertyKey) => { + return [...new Map(array.map(item => [item[key], item])).values()] as TDropdownOption[]; +}; + +const NONE_EDITABLE_FIELDS = ['category'] as const; +export const getIsEditable = (isEditable: boolean, title: string) => { + if (NONE_EDITABLE_FIELDS.includes(title)) return false; + + return isEditable; +}; + +export const composePickableCategoryType = ( + categoryValue: string, + typeValue: string, + documentsSchemas: TDocument[], +) => { + const documentCategoryDropdownOptions: Array<TDropdownOption> = []; + const documentTypesDropdownOptions: Array<TDropdownOption> = []; + documentsSchemas.forEach(document => { + const category = document.category; + if (category) { + documentCategoryDropdownOptions.push({ + value: category as string, + label: convertSnakeCaseToTitleCase(category), + }); + } + const type = document.type; + if (type) { + documentTypesDropdownOptions.push({ + dependantOn: 'category', + dependantValue: category as string, + value: type as string, + label: convertSnakeCaseToTitleCase(type), + }); + } + }); + + const categoryDropdownOptions = uniqueArrayByKey(documentCategoryDropdownOptions, 'value'); + const typeDropdownOptions = documentTypesDropdownOptions; + + return { + ...composeDataFormCell('category', categoryDropdownOptions, categoryValue), + ...composeDataFormCell('type', typeDropdownOptions, typeValue), + }; +}; +export const isExistingSchemaForDocument = documentsSchemas => { + return documentsSchemas && documentsSchemas.length > 0; +}; + +export const extractCountryCodeFromWorkflow = workflow => { + return workflow?.context?.documents?.find(document => { + return !!document?.issuer?.country; + })?.issuer?.country; +}; + +export const omitPropsFromObject = (obj, ...props) => { + const result = { ...obj }; + props.forEach(function (prop) { + delete result[prop]; + }); + return result; +}; diff --git a/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/utils/get-address-deep/get-address-deep.ts b/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/utils/get-address-deep/get-address-deep.ts new file mode 100644 index 0000000000..1ab0b7b986 --- /dev/null +++ b/apps/backoffice-v2/src/pages/Entity/hooks/useEntity/utils/get-address-deep/get-address-deep.ts @@ -0,0 +1,36 @@ +/** + * Traverses an object and returns the first address object found. + * @param value + */ +export const getAddressDeep = ( + value: unknown, +): + | { + country: string; + city: string; + street: string; + } + | undefined => { + if (!value || typeof value !== 'object') return; + + // Early return for Array type + if (Array.isArray(value)) { + for (let i = 0; i < value.length; i++) { + const result = getAddressDeep(value[i]); + + if (result) return result; + } + + return; + } + + for (const key of Object.keys(value)) { + if (key === 'address') return value[key]; + + const result = getAddressDeep(value[key]); + + if (result) return result; + } + + return; +}; diff --git a/apps/backoffice-v2/src/pages/Root/Root.error.tsx b/apps/backoffice-v2/src/pages/Root/Root.error.tsx index 0a095637c2..a3a48a3134 100644 --- a/apps/backoffice-v2/src/pages/Root/Root.error.tsx +++ b/apps/backoffice-v2/src/pages/Root/Root.error.tsx @@ -1,13 +1,9 @@ import { Button } from '../../common/components/atoms/Button/Button'; import { Link } from 'react-router-dom'; import { FunctionComponent } from 'react'; -import { AlertDescription } from '../../common/components/atoms/Alert/Alert.Description'; import { Providers } from '../../common/components/templates/Providers/Providers'; import { Toaster } from 'react-hot-toast'; -import { Layout } from '../../common/components/templates/Layout/Layout'; -import { Alert } from '../../common/components/atoms/Alert/Alert'; -import { AlertCircle } from 'lucide-react'; -import { AlertTitle } from '../../common/components/atoms/Alert/Alert.Title'; +import { ErrorAlert } from '../../common/components/atoms/ErrorAlert/ErrorAlert'; export const RootError: FunctionComponent = () => { return ( @@ -19,26 +15,22 @@ export const RootError: FunctionComponent = () => { duration: 1000 * 3, }} /> - <Layout> - <section - className={`col-span-full mx-auto mt-32 grid h-full w-full max-w-4xl grid-cols-2 flex-col`} - > - <div> - <Alert variant={`destructive`} className={`w-full max-w-lg`}> - <AlertCircle className="h-4 w-4" /> - <AlertTitle>Error</AlertTitle> - <AlertDescription>Something went wrong.</AlertDescription> - <div className={`flex justify-end`}> - <Button asChild className={`border-destructive`} variant={`outline`} size={`sm`}> - <Link to={`/en`} replace> - Try again - </Link> - </Button> - </div> - </Alert> - </div> - </section> - </Layout> + <section + className={`col-span-full mx-auto mt-32 grid h-full w-full max-w-4xl grid-cols-2 flex-col`} + > + <div> + <ErrorAlert> + Something went wrong. + <div className={`flex justify-end`}> + <Button asChild className={`border-destructive`} variant={`outline`} size={`sm`}> + <Link to={`/en`} replace> + Try again + </Link> + </Button> + </div> + </ErrorAlert> + </div> + </section> </Providers> ); }; diff --git a/apps/backoffice-v2/src/pages/Root/Root.loader.ts b/apps/backoffice-v2/src/pages/Root/Root.loader.ts index b487419236..31a470f1e4 100644 --- a/apps/backoffice-v2/src/pages/Root/Root.loader.ts +++ b/apps/backoffice-v2/src/pages/Root/Root.loader.ts @@ -5,5 +5,5 @@ export const rootLoader: LoaderFunction = ({ request }) => { if (url.pathname.startsWith('/en')) return null; - return redirect(`/en${url.pathname === '/' ? '' : url.pathname}`); + return redirect(`/en${url.pathname === '/' ? '' : url.pathname}${url.search}`); }; diff --git a/apps/backoffice-v2/src/pages/Root/Root.page.tsx b/apps/backoffice-v2/src/pages/Root/Root.page.tsx index 360cb3ff35..726ff32277 100644 --- a/apps/backoffice-v2/src/pages/Root/Root.page.tsx +++ b/apps/backoffice-v2/src/pages/Root/Root.page.tsx @@ -1,9 +1,15 @@ import { Outlet } from 'react-router-dom'; import { Providers } from '../../common/components/templates/Providers/Providers'; import { Toaster } from 'react-hot-toast'; -import { Layout } from '../../common/components/templates/Layout/Layout'; -import { FunctionComponent } from 'react'; -// import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; +import { FunctionComponent, lazy, Suspense } from 'react'; + +const ReactQueryDevtools = lazy(() => + process.env.NODE_ENV !== 'production' + ? import('@tanstack/react-query-devtools').then(module => ({ + default: module.ReactQueryDevtools, + })) + : Promise.resolve({ default: () => null }), +); export const Root: FunctionComponent = () => { return ( @@ -15,11 +21,10 @@ export const Root: FunctionComponent = () => { duration: 1000 * 3, }} /> - <Layout> - <Outlet /> - </Layout> - {/** Excluded in production by default */} - {/* <ReactQueryDevtools /> */} + <Outlet /> + <Suspense> + <ReactQueryDevtools /> + </Suspense> </Providers> ); }; diff --git a/apps/backoffice-v2/src/pages/SignIn/SignIn.page.tsx b/apps/backoffice-v2/src/pages/SignIn/SignIn.page.tsx index 56285a0150..24762afb36 100644 --- a/apps/backoffice-v2/src/pages/SignIn/SignIn.page.tsx +++ b/apps/backoffice-v2/src/pages/SignIn/SignIn.page.tsx @@ -6,10 +6,6 @@ import { Button } from '../../common/components/atoms/Button/Button'; import { Input } from '../../common/components/atoms/Input/Input'; import { Card } from '../../common/components/atoms/Card/Card'; import { BallerineLogo } from '../../common/components/atoms/icons'; -import { Alert } from '../../common/components/atoms/Alert/Alert'; -import { AlertCircle } from 'lucide-react'; -import { AlertDescription } from '../../common/components/atoms/Alert/Alert.Description'; -import { AlertTitle } from '../../common/components/atoms/Alert/Alert.Title'; import { useSignInMutation } from '../../domains/auth/hooks/mutations/useSignInMutation/useSignInMutation'; import { FunctionComponent, useCallback } from 'react'; import { useAuthContext } from '../../domains/auth/context/AuthProvider/hooks/useAuthContext/useAuthContext'; @@ -22,6 +18,9 @@ import { FormItem } from '../../common/components/organisms/Form/Form.Item'; import { FormLabel } from '../../common/components/organisms/Form/Form.Label'; import { FormControl } from '../../common/components/organisms/Form/Form.Control'; import { FormMessage } from '../../common/components/organisms/Form/Form.Message'; +import { env } from '../../common/env/env'; +import { ErrorAlert } from '../../common/components/atoms/ErrorAlert/ErrorAlert'; +import { FullScreenLoader } from '../../common/components/molecules/FullScreenLoader/FullScreenLoader'; export const SignIn: FunctionComponent = () => { const SignInSchema = z.object({ @@ -55,20 +54,23 @@ export const SignIn: FunctionComponent = () => { }, }); + // Handles a flash of content on sign in + if (isAuthenticated) return <FullScreenLoader />; + return ( <section className={`flex h-full flex-col items-center justify-center`}> <div className={`mb-16`}> - <BallerineLogo /> + {env.VITE_IMAGE_LOGO_URL ? ( + <img className={`w-40`} src={env.VITE_IMAGE_LOGO_URL} /> + ) : ( + <BallerineLogo /> + )} </div> <Card className={`w-full max-w-lg`}> <CardHeader className={`mb-2 text-center text-4xl font-bold`}>Sign In</CardHeader> <CardContent> {isErrorWithCode(error) && error?.code === 401 && ( - <Alert className={`mb-8`} variant={`destructive`}> - <AlertCircle className="h-4 w-4" /> - <AlertTitle>Error</AlertTitle> - <AlertDescription>Invalid credentials</AlertDescription> - </Alert> + <ErrorAlert>Invalid credentials</ErrorAlert> )} <Form {...signInForm}> <form onSubmit={signInForm.handleSubmit(onSubmit)} className="space-y-8"> diff --git a/apps/backoffice-v2/src/routes/Entities/pre-search-filters.ts b/apps/backoffice-v2/src/routes/Entities/pre-search-filters.ts deleted file mode 100644 index dcf5528e65..0000000000 --- a/apps/backoffice-v2/src/routes/Entities/pre-search-filters.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { CaseStatus, State } from '../../common/enums'; - -const sharedPreSearchFilters = { - sortDir: 'desc' as const, - pageSize: 10, - page: 1, - search: '', -} as const; - -export const preSearchFiltersByKind = { - businesses: { - sortBy: 'caseCreatedAt' as const, - entity: 'businesses' as const, - filter: { - assigneeId: [], - caseStatus: [CaseStatus.ACTIVE], - }, - ...sharedPreSearchFilters, - }, - individuals: { - sortBy: 'caseCreatedAt' as const, - entity: 'individuals' as const, - filter: { - approvalState: [], - assigneeId: [], - caseStatus: [CaseStatus.ACTIVE], - }, - ...sharedPreSearchFilters, - }, -} as const; diff --git a/apps/backoffice-v2/vite.config.ts b/apps/backoffice-v2/vite.config.ts index a67a09369c..f1c6b037f9 100644 --- a/apps/backoffice-v2/vite.config.ts +++ b/apps/backoffice-v2/vite.config.ts @@ -36,5 +36,8 @@ export default defineConfig(configEnv => { environment: 'jsdom', setupFiles: ['./src/tests-setup.ts'], }, + build: { + sourcemap: true, + }, }; }); diff --git a/apps/workflows-dashboard/.env.example b/apps/workflows-dashboard/.env.example new file mode 100644 index 0000000000..77f41d9853 --- /dev/null +++ b/apps/workflows-dashboard/.env.example @@ -0,0 +1,2 @@ +VITE_API_URL=http://localhost:3000/api/v1/ +VITE_API_KEY=secret diff --git a/apps/workflows-dashboard/.eslintrc.cjs b/apps/workflows-dashboard/.eslintrc.cjs new file mode 100644 index 0000000000..d16749b765 --- /dev/null +++ b/apps/workflows-dashboard/.eslintrc.cjs @@ -0,0 +1,14 @@ +module.exports = { + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': 'warn', + }, +}; diff --git a/apps/workflows-dashboard/.gitignore b/apps/workflows-dashboard/.gitignore new file mode 100644 index 0000000000..c6d2f98241 --- /dev/null +++ b/apps/workflows-dashboard/.gitignore @@ -0,0 +1,27 @@ +# 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? + +!.env.example +.env diff --git a/apps/workflows-dashboard/index.html b/apps/workflows-dashboard/index.html new file mode 100644 index 0000000000..9fb404d483 --- /dev/null +++ b/apps/workflows-dashboard/index.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <link rel="icon" type="image/png" sizes="32x32" href="/public/favicon-32x32.png" /> + <link rel="icon" type="image/png" sizes="16x16" href="/public/favicon-16x16.png" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Workflow Dashboard</title> + </head> + <body> + <div id="root"></div> + <script type="module" src="/src/main.tsx"></script> + </body> +</html> diff --git a/apps/workflows-dashboard/jest.config.ts b/apps/workflows-dashboard/jest.config.ts new file mode 100644 index 0000000000..cd73e848d3 --- /dev/null +++ b/apps/workflows-dashboard/jest.config.ts @@ -0,0 +1,16 @@ +import { resolve } from 'path'; + +/** @type {import('ts-jest').JestConfigWithTsJest} */ +export default { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['<rootDir>/**/*.test.ts'], + testPathIgnorePatterns: ['/node_modules/'], + coveragePathIgnorePatterns: ['node_modules', 'src/database', 'src/test', 'src/types'], + reporters: ['default'], + globals: { 'ts-jest': { diagnostics: false } }, + transform: {}, + moduleNameMapper: { + '^@app/(.*)$': `${resolve(__dirname, './src/$1')}`, + }, +}; diff --git a/apps/workflows-dashboard/package.json b/apps/workflows-dashboard/package.json new file mode 100644 index 0000000000..cc0322646e --- /dev/null +++ b/apps/workflows-dashboard/package.json @@ -0,0 +1,78 @@ +{ + "name": "@ballerine/workflows-dashboard", + "private": false, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "test": "NODE_ENV=test jest" + }, + "dependencies": { + "@lukemorales/query-key-factory": "^1.0.3", + "@radix-ui/react-avatar": "^1.0.3", + "@radix-ui/react-dialog": "^1.0.2", + "@radix-ui/react-dropdown-menu": "^2.0.5", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.0.1", + "@radix-ui/react-popover": "^1.0.6", + "@radix-ui/react-select": "^1.2.1", + "@radix-ui/react-separator": "^1.0.2", + "@radix-ui/react-slot": "^1.0.1", + "@radix-ui/react-tabs": "^1.0.4", + "@tailwindcss/line-clamp": "^0.4.4", + "@tanstack/react-query": "^4.28.0", + "@tanstack/react-table": "^8.9.2", + "@xstate/inspect": "^0.7.1", + "@xstate/react": "^3.2.2", + "axios": "^1.4.0", + "class-variance-authority": "^0.6.0", + "classnames": "^2.3.2", + "clsx": "^1.2.1", + "cmdk": "^0.2.0", + "dayjs": "^1.11.6", + "install": "^0.13.0", + "lodash": "^4.17.21", + "lucide-react": "^0.144.0", + "react": "^18.2.0", + "react-custom-scrollbars": "^4.2.1", + "react-dom": "^18.2.0", + "react-hook-form": "^7.43.9", + "react-json-view": "^1.21.3", + "react-router-dom": "^6.11.2", + "recharts": "^2.7.2", + "tailwind-merge": "^1.13.2", + "tailwindcss-animate": "^1.0.5", + "use-query-params": "^2.2.1", + "vite-plugin-terminal": "^1.1.0", + "xstate": "^4.38.0", + "zod": "^3.21.4" + }, + "devDependencies": { + "@types/axios": "^0.14.0", + "@types/classnames": "^2.3.1", + "@types/jest": "^26.0.19", + "@types/lodash": "^4.14.191", + "@types/moment": "^2.13.0", + "@types/node": "^20.3.1", + "@types/react": "^18.0.37", + "@types/react-custom-scrollbars": "^4.0.10", + "@types/react-dom": "^18.0.11", + "@typescript-eslint/eslint-plugin": "^5.59.0", + "@typescript-eslint/parser": "^5.59.0", + "@vitejs/plugin-react": "^4.0.0", + "autoprefixer": "^10.4.14", + "eslint": "^8.38.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.3.4", + "jest": "^29.5.0", + "postcss": "^8.4.24", + "tailwindcss": "^3.2.7", + "ts-jest": "^29.1.0", + "typescript": "^5.0.2", + "vite": "^4.3.9", + "vite-plugin-checker": "^0.6.1" + } +} diff --git a/apps/workflows-dashboard/postcss.config.cjs b/apps/workflows-dashboard/postcss.config.cjs new file mode 100644 index 0000000000..12a703d900 --- /dev/null +++ b/apps/workflows-dashboard/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/apps/workflows-dashboard/public/avatar.png b/apps/workflows-dashboard/public/avatar.png new file mode 100644 index 0000000000..3f5cc9deac Binary files /dev/null and b/apps/workflows-dashboard/public/avatar.png differ diff --git a/apps/workflows-dashboard/public/favicon-16x16.png b/apps/workflows-dashboard/public/favicon-16x16.png new file mode 100644 index 0000000000..a2fb2708f5 Binary files /dev/null and b/apps/workflows-dashboard/public/favicon-16x16.png differ diff --git a/apps/workflows-dashboard/public/favicon-32x32.png b/apps/workflows-dashboard/public/favicon-32x32.png new file mode 100644 index 0000000000..5664f07edd Binary files /dev/null and b/apps/workflows-dashboard/public/favicon-32x32.png differ diff --git a/apps/workflows-dashboard/src/App.tsx b/apps/workflows-dashboard/src/App.tsx new file mode 100644 index 0000000000..3329aff8af --- /dev/null +++ b/apps/workflows-dashboard/src/App.tsx @@ -0,0 +1,15 @@ +import { queryClient } from '@app/lib/react-query/query-client'; +import { QueryClientProvider } from '@tanstack/react-query'; +import { QueryParamProvider } from 'use-query-params'; +import { ReactRouter6Adapter } from 'use-query-params/adapters/react-router-6'; +import { Outlet } from 'react-router-dom'; + +export function App() { + return ( + <QueryParamProvider adapter={ReactRouter6Adapter}> + <QueryClientProvider client={queryClient}> + <Outlet /> + </QueryClientProvider> + </QueryParamProvider> + ); +} diff --git a/apps/workflows-dashboard/src/common/env/env.ts b/apps/workflows-dashboard/src/common/env/env.ts new file mode 100644 index 0000000000..aee1e5c67b --- /dev/null +++ b/apps/workflows-dashboard/src/common/env/env.ts @@ -0,0 +1,25 @@ +import type { ZodFormattedError } from 'zod'; +import { EnvSchema } from './schema'; +import { terminal } from 'virtual:terminal'; + +export const formatErrors = (errors: ZodFormattedError<Map<string, string>, string>) => { + console.info({ + errors, + }); + + return Object.entries(errors) + .map(([name, value]) => { + if (value && '_errors' in value) return `${name}: ${value._errors.join(', ')}\n`; + }) + .filter(Boolean); +}; + +const _env = EnvSchema.safeParse(import.meta.env); + +// TypeScript complains with !env.success +if (_env.success === false) { + terminal.error('β Invalid environment variables:\n', ...formatErrors(_env.error.format())); + throw new Error('Invalid environment variables'); +} + +export const env = _env.data; diff --git a/apps/workflows-dashboard/src/common/env/schema.ts b/apps/workflows-dashboard/src/common/env/schema.ts new file mode 100644 index 0000000000..e3af92172b --- /dev/null +++ b/apps/workflows-dashboard/src/common/env/schema.ts @@ -0,0 +1,7 @@ +import { z } from 'zod'; + +export const EnvSchema = z.object({ + MODE: z.enum(['development', 'production', 'test']), + VITE_API_URL: z.string().url().default('https://api-dev.ballerine.io/v2'), + VITE_IMAGE_LOGO_URL: z.string().optional(), +}); diff --git a/apps/workflows-dashboard/src/common/errors/http-error.ts b/apps/workflows-dashboard/src/common/errors/http-error.ts new file mode 100644 index 0000000000..20e4afdb9e --- /dev/null +++ b/apps/workflows-dashboard/src/common/errors/http-error.ts @@ -0,0 +1,5 @@ +export class HttpError extends Error { + constructor(public code: number, public message: string) { + super(message); + } +} diff --git a/apps/workflows-dashboard/src/common/hocs/withSessionProtected/index.ts b/apps/workflows-dashboard/src/common/hocs/withSessionProtected/index.ts new file mode 100644 index 0000000000..bb66280e46 --- /dev/null +++ b/apps/workflows-dashboard/src/common/hocs/withSessionProtected/index.ts @@ -0,0 +1 @@ +export * from './withSessionProtected'; diff --git a/apps/workflows-dashboard/src/common/hocs/withSessionProtected/utils/clear-referer-url.ts b/apps/workflows-dashboard/src/common/hocs/withSessionProtected/utils/clear-referer-url.ts new file mode 100644 index 0000000000..6825c6e25a --- /dev/null +++ b/apps/workflows-dashboard/src/common/hocs/withSessionProtected/utils/clear-referer-url.ts @@ -0,0 +1,3 @@ +export function clearRefererUrl(): void { + localStorage.removeItem('_ref'); +} diff --git a/apps/workflows-dashboard/src/common/hocs/withSessionProtected/utils/get-referer-url.ts b/apps/workflows-dashboard/src/common/hocs/withSessionProtected/utils/get-referer-url.ts new file mode 100644 index 0000000000..453ab58f2b --- /dev/null +++ b/apps/workflows-dashboard/src/common/hocs/withSessionProtected/utils/get-referer-url.ts @@ -0,0 +1,3 @@ +export function getRefererUrl(): string | null { + return localStorage.getItem('_ref') || null; +} diff --git a/apps/workflows-dashboard/src/common/hocs/withSessionProtected/utils/set-referer-url.ts b/apps/workflows-dashboard/src/common/hocs/withSessionProtected/utils/set-referer-url.ts new file mode 100644 index 0000000000..a211c055af --- /dev/null +++ b/apps/workflows-dashboard/src/common/hocs/withSessionProtected/utils/set-referer-url.ts @@ -0,0 +1,3 @@ +export function setRefererUrl(url: string) { + localStorage.setItem('_ref', url); +} diff --git a/apps/workflows-dashboard/src/common/hocs/withSessionProtected/withSessionProtected.tsx b/apps/workflows-dashboard/src/common/hocs/withSessionProtected/withSessionProtected.tsx new file mode 100644 index 0000000000..44447418f1 --- /dev/null +++ b/apps/workflows-dashboard/src/common/hocs/withSessionProtected/withSessionProtected.tsx @@ -0,0 +1,35 @@ +import { setRefererUrl } from '@app/common/hocs/withSessionProtected/utils/set-referer-url'; +import { useSession } from '@app/common/hooks/useSession'; +import { LoadingSpinner } from '@app/components/atoms/LoadingSpinner'; +import { useLayoutEffect } from 'react'; +import { Navigate } from 'react-router-dom'; + +export function withSessionProtected<TComponentProps extends object>( + Component: React.ComponentType<TComponentProps>, + signinPath = '/auth/signin', +): React.ComponentType<TComponentProps> { + function Wrapper(props: TComponentProps) { + const { isAuthenticated, isLoading } = useSession(); + + useLayoutEffect(() => { + if (!isLoading && !isAuthenticated && location.pathname !== signinPath) { + setRefererUrl(location.pathname); + } + }, [isAuthenticated, isLoading]); + + if (isLoading) { + return ( + <div className="flex h-full flex-col items-center justify-center"> + <LoadingSpinner /> + </div> + ); + } + + if (!isAuthenticated) return <Navigate to={signinPath} />; + + return <Component {...props} />; + } + Wrapper.displayName = `withSessionProtected(${Component.displayName})`; + + return Wrapper; +} diff --git a/apps/workflows-dashboard/src/common/hooks/useLogoutMutation/index.ts b/apps/workflows-dashboard/src/common/hooks/useLogoutMutation/index.ts new file mode 100644 index 0000000000..baf8cefcbc --- /dev/null +++ b/apps/workflows-dashboard/src/common/hooks/useLogoutMutation/index.ts @@ -0,0 +1 @@ +export * from './useLogoutMutation'; diff --git a/apps/workflows-dashboard/src/common/hooks/useLogoutMutation/useLogoutMutation.ts b/apps/workflows-dashboard/src/common/hooks/useLogoutMutation/useLogoutMutation.ts new file mode 100644 index 0000000000..3b483bf159 --- /dev/null +++ b/apps/workflows-dashboard/src/common/hooks/useLogoutMutation/useLogoutMutation.ts @@ -0,0 +1,15 @@ +import { fetchLogout } from '@app/domains/auth/api/logout'; +import { sessionKeys } from '@app/domains/auth/api/session/query-keys'; +import { queryClient } from '@app/lib/react-query/query-client'; +import { useMutation } from '@tanstack/react-query'; + +export function useLogoutMutation() { + const { mutate } = useMutation({ + mutationFn: fetchLogout, + onSuccess: () => queryClient.invalidateQueries(sessionKeys.details()), + }); + + return { + logout: mutate, + }; +} diff --git a/apps/workflows-dashboard/src/common/hooks/useSession/index.ts b/apps/workflows-dashboard/src/common/hooks/useSession/index.ts new file mode 100644 index 0000000000..c47a6987dd --- /dev/null +++ b/apps/workflows-dashboard/src/common/hooks/useSession/index.ts @@ -0,0 +1 @@ +export * from './useSession'; diff --git a/apps/workflows-dashboard/src/common/hooks/useSession/useSession.ts b/apps/workflows-dashboard/src/common/hooks/useSession/useSession.ts new file mode 100644 index 0000000000..1dc10be56c --- /dev/null +++ b/apps/workflows-dashboard/src/common/hooks/useSession/useSession.ts @@ -0,0 +1,25 @@ +import { sessionKeys } from '@app/domains/auth/api/session/query-keys'; +import { useQuery } from '@tanstack/react-query'; + +const ONE_MINUTE_IN_MS = 1000 * 60; + +export function useSession() { + const { + data: user, + isLoading, + refetch, + } = useQuery({ + ...sessionKeys.details(), + refetchInterval: ONE_MINUTE_IN_MS, + retry: retryCount => retryCount < 3, + }); + + const isAuthenticated = Boolean(!isLoading && user); + + return { + isLoading, + isAuthenticated, + user, + refresh: refetch, + }; +} diff --git a/apps/workflows-dashboard/src/common/hooks/useSorting/helpers/get-sorting-data-from-query.ts b/apps/workflows-dashboard/src/common/hooks/useSorting/helpers/get-sorting-data-from-query.ts new file mode 100644 index 0000000000..1b66ba8a56 --- /dev/null +++ b/apps/workflows-dashboard/src/common/hooks/useSorting/helpers/get-sorting-data-from-query.ts @@ -0,0 +1,30 @@ +import { z } from 'zod'; + +export interface SortingData { + key: string; + keyWithPrefix: string; + value: 'asc' | 'desc'; +} + +export function getSortingDataFromQuery(string: string, regex: RegExp): SortingData | null { + const sortingQuerySchema = z + .string() + .regex(regex) + .transform(sortingString => { + regex.lastIndex = 0; + const parseResult = regex.exec(sortingString); + if (!parseResult) return null; + const parseValues = [parseResult[1], parseResult[2], parseResult[3]]; + + const [keyWithPrefix, key, value] = parseValues; + + return { + keyWithPrefix, + key, + value: value as SortingData['value'], + }; + }) + .catch(null); + + return sortingQuerySchema.parse(string); +} diff --git a/apps/workflows-dashboard/src/common/hooks/useSorting/index.ts b/apps/workflows-dashboard/src/common/hooks/useSorting/index.ts new file mode 100644 index 0000000000..1ce5476513 --- /dev/null +++ b/apps/workflows-dashboard/src/common/hooks/useSorting/index.ts @@ -0,0 +1 @@ +export * from './useSorting'; diff --git a/apps/workflows-dashboard/src/common/hooks/useSorting/useSorting.ts b/apps/workflows-dashboard/src/common/hooks/useSorting/useSorting.ts new file mode 100644 index 0000000000..614cd4c914 --- /dev/null +++ b/apps/workflows-dashboard/src/common/hooks/useSorting/useSorting.ts @@ -0,0 +1,35 @@ +import { + getSortingDataFromQuery, + SortingData, +} from '@app/common/hooks/useSorting/helpers/get-sorting-data-from-query'; +import { buildSortingRegex } from '@app/common/hooks/useSorting/utils/build-sorting-regex'; +import { SortingParams } from '@app/common/types/sorting-params.types'; +import { useCallback, useMemo } from 'react'; +import { useSearchParams } from 'react-router-dom'; + +export function useSorting(prefix = 'order_by') { + const [searchParams, setSearchParams] = useSearchParams(); + + const parseResult: SortingData | null = useMemo(() => { + return getSortingDataFromQuery(searchParams.toString(), buildSortingRegex(prefix)); + }, [prefix, searchParams]); + + const setSorting = useCallback( + (key: string, value: 'asc' | 'desc') => { + if (parseResult) { + searchParams.delete(parseResult.keyWithPrefix); + } + + searchParams.set(`${prefix}_${key}`, value); + + setSearchParams(searchParams); + }, + [prefix, searchParams, parseResult, setSearchParams], + ); + + return { + sortingKey: parseResult?.key as SortingParams['orderBy'], + sortingDirection: parseResult?.value as SortingParams['orderDirection'], + setSorting, + }; +} diff --git a/apps/workflows-dashboard/src/common/hooks/useSorting/utils/build-sorting-regex.ts b/apps/workflows-dashboard/src/common/hooks/useSorting/utils/build-sorting-regex.ts new file mode 100644 index 0000000000..c9a1196e1a --- /dev/null +++ b/apps/workflows-dashboard/src/common/hooks/useSorting/utils/build-sorting-regex.ts @@ -0,0 +1,5 @@ +export function buildSortingRegex(prefix: string): RegExp { + const regexp = new RegExp(`(${prefix}_(.+))=(asc|desc)`, 'g'); + + return regexp; +} diff --git a/apps/workflows-dashboard/src/common/types/sorting-params.types.ts b/apps/workflows-dashboard/src/common/types/sorting-params.types.ts new file mode 100644 index 0000000000..18ef1fbbfb --- /dev/null +++ b/apps/workflows-dashboard/src/common/types/sorting-params.types.ts @@ -0,0 +1,4 @@ +export interface SortingParams { + orderBy?: string; + orderDirection?: 'asc' | 'desc'; +} diff --git a/apps/workflows-dashboard/src/common/utils/convert-ms-duration-to-time/convert-ms-duration-to-time.ts b/apps/workflows-dashboard/src/common/utils/convert-ms-duration-to-time/convert-ms-duration-to-time.ts new file mode 100644 index 0000000000..6239e4d454 --- /dev/null +++ b/apps/workflows-dashboard/src/common/utils/convert-ms-duration-to-time/convert-ms-duration-to-time.ts @@ -0,0 +1,13 @@ +export const convertMsDurationToTime = (duration: number) => { + const seconds = Math.floor((duration / 1000) % 60); + const minutes = Math.floor((duration / (1000 * 60)) % 60); + const hours = Math.floor((duration / (1000 * 60 * 60)) % 24); + const days = Math.floor(duration / (1000 * 60 * 60 * 24)); + + return { + days, + hours, + minutes, + seconds, + }; +}; diff --git a/apps/workflows-dashboard/src/common/utils/ctw/ctw.ts b/apps/workflows-dashboard/src/common/utils/ctw/ctw.ts new file mode 100644 index 0000000000..f344e541d2 --- /dev/null +++ b/apps/workflows-dashboard/src/common/utils/ctw/ctw.ts @@ -0,0 +1,4 @@ +import { ClassValue, clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export const ctw = (...classNames: Array<ClassValue>) => twMerge(clsx(classNames)); diff --git a/apps/workflows-dashboard/src/components/atoms/Alert/Alert.Description.tsx b/apps/workflows-dashboard/src/components/atoms/Alert/Alert.Description.tsx new file mode 100644 index 0000000000..05f5ce3ac1 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Alert/Alert.Description.tsx @@ -0,0 +1,10 @@ +import { ctw } from '@app/common/utils/ctw/ctw'; +import { forwardRef, HTMLAttributes } from 'react'; + +export const AlertDescription = forwardRef< + HTMLParagraphElement, + HTMLAttributes<HTMLParagraphElement> +>(({ className, ...props }, ref) => ( + <div ref={ref} className={ctw('text-sm [&_p]:leading-relaxed', className)} {...props} /> +)); +AlertDescription.displayName = 'AlertDescription'; diff --git a/apps/workflows-dashboard/src/components/atoms/Alert/Alert.Title.tsx b/apps/workflows-dashboard/src/components/atoms/Alert/Alert.Title.tsx new file mode 100644 index 0000000000..e32c592427 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Alert/Alert.Title.tsx @@ -0,0 +1,13 @@ +import { ctw } from '@app/common/utils/ctw/ctw'; +import { forwardRef, HTMLAttributes } from 'react'; + +export const AlertTitle = forwardRef<HTMLParagraphElement, HTMLAttributes<HTMLHeadingElement>>( + ({ className, ...props }, ref) => ( + <h5 + ref={ref} + className={ctw('mb-1 font-medium leading-none tracking-tight', className)} + {...props} + /> + ), +); +AlertTitle.displayName = 'AlertTitle'; diff --git a/apps/workflows-dashboard/src/components/atoms/Alert/Alert.tsx b/apps/workflows-dashboard/src/components/atoms/Alert/Alert.tsx new file mode 100644 index 0000000000..80f62dc378 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Alert/Alert.tsx @@ -0,0 +1,27 @@ +import { forwardRef, HTMLAttributes } from 'react'; +import { cva, VariantProps } from 'class-variance-authority'; +import { ctw } from '@app/common/utils/ctw/ctw'; + +const alertVariants = cva( + 'relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11', + { + variants: { + variant: { + default: 'bg-background text-foreground', + destructive: + 'text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +); + +export const Alert = forwardRef< + HTMLDivElement, + HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants> +>(({ className, variant, ...props }, ref) => ( + <div ref={ref} role="alert" className={ctw(alertVariants({ variant }), className)} {...props} /> +)); +Alert.displayName = 'Alert'; diff --git a/apps/workflows-dashboard/src/components/atoms/Avatar/Avatar.tsx b/apps/workflows-dashboard/src/components/atoms/Avatar/Avatar.tsx new file mode 100644 index 0000000000..ccfccd028c --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Avatar/Avatar.tsx @@ -0,0 +1,45 @@ +import * as React from 'react'; +import * as AvatarPrimitive from '@radix-ui/react-avatar'; + +import { cn } from '@app/lib/utils'; + +const Avatar = React.forwardRef< + React.ElementRef<typeof AvatarPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> +>(({ className, ...props }, ref) => ( + <AvatarPrimitive.Root + ref={ref} + className={cn('relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full', className)} + {...props} + /> +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; + +const AvatarImage = React.forwardRef< + React.ElementRef<typeof AvatarPrimitive.Image>, + React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image> +>(({ className, ...props }, ref) => ( + <AvatarPrimitive.Image + ref={ref} + className={cn('aspect-square h-full w-full', className)} + {...props} + /> +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; + +const AvatarFallback = React.forwardRef< + React.ElementRef<typeof AvatarPrimitive.Fallback>, + React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> +>(({ className, ...props }, ref) => ( + <AvatarPrimitive.Fallback + ref={ref} + className={cn( + 'bg-muted flex h-full w-full items-center justify-center rounded-full', + className, + )} + {...props} + /> +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; + +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/apps/workflows-dashboard/src/components/atoms/Avatar/index.ts b/apps/workflows-dashboard/src/components/atoms/Avatar/index.ts new file mode 100644 index 0000000000..27700fe3f3 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Avatar/index.ts @@ -0,0 +1 @@ +export * from './Avatar'; diff --git a/apps/workflows-dashboard/src/components/atoms/Badge/Badge.tsx b/apps/workflows-dashboard/src/components/atoms/Badge/Badge.tsx new file mode 100644 index 0000000000..a8f02ba9ca --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Badge/Badge.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { cva, type VariantProps } from 'class-variance-authority'; + +import { cn } from '@app/lib/utils'; + +const badgeVariants = cva( + 'inline-flex items-center border rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', + { + variants: { + variant: { + default: 'bg-primary hover:bg-primary/80 border-transparent text-primary-foreground', + secondary: + 'bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground', + destructive: + 'bg-destructive hover:bg-destructive/80 border-transparent text-destructive-foreground', + outline: 'text-foreground', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +); + +export interface BadgeProps + extends React.HTMLAttributes<HTMLDivElement>, + VariantProps<typeof badgeVariants> {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return <div className={cn(badgeVariants({ variant }), className)} {...props} />; +} + +export { Badge, badgeVariants }; diff --git a/apps/workflows-dashboard/src/components/atoms/Badge/index.ts b/apps/workflows-dashboard/src/components/atoms/Badge/index.ts new file mode 100644 index 0000000000..9c8edca28a --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Badge/index.ts @@ -0,0 +1 @@ +export * from './Badge'; diff --git a/apps/workflows-dashboard/src/components/atoms/Button/Button.tsx b/apps/workflows-dashboard/src/components/atoms/Button/Button.tsx new file mode 100644 index 0000000000..02ed3c7f6c --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Button/Button.tsx @@ -0,0 +1,48 @@ +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { cva, type VariantProps } from 'class-variance-authority'; + +import { cn } from '@app/lib/utils'; + +const buttonVariants = cva( + 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background', + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground hover:bg-primary/90', + destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90', + outline: 'border border-input hover:bg-accent hover:text-accent-foreground', + secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'underline-offset-4 hover:underline text-primary', + }, + size: { + default: 'h-10 py-2 px-4', + sm: 'h-9 px-3 rounded-md', + lg: 'h-11 px-8 rounded-md', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes<HTMLButtonElement>, + VariantProps<typeof buttonVariants> { + asChild?: boolean; +} + +const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : ('button' as any); + return ( + <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} /> + ); + }, +); +Button.displayName = 'Button'; + +export { Button, buttonVariants }; diff --git a/apps/workflows-dashboard/src/components/atoms/Button/index.ts b/apps/workflows-dashboard/src/components/atoms/Button/index.ts new file mode 100644 index 0000000000..8b166a86e4 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Button/index.ts @@ -0,0 +1 @@ +export * from './Button'; diff --git a/apps/workflows-dashboard/src/components/atoms/Card/Card.tsx b/apps/workflows-dashboard/src/components/atoms/Card/Card.tsx new file mode 100644 index 0000000000..5b4b9afab8 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Card/Card.tsx @@ -0,0 +1,56 @@ +import * as React from 'react'; + +import { cn } from '@app/lib/utils'; + +const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( + ({ className, ...props }, ref) => ( + <div + ref={ref} + className={cn('bg-card text-card-foreground rounded-lg border shadow-sm', className)} + {...props} + /> + ), +); +Card.displayName = 'Card'; + +const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( + ({ className, ...props }, ref) => ( + <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} /> + ), +); +CardHeader.displayName = 'CardHeader'; + +const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>( + ({ className, ...props }, ref) => ( + <h3 + ref={ref} + className={cn('text-lg font-semibold leading-none tracking-tight', className)} + {...props} + /> + ), +); +CardTitle.displayName = 'CardTitle'; + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes<HTMLParagraphElement> +>(({ className, ...props }, ref) => ( + <p ref={ref} className={cn('text-muted-foreground text-sm', className)} {...props} /> +)); +CardDescription.displayName = 'CardDescription'; + +const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( + ({ className, ...props }, ref) => ( + <div ref={ref} className={cn('p-6 pt-0', className)} {...props} /> + ), +); +CardContent.displayName = 'CardContent'; + +const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( + ({ className, ...props }, ref) => ( + <div ref={ref} className={cn(' flex items-center p-6 pt-0', className)} {...props} /> + ), +); +CardFooter.displayName = 'CardFooter'; + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }; diff --git a/apps/workflows-dashboard/src/components/atoms/Card/index.ts b/apps/workflows-dashboard/src/components/atoms/Card/index.ts new file mode 100644 index 0000000000..ca0b060473 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Card/index.ts @@ -0,0 +1 @@ +export * from './Card'; diff --git a/apps/workflows-dashboard/src/components/atoms/Command/Command.tsx b/apps/workflows-dashboard/src/components/atoms/Command/Command.tsx new file mode 100644 index 0000000000..e2bf3f966f --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Command/Command.tsx @@ -0,0 +1,144 @@ +'use client'; + +import * as React from 'react'; +import { Command as CommandPrimitive } from 'cmdk'; +import { Search } from 'lucide-react'; + +import { cn } from '@app/lib/utils'; +import { Dialog, DialogContent, DialogProps } from '@app/components/atoms/Dialog'; + +const Command = React.forwardRef< + React.ElementRef<typeof CommandPrimitive>, + React.ComponentPropsWithoutRef<typeof CommandPrimitive> +>(({ className, ...props }, ref) => ( + <CommandPrimitive + ref={ref} + className={cn( + 'bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md', + className, + )} + {...props} + /> +)); +Command.displayName = CommandPrimitive.displayName; + +interface CommandDialogProps extends DialogProps {} + +const CommandDialog = ({ children, ...props }: CommandDialogProps) => { + return ( + <Dialog {...props}> + <DialogContent className="overflow-hidden p-0 shadow-2xl"> + <Command className="[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> + {children} + </Command> + </DialogContent> + </Dialog> + ); +}; + +const CommandInput = React.forwardRef< + React.ElementRef<typeof CommandPrimitive.Input>, + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> +>(({ className, ...props }, ref) => ( + <div className="flex items-center border-b px-3" cmdk-input-wrapper=""> + <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" /> + <CommandPrimitive.Input + ref={ref} + className={cn( + 'placeholder:text-muted-foreground flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50', + className, + )} + {...props} + /> + </div> +)); + +CommandInput.displayName = CommandPrimitive.Input.displayName; + +const CommandList = React.forwardRef< + React.ElementRef<typeof CommandPrimitive.List>, + React.ComponentPropsWithoutRef<typeof CommandPrimitive.List> +>(({ className, ...props }, ref) => ( + <CommandPrimitive.List + ref={ref} + className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)} + {...props} + /> +)); + +CommandList.displayName = CommandPrimitive.List.displayName; + +const CommandEmpty = React.forwardRef< + React.ElementRef<typeof CommandPrimitive.Empty>, + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty> +>((props, ref) => ( + <CommandPrimitive.Empty ref={ref} className="py-6 text-center text-sm" {...props} /> +)); + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName; + +const CommandGroup = React.forwardRef< + React.ElementRef<typeof CommandPrimitive.Group>, + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group> +>(({ className, ...props }, ref) => ( + <CommandPrimitive.Group + ref={ref} + className={cn( + 'text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium', + className, + )} + {...props} + /> +)); + +CommandGroup.displayName = CommandPrimitive.Group.displayName; + +const CommandSeparator = React.forwardRef< + React.ElementRef<typeof CommandPrimitive.Separator>, + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator> +>(({ className, ...props }, ref) => ( + <CommandPrimitive.Separator + ref={ref} + className={cn('bg-border -mx-1 h-px', className)} + {...props} + /> +)); +CommandSeparator.displayName = CommandPrimitive.Separator.displayName; + +const CommandItem = React.forwardRef< + React.ElementRef<typeof CommandPrimitive.Item>, + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item> +>(({ className, ...props }, ref) => ( + <CommandPrimitive.Item + ref={ref} + className={cn( + 'aria-selected:bg-accent aria-selected:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50', + className, + )} + {...props} + /> +)); + +CommandItem.displayName = CommandPrimitive.Item.displayName; + +const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => { + return ( + <span + className={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)} + {...props} + /> + ); +}; +CommandShortcut.displayName = 'CommandShortcut'; + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +}; diff --git a/apps/workflows-dashboard/src/components/atoms/Command/index.ts b/apps/workflows-dashboard/src/components/atoms/Command/index.ts new file mode 100644 index 0000000000..a2eecb6bda --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Command/index.ts @@ -0,0 +1 @@ +export * from './Command'; diff --git a/apps/workflows-dashboard/src/components/atoms/Dialog/Dialog.tsx b/apps/workflows-dashboard/src/components/atoms/Dialog/Dialog.tsx new file mode 100644 index 0000000000..22540c85de --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Dialog/Dialog.tsx @@ -0,0 +1,108 @@ +'use client'; + +import * as React from 'react'; +import * as DialogPrimitive from '@radix-ui/react-dialog'; +import { X } from 'lucide-react'; + +import { cn } from '@app/lib/utils'; + +const Dialog = DialogPrimitive.Root; + +const DialogTrigger = DialogPrimitive.Trigger; + +const DialogPortal = ({ className, children, ...props }: DialogPrimitive.DialogPortalProps) => ( + <DialogPrimitive.Portal className={cn(className)} {...props}> + <div className="fixed inset-0 z-50 flex items-start justify-center sm:items-center"> + {children} + </div> + </DialogPrimitive.Portal> +); +DialogPortal.displayName = DialogPrimitive.Portal.displayName; + +const DialogOverlay = React.forwardRef< + React.ElementRef<typeof DialogPrimitive.Overlay>, + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> +>(({ className, ...props }, ref) => ( + <DialogPrimitive.Overlay + ref={ref} + className={cn( + 'bg-background/80 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in fixed inset-0 z-50 backdrop-blur-sm transition-all duration-100', + className, + )} + {...props} + /> +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; + +const DialogContent = React.forwardRef< + React.ElementRef<typeof DialogPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> +>(({ className, children, ...props }, ref) => ( + <DialogPortal> + <DialogOverlay /> + <DialogPrimitive.Content + ref={ref} + className={cn( + 'bg-background animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0 fixed z-50 grid w-full gap-4 rounded-b-lg border p-6 shadow-lg sm:max-w-lg sm:rounded-lg', + className, + )} + {...props} + > + {children} + <DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none"> + <X className="h-4 w-4" /> + <span className="sr-only">Close</span> + </DialogPrimitive.Close> + </DialogPrimitive.Content> + </DialogPortal> +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; + +const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => ( + <div className={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)} {...props} /> +); +DialogHeader.displayName = 'DialogHeader'; + +const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => ( + <div + className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)} + {...props} + /> +); +DialogFooter.displayName = 'DialogFooter'; + +const DialogTitle = React.forwardRef< + React.ElementRef<typeof DialogPrimitive.Title>, + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> +>(({ className, ...props }, ref) => ( + <DialogPrimitive.Title + ref={ref} + className={cn('text-lg font-semibold leading-none tracking-tight', className)} + {...props} + /> +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; + +const DialogDescription = React.forwardRef< + React.ElementRef<typeof DialogPrimitive.Description>, + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> +>(({ className, ...props }, ref) => ( + <DialogPrimitive.Description + ref={ref} + className={cn('text-muted-foreground text-sm', className)} + {...props} + /> +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; + +export { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +}; + +export type DialogProps = DialogPrimitive.DialogProps; diff --git a/apps/workflows-dashboard/src/components/atoms/Dialog/index.ts b/apps/workflows-dashboard/src/components/atoms/Dialog/index.ts new file mode 100644 index 0000000000..a5d3159726 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Dialog/index.ts @@ -0,0 +1 @@ +export * from './Dialog'; diff --git a/apps/workflows-dashboard/src/components/atoms/Dropdown/Dropdown.tsx b/apps/workflows-dashboard/src/components/atoms/Dropdown/Dropdown.tsx new file mode 100644 index 0000000000..7ada347e5d --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Dropdown/Dropdown.tsx @@ -0,0 +1,185 @@ +import * as React from 'react'; +import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; +import { Check, ChevronRight, Circle } from 'lucide-react'; + +import { cn } from '@app/lib/utils'; + +const DropdownMenu = DropdownMenuPrimitive.Root; + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; + +const DropdownMenuGroup = DropdownMenuPrimitive.Group; + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal; + +const DropdownMenuSub = DropdownMenuPrimitive.Sub; + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { + inset?: boolean; + } +>(({ className, inset, children, ...props }, ref) => ( + <DropdownMenuPrimitive.SubTrigger + ref={ref} + className={cn( + 'focus:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none', + inset && 'pl-8', + className, + )} + {...props} + > + {children} + <ChevronRight className="ml-auto h-4 w-4" /> + </DropdownMenuPrimitive.SubTrigger> +)); +DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName; + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.SubContent>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> +>(({ className, ...props }, ref) => ( + <DropdownMenuPrimitive.SubContent + ref={ref} + className={cn( + 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg', + className, + )} + {...props} + /> +)); +DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName; + +const DropdownMenuContent = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> +>(({ className, sideOffset = 4, ...props }, ref) => ( + <DropdownMenuPrimitive.Portal> + <DropdownMenuPrimitive.Content + ref={ref} + sideOffset={sideOffset} + className={cn( + 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md', + className, + )} + {...props} + /> + </DropdownMenuPrimitive.Portal> +)); +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; + +const DropdownMenuItem = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Item>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + <DropdownMenuPrimitive.Item + ref={ref} + className={cn( + 'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50', + inset && 'pl-8', + className, + )} + {...props} + /> +)); +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> +>(({ className, children, checked, ...props }, ref) => ( + <DropdownMenuPrimitive.CheckboxItem + ref={ref} + className={cn( + 'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50', + className, + )} + checked={checked} + {...props} + > + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> + <DropdownMenuPrimitive.ItemIndicator> + <Check className="h-4 w-4" /> + </DropdownMenuPrimitive.ItemIndicator> + </span> + {children} + </DropdownMenuPrimitive.CheckboxItem> +)); +DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName; + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> +>(({ className, children, ...props }, ref) => ( + <DropdownMenuPrimitive.RadioItem + ref={ref} + className={cn( + 'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50', + className, + )} + {...props} + > + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> + <DropdownMenuPrimitive.ItemIndicator> + <Circle className="h-2 w-2 fill-current" /> + </DropdownMenuPrimitive.ItemIndicator> + </span> + {children} + </DropdownMenuPrimitive.RadioItem> +)); +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Label>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + <DropdownMenuPrimitive.Label + ref={ref} + className={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)} + {...props} + /> +)); +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Separator>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> +>(({ className, ...props }, ref) => ( + <DropdownMenuPrimitive.Separator + ref={ref} + className={cn('bg-muted -mx-1 my-1 h-px', className)} + {...props} + /> +)); +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; + +const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => { + return ( + <span className={cn('ml-auto text-xs tracking-widest opacity-60', className)} {...props} /> + ); +}; +DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'; + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +}; diff --git a/apps/workflows-dashboard/src/components/atoms/Dropdown/index.ts b/apps/workflows-dashboard/src/components/atoms/Dropdown/index.ts new file mode 100644 index 0000000000..2f29bad4e6 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Dropdown/index.ts @@ -0,0 +1 @@ +export * from './Dropdown'; diff --git a/apps/workflows-dashboard/src/components/atoms/ErrorAlert/ErrorAlert.tsx b/apps/workflows-dashboard/src/components/atoms/ErrorAlert/ErrorAlert.tsx new file mode 100644 index 0000000000..823d2378fb --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/ErrorAlert/ErrorAlert.tsx @@ -0,0 +1,20 @@ +import { Alert } from '../Alert/Alert'; +import { AlertCircle } from 'lucide-react'; +import { AlertTitle } from '../Alert/Alert.Title'; +import { AlertDescription } from '../Alert/Alert.Description'; + +interface Props { + children: React.ReactNode; +} + +export const ErrorAlert = ({ children }: Props) => { + return ( + <div className={`mt-3 mb-1`}> + <Alert variant={`destructive`} className={`w-full max-w-lg`}> + <AlertCircle className="h-4 w-4" /> + <AlertTitle>Error</AlertTitle> + <AlertDescription>{children}</AlertDescription> + </Alert> + </div> + ); +}; diff --git a/apps/workflows-dashboard/src/components/atoms/HealthIndicator/HealthIndicator.tsx b/apps/workflows-dashboard/src/components/atoms/HealthIndicator/HealthIndicator.tsx new file mode 100644 index 0000000000..f1f049a1b0 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/HealthIndicator/HealthIndicator.tsx @@ -0,0 +1,21 @@ +import { HealthStatus } from '@app/utils/get-workflow-health-status'; +import classnames from 'classnames'; + +interface Props { + healthStatus: HealthStatus; + size?: number; +} + +export const HealthIndicator = ({ healthStatus, size = 20 }: Props) => { + return ( + <span + style={{ width: `${size}px`, height: `${size}px` }} + className={classnames('block', 'rounded-full', { + ['bg-green-400']: healthStatus === HealthStatus.healthy, + ['bg-red-400']: healthStatus === HealthStatus.failed, + ['bg-yellow-400']: healthStatus === HealthStatus.pending, + ['bg-orange-400']: healthStatus === HealthStatus['pending-longterm'], + })} + ></span> + ); +}; diff --git a/apps/workflows-dashboard/src/components/atoms/HealthIndicator/index.ts b/apps/workflows-dashboard/src/components/atoms/HealthIndicator/index.ts new file mode 100644 index 0000000000..0bd6afe835 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/HealthIndicator/index.ts @@ -0,0 +1 @@ +export * from './HealthIndicator'; diff --git a/apps/workflows-dashboard/src/components/atoms/Input/Input.tsx b/apps/workflows-dashboard/src/components/atoms/Input/Input.tsx new file mode 100644 index 0000000000..e724d08473 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Input/Input.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; + +import { cn } from '@app/lib/utils'; + +export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {} + +const Input = React.forwardRef<HTMLInputElement, InputProps>( + ({ className, type, ...props }, ref) => { + return ( + <input + type={type} + className={cn( + 'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', + className, + )} + ref={ref} + {...props} + /> + ); + }, +); +Input.displayName = 'Input'; + +export { Input }; diff --git a/apps/workflows-dashboard/src/components/atoms/Input/index.ts b/apps/workflows-dashboard/src/components/atoms/Input/index.ts new file mode 100644 index 0000000000..ba9fe7ebc6 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Input/index.ts @@ -0,0 +1 @@ +export * from './Input'; diff --git a/apps/workflows-dashboard/src/components/atoms/Label/Label.tsx b/apps/workflows-dashboard/src/components/atoms/Label/Label.tsx new file mode 100644 index 0000000000..a4872f05f2 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Label/Label.tsx @@ -0,0 +1,16 @@ +import { ComponentPropsWithoutRef, ElementRef, forwardRef } from 'react'; +import * as LabelPrimitive from '@radix-ui/react-label'; +import { cva, VariantProps } from 'class-variance-authority'; +import { ctw } from '@app/common/utils/ctw/ctw'; + +const labelVariants = cva( + 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', +); + +export const Label = forwardRef< + ElementRef<typeof LabelPrimitive.Root>, + ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants> +>(({ className, ...props }, ref) => ( + <LabelPrimitive.Root ref={ref} className={ctw(labelVariants(), className)} {...props} /> +)); +Label.displayName = LabelPrimitive.Root.displayName; diff --git a/apps/workflows-dashboard/src/components/atoms/LoadingSpinner/LoadingSpinner.tsx b/apps/workflows-dashboard/src/components/atoms/LoadingSpinner/LoadingSpinner.tsx new file mode 100644 index 0000000000..95e6838bba --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/LoadingSpinner/LoadingSpinner.tsx @@ -0,0 +1,34 @@ +import { cn } from '@app/lib/utils'; + +interface Props { + className?: string; + size?: 'small' | 'medium' | 'large'; +} + +export const LoadingSpinner = ({ className, size = 'medium' }: Props) => { + return ( + <div role="status" className={className}> + <svg + aria-hidden="true" + className={cn('animate-spin fill-black text-gray-200', { + 'h-4 w-4': size === 'small', + 'h-8 w-8': size === 'medium', + 'h-12 w-12': size === 'large', + })} + viewBox="0 0 100 101" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" + fill="currentColor" + /> + <path + d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" + fill="currentFill" + /> + </svg> + <span className="sr-only">Loading...</span> + </div> + ); +}; diff --git a/apps/workflows-dashboard/src/components/atoms/LoadingSpinner/index.ts b/apps/workflows-dashboard/src/components/atoms/LoadingSpinner/index.ts new file mode 100644 index 0000000000..58a46ee59c --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/LoadingSpinner/index.ts @@ -0,0 +1 @@ +export * from './LoadingSpinner'; diff --git a/apps/workflows-dashboard/src/components/atoms/PieChart/PieChart.tsx b/apps/workflows-dashboard/src/components/atoms/PieChart/PieChart.tsx new file mode 100644 index 0000000000..d18d2d47a7 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/PieChart/PieChart.tsx @@ -0,0 +1,43 @@ +import { PieChartData } from '@app/components/atoms/PieChart/types'; +import { calculateChartDataSum } from '@app/components/atoms/PieChart/utils/calculateChartDataSum'; +import { useMemo } from 'react'; +import * as RechartPrimitive from 'recharts'; + +interface Props { + size: number; + outerRadius: number; + innerRadius: number; + data: PieChartData[]; +} + +export const PieChart = ({ size, data, outerRadius, innerRadius }: Props) => { + const totalValue = useMemo(() => calculateChartDataSum(data), [data]); + + return ( + <RechartPrimitive.PieChart + width={size} + height={size} + margin={{ top: 0, right: 0, bottom: 0, left: 0 }} + > + <RechartPrimitive.Pie + data={data} + outerRadius={outerRadius} + innerRadius={innerRadius} + fill="#000000" + dataKey="value" + animationBegin={200} + animationDuration={800} + > + {/* {data.map((data, index) => ( + <RechartPrimitive.Cell + key={`cell-${index}`} + fill={data.fillColor ? data.fillColor : undefined} + /> + ))} */} + <RechartPrimitive.Label position={'center'} style={{ fontWeight: 'bold' }}> + {totalValue} + </RechartPrimitive.Label> + </RechartPrimitive.Pie> + </RechartPrimitive.PieChart> + ); +}; diff --git a/apps/workflows-dashboard/src/components/atoms/PieChart/index.ts b/apps/workflows-dashboard/src/components/atoms/PieChart/index.ts new file mode 100644 index 0000000000..eca21743b1 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/PieChart/index.ts @@ -0,0 +1,2 @@ +export * from './PieChart'; +export * from './types'; diff --git a/apps/workflows-dashboard/src/components/atoms/PieChart/types.ts b/apps/workflows-dashboard/src/components/atoms/PieChart/types.ts new file mode 100644 index 0000000000..e4d6f9d75d --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/PieChart/types.ts @@ -0,0 +1,4 @@ +export interface PieChartData { + value: number; + fill: string; +} diff --git a/apps/workflows-dashboard/src/components/atoms/PieChart/utils/calculateChartDataSum.ts b/apps/workflows-dashboard/src/components/atoms/PieChart/utils/calculateChartDataSum.ts new file mode 100644 index 0000000000..de7688df56 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/PieChart/utils/calculateChartDataSum.ts @@ -0,0 +1,7 @@ +import { PieChartData } from '@app/components/atoms/PieChart/types'; + +export function calculateChartDataSum(chartData: PieChartData[]) { + const sum = chartData.reduce((sum, dataItem) => (sum += dataItem.value), 0); + + return sum; +} diff --git a/apps/workflows-dashboard/src/components/atoms/Popover/Popover.tsx b/apps/workflows-dashboard/src/components/atoms/Popover/Popover.tsx new file mode 100644 index 0000000000..1de5164e0e --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Popover/Popover.tsx @@ -0,0 +1,31 @@ +'use client'; + +import * as React from 'react'; +import * as PopoverPrimitive from '@radix-ui/react-popover'; + +import { cn } from '@app/lib/utils'; + +const Popover = PopoverPrimitive.Root; + +const PopoverTrigger = PopoverPrimitive.Trigger; + +const PopoverContent = React.forwardRef< + React.ElementRef<typeof PopoverPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> +>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => ( + <PopoverPrimitive.Portal> + <PopoverPrimitive.Content + ref={ref} + align={align} + sideOffset={sideOffset} + className={cn( + 'bg-popover text-popover-foreground animate-in data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md outline-none', + className, + )} + {...props} + /> + </PopoverPrimitive.Portal> +)); +PopoverContent.displayName = PopoverPrimitive.Content.displayName; + +export { Popover, PopoverTrigger, PopoverContent }; diff --git a/apps/workflows-dashboard/src/components/atoms/Popover/index.ts b/apps/workflows-dashboard/src/components/atoms/Popover/index.ts new file mode 100644 index 0000000000..8f473de4b9 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Popover/index.ts @@ -0,0 +1 @@ +export * from './Popover'; diff --git a/apps/workflows-dashboard/src/components/atoms/Select/Select.tsx b/apps/workflows-dashboard/src/components/atoms/Select/Select.tsx new file mode 100644 index 0000000000..532269d860 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Select/Select.tsx @@ -0,0 +1,120 @@ +'use client'; + +import * as React from 'react'; +import * as SelectPrimitive from '@radix-ui/react-select'; +import { Check, ChevronDown } from 'lucide-react'; + +import { cn } from '@app/lib/utils'; + +const Select = SelectPrimitive.Root; + +const SelectGroup = SelectPrimitive.Group; + +const SelectValue = SelectPrimitive.Value; + +const SelectTrigger = React.forwardRef< + React.ElementRef<typeof SelectPrimitive.Trigger>, + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> +>(({ className, children, ...props }, ref) => ( + <SelectPrimitive.Trigger + ref={ref} + className={cn( + 'border-input ring-offset-background placeholder:text-muted-foreground focus:ring-ring flex h-10 w-full items-center justify-between rounded-md border bg-transparent px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', + className, + )} + {...props} + > + {children} + <SelectPrimitive.Icon asChild> + <ChevronDown className="h-4 w-4 opacity-50" /> + </SelectPrimitive.Icon> + </SelectPrimitive.Trigger> +)); +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; + +const SelectContent = React.forwardRef< + React.ElementRef<typeof SelectPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> +>(({ className, children, position = 'popper', ...props }, ref) => ( + <SelectPrimitive.Portal> + <SelectPrimitive.Content + ref={ref} + className={cn( + 'bg-popover text-popover-foreground animate-in fade-in-80 relative z-50 min-w-[8rem] overflow-hidden rounded-md border shadow-md', + position === 'popper' && 'translate-y-1', + className, + )} + position={position} + {...props} + > + <SelectPrimitive.Viewport + className={cn( + 'p-1', + position === 'popper' && + 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]', + )} + > + {children} + </SelectPrimitive.Viewport> + </SelectPrimitive.Content> + </SelectPrimitive.Portal> +)); +SelectContent.displayName = SelectPrimitive.Content.displayName; + +const SelectLabel = React.forwardRef< + React.ElementRef<typeof SelectPrimitive.Label>, + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> +>(({ className, ...props }, ref) => ( + <SelectPrimitive.Label + ref={ref} + className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)} + {...props} + /> +)); +SelectLabel.displayName = SelectPrimitive.Label.displayName; + +const SelectItem = React.forwardRef< + React.ElementRef<typeof SelectPrimitive.Item>, + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> +>(({ className, children, ...props }, ref) => ( + <SelectPrimitive.Item + ref={ref} + className={cn( + 'focus:bg-accent focus:text-accent-foreground relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50', + className, + )} + {...props} + > + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> + <SelectPrimitive.ItemIndicator> + <Check className="h-4 w-4" /> + </SelectPrimitive.ItemIndicator> + </span> + + <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> + </SelectPrimitive.Item> +)); +SelectItem.displayName = SelectPrimitive.Item.displayName; + +const SelectSeparator = React.forwardRef< + React.ElementRef<typeof SelectPrimitive.Separator>, + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> +>(({ className, ...props }, ref) => ( + <SelectPrimitive.Separator + ref={ref} + className={cn('bg-muted -mx-1 my-1 h-px', className)} + {...props} + /> +)); +SelectSeparator.displayName = SelectPrimitive.Separator.displayName; + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, +}; diff --git a/apps/workflows-dashboard/src/components/atoms/Select/index.ts b/apps/workflows-dashboard/src/components/atoms/Select/index.ts new file mode 100644 index 0000000000..7868ecbae2 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Select/index.ts @@ -0,0 +1 @@ +export * from './Select'; diff --git a/apps/workflows-dashboard/src/components/atoms/Separator/Separator.tsx b/apps/workflows-dashboard/src/components/atoms/Separator/Separator.tsx new file mode 100644 index 0000000000..09a83d2590 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Separator/Separator.tsx @@ -0,0 +1,26 @@ +'use client'; + +import * as React from 'react'; +import * as SeparatorPrimitive from '@radix-ui/react-separator'; + +import { cn } from '@app/lib/utils'; + +const Separator = React.forwardRef< + React.ElementRef<typeof SeparatorPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> +>(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => ( + <SeparatorPrimitive.Root + ref={ref} + decorative={decorative} + orientation={orientation} + className={cn( + 'bg-border shrink-0', + orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]', + className, + )} + {...props} + /> +)); +Separator.displayName = SeparatorPrimitive.Root.displayName; + +export { Separator }; diff --git a/apps/workflows-dashboard/src/components/atoms/Separator/index.ts b/apps/workflows-dashboard/src/components/atoms/Separator/index.ts new file mode 100644 index 0000000000..6e97479974 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Separator/index.ts @@ -0,0 +1 @@ +export * from './Separator'; diff --git a/apps/workflows-dashboard/src/components/atoms/Skeleton/Skeleton.tsx b/apps/workflows-dashboard/src/components/atoms/Skeleton/Skeleton.tsx new file mode 100644 index 0000000000..84bff68dfa --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Skeleton/Skeleton.tsx @@ -0,0 +1,7 @@ +import { cn } from '@app/lib/utils'; + +function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) { + return <div className={cn('bg-muted animate-pulse rounded-md', className)} {...props} />; +} + +export { Skeleton }; diff --git a/apps/workflows-dashboard/src/components/atoms/Skeleton/index.ts b/apps/workflows-dashboard/src/components/atoms/Skeleton/index.ts new file mode 100644 index 0000000000..66bc08df6b --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Skeleton/index.ts @@ -0,0 +1 @@ +export * from './Skeleton'; diff --git a/apps/workflows-dashboard/src/components/atoms/Table/Table.tsx b/apps/workflows-dashboard/src/components/atoms/Table/Table.tsx new file mode 100644 index 0000000000..a8d3be4418 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Table/Table.tsx @@ -0,0 +1,88 @@ +import { cn } from '@app/lib/utils'; +import * as React from 'react'; + +const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>( + ({ className, ...props }, ref) => ( + <table ref={ref} className={cn('caption-bottom w-full text-sm', className)} {...props} /> + ), +); +Table.displayName = 'Table'; + +const TableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes<HTMLTableSectionElement> +>(({ className, ...props }, ref) => ( + <thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} /> +)); +TableHeader.displayName = 'TableHeader'; + +const TableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes<HTMLTableSectionElement> +>(({ className, ...props }, ref) => ( + <tbody ref={ref} className={cn('[&_tr:last-child]:border-0', className)} {...props} /> +)); +TableBody.displayName = 'TableBody'; + +const TableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes<HTMLTableSectionElement> +>(({ className, ...props }, ref) => ( + <tfoot + ref={ref} + className={cn('bg-primary text-primary-foreground font-medium', className)} + {...props} + /> +)); +TableFooter.displayName = 'TableFooter'; + +const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>( + ({ className, ...props }, ref) => ( + <tr + ref={ref} + className={cn( + 'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors', + className, + )} + {...props} + /> + ), +); +TableRow.displayName = 'TableRow'; + +const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes<HTMLTableCellElement> +>(({ className, ...props }, ref) => ( + <th + ref={ref} + className={cn( + 'font-inter text-muted-foreground h-12 px-4 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0', + className, + )} + {...props} + /> +)); +TableHead.displayName = 'TableHead'; + +const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes<HTMLTableCellElement> +>(({ className, ...props }, ref) => ( + <td + ref={ref} + className={cn('font-inter p-4 align-middle [&:has([role=checkbox])]:pr-0', className)} + {...props} + /> +)); +TableCell.displayName = 'TableCell'; + +const TableCaption = React.forwardRef< + HTMLTableCaptionElement, + React.HTMLAttributes<HTMLTableCaptionElement> +>(({ className, ...props }, ref) => ( + <caption ref={ref} className={cn('text-muted-foreground mt-4 text-sm', className)} {...props} /> +)); +TableCaption.displayName = 'TableCaption'; + +export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }; diff --git a/apps/workflows-dashboard/src/components/atoms/Table/index.ts b/apps/workflows-dashboard/src/components/atoms/Table/index.ts new file mode 100644 index 0000000000..75193adc33 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Table/index.ts @@ -0,0 +1 @@ +export * from './Table'; diff --git a/apps/workflows-dashboard/src/components/atoms/Tabs/Tabs.tsx b/apps/workflows-dashboard/src/components/atoms/Tabs/Tabs.tsx new file mode 100644 index 0000000000..775dea6049 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Tabs/Tabs.tsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import * as TabsPrimitive from '@radix-ui/react-tabs'; + +import { cn } from '@app/lib/utils'; + +const Tabs = TabsPrimitive.Root; + +const TabsList = React.forwardRef< + React.ElementRef<typeof TabsPrimitive.List>, + React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> +>(({ className, ...props }, ref) => ( + <TabsPrimitive.List + ref={ref} + className={cn( + 'bg-muted text-muted-foreground inline-flex h-10 items-center justify-center rounded-md p-1', + className, + )} + {...props} + /> +)); +TabsList.displayName = TabsPrimitive.List.displayName; + +const TabsTrigger = React.forwardRef< + React.ElementRef<typeof TabsPrimitive.Trigger>, + React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> +>(({ className, ...props }, ref) => ( + <TabsPrimitive.Trigger + ref={ref} + className={cn( + 'ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm', + className, + )} + {...props} + /> +)); +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; + +const TabsContent = React.forwardRef< + React.ElementRef<typeof TabsPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content> +>(({ className, ...props }, ref) => ( + <TabsPrimitive.Content + ref={ref} + className={cn( + 'ring-offset-background focus-visible:ring-ring mt-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2', + className, + )} + {...props} + /> +)); +TabsContent.displayName = TabsPrimitive.Content.displayName; + +export { Tabs, TabsList, TabsTrigger, TabsContent }; diff --git a/apps/workflows-dashboard/src/components/atoms/Tabs/index.ts b/apps/workflows-dashboard/src/components/atoms/Tabs/index.ts new file mode 100644 index 0000000000..856dbbb347 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/Tabs/index.ts @@ -0,0 +1 @@ +export * from './Tabs'; diff --git a/apps/workflows-dashboard/src/components/atoms/icons/BallerineLogo/BallerineLogo.tsx b/apps/workflows-dashboard/src/components/atoms/icons/BallerineLogo/BallerineLogo.tsx new file mode 100644 index 0000000000..80612e5eb9 --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/icons/BallerineLogo/BallerineLogo.tsx @@ -0,0 +1,51 @@ +import { FunctionComponent, SVGProps } from 'react'; + +/** + * @description The Ballerine logo SVG found in Ballerine's Figma design. + * @param props + * @constructor + */ +export const BallerineLogo: FunctionComponent<SVGProps<SVGSVGElement>> = props => { + return ( + <svg + width="153" + height="34" + viewBox="0 0 153 34" + fill="none" + xmlns="http://www.w3.org/2000/svg" + {...props} + > + <g clipPath="url(#clip0_2190_10239)"> + <path + d="M15.0495 34C11.7759 34 9.24159 32.4362 7.99481 31.4763C7.16779 30.8415 7.02056 29.6617 7.66275 28.8443C8.30493 28.0268 9.49847 27.8812 10.3223 28.516C11.2151 29.2034 13.0571 30.343 15.3283 30.2439C19.1689 30.0767 22.7996 26.528 22.7996 22.9392C22.7996 21.1494 22.4644 19.9634 21.5622 18.5699L21.5278 18.5142C19.786 15.6004 16.0112 13.6402 12.1361 13.6402C11.1776 13.6402 10.2879 13.7641 9.48907 14.0118C8.48976 14.3215 7.4278 13.7703 7.11454 12.7825C6.80127 11.7947 7.35888 10.7419 8.35819 10.4353C9.52353 10.0761 10.7922 9.89343 12.133 9.89343C17.3708 9.89343 22.3266 12.5162 24.77 16.5789C26.0575 18.5823 26.5838 20.4279 26.5838 22.9392C26.5838 28.4727 21.2928 33.7399 15.4849 33.9907C15.3346 33.9969 15.1873 34 15.0401 34H15.0495Z" + fill="currentColor" + /> + <path + d="M6.81973 26.2184C2.93214 26.2184 0 23.3727 0 19.6011C0 18.1736 0.523149 16.7244 1.55692 15.2938L1.59451 15.2442C1.71668 15.0708 1.85138 14.9005 1.99548 14.7302C2.09573 14.6095 2.42152 14.2224 2.94467 13.7207C3.6965 12.9993 4.8963 13.0178 5.6262 13.761C6.3561 14.5042 6.33731 15.6901 5.58548 16.4116C5.17197 16.808 4.94015 17.0929 4.93702 17.096L4.89316 17.1486C4.82111 17.2291 4.75846 17.3096 4.70521 17.3871L4.64255 17.4769C4.43267 17.7679 3.79048 18.6535 3.79048 19.6042C3.79048 21.2918 5.03413 22.4716 6.81973 22.4716C8.80582 22.4716 12.6339 21.9328 15.9451 18.3191L15.967 18.2974L16.5183 17.7153C17.2326 16.9597 18.4324 16.9195 19.1999 17.6286C19.9642 18.3346 20.005 19.5206 19.2876 20.2792L18.7488 20.849C14.4602 25.5217 9.43548 26.2215 6.82286 26.2215L6.81973 26.2184Z" + fill="currentColor" + /> + <path + d="M9.12705 20.0439C8.19979 20.0439 7.38844 19.372 7.25373 18.4368C6.76818 15.1018 6.60841 12.0703 6.76504 9.16576C6.79324 4.10601 10.9659 0 16.0877 0C21.2096 0 25.4136 4.13388 25.4136 9.2184C25.4136 12.1849 24.5678 13.9375 23.2019 15.8047C22.5879 16.6439 21.4007 16.8297 20.5549 16.2228C19.7059 15.6158 19.518 14.4454 20.132 13.6062C21.1438 12.2251 21.6231 11.2157 21.6231 9.2184C21.6231 6.20237 19.1421 3.74991 16.0909 3.74991C13.0397 3.74991 10.5587 6.20237 10.5587 9.2184V9.32058C10.4083 11.9898 10.5555 14.7953 11.0066 17.9011C11.157 18.9261 10.4365 19.8767 9.39958 20.0253C9.30874 20.0377 9.21789 20.0439 9.12705 20.0439V20.0439Z" + fill="currentColor" + /> + <path + d="M16.4715 17.7618L17.64 16.486C17.8154 16.3126 18.2602 16.2971 18.4701 16.486C18.7458 16.7337 19.2658 17.2942 19.6511 18.2913C19.8516 18.8146 15.964 19.5144 16.4715 17.7649V17.7618Z" + fill="currentColor" + /> + <path + d="M2.87011 13.7703C3.50603 13.1789 4.2234 12.7578 4.87499 12.541C5.36994 12.3769 5.9996 12.3614 6.04346 13.0086C6.09045 13.7208 6.1625 14.5321 6.15937 15.0089C6.15937 15.5663 1.74863 15.2133 2.87011 13.7703Z" + fill="currentColor" + /> + </g> + <path + d="M41.0478 27.359H47.4084C52.0393 27.359 54.2711 25.0993 54.2711 21.8353C54.2711 19.0177 52.4857 17.3717 50.4492 16.8417C51.8441 16.2279 53.0158 14.8052 53.0158 12.7407C53.0158 10.1184 51.2024 7.8308 47.6316 7.8308H41.0478C40.7967 7.8308 40.6293 7.99819 40.6293 8.24926V26.9405C40.6293 27.1916 40.7967 27.359 41.0478 27.359ZM44.5628 23.8439V18.6829H47.1294C49.0543 18.6829 50.226 19.464 50.226 21.2495C50.226 23.0349 49.0543 23.8439 47.1294 23.8439H44.5628ZM44.5628 15.5863V11.3459H46.8504C48.329 11.3459 49.2496 11.8759 49.2496 13.3824C49.2496 14.9168 48.329 15.5863 46.8504 15.5863H44.5628ZM62.6626 27.6938C64.6991 27.6938 66.1777 26.829 67.1262 25.4341L67.182 26.9405C67.182 27.1916 67.3494 27.359 67.6005 27.359H70.3065C70.5576 27.359 70.7529 27.1916 70.7529 26.9405V14.1077C70.7529 13.8566 70.5855 13.6893 70.3344 13.6893H67.6005C67.3494 13.6893 67.182 13.8566 67.182 14.1077L67.1262 15.6142C66.2056 14.1914 64.727 13.3545 62.6626 13.3545C58.8128 13.3545 56.1625 16.479 56.1625 20.5241C56.1625 24.5972 58.8128 27.6938 62.6626 27.6938ZM59.9287 20.5241C59.9287 18.3481 61.4351 16.8696 63.4716 16.8696C65.5081 16.8696 66.9309 18.3202 66.9309 20.5241C66.9309 22.728 65.5081 24.1787 63.4716 24.1787C61.4351 24.1787 59.9287 22.728 59.9287 20.5241ZM74.4964 27.359H77.5372C77.7883 27.359 77.9556 27.1916 77.9556 26.9405V7.8308C77.9556 7.57972 77.7883 7.41234 77.5372 7.41234H74.4964C74.2453 7.41234 74.0779 7.57972 74.0779 7.8308V26.9405C74.0779 27.1916 74.2453 27.359 74.4964 27.359ZM81.6887 27.359H84.7295C84.9806 27.359 85.1479 27.1916 85.1479 26.9405V7.8308C85.1479 7.57972 84.9806 7.41234 84.7295 7.41234H81.6887C81.4376 7.41234 81.2702 7.57972 81.2702 7.8308V26.9405C81.2702 27.1916 81.4376 27.359 81.6887 27.359ZM95.2695 27.6938C97.1944 27.6938 98.9519 27.1358 100.124 25.9641C100.375 25.741 100.403 25.5457 100.263 25.3504L99.2309 23.9276C99.0914 23.7323 98.9519 23.7044 98.7567 23.816C97.6408 24.4577 96.6365 24.653 95.5764 24.653C93.3725 24.653 91.9497 23.7044 91.5312 21.9748H99.8447C101.044 21.9748 101.351 21.2216 101.351 19.8825C101.351 16.4232 98.9519 13.3545 94.7952 13.3545C90.5827 13.3545 87.7651 16.4511 87.7651 20.4683C87.7651 24.5972 90.778 27.6938 95.2695 27.6938ZM91.4754 19.2688C91.8102 17.4554 93.1214 16.5348 94.8231 16.5348C96.4691 16.5348 97.6966 17.4554 97.9197 19.2688H91.4754ZM104.355 27.359H107.368C107.619 27.359 107.787 27.1916 107.787 26.9405V20.1057C107.787 18.1529 108.791 16.8975 110.688 16.8975C111.079 16.8975 111.385 16.9533 111.692 17.037C112.055 17.1206 112.25 17.037 112.25 16.7022V14.2472C112.25 13.9961 112.195 13.8288 111.999 13.6893C111.776 13.5219 111.385 13.3545 110.744 13.3545C109.126 13.3545 108.261 14.4425 107.787 15.8932L107.675 14.1356C107.647 13.8009 107.508 13.6893 107.257 13.6893H104.355C104.104 13.6893 103.937 13.8566 103.937 14.1077V26.9405C103.937 27.1916 104.104 27.359 104.355 27.359ZM116.706 11.2622C117.934 11.2622 118.826 10.3137 118.826 9.11408C118.826 7.94239 117.934 6.99388 116.706 6.99388C115.479 6.99388 114.558 7.94239 114.558 9.11408C114.558 10.3137 115.479 11.2622 116.706 11.2622ZM114.753 26.9405C114.753 27.1916 114.921 27.359 115.172 27.359H118.213C118.464 27.359 118.631 27.1916 118.631 26.9405V14.1077C118.631 13.8566 118.464 13.6893 118.213 13.6893H115.172C114.921 13.6893 114.753 13.8566 114.753 14.1077V26.9405ZM122.391 27.359H125.404C125.655 27.359 125.822 27.1916 125.822 26.9405V19.8546C125.822 17.9297 126.882 16.8975 128.5 16.8975C130.146 16.8975 131.039 17.9297 131.039 19.8546V26.9405C131.039 27.1916 131.206 27.359 131.457 27.359H134.442C134.693 27.359 134.889 27.1916 134.889 26.9405V18.9898C134.889 15.4189 132.796 13.3545 129.951 13.3545C127.914 13.3545 126.603 14.3588 125.822 15.7537L125.71 14.1356C125.71 13.8009 125.543 13.6893 125.292 13.6893H122.391C122.14 13.6893 121.972 13.8566 121.972 14.1077V26.9405C121.972 27.1916 122.14 27.359 122.391 27.359ZM144.989 27.6938C146.914 27.6938 148.672 27.1358 149.843 25.9641C150.094 25.741 150.122 25.5457 149.983 25.3504L148.95 23.9276C148.811 23.7323 148.672 23.7044 148.476 23.816C147.36 24.4577 146.356 24.653 145.296 24.653C143.092 24.653 141.669 23.7044 141.251 21.9748H149.564C150.764 21.9748 151.071 21.2216 151.071 19.8825C151.071 16.4232 148.672 13.3545 144.515 13.3545C140.302 13.3545 137.485 16.4511 137.485 20.4683C137.485 24.5972 140.498 27.6938 144.989 27.6938ZM141.195 19.2688C141.53 17.4554 142.841 16.5348 144.543 16.5348C146.189 16.5348 147.416 17.4554 147.639 19.2688H141.195Z" + fill="currentColor" + /> + <defs> + <clipPath id="clip0_2190_10239"> + <rect width="26.5897" height="34" fill="white" /> + </clipPath> + </defs> + </svg> + ); +}; diff --git a/apps/workflows-dashboard/src/components/atoms/icons/BallerineLogo/index.ts b/apps/workflows-dashboard/src/components/atoms/icons/BallerineLogo/index.ts new file mode 100644 index 0000000000..655531d17f --- /dev/null +++ b/apps/workflows-dashboard/src/components/atoms/icons/BallerineLogo/index.ts @@ -0,0 +1 @@ +export * from './BallerineLogo'; diff --git a/apps/workflows-dashboard/src/components/layouts/DashboardLayout/DashboardLayout.tsx b/apps/workflows-dashboard/src/components/layouts/DashboardLayout/DashboardLayout.tsx new file mode 100644 index 0000000000..ca3b51a1a6 --- /dev/null +++ b/apps/workflows-dashboard/src/components/layouts/DashboardLayout/DashboardLayout.tsx @@ -0,0 +1,22 @@ +import { Header } from '@app/components/organisms/Header'; +import { QueryParamProvider } from 'use-query-params'; +import { ReactRouter6Adapter } from 'use-query-params/adapters/react-router-6'; + +interface Props { + pageName: string; + children: React.ReactNode; +} + +export const DashboardLayout = ({ pageName, children }: Props) => { + return ( + <div className="box-border flex h-screen flex-col"> + <Header /> + <div className="flex flex-1 flex-col gap-4 p-6"> + <h2 className="text-3xl font-bold tracking-tight ">{pageName}</h2> + <div className="flex-1 overflow-auto"> + <QueryParamProvider adapter={ReactRouter6Adapter}>{children}</QueryParamProvider> + </div> + </div> + </div> + ); +}; diff --git a/apps/workflows-dashboard/src/components/layouts/DashboardLayout/index.ts b/apps/workflows-dashboard/src/components/layouts/DashboardLayout/index.ts new file mode 100644 index 0000000000..fc36c152c7 --- /dev/null +++ b/apps/workflows-dashboard/src/components/layouts/DashboardLayout/index.ts @@ -0,0 +1 @@ +export * from './DashboardLayout'; diff --git a/apps/workflows-dashboard/src/components/molecules/FacetedFilter/FacetedFilter.tsx b/apps/workflows-dashboard/src/components/molecules/FacetedFilter/FacetedFilter.tsx new file mode 100644 index 0000000000..83070a8013 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/FacetedFilter/FacetedFilter.tsx @@ -0,0 +1,118 @@ +import { Check, LucideIcon, PlusCircle } from 'lucide-react'; + +import { cn } from '@app/lib/utils'; +// import { Separator } from "@/components/ui/separator" +import { Popover, PopoverContent, PopoverTrigger } from '@app/components/atoms/Popover'; +import { Button } from '@app/components/atoms/Button'; +import { Separator } from '@app/components/atoms/Separator'; +import { Badge } from '@app/components/atoms/Badge'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, +} from '@app/components/atoms/Command'; + +export interface FacetedFilterOption { + label: string; + value: string; + icon?: LucideIcon; +} + +interface Props { + title?: string; + options: FacetedFilterOption[]; + value: string[]; + onChange: (values: string[]) => void; +} + +export function FacetedFilter({ title, options, value, onChange }: Props) { + return ( + <Popover> + <PopoverTrigger asChild> + <Button variant="outline" size="sm" className="font-inter h-8 border-dashed"> + <PlusCircle className="mr-2 h-4 w-4" /> + {title} + {value.length > 0 && ( + <> + <Separator orientation="vertical" className="mx-2 h-4" /> + <Badge variant="secondary" className="rounded-sm px-1 font-normal lg:hidden"> + {value.length} + </Badge> + <div className="hidden space-x-1 lg:flex"> + {value.length > 2 ? ( + <Badge variant="secondary" className="rounded-sm px-1 font-normal"> + {value.length} selected + </Badge> + ) : ( + options + .filter(option => value.includes(option.value)) + .map(option => ( + <Badge + variant="secondary" + key={option.value} + className="rounded-sm px-1 font-normal" + > + {option.label} + </Badge> + )) + )} + </div> + </> + )} + </Button> + </PopoverTrigger> + <PopoverContent className="w-[200px] p-0" align="start"> + <Command className="font-inter"> + <CommandInput placeholder={title} /> + <CommandList> + <CommandEmpty>No results found.</CommandEmpty> + <CommandGroup> + {options.map(option => { + const isSelected = value.includes(option.value); + return ( + <CommandItem + key={option.value} + onSelect={() => { + if (isSelected) { + onChange(value.filter(selectedValue => selectedValue !== option.value)); + } else { + onChange([...value, option.value]); + } + }} + > + <div + className={cn( + 'border-primary mr-2 flex h-4 w-4 items-center justify-center rounded-sm border', + isSelected + ? 'bg-primary text-primary-foreground' + : 'opacity-50 [&_svg]:invisible', + )} + > + <Check className={cn('h-4 w-4')} /> + </div> + {option.icon && <option.icon className="text-muted-foreground mr-2 h-4 w-4" />} + <span>{option.label}</span> + </CommandItem> + ); + })} + </CommandGroup> + {value.length > 0 && ( + <> + <CommandSeparator /> + <CommandGroup> + <CommandItem className="justify-center text-center" onSelect={() => onChange([])}> + Clear filters + </CommandItem> + </CommandGroup> + </> + )} + </CommandList> + </Command> + </PopoverContent> + </Popover> + ); +} diff --git a/apps/workflows-dashboard/src/components/molecules/FacetedFilter/index.ts b/apps/workflows-dashboard/src/components/molecules/FacetedFilter/index.ts new file mode 100644 index 0000000000..5bf29065f5 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/FacetedFilter/index.ts @@ -0,0 +1 @@ +export * from './FacetedFilter'; diff --git a/apps/workflows-dashboard/src/components/molecules/Header/Header.tsx b/apps/workflows-dashboard/src/components/molecules/Header/Header.tsx new file mode 100644 index 0000000000..b163272e83 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/Header/Header.tsx @@ -0,0 +1,24 @@ +import { useLogoutMutation } from '@app/common/hooks/useLogoutMutation'; +import { Input } from '@app/components/atoms/Input'; +import { headerNavigationLinks } from '@app/components/molecules/Header/header-navigation-links'; +import { Navigation } from '@app/components/molecules/Navigation'; +import { UserNavigation } from '@app/components/molecules/UserNavigation'; + +export const Header = () => { + const { logout } = useLogoutMutation(); + return ( + <div className="border-b"> + <div className="flex h-16 flex-nowrap items-center justify-between px-4"> + <div className="flex flex-1 gap-4"> + <UserNavigation onLogout={logout} /> + <Navigation items={headerNavigationLinks} /> + </div> + <div className="flex gap-4"> + <div> + <Input placeholder="Search" /> + </div> + </div> + </div> + </div> + ); +}; diff --git a/apps/workflows-dashboard/src/components/molecules/Header/header-navigation-links.ts b/apps/workflows-dashboard/src/components/molecules/Header/header-navigation-links.ts new file mode 100644 index 0000000000..2963b14c96 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/Header/header-navigation-links.ts @@ -0,0 +1,12 @@ +import { NavigationLink } from '@app/components/molecules/Navigation'; + +export const headerNavigationLinks: NavigationLink[] = [ + { + path: '/overview', + label: 'Overview', + }, + { + path: '/workflows', + label: 'Workflows', + }, +]; diff --git a/apps/workflows-dashboard/src/components/molecules/Header/index.ts b/apps/workflows-dashboard/src/components/molecules/Header/index.ts new file mode 100644 index 0000000000..266dec8a1b --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/Header/index.ts @@ -0,0 +1 @@ +export * from './Header'; diff --git a/apps/workflows-dashboard/src/components/molecules/MetricCard/MetricCard.tsx b/apps/workflows-dashboard/src/components/molecules/MetricCard/MetricCard.tsx new file mode 100644 index 0000000000..2004bcdd25 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/MetricCard/MetricCard.tsx @@ -0,0 +1,48 @@ +import classnames from 'classnames'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, +} from '@app/components/atoms/Card'; +import { MetricCardContent } from '@app/components/molecules/MetricCard/MetricCardContent'; +import { MetricCardTitle } from '@app/components/molecules/MetricCard/MetricCardTitle'; +import { Skeleton } from '@app/components/atoms/Skeleton'; + +interface Props { + className?: string; + isLoading?: boolean; + title: React.ReactNode; + content?: React.ReactNode; + description?: string; +} + +export function MetricCard({ title, content, description, className, isLoading }: Props) { + return ( + <Card className={classnames('flex h-full flex-col flex-nowrap', className)}> + {isLoading ? ( + <Skeleton className="ml-4 mr-4 mt-4 h-6" /> + ) : ( + <CardHeader className="dfgdf p-4">{title}</CardHeader> + )} + <CardContent className={classnames('flex-1 p-4 pb-2')}> + {isLoading ? ( + <Skeleton className="h-20" /> + ) : ( + <div className={classnames('w-full', 'h-full')}>{content}</div> + )} + </CardContent> + <CardFooter className="p-4 pt-0"> + {isLoading ? ( + <Skeleton className="h-6" /> + ) : description ? ( + <CardDescription className="w-full text-xs">{description}</CardDescription> + ) : null} + </CardFooter> + </Card> + ); +} + +MetricCard.Title = MetricCardTitle; +MetricCard.Content = MetricCardContent; diff --git a/apps/workflows-dashboard/src/components/molecules/MetricCard/MetricCardContent.tsx b/apps/workflows-dashboard/src/components/molecules/MetricCard/MetricCardContent.tsx new file mode 100644 index 0000000000..3797533add --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/MetricCard/MetricCardContent.tsx @@ -0,0 +1,9 @@ +interface Props { + children: React.ReactNode; +} + +export function MetricCardContent({ children }: Props) { + return <div className="h-full">{children}</div>; +} + +MetricCardContent.displayName = 'MetricCardContent'; diff --git a/apps/workflows-dashboard/src/components/molecules/MetricCard/MetricCardTitle.tsx b/apps/workflows-dashboard/src/components/molecules/MetricCard/MetricCardTitle.tsx new file mode 100644 index 0000000000..5acbd2c293 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/MetricCard/MetricCardTitle.tsx @@ -0,0 +1,12 @@ +import { CardTitle } from '@app/components/atoms/Card'; + +interface Props { + title: React.ReactNode; + className?: string; +} + +export function MetricCardTitle({ title, className }: Props) { + return <CardTitle className={className}>{title}</CardTitle>; +} + +MetricCardTitle.displayName = 'MetricCardTitle'; diff --git a/apps/workflows-dashboard/src/components/molecules/MetricCard/index.ts b/apps/workflows-dashboard/src/components/molecules/MetricCard/index.ts new file mode 100644 index 0000000000..ff6adfe2d3 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/MetricCard/index.ts @@ -0,0 +1 @@ +export * from './MetricCard'; diff --git a/apps/workflows-dashboard/src/components/molecules/Navigation/Navigation.tsx b/apps/workflows-dashboard/src/components/molecules/Navigation/Navigation.tsx new file mode 100644 index 0000000000..d271383639 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/Navigation/Navigation.tsx @@ -0,0 +1,30 @@ +import { memo } from 'react'; +import { NavLink } from 'react-router-dom'; + +export interface NavigationLink { + path: string; + label: string; +} + +interface Props { + items: NavigationLink[]; +} + +export const Navigation = memo(({ items }: Props) => { + return ( + <nav className="flex items-center space-x-4 lg:space-x-6"> + {items.map((navItem, index) => ( + <NavLink + key={`nav-item-${index}`} + className={({ isActive }) => + isActive + ? 'hover:text-primary text-sm font-medium transition-colors' + : 'text-muted-foreground hover:text-primary text-sm font-medium transition-colors' + } + to={navItem.path} + children={navItem.label} + /> + ))} + </nav> + ); +}); diff --git a/apps/workflows-dashboard/src/components/molecules/Navigation/index.ts b/apps/workflows-dashboard/src/components/molecules/Navigation/index.ts new file mode 100644 index 0000000000..95e14a93f1 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/Navigation/index.ts @@ -0,0 +1 @@ +export * from './Navigation'; diff --git a/apps/workflows-dashboard/src/components/molecules/Pagination/Pagination.tsx b/apps/workflows-dashboard/src/components/molecules/Pagination/Pagination.tsx new file mode 100644 index 0000000000..3afc2bf88c --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/Pagination/Pagination.tsx @@ -0,0 +1,97 @@ +import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react'; +import { Button } from '@app/components/atoms/Button'; +import { useCallback } from 'react'; + +interface Props { + totalPages: number; + page: number; + onChange: (nextPage: number) => void; +} + +export const Pagination = ({ totalPages, page, onChange }: Props) => { + const changePage = useCallback( + (nextPage: number) => { + const isPageInRangeOfPages = nextPage <= totalPages || nextPage <= 1; + + if (!isPageInRangeOfPages) return; + + onChange(nextPage); + }, + [onChange, totalPages], + ); + + const goToFirstPage = useCallback(() => { + const FIRST_PAGE_NUMBER = 1; + + changePage(FIRST_PAGE_NUMBER); + }, [changePage]); + + const goToPreviousPage = useCallback(() => { + const PREV_PAGE_NUMBER = page - 1; + + changePage(PREV_PAGE_NUMBER); + }, [page, changePage]); + + const goToLastPage = useCallback(() => { + const LAST_PAGE_NUMBER = totalPages; + + changePage(LAST_PAGE_NUMBER); + }, [totalPages, changePage]); + + const goToNextPage = useCallback(() => { + const NEXT_PAGE_NUMBER = page + 1; + + changePage(NEXT_PAGE_NUMBER); + }, [page, changePage]); + + const isFirstPage = page === 1; + const isLastPage = page === totalPages; + + return ( + <div className="flex items-center justify-between px-2"> + <div className="flex items-center space-x-6 lg:space-x-8"> + <div className="flex items-center justify-center text-sm"> + Page {page} of {totalPages} + </div> + <div className="flex items-center space-x-2"> + <Button + variant="outline" + className="hidden h-8 w-8 p-0 lg:flex" + onClick={goToFirstPage} + disabled={isFirstPage} + > + <span className="sr-only ">Go to first page</span> + <ChevronsLeft className="h-4 w-4" /> + </Button> + <Button + variant="outline" + className="h-8 w-8 p-0" + onClick={goToPreviousPage} + disabled={isFirstPage} + > + <span className="sr-only">Go to previous page</span> + <ChevronLeft className="h-4 w-4" /> + </Button> + <Button + variant="outline" + className="h-8 w-8 p-0" + onClick={goToNextPage} + disabled={isLastPage} + > + <span className="sr-only">Go to next page</span> + <ChevronRight className="h-4 w-4" /> + </Button> + <Button + variant="outline" + className="hidden h-8 w-8 p-0 lg:flex" + onClick={goToLastPage} + disabled={isLastPage} + > + <span className="sr-only">Go to last page</span> + <ChevronsRight className="h-4 w-4" /> + </Button> + </div> + </div> + </div> + ); +}; diff --git a/apps/workflows-dashboard/src/components/molecules/Pagination/index.ts b/apps/workflows-dashboard/src/components/molecules/Pagination/index.ts new file mode 100644 index 0000000000..e016c96b72 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/Pagination/index.ts @@ -0,0 +1 @@ +export * from './Pagination'; diff --git a/apps/workflows-dashboard/src/components/molecules/UserNavigation/UserNavigation.tsx b/apps/workflows-dashboard/src/components/molecules/UserNavigation/UserNavigation.tsx new file mode 100644 index 0000000000..566cdf3b07 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/UserNavigation/UserNavigation.tsx @@ -0,0 +1,69 @@ +import { CreditCard, LogOut, PlusCircle, Settings, User } from 'lucide-react'; + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from '@app/components/atoms/Dropdown'; +import { Button } from '@app/components/atoms/Button'; +import { Avatar, AvatarFallback, AvatarImage } from '@app/components/atoms/Avatar'; + +interface Props { + onLogout: () => void; +} + +export function UserNavigation({ onLogout }: Props) { + return ( + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button variant="ghost" className="relative h-8 w-8 rounded-full"> + <Avatar className="h-6 w-6"> + <AvatarImage src="/avatar.png" alt="@shadcn" /> + <AvatarFallback>SC</AvatarFallback> + </Avatar> + </Button> + </DropdownMenuTrigger> + <DropdownMenuContent className="w-56" align="end" forceMount> + <DropdownMenuLabel className="font-normal"> + <div className="flex flex-col space-y-1"> + <p className="text-sm font-medium leading-none">example</p> + <p className="text-muted-foreground text-xs leading-none">m@example.com</p> + </div> + </DropdownMenuLabel> + <DropdownMenuSeparator /> + <DropdownMenuGroup> + <DropdownMenuItem> + <User className="mr-2 h-4 w-4" /> + <span>Profile</span> + <DropdownMenuShortcut>β§βP</DropdownMenuShortcut> + </DropdownMenuItem> + <DropdownMenuItem> + <CreditCard className="mr-2 h-4 w-4" /> + <span>Billing</span> + <DropdownMenuShortcut>βB</DropdownMenuShortcut> + </DropdownMenuItem> + <DropdownMenuItem> + <Settings className="mr-2 h-4 w-4" /> + <span>Settings</span> + <DropdownMenuShortcut>βS</DropdownMenuShortcut> + </DropdownMenuItem> + <DropdownMenuItem> + <PlusCircle className="mr-2 h-4 w-4" /> + <span>New Team</span> + </DropdownMenuItem> + </DropdownMenuGroup> + <DropdownMenuSeparator /> + <DropdownMenuItem onSelect={onLogout}> + <LogOut className="mr-2 h-4 w-4" /> + <span>Log out</span> + <DropdownMenuShortcut>β§βQ</DropdownMenuShortcut> + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + ); +} diff --git a/apps/workflows-dashboard/src/components/molecules/UserNavigation/index.ts b/apps/workflows-dashboard/src/components/molecules/UserNavigation/index.ts new file mode 100644 index 0000000000..e620fb00b3 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/UserNavigation/index.ts @@ -0,0 +1 @@ +export * from './UserNavigation'; diff --git a/apps/workflows-dashboard/src/components/molecules/WeeklyBarChart/WeeklyBarChart.tsx b/apps/workflows-dashboard/src/components/molecules/WeeklyBarChart/WeeklyBarChart.tsx new file mode 100644 index 0000000000..6fb20f507b --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WeeklyBarChart/WeeklyBarChart.tsx @@ -0,0 +1,43 @@ +import { memo, useMemo } from 'react'; +import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis } from 'recharts'; +import { getWeekDayName } from '@app/components/molecules/WeeklyBarChart/utils/get-week-day-name'; + +export interface WeeklyBarChartData { + value: number; + date: Date; +} + +interface BarChartData { + name: string; + value: number; +} + +interface Props { + data: WeeklyBarChartData[]; +} + +export const WeeklyBarChart = memo(({ data }: Props) => { + const chartData = useMemo((): BarChartData[] => { + return data.map(item => ({ + name: `${getWeekDayName(item.date)} ${item.date.getDate()}`, + value: item.value, + })); + }, [data]); + + return ( + <ResponsiveContainer width="100%" height={'100%'}> + <BarChart data={chartData}> + <XAxis dataKey="name" stroke="#888888" fontSize={12} tickLine={false} axisLine={false} /> + <YAxis + stroke="#888888" + fontSize={12} + tickLine={false} + axisLine={false} + padding={{ top: 0, bottom: 0 }} + allowDecimals={false} + /> + <Bar dataKey="value" fill="#adfa1d" radius={[4, 4, 0, 0]} /> + </BarChart> + </ResponsiveContainer> + ); +}); diff --git a/apps/workflows-dashboard/src/components/molecules/WeeklyBarChart/index.ts b/apps/workflows-dashboard/src/components/molecules/WeeklyBarChart/index.ts new file mode 100644 index 0000000000..046c66018e --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WeeklyBarChart/index.ts @@ -0,0 +1 @@ +export * from './WeeklyBarChart'; diff --git a/apps/workflows-dashboard/src/components/molecules/WeeklyBarChart/utils/get-week-day-name.ts b/apps/workflows-dashboard/src/components/molecules/WeeklyBarChart/utils/get-week-day-name.ts new file mode 100644 index 0000000000..bf8a3ba241 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WeeklyBarChart/utils/get-week-day-name.ts @@ -0,0 +1,2 @@ +export const getWeekDayName = (date: Date) => + date.toLocaleDateString('en-US', { weekday: 'short' }); diff --git a/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/WorkflowsTable.tsx b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/WorkflowsTable.tsx new file mode 100644 index 0000000000..ebbce4418a --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/WorkflowsTable.tsx @@ -0,0 +1,134 @@ +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@app/components/atoms/Table'; +import { memo, useMemo } from 'react'; +import classnames from 'classnames'; +import { useReactTable, flexRender, getCoreRowModel, SortingState } from '@tanstack/react-table'; +import { IWorkflow } from '@app/domains/workflows/api/workflow'; +import { defaultColumns } from '@app/components/molecules/WorkflowsTable/columns'; +import Scrollbars from 'react-custom-scrollbars'; +import { + InputColumn, + WorkflowsTableSorting, + WorkflowTableColumnDef, +} from '@app/components/molecules/WorkflowsTable/types'; +import keyBy from 'lodash/keyBy'; +import { mergeColumns } from '@app/components/molecules/WorkflowsTable/utils/merge-columns'; + +interface Props { + items: IWorkflow[]; + sorting?: WorkflowsTableSorting; + isFetching?: boolean; + columns?: InputColumn[]; + onSort: (key: string, direction: 'asc' | 'desc') => void; +} + +export const WorkflowsTable = memo(({ items, isFetching, sorting, columns, onSort }: Props) => { + // merging column parameters if provided + const tableColumns = useMemo((): WorkflowTableColumnDef<IWorkflow>[] => { + if (!Array.isArray(columns) || !columns.length) return defaultColumns; + + const columnsMap = keyBy(columns, 'id'); + + return defaultColumns.map(defaultColumn => { + const columnParams = columnsMap[defaultColumn.accessorKey]; + + if (!columnParams) return defaultColumn; + + return mergeColumns(defaultColumn, columnParams); + }); + }, [columns]); + + const table = useReactTable({ + columns: tableColumns, + data: items, + enableColumnResizing: true, + manualSorting: false, + state: { + sorting: sorting + ? [ + { + id: sorting.key, + desc: sorting.direction === 'desc', + }, + ] + : [], + }, + onSortingChange: updater => { + if (typeof updater === 'function') { + const newSortingValue = updater(table.getState().sorting); + table.setSorting(newSortingValue); + } else { + const sortingState = updater as SortingState; + onSort(sortingState[0].id, sortingState[0].desc ? 'desc' : 'asc'); + } + }, + getCoreRowModel: getCoreRowModel(), + }); + + const isEmpty = !items.length && !isFetching; + + return ( + <div + className={classnames('relative w-full overflow-auto bg-white', 'rounded-md border', { + ['opacity-40']: isFetching, + ['pointer-events-none']: isFetching, + })} + > + <Scrollbars autoHide> + <Table> + <TableHeader> + {table.getHeaderGroups().map(({ id: headerRowId, headers }) => { + return ( + <TableRow key={headerRowId}> + {headers.map(header => ( + <TableHead key={header.id} className="sticky top-0 w-1/4 bg-white"> + {flexRender(header.column.columnDef.header, header.getContext())} + </TableHead> + ))} + </TableRow> + ); + })} + </TableHeader> + <TableBody> + {isEmpty ? ( + <TableRow> + <TableCell colSpan={table.getAllColumns().length} className="text-center"> + Workflows not found. + </TableCell> + </TableRow> + ) : ( + table.getRowModel().rows.map(row => { + return ( + <TableRow key={row.id}> + {row.getVisibleCells().map(cell => { + return ( + <TableCell + key={cell.id} + className="max-w-1/4 w-1/4 whitespace-nowrap" + title={String(cell.getValue())} + style={{ + minWidth: `${cell.column.getSize()}px`, + }} + > + <div className="line-clamp-1 overflow-hidden text-ellipsis break-all"> + {flexRender(cell.column.columnDef.cell, cell.getContext())} + </div> + </TableCell> + ); + })} + </TableRow> + ); + }) + )} + </TableBody> + </Table> + </Scrollbars> + </div> + ); +}); diff --git a/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/columns.tsx b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/columns.tsx new file mode 100644 index 0000000000..2599698181 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/columns.tsx @@ -0,0 +1,70 @@ +import { HealthIndicator } from '@app/components/atoms/HealthIndicator'; +import { ContextViewColumn } from '@app/components/molecules/WorkflowsTable/components/ContextViewColumn'; +import { DataTableColumnHeader } from '@app/components/molecules/WorkflowsTable/components/DataTableColumnHeader'; +import { WorkflowTableColumnDef } from '@app/components/molecules/WorkflowsTable/types'; +import { formatDate } from '@app/components/molecules/WorkflowsTable/utils/format-date'; +import { IWorkflow } from '@app/domains/workflows/api/workflow'; +import { getWorkflowHealthStatus } from '@app/utils/get-workflow-health-status'; + +export const defaultColumns: WorkflowTableColumnDef<IWorkflow>[] = [ + { + accessorKey: 'id', + cell: info => info.getValue<string>(), + header: () => 'ID', + }, + { + accessorKey: 'workflowDefinitionName', + cell: info => info.getValue<string>(), + header: ({ column }) => ( + <DataTableColumnHeader column={column} title="Workflow Definition Name" /> + ), + }, + { + accessorKey: 'status', + cell: info => ( + <div className="font-inter flex flex-row flex-nowrap gap-4 font-medium capitalize"> + <HealthIndicator healthStatus={getWorkflowHealthStatus(info.row.original)} /> + {info.getValue<string>() || ''} + </div> + ), + header: ({ column }) => <DataTableColumnHeader column={column} title="Status" />, + }, + { + accessorKey: 'state', + cell: info => info.getValue<string>(), + header: ({ column }) => <DataTableColumnHeader column={column} title="State" />, + }, + { + accessorKey: 'assignee', + accessorFn: row => (row.assignee ? `${row.assignee.firstName} ${row.assignee.lastName}` : '-'), + cell: info => info.getValue<string>(), + header: ({ column }) => <DataTableColumnHeader column={column} title="Assign To" />, + }, + { + accessorKey: 'context', + accessorFn: row => JSON.stringify(row.context), + cell: info => <ContextViewColumn context={info.getValue<string>()} />, + header: () => 'Context', + }, + { + accessorKey: 'view-workflow', + accessorFn: row => row.id, + cell: () => '-', + header: () => 'Workflow', + }, + { + accessorKey: 'resolvedAt', + cell: info => (info.getValue<Date>() ? formatDate(info.getValue<Date>()) : '-'), + header: ({ column }) => <DataTableColumnHeader column={column} title="Resolved At" />, + }, + { + accessorKey: 'createdBy', + cell: info => info.getValue<string>(), + header: ({ column }) => <DataTableColumnHeader column={column} title="Created By" />, + }, + { + accessorKey: 'createdAt', + cell: info => formatDate(info.getValue<Date>()), + header: ({ column }) => <DataTableColumnHeader column={column} title="Created At" />, + }, +]; diff --git a/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/components/ContextViewColumn/ContextViewColumn.tsx b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/components/ContextViewColumn/ContextViewColumn.tsx new file mode 100644 index 0000000000..67e3e1b39e --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/components/ContextViewColumn/ContextViewColumn.tsx @@ -0,0 +1,29 @@ +import { Button } from '@app/components/atoms/Button'; +import { Dialog, DialogContent, DialogTrigger } from '@app/components/atoms/Dialog'; +import { CodeIcon } from 'lucide-react'; +import Scrollbars from 'react-custom-scrollbars'; +import ReactJson from 'react-json-view'; + +interface Props { + context: string; +} + +export const ContextViewColumn = ({ context }: Props) => { + return ( + <Dialog> + <DialogTrigger asChild> + <Button className="flex items-center gap-2"> + <CodeIcon size="16" /> + View context + </Button> + </DialogTrigger> + <DialogContent className="h-[80vh] min-w-[80%]"> + <div className="pr-4"> + <Scrollbars> + <ReactJson src={context ? JSON.parse(context) : {}} /> + </Scrollbars> + </div> + </DialogContent> + </Dialog> + ); +}; diff --git a/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/components/ContextViewColumn/index.ts b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/components/ContextViewColumn/index.ts new file mode 100644 index 0000000000..66b740a642 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/components/ContextViewColumn/index.ts @@ -0,0 +1 @@ +export * from './ContextViewColumn'; diff --git a/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/components/DataTableColumnHeader/DataTableColumnHeader.tsx b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/components/DataTableColumnHeader/DataTableColumnHeader.tsx new file mode 100644 index 0000000000..18aa463ef8 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/components/DataTableColumnHeader/DataTableColumnHeader.tsx @@ -0,0 +1,61 @@ +import { Column } from '@tanstack/react-table'; +import { ChevronsUpDown, EyeOff, SortAsc, SortDesc } from 'lucide-react'; + +import { cn } from '@app/lib/utils'; +import { Button } from '@app/components/atoms/Button'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@app/components/atoms/Dropdown'; + +interface DataTableColumnHeaderProps<TData, TValue> extends React.HTMLAttributes<HTMLDivElement> { + column: Column<TData, TValue>; + title: string; +} + +export function DataTableColumnHeader<TData, TValue>({ + column, + title, + className, +}: DataTableColumnHeaderProps<TData, TValue>) { + if (!column.getCanSort()) { + return <div className={cn(className)}>{title}</div>; + } + + return ( + <div className={cn('flex items-center space-x-2 whitespace-nowrap', className)}> + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button variant="ghost" size="sm" className="data-[state=open]:bg-accent -ml-3 h-8"> + <span>{title}</span> + {column.getIsSorted() === 'desc' ? ( + <SortDesc className="ml-2 h-4 w-4" /> + ) : column.getIsSorted() === 'asc' ? ( + <SortAsc className="ml-2 h-4 w-4" /> + ) : ( + <ChevronsUpDown className="ml-2 h-4 w-4" /> + )} + </Button> + </DropdownMenuTrigger> + <DropdownMenuContent align="start"> + <DropdownMenuItem onClick={() => column.toggleSorting(false)}> + <SortAsc className="text-muted-foreground/70 mr-2 h-3.5 w-3.5" /> + Asc + </DropdownMenuItem> + <DropdownMenuItem onClick={() => column.toggleSorting(true)}> + <SortDesc className="text-muted-foreground/70 mr-2 h-3.5 w-3.5" /> + Desc + </DropdownMenuItem> + <DropdownMenuSeparator /> + <DropdownMenuItem onClick={() => column.toggleVisibility(false)}> + <EyeOff className="text-muted-foreground/70 mr-2 h-3.5 w-3.5" /> + Hide + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + </div> + ); +} diff --git a/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/components/DataTableColumnHeader/index.ts b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/components/DataTableColumnHeader/index.ts new file mode 100644 index 0000000000..6aa469ad2b --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/components/DataTableColumnHeader/index.ts @@ -0,0 +1 @@ +export * from './DataTableColumnHeader'; diff --git a/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/index.ts b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/index.ts new file mode 100644 index 0000000000..0d82108883 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/index.ts @@ -0,0 +1 @@ +export * from './WorkflowsTable'; diff --git a/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/types.ts b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/types.ts new file mode 100644 index 0000000000..2758c6fa2b --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/types.ts @@ -0,0 +1,28 @@ +import { IWorkflow } from '@app/domains/workflows/api/workflow'; +import { AccessorFnColumnDef, ColumnDef } from '@tanstack/react-table'; + +export interface WorkflowsTableSorting { + key: string; + direction: 'asc' | 'desc'; +} + +export type WorkflowTableColumnKeys = + | 'id' + | 'workflowDefinitionName' + | 'status' + | 'state' + | 'assignee' + | 'context' + | 'view-workflow' + | 'resolvedAt' + | 'createdBy' + | 'createdAt'; + +export type WorkflowTableColumnDef<TData> = Omit<ColumnDef<TData>, 'accessorKey'> & { + accessorFn?: AccessorFnColumnDef<TData>['accessorFn']; + accessorKey: WorkflowTableColumnKeys; +}; + +export type InputColumn<TData = IWorkflow> = Partial<WorkflowTableColumnDef<TData>> & { + id: WorkflowTableColumnKeys; +}; diff --git a/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/utils/format-date.ts b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/utils/format-date.ts new file mode 100644 index 0000000000..b078ba7457 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/utils/format-date.ts @@ -0,0 +1,3 @@ +export function formatDate(date: Date): string { + return new Date(date).toLocaleString(); +} diff --git a/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/utils/merge-columns.ts b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/utils/merge-columns.ts new file mode 100644 index 0000000000..9898ac3b59 --- /dev/null +++ b/apps/workflows-dashboard/src/components/molecules/WorkflowsTable/utils/merge-columns.ts @@ -0,0 +1,14 @@ +import { + InputColumn, + WorkflowTableColumnDef, +} from '@app/components/molecules/WorkflowsTable/types'; + +export function mergeColumns<TColumnData>( + leftColumn: WorkflowTableColumnDef<TColumnData>, + rightColumn: InputColumn<TColumnData>, +) { + return { + ...leftColumn, + ...rightColumn, + }; +} diff --git a/apps/workflows-dashboard/src/components/organisms/Form/Form.Control.tsx b/apps/workflows-dashboard/src/components/organisms/Form/Form.Control.tsx new file mode 100644 index 0000000000..bcb5e54041 --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Form/Form.Control.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { useFormField } from './hooks/useFormField/useFormField'; + +export const FormControl = React.forwardRef< + React.ElementRef<typeof Slot>, + React.ComponentPropsWithoutRef<typeof Slot> +>(({ ...props }, ref) => { + const { error, formItemId, formDescriptionId, formMessageId } = useFormField(); + + return ( + <Slot + ref={ref} + id={formItemId} + aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`} + aria-invalid={!!error} + {...props} + /> + ); +}); +FormControl.displayName = 'FormControl'; diff --git a/apps/workflows-dashboard/src/components/organisms/Form/Form.Description.tsx b/apps/workflows-dashboard/src/components/organisms/Form/Form.Description.tsx new file mode 100644 index 0000000000..b9d9b65949 --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Form/Form.Description.tsx @@ -0,0 +1,20 @@ +import { ctw } from '@app/common/utils/ctw/ctw'; +import * as React from 'react'; +import { useFormField } from './hooks/useFormField/useFormField'; + +export const FormDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes<HTMLParagraphElement> +>(({ className, ...props }, ref) => { + const { formDescriptionId } = useFormField(); + + return ( + <p + ref={ref} + id={formDescriptionId} + className={ctw('text-muted-foreground text-sm', className)} + {...props} + /> + ); +}); +FormDescription.displayName = 'FormDescription'; diff --git a/apps/workflows-dashboard/src/components/organisms/Form/Form.Field.tsx b/apps/workflows-dashboard/src/components/organisms/Form/Form.Field.tsx new file mode 100644 index 0000000000..5481e2b1b9 --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Form/Form.Field.tsx @@ -0,0 +1,15 @@ +import { Controller, ControllerProps, FieldPath, FieldValues } from 'react-hook-form'; +import { FormFieldContext } from './Form.FieldContext'; + +export const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>, +>({ + ...props +}: ControllerProps<TFieldValues, TName>) => { + return ( + <FormFieldContext.Provider value={{ name: props.name }}> + <Controller {...props} /> + </FormFieldContext.Provider> + ); +}; diff --git a/apps/workflows-dashboard/src/components/organisms/Form/Form.FieldContext.tsx b/apps/workflows-dashboard/src/components/organisms/Form/Form.FieldContext.tsx new file mode 100644 index 0000000000..081e9659e7 --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Form/Form.FieldContext.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import { FormFieldContextValue } from './types'; + +export const FormFieldContext = React.createContext<FormFieldContextValue>( + {} as FormFieldContextValue, +); diff --git a/apps/workflows-dashboard/src/components/organisms/Form/Form.Item.tsx b/apps/workflows-dashboard/src/components/organisms/Form/Form.Item.tsx new file mode 100644 index 0000000000..36ec49c81d --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Form/Form.Item.tsx @@ -0,0 +1,16 @@ +import { ctw } from '@app/common/utils/ctw/ctw'; +import * as React from 'react'; +import { FormItemContext } from './Form.ItemContext'; + +export const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( + ({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + <FormItemContext.Provider value={{ id }}> + <div ref={ref} className={ctw('space-y-2', className)} {...props} /> + </FormItemContext.Provider> + ); + }, +); +FormItem.displayName = 'FormItem'; diff --git a/apps/workflows-dashboard/src/components/organisms/Form/Form.ItemContext.tsx b/apps/workflows-dashboard/src/components/organisms/Form/Form.ItemContext.tsx new file mode 100644 index 0000000000..4d3f369c5a --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Form/Form.ItemContext.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import { FormItemContextValue } from './types'; + +export const FormItemContext = React.createContext<FormItemContextValue>( + {} as FormItemContextValue, +); diff --git a/apps/workflows-dashboard/src/components/organisms/Form/Form.Label.tsx b/apps/workflows-dashboard/src/components/organisms/Form/Form.Label.tsx new file mode 100644 index 0000000000..b6f4ed1dba --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Form/Form.Label.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import * as LabelPrimitive from '@radix-ui/react-label'; +import { useFormField } from './hooks/useFormField/useFormField'; +import { Label } from '@app/components/atoms/Label/Label'; +import { ctw } from '@app/common/utils/ctw/ctw'; + +export const FormLabel = React.forwardRef< + React.ElementRef<typeof LabelPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return ( + <Label + ref={ref} + className={ctw(error && 'text-destructive', className)} + htmlFor={formItemId} + {...props} + /> + ); +}); +FormLabel.displayName = 'FormLabel'; diff --git a/apps/workflows-dashboard/src/components/organisms/Form/Form.Message.tsx b/apps/workflows-dashboard/src/components/organisms/Form/Form.Message.tsx new file mode 100644 index 0000000000..3a8cdc246d --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Form/Form.Message.tsx @@ -0,0 +1,27 @@ +import { ctw } from '@app/common/utils/ctw/ctw'; +import * as React from 'react'; +import { useFormField } from './hooks/useFormField/useFormField'; + +export const FormMessage = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes<HTMLParagraphElement> +>(({ className, children, ...props }, ref) => { + const { error, formMessageId } = useFormField(); + const body = error ? String(error?.message) : children; + + if (!body) { + return null; + } + + return ( + <p + ref={ref} + id={formMessageId} + className={ctw('text-destructive text-sm font-medium', className)} + {...props} + > + {body} + </p> + ); +}); +FormMessage.displayName = 'FormMessage'; diff --git a/apps/workflows-dashboard/src/components/organisms/Form/Form.tsx b/apps/workflows-dashboard/src/components/organisms/Form/Form.tsx new file mode 100644 index 0000000000..287908ec98 --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Form/Form.tsx @@ -0,0 +1,3 @@ +import { FormProvider } from 'react-hook-form'; + +export const Form = FormProvider; diff --git a/apps/workflows-dashboard/src/components/organisms/Form/hooks/useFormField/useFormField.tsx b/apps/workflows-dashboard/src/components/organisms/Form/hooks/useFormField/useFormField.tsx new file mode 100644 index 0000000000..ed68a2c47a --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Form/hooks/useFormField/useFormField.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { useFormContext } from 'react-hook-form'; +import { FormFieldContext } from '../../Form.FieldContext'; +import { FormItemContext } from '../../Form.ItemContext'; + +export const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error('useFormField should be used within <FormField>'); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +}; diff --git a/apps/workflows-dashboard/src/components/organisms/Form/types.ts b/apps/workflows-dashboard/src/components/organisms/Form/types.ts new file mode 100644 index 0000000000..f1751cb966 --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Form/types.ts @@ -0,0 +1,12 @@ +import { FieldPath, FieldValues } from 'react-hook-form'; + +export type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>, +> = { + name: TName; +}; + +export type FormItemContextValue = { + id: string; +}; diff --git a/apps/workflows-dashboard/src/components/organisms/Header/Header.tsx b/apps/workflows-dashboard/src/components/organisms/Header/Header.tsx new file mode 100644 index 0000000000..23a34d24e0 --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Header/Header.tsx @@ -0,0 +1,25 @@ +import { useLogoutMutation } from '@app/common/hooks/useLogoutMutation'; +import { Input } from '@app/components/atoms/Input'; +import { Navigation } from '@app/components/molecules/Navigation'; +import { UserNavigation } from '@app/components/molecules/UserNavigation'; +import { headerNavigationLinks } from './header-navigation-links'; + +export const Header = () => { + const { logout } = useLogoutMutation(); + + return ( + <div className="border-b"> + <div className="flex h-16 flex-nowrap items-center justify-between px-4"> + <div className="flex flex-1 gap-4"> + <UserNavigation onLogout={logout} /> + <Navigation items={headerNavigationLinks} /> + </div> + <div className="flex gap-4"> + <div> + <Input placeholder="Search" /> + </div> + </div> + </div> + </div> + ); +}; diff --git a/apps/workflows-dashboard/src/components/organisms/Header/header-navigation-links.ts b/apps/workflows-dashboard/src/components/organisms/Header/header-navigation-links.ts new file mode 100644 index 0000000000..2963b14c96 --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Header/header-navigation-links.ts @@ -0,0 +1,12 @@ +import { NavigationLink } from '@app/components/molecules/Navigation'; + +export const headerNavigationLinks: NavigationLink[] = [ + { + path: '/overview', + label: 'Overview', + }, + { + path: '/workflows', + label: 'Workflows', + }, +]; diff --git a/apps/workflows-dashboard/src/components/organisms/Header/index.ts b/apps/workflows-dashboard/src/components/organisms/Header/index.ts new file mode 100644 index 0000000000..266dec8a1b --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/Header/index.ts @@ -0,0 +1 @@ +export * from './Header'; diff --git a/apps/workflows-dashboard/src/components/organisms/XstateVisualizer/XstateVisualizer.tsx b/apps/workflows-dashboard/src/components/organisms/XstateVisualizer/XstateVisualizer.tsx new file mode 100644 index 0000000000..6eea62d027 --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/XstateVisualizer/XstateVisualizer.tsx @@ -0,0 +1,46 @@ +import { memo, useLayoutEffect, useMemo, useRef } from 'react'; +import { inspect } from '@xstate/inspect'; +import { createMachine } from 'xstate'; +import { useInterpret, useMachine } from '@xstate/react'; +import { deserializeStateDefinition } from '@app/components/organisms/XstateVisualizer/utils/deserialize-state-definition'; + +interface Props { + stateDefinition: Record<string, any>; + state?: string; +} + +export const XstateVisualizer = memo(({ stateDefinition, state }: Props) => { + const _machine = useMemo( + () => + createMachine( + deserializeStateDefinition({ + ...stateDefinition, + initial: state || stateDefinition.initial, + }), + ), + [stateDefinition, state], + ); + const [stateMachine] = useMachine(_machine); + + useInterpret(_machine, { devTools: true }); + + const iframeRef = useRef<HTMLIFrameElement | null>(null); + + useLayoutEffect(() => { + if (!iframeRef.current) return; + + inspect({ iframe: iframeRef.current }); + }, [iframeRef, state, stateMachine.value]); + + return ( + <div className="h-full w-full"> + <iframe + ref={iframeRef} + data-xstate + style={{ width: 'calc(100% + clamp(40rem, 40rem + 0px, 100%))' }} + // width="100%" + height="100%" + /> + </div> + ); +}); diff --git a/apps/workflows-dashboard/src/components/organisms/XstateVisualizer/index.ts b/apps/workflows-dashboard/src/components/organisms/XstateVisualizer/index.ts new file mode 100644 index 0000000000..72b67af0af --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/XstateVisualizer/index.ts @@ -0,0 +1 @@ +export * from './XstateVisualizer'; diff --git a/apps/workflows-dashboard/src/components/organisms/XstateVisualizer/utils/deserialize-state-definition.ts b/apps/workflows-dashboard/src/components/organisms/XstateVisualizer/utils/deserialize-state-definition.ts new file mode 100644 index 0000000000..f01bcaabb7 --- /dev/null +++ b/apps/workflows-dashboard/src/components/organisms/XstateVisualizer/utils/deserialize-state-definition.ts @@ -0,0 +1,34 @@ +import forEach from 'lodash/forEach'; + +function removeKeyAndValue<T extends object>( + object: T, + keyToSearch: string, + valueToSearch: unknown, +): void { + if (typeof object !== 'object' || object === null) { + return; + } + + if (Array.isArray(object)) { + forEach(object, item => removeKeyAndValue(item, keyToSearch, valueToSearch)); + return; + } + + forEach(object, (value, key) => { + if (key === keyToSearch && value === valueToSearch) { + delete object[key as keyof T]; + } else { + removeKeyAndValue(value as T, keyToSearch, valueToSearch); + } + }); +} + +// type: final breaks X-state visualizer +// to avoid this for now we have to remove those values from state defintiion +export const deserializeStateDefinition = (state: Record<string, any>) => { + state = JSON.parse(JSON.stringify(state)); + + removeKeyAndValue(state, 'type', 'final'); + + return state; +}; diff --git a/apps/workflows-dashboard/src/domains/auth/api/login/index.ts b/apps/workflows-dashboard/src/domains/auth/api/login/index.ts new file mode 100644 index 0000000000..496bc6e43a --- /dev/null +++ b/apps/workflows-dashboard/src/domains/auth/api/login/index.ts @@ -0,0 +1,2 @@ +export * from './login.api'; +export * from './login.types'; diff --git a/apps/workflows-dashboard/src/domains/auth/api/login/login.api.ts b/apps/workflows-dashboard/src/domains/auth/api/login/login.api.ts new file mode 100644 index 0000000000..75bf9ce709 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/auth/api/login/login.api.ts @@ -0,0 +1,8 @@ +import { GetSignInDto, GetSignInResponse } from '@app/domains/auth/api/login/login.types'; +import { request } from '@app/lib/request'; + +export async function fetchSignIn(dto: GetSignInDto): Promise<GetSignInResponse> { + const result = await request.post<GetSignInResponse>('internal/auth/login', dto); + + return result.data; +} diff --git a/apps/workflows-dashboard/src/domains/auth/api/login/login.types.ts b/apps/workflows-dashboard/src/domains/auth/api/login/login.types.ts new file mode 100644 index 0000000000..edf4260dd4 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/auth/api/login/login.types.ts @@ -0,0 +1,10 @@ +import { IUser } from '@app/domains/auth/common/types'; + +export interface GetSignInDto { + email: string; + password: string; +} + +export interface GetSignInResponse { + user: IUser; +} diff --git a/apps/workflows-dashboard/src/domains/auth/api/logout/index.ts b/apps/workflows-dashboard/src/domains/auth/api/logout/index.ts new file mode 100644 index 0000000000..691c49bae5 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/auth/api/logout/index.ts @@ -0,0 +1 @@ +export * from './logout.api'; diff --git a/apps/workflows-dashboard/src/domains/auth/api/logout/logout.api.ts b/apps/workflows-dashboard/src/domains/auth/api/logout/logout.api.ts new file mode 100644 index 0000000000..2bc0917226 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/auth/api/logout/logout.api.ts @@ -0,0 +1,7 @@ +import { request } from '@app/lib/request'; + +export async function fetchLogout(): Promise<boolean> { + await request.post('/internal/auth/logout'); + + return true; +} diff --git a/apps/workflows-dashboard/src/domains/auth/api/session/index.ts b/apps/workflows-dashboard/src/domains/auth/api/session/index.ts new file mode 100644 index 0000000000..1c393ebff5 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/auth/api/session/index.ts @@ -0,0 +1 @@ +export * from './session.api'; diff --git a/apps/workflows-dashboard/src/domains/auth/api/session/query-keys.ts b/apps/workflows-dashboard/src/domains/auth/api/session/query-keys.ts new file mode 100644 index 0000000000..c442aaaac4 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/auth/api/session/query-keys.ts @@ -0,0 +1,9 @@ +import { fetchSession } from '@app/domains/auth/api/session/session.api'; +import { createQueryKeys } from '@lukemorales/query-key-factory'; + +export const sessionKeys = createQueryKeys('session', { + details: () => ({ + queryKey: ['session'], + queryFn: fetchSession, + }), +}); diff --git a/apps/workflows-dashboard/src/domains/auth/api/session/session.api.ts b/apps/workflows-dashboard/src/domains/auth/api/session/session.api.ts new file mode 100644 index 0000000000..78793f28b2 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/auth/api/session/session.api.ts @@ -0,0 +1,9 @@ +import { GetSessionResponse } from '@app/domains/auth/api/session/session.types'; +import { IUser } from '@app/domains/auth/common/types'; +import { request } from '@app/lib/request'; + +export async function fetchSession(): Promise<IUser | null> { + const result = await request.get<GetSessionResponse>('internal/auth/session'); + + return result.data.user ? result.data.user : null; +} diff --git a/apps/workflows-dashboard/src/domains/auth/api/session/session.types.ts b/apps/workflows-dashboard/src/domains/auth/api/session/session.types.ts new file mode 100644 index 0000000000..a6abb7cd14 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/auth/api/session/session.types.ts @@ -0,0 +1,5 @@ +import { IUser } from '@app/domains/auth/common/types'; + +export interface GetSessionResponse { + user?: IUser; +} diff --git a/apps/workflows-dashboard/src/domains/auth/common/types.ts b/apps/workflows-dashboard/src/domains/auth/common/types.ts new file mode 100644 index 0000000000..b907960e9f --- /dev/null +++ b/apps/workflows-dashboard/src/domains/auth/common/types.ts @@ -0,0 +1,8 @@ +export interface IUser { + id: string; + email: string; + firstName: string; + lastName: string; + roles: string[]; + lastActiveAt: Date | null; +} diff --git a/apps/workflows-dashboard/src/domains/user/api/user-stats/index.ts b/apps/workflows-dashboard/src/domains/user/api/user-stats/index.ts new file mode 100644 index 0000000000..113a186ab6 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/user/api/user-stats/index.ts @@ -0,0 +1,3 @@ +export * from './query-keys'; +export * from './user-stats.api'; +export * from './user-stats.types'; diff --git a/apps/workflows-dashboard/src/domains/user/api/user-stats/query-keys.ts b/apps/workflows-dashboard/src/domains/user/api/user-stats/query-keys.ts new file mode 100644 index 0000000000..50d6482457 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/user/api/user-stats/query-keys.ts @@ -0,0 +1,20 @@ +import { + fetchUserDailyCasesResolvedStats, + fetchUserStats, +} from '@app/domains/user/api/user-stats/user-stats.api'; +import { + GetUserDailyCasesResolvedStatsDto, + GetUserStatsDto, +} from '@app/domains/user/api/user-stats/user-stats.types'; +import { createQueryKeys } from '@lukemorales/query-key-factory'; + +export const userStatsQueryKeys = createQueryKeys('user-stats', { + userStats: (query: GetUserStatsDto) => ({ + queryKey: [query], + queryFn: () => fetchUserStats(query), + }), + userDailyCasesResolvedStats: (query: GetUserDailyCasesResolvedStatsDto) => ({ + queryKey: [query], + queryFn: () => fetchUserDailyCasesResolvedStats(query), + }), +}); diff --git a/apps/workflows-dashboard/src/domains/user/api/user-stats/user-stats.api.ts b/apps/workflows-dashboard/src/domains/user/api/user-stats/user-stats.api.ts new file mode 100644 index 0000000000..7dfd0b97e1 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/user/api/user-stats/user-stats.api.ts @@ -0,0 +1,37 @@ +import { + GetUserDailyCasesResolvedStatsDto, + GetUserStatsDto, + IResolvedCasesDailyMetric, + IUserStats, + UserStats, +} from '@app/domains/user/api/user-stats/user-stats.types'; +import { request } from '@app/lib/request'; + +export const fetchUserStats = async (query: GetUserStatsDto): Promise<UserStats> => { + const result = await request.get<IUserStats>(`/metrics/users/workflow-processing-statistic`, { + params: query, + }); + + const { approvalRate, averageAssignmentTime, averageResolutionTime, averageReviewTime } = + result.data || ({} as IUserStats); + + const userStats: UserStats = { + approvalRate: parseFloat(approvalRate), + averageAssignmentTime: parseInt(averageAssignmentTime), + averageResolutionTime: parseInt(averageResolutionTime), + averageReviewTime: parseInt(averageReviewTime), + }; + + return userStats; +}; + +export const fetchUserDailyCasesResolvedStats = async ( + query: GetUserDailyCasesResolvedStatsDto, +): Promise<IResolvedCasesDailyMetric[]> => { + const result = await request.get<IResolvedCasesDailyMetric[]>( + `/metrics/users/cases-resolved-daily`, + { params: query }, + ); + + return result.data; +}; diff --git a/apps/workflows-dashboard/src/domains/user/api/user-stats/user-stats.types.ts b/apps/workflows-dashboard/src/domains/user/api/user-stats/user-stats.types.ts new file mode 100644 index 0000000000..6dd4a5d08b --- /dev/null +++ b/apps/workflows-dashboard/src/domains/user/api/user-stats/user-stats.types.ts @@ -0,0 +1,26 @@ +export interface IResolvedCasesDailyMetric { + date: string; + count: number; +} + +export interface IUserStats { + approvalRate: string; + averageResolutionTime: string; + averageAssignmentTime: string; + averageReviewTime: string; +} + +export interface UserStats { + approvalRate: number; + averageResolutionTime: number; + averageAssignmentTime: number; + averageReviewTime: number; +} + +export interface GetUserStatsDto { + fromDate: number; +} + +export interface GetUserDailyCasesResolvedStatsDto { + fromDate: number; +} diff --git a/apps/workflows-dashboard/src/domains/user/api/users-stats/index.ts b/apps/workflows-dashboard/src/domains/user/api/users-stats/index.ts new file mode 100644 index 0000000000..f6fb0b6a1f --- /dev/null +++ b/apps/workflows-dashboard/src/domains/user/api/users-stats/index.ts @@ -0,0 +1,3 @@ +export * from './query-keys'; +export * from './users-stats.api'; +export * from './users-stats.types'; diff --git a/apps/workflows-dashboard/src/domains/user/api/users-stats/query-keys.ts b/apps/workflows-dashboard/src/domains/user/api/users-stats/query-keys.ts new file mode 100644 index 0000000000..224fd662ab --- /dev/null +++ b/apps/workflows-dashboard/src/domains/user/api/users-stats/query-keys.ts @@ -0,0 +1,20 @@ +import { + fetchUsersAssignedCasesStats, + fetchUsersResolvedCasesStats, +} from '@app/domains/user/api/users-stats/users-stats.api'; +import { + GetUsersAssignedCasesStatsDto, + GetUsersCaseResolvingStats, +} from '@app/domains/user/api/users-stats/users-stats.types'; +import { createQueryKeys } from '@lukemorales/query-key-factory'; + +export const usersStatsQueryKeys = createQueryKeys('users-stats', { + casesResolvedStats: (query: GetUsersCaseResolvingStats) => ({ + queryKey: [query], + queryFn: () => fetchUsersResolvedCasesStats(query), + }), + casesAssignedStats: (query: GetUsersAssignedCasesStatsDto) => ({ + queryKey: [query], + queryFn: () => fetchUsersAssignedCasesStats(query), + }), +}); diff --git a/apps/workflows-dashboard/src/domains/user/api/users-stats/users-stats.api.ts b/apps/workflows-dashboard/src/domains/user/api/users-stats/users-stats.api.ts new file mode 100644 index 0000000000..4f75c22fd3 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/user/api/users-stats/users-stats.api.ts @@ -0,0 +1,30 @@ +import { + GetUsersAssignedCasesStatsDto, + GetUsersCaseResolvingStats, + IUserCaseResolvingStats, +} from '@app/domains/user/api/users-stats/users-stats.types'; +import { request } from '@app/lib/request'; + +export const fetchUsersResolvedCasesStats = async ( + query: GetUsersCaseResolvingStats, +): Promise<IUserCaseResolvingStats[]> => { + const result = await request.get<IUserCaseResolvingStats[]>( + '/metrics/users/users-resolved-cases-statistic', + { params: query }, + ); + + return result.data; +}; + +export const fetchUsersAssignedCasesStats = async ( + query: GetUsersAssignedCasesStatsDto, +): Promise<IUserCaseResolvingStats[]> => { + const result = await request.get<IUserCaseResolvingStats[]>( + `/metrics/users/users-assigned-cases-statistic`, + { + params: query, + }, + ); + + return result.data; +}; diff --git a/apps/workflows-dashboard/src/domains/user/api/users-stats/users-stats.types.ts b/apps/workflows-dashboard/src/domains/user/api/users-stats/users-stats.types.ts new file mode 100644 index 0000000000..fc685f96d6 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/user/api/users-stats/users-stats.types.ts @@ -0,0 +1,15 @@ +export interface IUserCaseResolvingStats { + id: string; + firstName: string; + lastName: string; + casesCount: number; + email: string; +} + +export interface GetUsersCaseResolvingStats { + fromDate: number; +} + +export interface GetUsersAssignedCasesStatsDto { + fromDate?: number; +} diff --git a/apps/workflows-dashboard/src/domains/workflows/api/users/index.ts b/apps/workflows-dashboard/src/domains/workflows/api/users/index.ts new file mode 100644 index 0000000000..01fa2fe910 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/api/users/index.ts @@ -0,0 +1,3 @@ +export * from './query-keys'; +export * from './users.api'; +export * from './users.types'; diff --git a/apps/workflows-dashboard/src/domains/workflows/api/users/query-keys.ts b/apps/workflows-dashboard/src/domains/workflows/api/users/query-keys.ts new file mode 100644 index 0000000000..2b9626984d --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/api/users/query-keys.ts @@ -0,0 +1,10 @@ +import { fetchActiveUsers } from '@app/domains/workflows/api/users/users.api'; +import { GetActiveUsersDto } from '@app/domains/workflows/api/users/users.types'; +import { createQueryKeys } from '@lukemorales/query-key-factory'; + +export const usersKeys = createQueryKeys('users', { + activeUsers: (query: GetActiveUsersDto) => ({ + queryKey: [{}, query], + queryFn: () => fetchActiveUsers(query), + }), +}); diff --git a/apps/workflows-dashboard/src/domains/workflows/api/users/users.api.ts b/apps/workflows-dashboard/src/domains/workflows/api/users/users.api.ts new file mode 100644 index 0000000000..40f0b1b01e --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/api/users/users.api.ts @@ -0,0 +1,9 @@ +import { IUser } from '@app/domains/auth/common/types'; +import { GetActiveUsersDto } from '@app/domains/workflows/api/users/users.types'; +import { request } from '@app/lib/request'; + +export const fetchActiveUsers = async (query: GetActiveUsersDto): Promise<IUser[]> => { + const result = await request.get<IUser[]>('/metrics/users', { params: query }); + + return result.data; +}; diff --git a/apps/workflows-dashboard/src/domains/workflows/api/users/users.types.ts b/apps/workflows-dashboard/src/domains/workflows/api/users/users.types.ts new file mode 100644 index 0000000000..b95f59e164 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/api/users/users.types.ts @@ -0,0 +1,11 @@ +export interface IUser { + id: string; + email: string; + firstName: string; + lastName: string; + lastActiveAt: string | null; +} + +export interface GetActiveUsersDto { + // fromDate: number; +} diff --git a/apps/workflows-dashboard/src/domains/workflows/api/workflow-metrics/index.ts b/apps/workflows-dashboard/src/domains/workflows/api/workflow-metrics/index.ts new file mode 100644 index 0000000000..35b7baafab --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/api/workflow-metrics/index.ts @@ -0,0 +1 @@ +export * from './workflow-metrics.types'; diff --git a/apps/workflows-dashboard/src/domains/workflows/api/workflow-metrics/query-keys.ts b/apps/workflows-dashboard/src/domains/workflows/api/workflow-metrics/query-keys.ts new file mode 100644 index 0000000000..a801e75a71 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/api/workflow-metrics/query-keys.ts @@ -0,0 +1,17 @@ +import { + fetchCasesPerStatusStats, + fetchWorkflowStats, +} from '@app/domains/workflows/api/workflow-metrics/workflow-metrics.api'; +import { GetCasesPerStatusDto } from '@app/domains/workflows/api/workflow-metrics/workflow-metrics.types'; +import { createQueryKeys } from '@lukemorales/query-key-factory'; + +export const workflowMetricsKeys = createQueryKeys('workflow-metrics', { + workflowRuntimeStats: () => ({ + queryKey: [{}], + queryFn: () => fetchWorkflowStats(), + }), + workflowCasesPerStatusStats: (query: GetCasesPerStatusDto) => ({ + queryKey: [query], + queryFn: () => fetchCasesPerStatusStats(query), + }), +}); diff --git a/apps/workflows-dashboard/src/domains/workflows/api/workflow-metrics/workflow-metrics.api.ts b/apps/workflows-dashboard/src/domains/workflows/api/workflow-metrics/workflow-metrics.api.ts new file mode 100644 index 0000000000..9fbe017282 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/api/workflow-metrics/workflow-metrics.api.ts @@ -0,0 +1,25 @@ +import { + GetCasesPerStatusDto, + ICasesPerStatusStats, + IWorkflowDefinitionStats, +} from '@app/domains/workflows/api/workflow-metrics/workflow-metrics.types'; +import { request } from '@app/lib/request'; + +export const fetchWorkflowStats = async (): Promise<IWorkflowDefinitionStats[]> => { + const result = await request.get<IWorkflowDefinitionStats[]>( + '/metrics/workflows/runtimes-statistic', + ); + + return result.data; +}; + +export const fetchCasesPerStatusStats = async ( + query: GetCasesPerStatusDto, +): Promise<ICasesPerStatusStats> => { + const result = await request.get<ICasesPerStatusStats>( + '/metrics/workflows/runtimes-status-count', + { params: query }, + ); + + return result.data; +}; diff --git a/apps/workflows-dashboard/src/domains/workflows/api/workflow-metrics/workflow-metrics.types.ts b/apps/workflows-dashboard/src/domains/workflows/api/workflow-metrics/workflow-metrics.types.ts new file mode 100644 index 0000000000..5ec6ce549e --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/api/workflow-metrics/workflow-metrics.types.ts @@ -0,0 +1,21 @@ +import { IWorkflowStatus } from '@app/domains/workflows/api/workflow/workflow.types'; + +export type WorkflowStatsPerStatus = Record<IWorkflowStatus, number>; +export interface IWorkflowDefinitionStats extends WorkflowStatsPerStatus { + id: string; + name: string; +} + +export interface IAgentCasesStats { + id: string; + firstName: string; + lastName: string; + casesCount: number; +} + +export type ICasesPerStatusStats = Record<IWorkflowStatus, number>; + +export interface GetCasesPerStatusDto { + // UNIX timestamp + fromDate?: number; +} diff --git a/apps/workflows-dashboard/src/domains/workflows/api/workflow/index.ts b/apps/workflows-dashboard/src/domains/workflows/api/workflow/index.ts new file mode 100644 index 0000000000..c1cf574339 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/api/workflow/index.ts @@ -0,0 +1,3 @@ +export * from './workflow.api'; +export * from './workflow.types'; +export * from './query-keys'; diff --git a/apps/workflows-dashboard/src/domains/workflows/api/workflow/query-keys.ts b/apps/workflows-dashboard/src/domains/workflows/api/workflow/query-keys.ts new file mode 100644 index 0000000000..556497a8d0 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/api/workflow/query-keys.ts @@ -0,0 +1,19 @@ +import { SortingParams } from '@app/common/types/sorting-params.types'; +import { + fetchWorkflowDefinition, + fetchWorkflows, + GetWorkflowDefinitionDto, + GetWorkflowsDto, +} from '@app/domains/workflows/api/workflow'; +import { createQueryKeys } from '@lukemorales/query-key-factory'; + +export const workflowKeys = createQueryKeys('workflows', { + list: (query: GetWorkflowsDto, sorting: SortingParams) => ({ + queryKey: [{ query, sorting }], + queryFn: () => fetchWorkflows(query, sorting), + }), + workflowDefinition: (query: GetWorkflowDefinitionDto) => ({ + queryKey: [query], + queryFn: () => fetchWorkflowDefinition(query), + }), +}); diff --git a/apps/workflows-dashboard/src/domains/workflows/api/workflow/workflow.api.ts b/apps/workflows-dashboard/src/domains/workflows/api/workflow/workflow.api.ts new file mode 100644 index 0000000000..aa78662874 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/api/workflow/workflow.api.ts @@ -0,0 +1,33 @@ +import { SortingParams } from '@app/common/types/sorting-params.types'; +import { + GetWorkflowDefinitionDto, + GetWorkflowDefinitionResponse, + GetWorkflowResponse, + GetWorkflowsDto, + IWorkflowDefinition, +} from '@app/domains/workflows/api/workflow/workflow.types'; +import { request } from '@app/lib/request'; + +export const fetchWorkflows = async ( + query: GetWorkflowsDto, + sortingParams: SortingParams, +): Promise<GetWorkflowResponse> => { + const result = await request.get<GetWorkflowResponse>('/external/workflows', { + params: { + ...query, + ...sortingParams, + }, + }); + + return result.data; +}; + +export const fetchWorkflowDefinition = async ( + query: GetWorkflowDefinitionDto, +): Promise<IWorkflowDefinition> => { + const result = await request.get<GetWorkflowDefinitionResponse>( + `/external/workflows/workflow-definition/${query.workflowId}`, + ); + + return result.data?.definition || {}; +}; diff --git a/apps/workflows-dashboard/src/domains/workflows/api/workflow/workflow.types.ts b/apps/workflows-dashboard/src/domains/workflows/api/workflow/workflow.types.ts new file mode 100644 index 0000000000..764c225c7d --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/api/workflow/workflow.types.ts @@ -0,0 +1,43 @@ +export type IWorkflowStatus = 'active' | 'completed' | 'failed'; + +export interface IWorkflowAssignee { + firstName: string; + lastName: string; +} +export interface IWorkflow { + id: string; + workflowDefinitionName: string; + workflowDefinitionId: string; + status: IWorkflowStatus; + state: string | null; + assignee: IWorkflowAssignee | null; + context: object; + createdAt: Date; + updatedAt: Date; + resolvedAt: Date | null; +} + +export interface GetWorkflowResponse { + results: IWorkflow[]; + meta: { + pages: number; + total: number; + }; +} + +export interface GetWorkflowsDto { + status?: IWorkflowStatus[]; + page?: number; + limit?: number; + orderBy?: string; + orderDirection?: 'asc' | 'desc'; +} + +export type IWorkflowDefinition = object; + +export interface GetWorkflowDefinitionResponse { + definition: IWorkflowDefinition; +} +export interface GetWorkflowDefinitionDto { + workflowId: string; +} diff --git a/apps/workflows-dashboard/src/domains/workflows/index.ts b/apps/workflows-dashboard/src/domains/workflows/index.ts new file mode 100644 index 0000000000..53b1895059 --- /dev/null +++ b/apps/workflows-dashboard/src/domains/workflows/index.ts @@ -0,0 +1 @@ +export * from './api/workflow/query-keys'; diff --git a/apps/workflows-dashboard/src/index.css b/apps/workflows-dashboard/src/index.css new file mode 100644 index 0000000000..81b617783f --- /dev/null +++ b/apps/workflows-dashboard/src/index.css @@ -0,0 +1,82 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap'); +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + + --ring: 215 20.2% 65.1%; + + --radius: 0.5rem; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 85.7% 97.3%; + + --ring: 217.2 32.6% 17.5%; + } +} + +@layer base { + * { + @apply border-border; + } + html, + body, + #root { + height: 100%; + @apply bg-background text-foreground font-inter; + } +} diff --git a/apps/workflows-dashboard/src/lib/react-query/query-client.ts b/apps/workflows-dashboard/src/lib/react-query/query-client.ts new file mode 100644 index 0000000000..8ce795d403 --- /dev/null +++ b/apps/workflows-dashboard/src/lib/react-query/query-client.ts @@ -0,0 +1,7 @@ +import { QueryClient } from '@tanstack/react-query'; + +const FIVE_MIN_CACHE_TIME_IN_MS = 5 * 60 * 1000; + +export const queryClient = new QueryClient({ + defaultOptions: { queries: { staleTime: FIVE_MIN_CACHE_TIME_IN_MS } }, +}); diff --git a/apps/workflows-dashboard/src/lib/request/index.ts b/apps/workflows-dashboard/src/lib/request/index.ts new file mode 100644 index 0000000000..56e4b0555f --- /dev/null +++ b/apps/workflows-dashboard/src/lib/request/index.ts @@ -0,0 +1 @@ +export * from './request'; diff --git a/apps/workflows-dashboard/src/lib/request/request.ts b/apps/workflows-dashboard/src/lib/request/request.ts new file mode 100644 index 0000000000..e31ef1a1c6 --- /dev/null +++ b/apps/workflows-dashboard/src/lib/request/request.ts @@ -0,0 +1,14 @@ +import axios from 'axios'; + +export const request = axios.create({ + baseURL: import.meta.env.VITE_API_URL, + withCredentials: true, +}); + +request.interceptors.request.use(config => { + if (config.headers) { + config.headers['Authorization'] = `Api-Key ${import.meta.env.VITE_API_KEY}`; + return config; + } + return config; +}); diff --git a/apps/workflows-dashboard/src/lib/utils.ts b/apps/workflows-dashboard/src/lib/utils.ts new file mode 100644 index 0000000000..e57f980282 --- /dev/null +++ b/apps/workflows-dashboard/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { ClassValue, clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/apps/workflows-dashboard/src/main.tsx b/apps/workflows-dashboard/src/main.tsx new file mode 100644 index 0000000000..83b38bd163 --- /dev/null +++ b/apps/workflows-dashboard/src/main.tsx @@ -0,0 +1,11 @@ +import { router } from '@app/router'; +import * as React from 'react'; +import * as ReactDOM from 'react-dom/client'; +import { RouterProvider } from 'react-router-dom'; +import './index.css'; + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + <React.StrictMode> + <RouterProvider router={router}></RouterProvider> + </React.StrictMode>, +); diff --git a/apps/workflows-dashboard/src/pages/Overview/Overview.tsx b/apps/workflows-dashboard/src/pages/Overview/Overview.tsx new file mode 100644 index 0000000000..b01ba3c25d --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/Overview.tsx @@ -0,0 +1,20 @@ +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@app/components/atoms/Tabs'; +import { DashboardLayout } from '@app/components/layouts/DashboardLayout'; +import { OverviewTabContent } from '@app/pages/Overview/components/organisms/OverviewTabContent'; + +export const Overview = () => { + return ( + <DashboardLayout pageName="Overview"> + <div className="flex h-full flex-col gap-4"> + <Tabs defaultValue="overview" className="flex flex-1 flex-col"> + <TabsList className="flex flex-row justify-start self-start"> + <TabsTrigger value="overview">Overview</TabsTrigger> + </TabsList> + <TabsContent value="overview" className="flex flex-1 flex-col space-y-4"> + <OverviewTabContent /> + </TabsContent> + </Tabs> + </div> + </DashboardLayout> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Overview/components/molecules/DailyResolvedCasesChart/DailyResolvedCasesChart.tsx b/apps/workflows-dashboard/src/pages/Overview/components/molecules/DailyResolvedCasesChart/DailyResolvedCasesChart.tsx new file mode 100644 index 0000000000..e6d3a4a2ab --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/molecules/DailyResolvedCasesChart/DailyResolvedCasesChart.tsx @@ -0,0 +1,22 @@ +import { useMemo } from 'react'; +import { WeeklyBarChart } from '@app/components/molecules/WeeklyBarChart'; + +export interface DailyResolvedCasesChartData { + value: number; + date: Date; +} + +interface Props { + data: DailyResolvedCasesChartData[]; +} + +export const DailyResolvedCasesChart = ({ data }: Props) => { + const chartData = useMemo(() => { + return data.map(item => ({ + date: new Date(item.date), + value: item.value, + })); + }, [data]); + + return <WeeklyBarChart data={chartData} />; +}; diff --git a/apps/workflows-dashboard/src/pages/Overview/components/molecules/DailyResolvedCasesChart/index.ts b/apps/workflows-dashboard/src/pages/Overview/components/molecules/DailyResolvedCasesChart/index.ts new file mode 100644 index 0000000000..029c4e7750 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/molecules/DailyResolvedCasesChart/index.ts @@ -0,0 +1 @@ +export * from './DailyResolvedCasesChart'; diff --git a/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/UserStats.tsx b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/UserStats.tsx new file mode 100644 index 0000000000..17167d69b4 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/UserStats.tsx @@ -0,0 +1,35 @@ +import { UserStats as IUserStats } from '@app/domains/user/api/user-stats'; +import { DurationCard } from '@app/pages/Overview/components/molecules/UserStats/components/DurationCard'; +import { PercentageCard } from '@app/pages/Overview/components/molecules/UserStats/components/PercentageCard'; + +interface Props { + userStats: IUserStats; + isLoading: boolean; +} + +export const UserStats = ({ userStats, isLoading }: Props) => { + return ( + <div className="grid w-full gap-4 md:grid-cols-2 lg:grid-cols-4"> + <PercentageCard + title="Approved rate out of resolved" + isLoading={isLoading} + percentage={userStats.approvalRate} + /> + <DurationCard + title="Average resolution time" + isLoading={isLoading} + duration={userStats.averageResolutionTime} + /> + <DurationCard + title="Average time to assignment" + isLoading={isLoading} + duration={userStats.averageAssignmentTime} + /> + <DurationCard + title="Average review time" + isLoading={isLoading} + duration={userStats.averageReviewTime} + /> + </div> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/DurationCard/DurationCard.tsx b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/DurationCard/DurationCard.tsx new file mode 100644 index 0000000000..40ddbd073b --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/DurationCard/DurationCard.tsx @@ -0,0 +1,24 @@ +import { MetricCard } from '@app/components/molecules/MetricCard'; +import { DurationCardContent } from '@app/pages/Overview/components/molecules/UserStats/components/DurationCard/DurationCardContent'; +import { memo } from 'react'; + +interface Props { + title: string; + isLoading: boolean; + duration: number; +} + +export const DurationCard = memo(({ title, isLoading, duration }: Props) => { + return ( + <MetricCard + title={<MetricCard.Title className="text-sm" title={title} />} + content={ + <span className="text-3xl font-bold"> + <DurationCardContent duration={duration} /> + </span> + } + description="( last 30 days )" + isLoading={isLoading} + /> + ); +}); diff --git a/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/DurationCard/DurationCardContent.tsx b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/DurationCard/DurationCardContent.tsx new file mode 100644 index 0000000000..90b51f6adf --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/DurationCard/DurationCardContent.tsx @@ -0,0 +1,23 @@ +import { convertMsDurationToTime } from '@app/common/utils/convert-ms-duration-to-time/convert-ms-duration-to-time'; +import { memo, useMemo } from 'react'; + +interface Props { + duration: number; +} + +export const DurationCardContent = memo(({ duration }: Props) => { + const time = useMemo(() => { + const normalizedDuration = Math.abs(duration); + const time = convertMsDurationToTime(normalizedDuration); + const stringifiedTime: Record<string, string> = {}; + + // converting time values to two-digit format when needed + Object.entries(time).forEach(([key, value]) => { + stringifiedTime[key] = value < 10 ? `0${value}` : String(value); + }); + + return stringifiedTime; + }, [duration]); + + return <>{`${time.days ? `${time.days}:` : ''}${time.hours}:${time.minutes}:${time.seconds}`}</>; +}); diff --git a/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/DurationCard/index.ts b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/DurationCard/index.ts new file mode 100644 index 0000000000..9ee93c864d --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/DurationCard/index.ts @@ -0,0 +1 @@ +export * from './DurationCard'; diff --git a/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/PercentageCard/PercentageCard.tsx b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/PercentageCard/PercentageCard.tsx new file mode 100644 index 0000000000..7e6b5bc3be --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/PercentageCard/PercentageCard.tsx @@ -0,0 +1,19 @@ +import { MetricCard } from '@app/components/molecules/MetricCard'; +import { memo } from 'react'; + +interface Props { + title: string; + percentage: number; + isLoading: boolean; +} + +export const PercentageCard = memo(({ title, percentage, isLoading }: Props) => { + return ( + <MetricCard + title={<MetricCard.Title className="text-sm" title={title} />} + content={<span className="text-3xl font-bold">{`${percentage.toFixed(2)}%`}</span>} + description="( last 30 days )" + isLoading={isLoading} + /> + ); +}); diff --git a/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/PercentageCard/index.ts b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/PercentageCard/index.ts new file mode 100644 index 0000000000..b9f4b0b212 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/components/PercentageCard/index.ts @@ -0,0 +1 @@ +export * from './PercentageCard'; diff --git a/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/index.ts b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/index.ts new file mode 100644 index 0000000000..70f6cc8783 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UserStats/index.ts @@ -0,0 +1 @@ +export * from './UserStats'; diff --git a/apps/workflows-dashboard/src/pages/Overview/components/molecules/UsersResolvingStatsList/ListItem.tsx b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UsersResolvingStatsList/ListItem.tsx new file mode 100644 index 0000000000..c6e2fc2e82 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UsersResolvingStatsList/ListItem.tsx @@ -0,0 +1,21 @@ +import { Avatar, AvatarImage } from '@app/components/atoms/Avatar'; +import { IUserCaseResolvingStats } from '@app/domains/user/api/users-stats'; + +interface Props { + item: IUserCaseResolvingStats; +} + +export const ListItem = ({ item }: Props) => { + return ( + <div className="flex items-center"> + <Avatar className="flex h-9 w-9 items-center justify-center space-y-0 border"> + <AvatarImage src="/avatar.png" alt="Avatar" /> + </Avatar> + <div className="ml-4 space-y-1"> + <p className="text-sm font-medium leading-none">{`${item.firstName} ${item.lastName}`}</p> + <p className="text-muted-foreground text-sm">{item.email}</p> + </div> + <div className="ml-auto font-medium">{item.casesCount}</div> + </div> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Overview/components/molecules/UsersResolvingStatsList/UsersResolvingStatsList.tsx b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UsersResolvingStatsList/UsersResolvingStatsList.tsx new file mode 100644 index 0000000000..ce0a14d726 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UsersResolvingStatsList/UsersResolvingStatsList.tsx @@ -0,0 +1,26 @@ +import { Skeleton } from '@app/components/atoms/Skeleton'; +import { IUserCaseResolvingStats } from '@app/domains/user/api/users-stats'; +import { ListItem } from '@app/pages/Overview/components/molecules/UsersResolvingStatsList/ListItem'; +import Scrollbars from 'react-custom-scrollbars'; + +export interface Props { + items: IUserCaseResolvingStats[]; + isLoading?: boolean; +} + +export const UsersResolvingStatsList = ({ items, isLoading }: Props) => { + const isEmpty = !isLoading && !items.length; + + return ( + <Scrollbars autoHide> + <div className="flex h-full flex-col gap-4 pr-4"> + {isLoading ? ( + <Skeleton className="h-full w-full" /> + ) : ( + items.map(item => <ListItem key={item.id} item={item} />) + )} + {isEmpty ? <span className="font-inter">No activity found.</span> : null} + </div> + </Scrollbars> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Overview/components/molecules/UsersResolvingStatsList/index.ts b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UsersResolvingStatsList/index.ts new file mode 100644 index 0000000000..d265d7b985 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/molecules/UsersResolvingStatsList/index.ts @@ -0,0 +1 @@ +export * from './UsersResolvingStatsList'; diff --git a/apps/workflows-dashboard/src/pages/Overview/components/organisms/OverviewTabContent/OverviewTabContent.tsx b/apps/workflows-dashboard/src/pages/Overview/components/organisms/OverviewTabContent/OverviewTabContent.tsx new file mode 100644 index 0000000000..549dd08908 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/organisms/OverviewTabContent/OverviewTabContent.tsx @@ -0,0 +1,78 @@ +import { MetricCard } from '@app/components/molecules/MetricCard'; +import { + DailyResolvedCasesChart, + DailyResolvedCasesChartData, +} from '@app/pages/Overview/components/molecules/DailyResolvedCasesChart'; +import { UsersResolvingStatsList } from '@app/pages/Overview/components/molecules/UsersResolvingStatsList'; +import { UserStats } from '@app/pages/Overview/components/molecules/UserStats'; +import { useCaseResolvingStatsQuery } from '@app/pages/Overview/hooks/useCaseResolvingStatsQuery'; +import { useUserDailyCaseResolvingStatsQuery } from '@app/pages/Overview/hooks/useUserDailyCaseResolvingStatsQuery'; +import { useUserStatsQuery } from '@app/pages/Overview/hooks/useUserStatsQuery'; +import { useMemo } from 'react'; + +export const OverviewTabContent = () => { + const { isLoading, data } = useUserStatsQuery(); + const { data: caseResolvingStatsData } = useCaseResolvingStatsQuery(); + const { data: usersCaseResolvingStats, isLoading: isLoadingCaseResolvingStats } = + useUserDailyCaseResolvingStatsQuery(); + + const chartData = useMemo((): DailyResolvedCasesChartData[] => { + return caseResolvingStatsData.map(item => ({ + value: item.count, + date: new Date(item.date), + })); + }, [caseResolvingStatsData]); + + return ( + <div className="flex h-full flex-col gap-4"> + <div> + <UserStats isLoading={isLoading} userStats={data} /> + </div> + <div className="flex-1"> + <div className="grid h-full grid-cols-12 gap-4"> + <MetricCard + className="col-span-8" + title={ + <MetricCard.Title + className="text-sm" + title={ + <div className="flex justify-between"> + <span>Resolved cases per day </span> + <span className="text-muted-foreground text-sm font-normal"> + ( last 7 days ) + </span> + </div> + } + /> + } + content={<DailyResolvedCasesChart data={chartData} />} + ></MetricCard> + <MetricCard + className="col-span-4" + title={ + <MetricCard.Title + title={ + <MetricCard.Title + className="text-sm" + title={ + <div className="flex justify-between"> + <span>Agents resolved cases </span> + <span className="text-muted-foreground text-sm font-normal">( today )</span> + </div> + } + /> + } + /> + } + content={ + <UsersResolvingStatsList + items={usersCaseResolvingStats} + isLoading={isLoadingCaseResolvingStats} + /> + } + /> + </div> + </div> + </div> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Overview/components/organisms/OverviewTabContent/index.ts b/apps/workflows-dashboard/src/pages/Overview/components/organisms/OverviewTabContent/index.ts new file mode 100644 index 0000000000..a45baf571f --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/components/organisms/OverviewTabContent/index.ts @@ -0,0 +1 @@ +export * from './OverviewTabContent'; diff --git a/apps/workflows-dashboard/src/pages/Overview/hooks/useCaseResolvingStatsQuery/index.ts b/apps/workflows-dashboard/src/pages/Overview/hooks/useCaseResolvingStatsQuery/index.ts new file mode 100644 index 0000000000..fb6386896e --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/hooks/useCaseResolvingStatsQuery/index.ts @@ -0,0 +1 @@ +export * from './useCaseResolvingStatsQuery'; diff --git a/apps/workflows-dashboard/src/pages/Overview/hooks/useCaseResolvingStatsQuery/useCaseResolvingStatsQuery.ts b/apps/workflows-dashboard/src/pages/Overview/hooks/useCaseResolvingStatsQuery/useCaseResolvingStatsQuery.ts new file mode 100644 index 0000000000..de1faab4de --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/hooks/useCaseResolvingStatsQuery/useCaseResolvingStatsQuery.ts @@ -0,0 +1,17 @@ +import { userStatsQueryKeys } from '@app/domains/user/api/user-stats'; +import { useQuery } from '@tanstack/react-query'; +import dayjs from 'dayjs'; +import { useMemo } from 'react'; + +export const useCaseResolvingStatsQuery = () => { + const initialDate = useMemo(() => +dayjs().subtract(6, 'day').startOf('day').toDate(), []); + const { data = [], isLoading } = useQuery({ + ...userStatsQueryKeys.userDailyCasesResolvedStats({ fromDate: initialDate }), + enabled: Boolean(initialDate), + }); + + return { + data, + isLoading, + }; +}; diff --git a/apps/workflows-dashboard/src/pages/Overview/hooks/useUserDailyCaseResolvingStatsQuery/index.ts b/apps/workflows-dashboard/src/pages/Overview/hooks/useUserDailyCaseResolvingStatsQuery/index.ts new file mode 100644 index 0000000000..727588de0c --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/hooks/useUserDailyCaseResolvingStatsQuery/index.ts @@ -0,0 +1 @@ +export * from './useUserDailyCaseResolvingStatsQuery'; diff --git a/apps/workflows-dashboard/src/pages/Overview/hooks/useUserDailyCaseResolvingStatsQuery/useUserDailyCaseResolvingStatsQuery.ts b/apps/workflows-dashboard/src/pages/Overview/hooks/useUserDailyCaseResolvingStatsQuery/useUserDailyCaseResolvingStatsQuery.ts new file mode 100644 index 0000000000..ed9610a154 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/hooks/useUserDailyCaseResolvingStatsQuery/useUserDailyCaseResolvingStatsQuery.ts @@ -0,0 +1,16 @@ +import { usersStatsQueryKeys } from '@app/domains/user/api/users-stats'; +import { useQuery } from '@tanstack/react-query'; +import dayjs from 'dayjs'; +import { useMemo } from 'react'; + +export const useUserDailyCaseResolvingStatsQuery = () => { + const initialDate = useMemo(() => +dayjs().startOf('day').toDate(), []); + const { data = [], isLoading } = useQuery( + usersStatsQueryKeys.casesResolvedStats({ fromDate: initialDate }), + ); + + return { + data, + isLoading, + }; +}; diff --git a/apps/workflows-dashboard/src/pages/Overview/hooks/useUserStatsQuery/index.ts b/apps/workflows-dashboard/src/pages/Overview/hooks/useUserStatsQuery/index.ts new file mode 100644 index 0000000000..797bffad11 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/hooks/useUserStatsQuery/index.ts @@ -0,0 +1 @@ +export * from './useUserStatsQuery'; diff --git a/apps/workflows-dashboard/src/pages/Overview/hooks/useUserStatsQuery/useUserStatsQuery.ts b/apps/workflows-dashboard/src/pages/Overview/hooks/useUserStatsQuery/useUserStatsQuery.ts new file mode 100644 index 0000000000..24a6129629 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/hooks/useUserStatsQuery/useUserStatsQuery.ts @@ -0,0 +1,23 @@ +import dayjs from 'dayjs'; +import { UserStats, userStatsQueryKeys } from '@app/domains/user/api/user-stats'; +import { useQuery } from '@tanstack/react-query'; +import { useMemo } from 'react'; + +const defaultValues: UserStats = { + approvalRate: 0, + averageResolutionTime: 0, + averageReviewTime: 0, + averageAssignmentTime: 0, +}; + +export const useUserStatsQuery = () => { + const initialDate = useMemo(() => +dayjs().subtract(30, 'days').toDate(), []); + const { data = defaultValues, isLoading } = useQuery( + userStatsQueryKeys.userStats({ fromDate: initialDate }), + ); + + return { + data, + isLoading, + }; +}; diff --git a/apps/workflows-dashboard/src/pages/Overview/index.ts b/apps/workflows-dashboard/src/pages/Overview/index.ts new file mode 100644 index 0000000000..64d1896b62 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Overview/index.ts @@ -0,0 +1 @@ +export * from './Overview'; diff --git a/apps/workflows-dashboard/src/pages/SignIn/SignIn.tsx b/apps/workflows-dashboard/src/pages/SignIn/SignIn.tsx new file mode 100644 index 0000000000..86fa8ecb7e --- /dev/null +++ b/apps/workflows-dashboard/src/pages/SignIn/SignIn.tsx @@ -0,0 +1,54 @@ +import { BallerineLogo } from '@app/components/atoms/icons/BallerineLogo'; +import { env } from '@app/common/env/env'; +import { SignInForm } from '@app/pages/SignIn/components/SignInForm'; +import { useSignInMutation } from '@app/pages/SignIn/hooks/useSignInMutation'; +import { Navigate, useNavigate } from 'react-router-dom'; +import { getRefererUrl } from '@app/common/hocs/withSessionProtected/utils/get-referer-url'; +import { clearRefererUrl } from '@app/common/hocs/withSessionProtected/utils/clear-referer-url'; +import { useSession } from '@app/common/hooks/useSession'; +import { LoadingSpinner } from '@app/components/atoms/LoadingSpinner'; + +export function SignIn() { + const navigate = useNavigate(); + const { isLoading: isLoadingSession, isAuthenticated, refresh } = useSession(); + const { isLoading, errorCode, signIn } = useSignInMutation({ + onSuccess: () => { + refresh(); + const refUrl = getRefererUrl(); + navigate(refUrl || '/'); + + if (refUrl) { + clearRefererUrl(); + } + }, + }); + + if (isLoadingSession) { + return ( + <div className={`flex h-full flex-col items-center justify-center`}> + <LoadingSpinner /> + </div> + ); + } + + if (!isLoadingSession && isAuthenticated) { + return <Navigate to="/" replace />; + } + + return ( + <section className={`flex h-full flex-col items-center justify-center`}> + <div className={`mb-16`}> + {env.VITE_IMAGE_LOGO_URL ? ( + <img className={`w-40`} src={env.VITE_IMAGE_LOGO_URL} /> + ) : ( + <BallerineLogo /> + )} + </div> + <SignInForm + isSubmitting={isLoading} + alert={errorCode ? <SignInForm.ErrorAlert errorCode={errorCode} /> : null} + onSubmit={values => signIn(values)} + /> + </section> + ); +} diff --git a/apps/workflows-dashboard/src/pages/SignIn/components/SignInForm/ErrorAlert.tsx b/apps/workflows-dashboard/src/pages/SignIn/components/SignInForm/ErrorAlert.tsx new file mode 100644 index 0000000000..6f50ff51f7 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/SignIn/components/SignInForm/ErrorAlert.tsx @@ -0,0 +1,21 @@ +import { ErrorAlert } from '@app/components/atoms/ErrorAlert/ErrorAlert'; + +interface Props { + errorCode: number; +} + +const getErrorMessageByErrorCode = (code: number) => { + const defaultMessage = `Something went wrong. Try again later.`; + + const codeToMessageMap: Record<number, string> = { + [401]: 'Invalid credentials', + }; + + return codeToMessageMap[code] || defaultMessage; +}; + +export function FormErrorAlert({ errorCode }: Props) { + return <ErrorAlert>{getErrorMessageByErrorCode(errorCode)}</ErrorAlert>; +} + +FormErrorAlert.displayName = 'FormErrorAlert'; diff --git a/apps/workflows-dashboard/src/pages/SignIn/components/SignInForm/SignInForm.tsx b/apps/workflows-dashboard/src/pages/SignIn/components/SignInForm/SignInForm.tsx new file mode 100644 index 0000000000..3c45f40f92 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/SignIn/components/SignInForm/SignInForm.tsx @@ -0,0 +1,75 @@ +import { Button } from '@app/components/atoms/Button'; +import { Card, CardContent, CardHeader } from '@app/components/atoms/Card'; +import { Input } from '@app/components/atoms/Input'; +import { Form } from '@app/components/organisms/Form/Form'; +import { FormControl } from '@app/components/organisms/Form/Form.Control'; +import { FormField } from '@app/components/organisms/Form/Form.Field'; +import { FormItem } from '@app/components/organisms/Form/Form.Item'; +import { FormLabel } from '@app/components/organisms/Form/Form.Label'; +import { FormMessage } from '@app/components/organisms/Form/Form.Message'; +import { SignInFormValues } from '@app/pages/SignIn/components/SignInForm/types'; +import { useForm } from 'react-hook-form'; +import classnames from 'classnames'; +import { FormErrorAlert } from '@app/pages/SignIn/components/SignInForm/ErrorAlert'; + +interface Props { + isSubmitting: boolean; + alert?: React.ReactNode; + onSubmit: (values: SignInFormValues) => void; +} + +export function SignInForm({ isSubmitting, alert = null, onSubmit }: Props) { + const form = useForm<SignInFormValues>(); + + return ( + <Card className={`w-full max-w-lg`}> + <CardHeader className={`mb-2 text-center text-4xl font-bold`}>Sign In</CardHeader> + <CardContent> + {alert} + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8"> + <FormField + control={form.control} + name="email" + render={({ field }) => ( + <FormItem> + <FormLabel>Email</FormLabel> + <FormControl> + <Input required type={'email'} {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + <FormField + control={form.control} + name="password" + render={({ field }) => ( + <FormItem> + <FormLabel>Password</FormLabel> + <FormControl> + <Input required type={'password'} {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + <div className={`flex justify-end`}> + <Button + type="submit" + className={classnames(`ms-auto mt-3`, { + ['opacity-50']: isSubmitting, + ['pointer-events-none']: isSubmitting, + })} + > + Sign In + </Button> + </div> + </form> + </Form> + </CardContent> + </Card> + ); +} + +SignInForm.ErrorAlert = FormErrorAlert; diff --git a/apps/workflows-dashboard/src/pages/SignIn/components/SignInForm/index.ts b/apps/workflows-dashboard/src/pages/SignIn/components/SignInForm/index.ts new file mode 100644 index 0000000000..85d8e1b31d --- /dev/null +++ b/apps/workflows-dashboard/src/pages/SignIn/components/SignInForm/index.ts @@ -0,0 +1 @@ +export * from './SignInForm'; diff --git a/apps/workflows-dashboard/src/pages/SignIn/components/SignInForm/types.ts b/apps/workflows-dashboard/src/pages/SignIn/components/SignInForm/types.ts new file mode 100644 index 0000000000..d1b7e0c2a2 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/SignIn/components/SignInForm/types.ts @@ -0,0 +1,4 @@ +export interface SignInFormValues { + email: string; + password: string; +} diff --git a/apps/workflows-dashboard/src/pages/SignIn/hooks/useSignInMutation/index.ts b/apps/workflows-dashboard/src/pages/SignIn/hooks/useSignInMutation/index.ts new file mode 100644 index 0000000000..485d6a9894 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/SignIn/hooks/useSignInMutation/index.ts @@ -0,0 +1 @@ +export * from './useSignInMutation'; diff --git a/apps/workflows-dashboard/src/pages/SignIn/hooks/useSignInMutation/useSignInMutation.tsx b/apps/workflows-dashboard/src/pages/SignIn/hooks/useSignInMutation/useSignInMutation.tsx new file mode 100644 index 0000000000..34fca75491 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/SignIn/hooks/useSignInMutation/useSignInMutation.tsx @@ -0,0 +1,20 @@ +import { fetchSignIn, GetSignInDto, GetSignInResponse } from '@app/domains/auth/api/login'; +import { useMutation } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; + +export interface UseSignInParams { + onSuccess?: () => void; +} + +export function useSignInMutation({ onSuccess }: UseSignInParams) { + const mutation = useMutation<GetSignInResponse, AxiosError, GetSignInDto>({ + mutationFn: fetchSignIn, + onSuccess, + }); + + return { + isLoading: mutation.isLoading, + errorCode: mutation.error?.response ? mutation.error.response.status : null, + signIn: mutation.mutate, + }; +} diff --git a/apps/workflows-dashboard/src/pages/SignIn/index.ts b/apps/workflows-dashboard/src/pages/SignIn/index.ts new file mode 100644 index 0000000000..a573a358ee --- /dev/null +++ b/apps/workflows-dashboard/src/pages/SignIn/index.ts @@ -0,0 +1 @@ +export * from './SignIn'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/Workflows.tsx b/apps/workflows-dashboard/src/pages/Workflows/Workflows.tsx new file mode 100644 index 0000000000..4b4e5caaf3 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/Workflows.tsx @@ -0,0 +1,78 @@ +import { Pagination } from '@app/components/molecules/Pagination'; +import { StatusFilterComponent } from '@app/pages/Workflows/components/molecules/StatusFilterComponent'; +import { useWorkflowsQuery } from '@app/pages/Workflows/hooks/useWorkflowsQuery'; +import { useCallback } from 'react'; +import { WorkflowsList } from '@app/pages/Workflows/components/organisms/WorkflowsList'; +import { WorkflowsLayout } from '@app/pages/Workflows/components/layouts/WorkflowsLayout'; +import { DashboardLayout } from '@app/components/layouts/DashboardLayout'; +import { useSorting } from '@app/common/hooks/useSorting'; +import { WorkflowsMetricLayout } from '@app/pages/Workflows/components/layouts/WorkflowsMetricLayout'; +import { ActivePerWorkflow } from '@app/pages/Workflows/components/organisms/metrics/ActivePerWorkflow'; +import { WorkflowFiltersProps } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/hocs/withWorkflowFilters/types'; +import { withWorkflowFilters } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/hocs/withWorkflowFilters'; +import { FilterComponent } from '@app/pages/Workflows/components/organisms/WorkflowFilters/types'; +import { WorkflowFilters } from '@app/pages/Workflows/components/organisms/WorkflowFilters'; +import { AgentCasesStats } from '@app/pages/Workflows/components/organisms/metrics/AgentCasesStats'; +import { CasesPerStatusStats } from '@app/pages/Workflows/components/organisms/metrics/CasesPerStatusStats'; +import { AgentsActivityStats } from '@app/pages/Workflows/components/organisms/metrics/AgentsActivityStats'; + +const filterComponents: FilterComponent[] = [StatusFilterComponent]; + +interface Props extends WorkflowFiltersProps {} + +export const Workflows = withWorkflowFilters(({ filters, updateFilters }: Props) => { + const { sortingKey, sortingDirection } = useSorting('order_by'); + const { fromDate: _, ...workflowsFilters } = filters; + + const { data, isLoading, isFetching } = useWorkflowsQuery( + workflowsFilters, + sortingKey && sortingDirection + ? { orderBy: sortingKey, orderDirection: sortingDirection } + : undefined, + ); + + const handlePageChange = useCallback( + (nextPage: number) => { + updateFilters({ page: nextPage }); + }, + [updateFilters], + ); + + return ( + <DashboardLayout pageName="Workflows"> + <WorkflowsLayout> + <WorkflowsLayout.Header> + <WorkflowsMetricLayout> + <WorkflowsMetricLayout.Item> + <ActivePerWorkflow /> + </WorkflowsMetricLayout.Item> + <WorkflowsMetricLayout.Item> + <AgentsActivityStats /> + </WorkflowsMetricLayout.Item> + <WorkflowsMetricLayout.Item> + <AgentCasesStats /> + </WorkflowsMetricLayout.Item> + <WorkflowsMetricLayout.Item> + <CasesPerStatusStats /> + </WorkflowsMetricLayout.Item> + </WorkflowsMetricLayout> + <WorkflowFilters + components={filterComponents} + values={filters} + onChange={updateFilters} + /> + </WorkflowsLayout.Header> + <WorkflowsLayout.Main> + <WorkflowsList workflows={data.results} isLoading={isLoading} isFetching={isFetching} /> + </WorkflowsLayout.Main> + <WorkflowsLayout.Footer> + <Pagination + totalPages={data.meta.pages || 1} + page={filters.page || 1} + onChange={handlePageChange} + /> + </WorkflowsLayout.Footer> + </WorkflowsLayout> + </DashboardLayout> + ); +}); diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/Footer.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/Footer.tsx new file mode 100644 index 0000000000..46d592c4d9 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/Footer.tsx @@ -0,0 +1,7 @@ +interface Props { + children: React.ReactNode; +} + +export const Footer = ({ children }: Props) => { + return <footer className="flex justify-center">{children}</footer>; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/Header.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/Header.tsx new file mode 100644 index 0000000000..29a147aeda --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/Header.tsx @@ -0,0 +1,7 @@ +interface Props { + children: React.ReactNode; +} + +export const Header = ({ children }: Props) => { + return <div className="flex flex-col gap-4">{children}</div>; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/Main.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/Main.tsx new file mode 100644 index 0000000000..efff452b92 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/Main.tsx @@ -0,0 +1,7 @@ +interface Props { + children: React.ReactNode; +} + +export const Main = ({ children }: Props) => { + return <main className="flex flex-1 overflow-auto">{children}</main>; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/WorkflowsLayout.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/WorkflowsLayout.tsx new file mode 100644 index 0000000000..bec58703d0 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/WorkflowsLayout.tsx @@ -0,0 +1,15 @@ +import { Footer } from './Footer'; +import { Header } from './Header'; +import { Main } from './Main'; + +interface Props { + children: React.ReactNode[]; +} + +export function WorkflowsLayout({ children }: Props) { + return <div className="flex h-full flex-col gap-4">{children}</div>; +} + +WorkflowsLayout.Header = Header; +WorkflowsLayout.Main = Main; +WorkflowsLayout.Footer = Footer; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/index.ts new file mode 100644 index 0000000000..a837b7f25b --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsLayout/index.ts @@ -0,0 +1 @@ +export * from './WorkflowsLayout'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsMetricLayout/WorkflowsMetricLayout.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsMetricLayout/WorkflowsMetricLayout.tsx new file mode 100644 index 0000000000..0b7ce7af80 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsMetricLayout/WorkflowsMetricLayout.tsx @@ -0,0 +1,13 @@ +import { WorkflowsMetricLayoutItem } from '@app/pages/Workflows/components/layouts/WorkflowsMetricLayout/WorkflowsMetricLayoutItem'; + +interface Props { + children: React.ReactNode | React.ReactNode[]; +} + +export function WorkflowsMetricLayout({ children }: Props) { + return <div className="flex w-full gap-4">{children}</div>; +} + +WorkflowsMetricLayout.Item = WorkflowsMetricLayoutItem; + +WorkflowsMetricLayout.displayName = 'WorkflowsMetricLayout'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsMetricLayout/WorkflowsMetricLayoutItem.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsMetricLayout/WorkflowsMetricLayoutItem.tsx new file mode 100644 index 0000000000..d0f449f763 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsMetricLayout/WorkflowsMetricLayoutItem.tsx @@ -0,0 +1,9 @@ +interface Props { + children: React.ReactNode; +} + +export function WorkflowsMetricLayoutItem({ children }: Props) { + return <div className="min-h-[220px] w-1/4">{children}</div>; +} + +WorkflowsMetricLayoutItem.displayName = 'WorkflowsMetricLayoutItem'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsMetricLayout/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsMetricLayout/index.ts new file mode 100644 index 0000000000..62e620c881 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/layouts/WorkflowsMetricLayout/index.ts @@ -0,0 +1 @@ +export * from './WorkflowsMetricLayout'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/ActivePerWorkflowChart/ActivePerWorkflowChart.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/ActivePerWorkflowChart/ActivePerWorkflowChart.tsx new file mode 100644 index 0000000000..49a5776fa2 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/ActivePerWorkflowChart/ActivePerWorkflowChart.tsx @@ -0,0 +1,41 @@ +import { MetricCard } from '@app/components/molecules/MetricCard'; +import { ChartProps } from '@app/pages/Workflows/components/molecules/common/types'; +import { + WorkflowChart, + WorkflowChartData, +} from '@app/pages/Workflows/components/organisms/WorkflowStatusChart'; +import { useMemo } from 'react'; + +export interface ActivePerWorkflowChartData { + workflowName: string; + count: number; + fillColor: string; +} + +interface Props extends ChartProps { + data: ActivePerWorkflowChartData[]; +} + +export const ActivePerWorkflowChart = ({ isLoading, data }: Props) => { + const chartData: WorkflowChartData[] = useMemo( + () => + data.map(item => ({ + label: item.workflowName, + value: item.count, + fill: item.fillColor, + })), + [data], + ); + + return ( + <MetricCard + isLoading={isLoading} + title={<MetricCard.Title title="Active per workflow" />} + content={ + <MetricCard.Content> + <WorkflowChart size={90} innerRadius={26} outerRadius={40} data={chartData} /> + </MetricCard.Content> + } + /> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/ActivePerWorkflowChart/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/ActivePerWorkflowChart/index.ts new file mode 100644 index 0000000000..fc95eeb11c --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/ActivePerWorkflowChart/index.ts @@ -0,0 +1 @@ +export * from './ActivePerWorkflowChart'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/AgentCasesChart/AgentCasesChart.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/AgentCasesChart/AgentCasesChart.tsx new file mode 100644 index 0000000000..c67669ae87 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/AgentCasesChart/AgentCasesChart.tsx @@ -0,0 +1,38 @@ +import { ChartProps } from '@app/pages/Workflows/components/molecules/common/types'; +import { MetricListChart } from '@app/pages/Workflows/components/molecules/MetricListChart'; +import { memo, useMemo } from 'react'; + +export interface AgentCasesChartData { + id: string; + agentName: string; + casesCount: number; +} + +interface Props extends ChartProps { + data: AgentCasesChartData[]; +} + +export const AgentCasesChart = memo(({ isLoading, data }: Props) => { + const chartItems = useMemo( + () => + data.map(item => ( + <div + className="flex flex-nowrap justify-between" + key={`agent-cases-chart-data-key-${item.id}`} + > + <div className="text-sm">{item.agentName}</div> + <div className="text-sm font-medium">{item.casesCount}</div> + </div> + )), + [data], + ); + + return ( + <MetricListChart + title="Assigned Cases per agent" + isLoading={isLoading} + items={chartItems} + emptyPlaceholder={<div className="text-sm font-medium">No active cases.</div>} + /> + ); +}); diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/AgentCasesChart/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/AgentCasesChart/index.ts new file mode 100644 index 0000000000..4cfe7255af --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/AgentCasesChart/index.ts @@ -0,0 +1 @@ +export * from './AgentCasesChart'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/AgentsActivityChart/AgentsActivityChart.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/AgentsActivityChart/AgentsActivityChart.tsx new file mode 100644 index 0000000000..272e7d4b5b --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/AgentsActivityChart/AgentsActivityChart.tsx @@ -0,0 +1,49 @@ +import classnames from 'classnames'; +import dayjs from 'dayjs'; +import { ChartProps } from '@app/pages/Workflows/components/molecules/common/types'; +import { MetricListChart } from '@app/pages/Workflows/components/molecules/MetricListChart'; +import { useMemo } from 'react'; + +export interface LoggedInAgentsChartData { + id: string; + fullName: string; + lastActiveAt: Date | null; +} + +interface Props extends ChartProps { + data: LoggedInAgentsChartData[]; +} + +export const AgentsActivityChart = ({ isLoading, data }: Props) => { + const chartItems = useMemo( + () => + data.map(item => { + const hourDifference = Math.abs(dayjs(item.lastActiveAt).diff(Date.now(), 'hour')); + + return ( + <div + className="flex flex-nowrap items-center justify-between" + key={`agent-cases-chart-data-key-${item.id}`} + > + <div className="text-sm">{item.fullName}</div> + <div + className={classnames('h-4 w-4 rounded-full', { + 'bg-red-600': hourDifference > 1, + 'bg-green-600': hourDifference < 1, + })} + ></div> + </div> + ); + }), + [data], + ); + + return ( + <MetricListChart + title="Online/Offline Agents" + isLoading={isLoading} + items={chartItems} + emptyPlaceholder={<div>No recent activity found.</div>} + /> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/AgentsActivityChart/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/AgentsActivityChart/index.ts new file mode 100644 index 0000000000..6c9d1806a6 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/AgentsActivityChart/index.ts @@ -0,0 +1 @@ +export * from './AgentsActivityChart'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/CasesPerStatusChart/CasesPerStatusChart.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/CasesPerStatusChart/CasesPerStatusChart.tsx new file mode 100644 index 0000000000..fdecfdc8a3 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/CasesPerStatusChart/CasesPerStatusChart.tsx @@ -0,0 +1,33 @@ +import { ChartProps } from '@app/pages/Workflows/components/molecules/common/types'; +import { MetricListChart } from '@app/pages/Workflows/components/molecules/MetricListChart'; +import { useMemo } from 'react'; + +export interface CasesPerStatusChartData { + id: string; + status: string; + casesCount: number; +} + +interface Props extends ChartProps { + data: CasesPerStatusChartData[]; +} + +export const CasesPerStatusChart = ({ isLoading, data }: Props) => { + const chartItems = useMemo( + () => + data.map(item => ( + <div + className="flex flex-nowrap justify-between" + key={`agent-cases-chart-data-key-${item.id}`} + > + <div className="text-sm font-medium">{item.status}</div> + <div className="text-sm font-medium">{item.casesCount}</div> + </div> + )), + [data], + ); + + return ( + <MetricListChart title="Amount of cases per status" isLoading={isLoading} items={chartItems} /> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/CasesPerStatusChart/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/CasesPerStatusChart/index.ts new file mode 100644 index 0000000000..22bec382f8 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/CasesPerStatusChart/index.ts @@ -0,0 +1 @@ +export * from './CasesPerStatusChart'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/MetricListChart/MetricListChart.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/MetricListChart/MetricListChart.tsx new file mode 100644 index 0000000000..49c0ae9bff --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/MetricListChart/MetricListChart.tsx @@ -0,0 +1,37 @@ +import { MetricCard } from '@app/components/molecules/MetricCard'; +import Scrollbars from 'react-custom-scrollbars'; + +interface Props { + isLoading?: boolean; + items: React.ReactNode[]; + title: string; + description?: string; + emptyPlaceholder?: React.ReactNode; +} + +export const MetricListChart = ({ + title, + description, + items, + isLoading, + emptyPlaceholder = null, +}: Props) => { + const isEmpty = !isLoading && !items.length; + + return ( + <MetricCard + isLoading={isLoading} + title={<MetricCard.Title title={title} />} + description={description} + content={ + <MetricCard.Content> + <div className="flex h-full pb-2"> + <Scrollbars> + <div className="flex flex-col gap-1 pr-4">{isEmpty ? emptyPlaceholder : items}</div> + </Scrollbars> + </div> + </MetricCard.Content> + } + /> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/MetricListChart/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/MetricListChart/index.ts new file mode 100644 index 0000000000..745418bb94 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/MetricListChart/index.ts @@ -0,0 +1 @@ +export * from './MetricListChart'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/StatusFilterComponent/StatusFilterComponent.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/StatusFilterComponent/StatusFilterComponent.tsx new file mode 100644 index 0000000000..8569b487bb --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/StatusFilterComponent/StatusFilterComponent.tsx @@ -0,0 +1,22 @@ +import { FacetedFilter, FacetedFilterOption } from '@app/components/molecules/FacetedFilter'; +import { IWorkflowStatus } from '@app/domains/workflows/api/workflow'; +import { FilterComponent } from '@app/pages/Workflows/components/organisms/WorkflowFilters/types'; + +const options: FacetedFilterOption[] = [ + { label: 'Active', value: 'active' }, + { label: 'Failed', value: 'failed' }, + { label: 'Completed', value: 'completed' }, +]; + +export const StatusFilterComponent: FilterComponent = ({ filterValues, onChange }) => { + return ( + <FacetedFilter + value={filterValues.status || []} + title="Status" + options={options} + onChange={updatedValue => onChange({ status: updatedValue as IWorkflowStatus[] })} + /> + ); +}; + +StatusFilterComponent.displayName = 'StatusFilterComponent'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/StatusFilterComponent/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/StatusFilterComponent/index.ts new file mode 100644 index 0000000000..5bf43f4d1f --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/StatusFilterComponent/index.ts @@ -0,0 +1 @@ +export * from './StatusFilterComponent'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/molecules/common/types.ts b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/common/types.ts new file mode 100644 index 0000000000..3850d1d61a --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/molecules/common/types.ts @@ -0,0 +1,3 @@ +export interface ChartProps { + isLoading?: boolean; +} diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowFilters/WorkflowFilters.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowFilters/WorkflowFilters.tsx new file mode 100644 index 0000000000..340af1aff9 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowFilters/WorkflowFilters.tsx @@ -0,0 +1,26 @@ +import { FilterComponent } from '@app/pages/Workflows/components/organisms/WorkflowFilters/types'; +import { + WorkflowFilterValues, + WorkflowFiltersUpdater, +} from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.types'; +import { memo } from 'react'; + +interface Props { + values: WorkflowFilterValues; + components: FilterComponent[]; + onChange: WorkflowFiltersUpdater; +} + +export const WorkflowFilters = memo(({ values, components, onChange }: Props) => { + return ( + <div className="flex justify-between"> + {components.map(Component => { + return ( + <div className="w-1/4" key={`filter-component-${Component.displayName}`}> + <Component filterValues={values} onChange={onChange} /> + </div> + ); + })} + </div> + ); +}); diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowFilters/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowFilters/index.ts new file mode 100644 index 0000000000..7f2f006688 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowFilters/index.ts @@ -0,0 +1 @@ +export * from './WorkflowFilters'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowFilters/types.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowFilters/types.ts new file mode 100644 index 0000000000..9b9256354f --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowFilters/types.ts @@ -0,0 +1,11 @@ +import { + WorkflowFilterValues, + WorkflowFiltersUpdater, +} from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.types'; + +export interface FilterComponentProps { + filterValues: WorkflowFilterValues; + onChange: WorkflowFiltersUpdater; +} + +export type FilterComponent = React.ComponentType<FilterComponentProps>; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/Label.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/Label.tsx new file mode 100644 index 0000000000..23a179c47a --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/Label.tsx @@ -0,0 +1,29 @@ +interface Props { + cx: number; + cy: number; + midAngle: number; + innerRadius: number; + outerRadius: number; + percent: number; +} + +const RADIAN = Math.PI / 180; + +export const Label = ({ cx, cy, midAngle, innerRadius, outerRadius, percent }: Props) => { + const radius = innerRadius + (outerRadius - innerRadius) * 0.5; + const x = cx + radius * Math.cos(-midAngle * RADIAN); + const y = cy + radius * Math.sin(-midAngle * RADIAN); + + return percent === 0 ? null : ( + <text + x={x} + y={y} + fill="white" + textAnchor={x > cx ? 'start' : 'end'} + dominantBaseline="central" + fontFamily="inter" + > + {`${(percent * 100).toFixed(0)}%`} + </text> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/WorkflowStatusChart.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/WorkflowStatusChart.tsx new file mode 100644 index 0000000000..f22bc293f5 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/WorkflowStatusChart.tsx @@ -0,0 +1,30 @@ +import { PieChart, PieChartData } from '@app/components/atoms/PieChart'; +import { IWorkflowStatus } from '@app/domains/workflows/api/workflow'; +import { WorkflowChartDetails } from '@app/pages/Workflows/components/organisms/WorkflowStatusChart/components/WorkflowChartDetails'; +import { memo } from 'react'; + +export interface WorkflowChartData extends PieChartData { + label: string; +} + +export type WorkflowStatusCount = Record<IWorkflowStatus, number>; + +interface Props { + data: WorkflowChartData[]; + size: number; + innerRadius: number; + outerRadius: number; +} + +export const WorkflowChart = memo(({ data, size, innerRadius, outerRadius }: Props) => { + return ( + <div className="align-center flex flex-row flex-nowrap gap-8"> + <div> + <PieChart data={data} size={size} innerRadius={innerRadius} outerRadius={outerRadius} /> + </div> + <div className="align-center flex flex-col justify-center"> + <WorkflowChartDetails data={data} /> + </div> + </div> + ); +}); diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/components/WorkflowChartDetails/WorkflowChartDetails.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/components/WorkflowChartDetails/WorkflowChartDetails.tsx new file mode 100644 index 0000000000..1a34bd3359 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/components/WorkflowChartDetails/WorkflowChartDetails.tsx @@ -0,0 +1,32 @@ +import { calculateChartDataSum } from '@app/components/atoms/PieChart/utils/calculateChartDataSum'; +import { WorkflowChartData } from '@app/pages/Workflows/components/organisms/WorkflowStatusChart/WorkflowStatusChart'; +import { useMemo } from 'react'; + +interface Props { + data: WorkflowChartData[]; +} + +export const WorkflowChartDetails = ({ data }: Props) => { + const totalValue = useMemo(() => calculateChartDataSum(data), [data]); + + return ( + <div className="flex flex-col gap-2"> + {data.map((item, index) => { + const percentOfTotal = ((item.value / totalValue) * 100).toFixed(2); + return ( + <div + key={`chart-details-item-${index}`} + className="flex md:flex-col md:items-start md:gap-1 lg:flex-row lg:items-center lg:gap-4 " + title={item.label} + > + <div className={`h-1 min-w-[20px] rounded`} style={{ background: item.fill }} /> + <div className="flex gap-1 text-xs md:flex-col lg:flex-row"> + <div className="w-14">{percentOfTotal}%</div> + <span className="line-clamp-1 break-all font-medium">{item.label}</span> + </div> + </div> + ); + })} + </div> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/components/WorkflowChartDetails/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/components/WorkflowChartDetails/index.ts new file mode 100644 index 0000000000..e46932c334 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/components/WorkflowChartDetails/index.ts @@ -0,0 +1 @@ +export * from './WorkflowChartDetails'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/consts.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/consts.ts new file mode 100644 index 0000000000..612614604f --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/consts.ts @@ -0,0 +1,3 @@ +export const GREEN_CHART_HEX_COLOR = '#22c55e'; +export const ORANGE_CHART_HEX_COLOR = '#ea580c'; +export const RED_CHART_HEX_COLOR = '#dc2626'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/helpers.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/helpers.ts new file mode 100644 index 0000000000..b6813b2ed1 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/helpers.ts @@ -0,0 +1,22 @@ +import { IWorkflowStatus } from '@app/domains/workflows/api/workflow'; +import { GREEN_CHART_HEX_COLOR, ORANGE_CHART_HEX_COLOR, RED_CHART_HEX_COLOR } from './consts'; + +export function getChartColorByWorkflowStatus(status: IWorkflowStatus): string { + const fillColorMapByStatus: Record<IWorkflowStatus, string> = { + active: ORANGE_CHART_HEX_COLOR, + completed: GREEN_CHART_HEX_COLOR, + failed: RED_CHART_HEX_COLOR, + }; + + return fillColorMapByStatus[status]; +} + +export function getChartLabelByStatus(status: IWorkflowStatus): string { + const labelMapByStatus: Record<IWorkflowStatus, string> = { + active: 'Active', + completed: 'Completed', + failed: 'Failed', + }; + + return labelMapByStatus[status]; +} diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/index.ts new file mode 100644 index 0000000000..5b3e988ce7 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowStatusChart/index.ts @@ -0,0 +1,3 @@ +export * from './WorkflowStatusChart'; +export * from './consts'; +export * from './helpers'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowsList/WorkflowsList.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowsList/WorkflowsList.tsx new file mode 100644 index 0000000000..23f6e92984 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowsList/WorkflowsList.tsx @@ -0,0 +1,42 @@ +import { useSorting } from '@app/common/hooks/useSorting'; +import { LoadingSpinner } from '@app/components/atoms/LoadingSpinner'; +import { WorkflowsTable } from '@app/components/molecules/WorkflowsTable'; +import { InputColumn } from '@app/components/molecules/WorkflowsTable/types'; +import { IWorkflow } from '@app/domains/workflows/api/workflow'; +import { ViewWorkflow } from '@app/pages/Workflows/components/organisms/WorkflowsList/components/ViewWorkflow'; + +interface Props { + workflows: IWorkflow[]; + isLoading?: boolean; + isFetching?: boolean; +} + +const columns: InputColumn[] = [ + { + id: 'view-workflow', + accessorFn: workflow => workflow, + cell: info => <ViewWorkflow workflow={info.getValue<IWorkflow>()} />, + }, +]; + +export const WorkflowsList = ({ workflows, isLoading, isFetching }: Props) => { + const { sortingKey, sortingDirection, setSorting } = useSorting(); + + return isLoading ? ( + <div className="flex w-full justify-center"> + <LoadingSpinner /> + </div> + ) : ( + <WorkflowsTable + items={workflows} + isFetching={isFetching} + onSort={setSorting} + sorting={ + sortingKey && sortingDirection + ? { key: sortingKey, direction: sortingDirection } + : undefined + } + columns={columns} + /> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowsList/components/ViewWorkflow/ViewWorkflow.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowsList/components/ViewWorkflow/ViewWorkflow.tsx new file mode 100644 index 0000000000..71169a5620 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowsList/components/ViewWorkflow/ViewWorkflow.tsx @@ -0,0 +1,36 @@ +import { Button } from '@app/components/atoms/Button'; +import { Dialog, DialogContent, DialogTrigger } from '@app/components/atoms/Dialog'; +import { XstateVisualizer } from '@app/components/organisms/XstateVisualizer'; +import { IWorkflow } from '@app/domains/workflows/api/workflow'; +import { useWorkflowDefinitionQuery } from '@app/pages/Workflows/hooks/useWorkflowDefinitionQuery'; +import { NetworkIcon } from 'lucide-react'; +import { useState } from 'react'; + +interface Props { + workflow: IWorkflow; +} + +export const ViewWorkflow = ({ workflow }: Props) => { + const [isDialogOpen, setOpen] = useState(false); + const { data } = useWorkflowDefinitionQuery( + isDialogOpen ? workflow.workflowDefinitionId : undefined, + ); + + return ( + <Dialog open={isDialogOpen} onOpenChange={setOpen}> + <DialogTrigger asChild> + <Button className="flex items-center gap-2"> + <NetworkIcon size={'16'} /> + Show Workflow + </Button> + </DialogTrigger> + <DialogContent className="h-[80vh] min-w-[80vw] overflow-hidden"> + {data ? ( + <div className="h-full w-full overflow-hidden p-4"> + <XstateVisualizer stateDefinition={data} state={workflow.state || ''} /> + </div> + ) : null} + </DialogContent> + </Dialog> + ); +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowsList/components/ViewWorkflow/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowsList/components/ViewWorkflow/index.ts new file mode 100644 index 0000000000..c8de515625 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowsList/components/ViewWorkflow/index.ts @@ -0,0 +1 @@ +export * from './ViewWorkflow'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowsList/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowsList/index.ts new file mode 100644 index 0000000000..e130a5fba2 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/WorkflowsList/index.ts @@ -0,0 +1 @@ +export * from './WorkflowsList'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/ActivePerWorkflow.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/ActivePerWorkflow.tsx new file mode 100644 index 0000000000..c4c99adbb1 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/ActivePerWorkflow.tsx @@ -0,0 +1,24 @@ +import { + ActivePerWorkflowChart, + ActivePerWorkflowChartData, +} from '@app/pages/Workflows/components/molecules/ActivePerWorkflowChart'; +import { useActivePerWorkflowStatsQuery } from '@app/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/hooks/useActivePerWorkflowStatsQuery'; +import { useMemo } from 'react'; + +const colorScheme = ['#52D726', '#FFEC00', '#FF7300', '#FF0000']; + +export const ActivePerWorkflow = () => { + const { data, isLoading } = useActivePerWorkflowStatsQuery(); + + const chartData = useMemo( + (): ActivePerWorkflowChartData[] => + data.map((item, index) => ({ + workflowName: item.name, + count: item.active, + fillColor: colorScheme[index] || '#000', + })), + [data], + ); + + return <ActivePerWorkflowChart isLoading={isLoading} data={chartData} />; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/hooks/useActivePerWorkflowStatsQuery/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/hooks/useActivePerWorkflowStatsQuery/index.ts new file mode 100644 index 0000000000..f772e959b8 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/hooks/useActivePerWorkflowStatsQuery/index.ts @@ -0,0 +1 @@ +export * from './useActivePerWorkflowStatsQuery'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/hooks/useActivePerWorkflowStatsQuery/useActivePerWorkflowStatsQuery.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/hooks/useActivePerWorkflowStatsQuery/useActivePerWorkflowStatsQuery.ts new file mode 100644 index 0000000000..aa19251a88 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/hooks/useActivePerWorkflowStatsQuery/useActivePerWorkflowStatsQuery.ts @@ -0,0 +1,12 @@ +import { workflowMetricsKeys } from '@app/domains/workflows/api/workflow-metrics/query-keys'; +import { useQuery } from '@tanstack/react-query'; + +export const useActivePerWorkflowStatsQuery = () => { + const { data = [], isLoading, isFetching } = useQuery(workflowMetricsKeys.workflowRuntimeStats()); + + return { + data, + isFetching, + isLoading, + }; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/index.ts new file mode 100644 index 0000000000..9aa8d90474 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/ActivePerWorkflow/index.ts @@ -0,0 +1 @@ +export * from './ActivePerWorkflow'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentCasesStats/AgentCasesStats.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentCasesStats/AgentCasesStats.tsx new file mode 100644 index 0000000000..a8a15f9c6e --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentCasesStats/AgentCasesStats.tsx @@ -0,0 +1,22 @@ +import { + AgentCasesChart, + AgentCasesChartData, +} from '@app/pages/Workflows/components/molecules/AgentCasesChart'; +import { useUsersAssignedCasesStatsQuery } from '@app/pages/Workflows/components/organisms/metrics/AgentCasesStats/hooks/useUsersAssignedCasesStatsQuery'; +import { useMemo } from 'react'; + +export const AgentCasesStats = () => { + const { data = [], isLoading } = useUsersAssignedCasesStatsQuery(); + + const chartData = useMemo( + (): AgentCasesChartData[] => + data?.map(item => ({ + id: item.id, + agentName: `${item.firstName} ${item.lastName}`, + casesCount: item.casesCount, + })), + [data], + ); + + return <AgentCasesChart isLoading={isLoading} data={chartData} />; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentCasesStats/hooks/useUsersAssignedCasesStatsQuery/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentCasesStats/hooks/useUsersAssignedCasesStatsQuery/index.ts new file mode 100644 index 0000000000..fb59376527 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentCasesStats/hooks/useUsersAssignedCasesStatsQuery/index.ts @@ -0,0 +1 @@ +export * from './useUsersAssignedCasesStatsQuery'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentCasesStats/hooks/useUsersAssignedCasesStatsQuery/useUsersAssignedCasesStatsQuery.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentCasesStats/hooks/useUsersAssignedCasesStatsQuery/useUsersAssignedCasesStatsQuery.ts new file mode 100644 index 0000000000..f3a01ba89c --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentCasesStats/hooks/useUsersAssignedCasesStatsQuery/useUsersAssignedCasesStatsQuery.ts @@ -0,0 +1,18 @@ +import { usersStatsQueryKeys } from '@app/domains/user/api/users-stats'; +import { useWorkflowFilters } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowFilters'; +import { useQuery } from '@tanstack/react-query'; + +export const useUsersAssignedCasesStatsQuery = () => { + const { filters } = useWorkflowFilters(); + const { data, isLoading } = useQuery({ + ...usersStatsQueryKeys.casesAssignedStats({ + // fromDate: filters.fromDate!, + }), + enabled: Boolean(filters.fromDate), + }); + + return { + data, + isLoading, + }; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentCasesStats/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentCasesStats/index.ts new file mode 100644 index 0000000000..5985063993 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentCasesStats/index.ts @@ -0,0 +1 @@ +export * from './AgentCasesStats'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentsActivityStats/AgentsActivityStats.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentsActivityStats/AgentsActivityStats.tsx new file mode 100644 index 0000000000..66c2cbbb24 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentsActivityStats/AgentsActivityStats.tsx @@ -0,0 +1,22 @@ +import { + AgentsActivityChart, + LoggedInAgentsChartData, +} from '@app/pages/Workflows/components/molecules/AgentsActivityChart'; +import { useActiveUsersQuery } from '@app/pages/Workflows/components/organisms/metrics/AgentsActivityStats/hooks/useActiveUsersQuery'; +import { useMemo } from 'react'; + +export const AgentsActivityStats = () => { + const { data, isLoading } = useActiveUsersQuery(); + + const chartData = useMemo( + (): LoggedInAgentsChartData[] => + data.map(item => ({ + id: item.id, + fullName: `${item.firstName} ${item.lastName}`, + lastActiveAt: item.lastActiveAt ? new Date(item.lastActiveAt) : null, + })), + [data], + ); + + return <AgentsActivityChart isLoading={isLoading} data={chartData} />; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentsActivityStats/hooks/useActiveUsersQuery/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentsActivityStats/hooks/useActiveUsersQuery/index.ts new file mode 100644 index 0000000000..92a4e587e1 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentsActivityStats/hooks/useActiveUsersQuery/index.ts @@ -0,0 +1 @@ +export * from './useActiveUsersQuery'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentsActivityStats/hooks/useActiveUsersQuery/useActiveUsersQuery.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentsActivityStats/hooks/useActiveUsersQuery/useActiveUsersQuery.ts new file mode 100644 index 0000000000..a2f2224df9 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentsActivityStats/hooks/useActiveUsersQuery/useActiveUsersQuery.ts @@ -0,0 +1,13 @@ +import { usersKeys } from '@app/domains/workflows/api/users'; +import { useQuery } from '@tanstack/react-query'; + +export const useActiveUsersQuery = () => { + const { data = [], isLoading } = useQuery({ + ...usersKeys.activeUsers({}), + }); + + return { + data, + isLoading, + }; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentsActivityStats/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentsActivityStats/index.ts new file mode 100644 index 0000000000..1c998d9500 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/AgentsActivityStats/index.ts @@ -0,0 +1 @@ +export * from './AgentsActivityStats'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/CasesPerStatusStats.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/CasesPerStatusStats.tsx new file mode 100644 index 0000000000..843c81458d --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/CasesPerStatusStats.tsx @@ -0,0 +1,31 @@ +import { IWorkflowStatus } from '@app/domains/workflows/api/workflow'; +import { + CasesPerStatusChart, + CasesPerStatusChartData, +} from '@app/pages/Workflows/components/molecules/CasesPerStatusChart'; +import { useCasesPerStatusQuery } from '@app/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/hooks/useCasesPerStatusQuery'; +import { useMemo } from 'react'; + +const statusToTitleMap: Record<IWorkflowStatus, string> = { + active: 'Active', + completed: 'Completed', + failed: 'Failed', +}; + +export const CasesPerStatusStats = () => { + const { data, isLoading } = useCasesPerStatusQuery(); + + const chartData = useMemo( + (): CasesPerStatusChartData[] => + Object.entries(data) + .map(([status, count]) => ({ + id: status, + status: statusToTitleMap[status as IWorkflowStatus] || status, + casesCount: count, + })) + .sort((dataA, dataB) => dataA.status.localeCompare(dataB.status)), + [data], + ); + + return <CasesPerStatusChart isLoading={isLoading} data={chartData} />; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/hooks/useCasesPerStatusQuery/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/hooks/useCasesPerStatusQuery/index.ts new file mode 100644 index 0000000000..5a7b80b3d1 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/hooks/useCasesPerStatusQuery/index.ts @@ -0,0 +1 @@ +export * from './useCasesPerStatusQuery'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/hooks/useCasesPerStatusQuery/useCasesPerStatusQuery.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/hooks/useCasesPerStatusQuery/useCasesPerStatusQuery.ts new file mode 100644 index 0000000000..51b9e6206c --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/hooks/useCasesPerStatusQuery/useCasesPerStatusQuery.ts @@ -0,0 +1,15 @@ +import { ICasesPerStatusStats } from '@app/domains/workflows/api/workflow-metrics'; +import { workflowMetricsKeys } from '@app/domains/workflows/api/workflow-metrics/query-keys'; +import { useQuery } from '@tanstack/react-query'; + +export const useCasesPerStatusQuery = () => { + const { data = { active: 0, completed: 0, failed: 0 } as ICasesPerStatusStats, isLoading } = + useQuery({ + ...workflowMetricsKeys.workflowCasesPerStatusStats({}), + }); + + return { + data, + isLoading, + }; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/index.ts new file mode 100644 index 0000000000..0a4715dc1f --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/organisms/metrics/CasesPerStatusStats/index.ts @@ -0,0 +1 @@ +export * from './CasesPerStatusStats'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/WorkflowsFiltersProvider.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/WorkflowsFiltersProvider.tsx new file mode 100644 index 0000000000..b9fa1f9d1e --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/WorkflowsFiltersProvider.tsx @@ -0,0 +1,38 @@ +import { deserializeQueryParams } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/helpers/deserializeQueryParams'; +import { useWorkflowsQueryParams } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowsQueryParams'; +import { + WorkflowFilterValues, + WorkflowFiltersContext, +} from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.types'; +import { useCallback, useMemo } from 'react'; +import { workflowsFilterContext } from './workflows-filters.context'; + +const { Provider } = workflowsFilterContext; + +interface Props { + children: React.ReactNode | React.ReactNode[]; +} + +export const WorkflowsFiltersProvider = ({ children }: Props) => { + const { query, setQuery } = useWorkflowsQueryParams(); + + const filterValues = useMemo(() => deserializeQueryParams(query), [query]); + + const updateFilters = useCallback( + (filters: Partial<WorkflowFilterValues>) => { + setQuery(filters); + }, + [setQuery], + ); + + const context = useMemo(() => { + const ctx: WorkflowFiltersContext = { + filters: filterValues, + updateFilters, + }; + + return ctx; + }, [filterValues, updateFilters]); + + return <Provider value={context}>{children}</Provider>; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/helpers/deserializeQueryParams.ts b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/helpers/deserializeQueryParams.ts new file mode 100644 index 0000000000..e09a895317 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/helpers/deserializeQueryParams.ts @@ -0,0 +1,15 @@ +import { WorkflowsQueryParams } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowsQueryParams/types'; +import { WorkflowFilterValues } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.types'; + +export const deserializeQueryParams = (query: WorkflowsQueryParams): WorkflowFilterValues => { + const filters: WorkflowFilterValues = { + page: query.page, + limit: query.limit, + fromDate: query.fromDate, + status: Array.isArray(query.status) + ? (query.status as WorkflowFilterValues['status']) + : undefined, + }; + + return filters; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hocs/withWorkflowFilters/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hocs/withWorkflowFilters/index.ts new file mode 100644 index 0000000000..10e0be2e9e --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hocs/withWorkflowFilters/index.ts @@ -0,0 +1 @@ +export * from './withWorkflowFilters'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hocs/withWorkflowFilters/types.ts b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hocs/withWorkflowFilters/types.ts new file mode 100644 index 0000000000..fa2fdfdc14 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hocs/withWorkflowFilters/types.ts @@ -0,0 +1,9 @@ +import { + WorkflowFilterValues, + WorkflowFiltersUpdater, +} from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.types'; + +export interface WorkflowFiltersProps { + filters: WorkflowFilterValues; + updateFilters: WorkflowFiltersUpdater; +} diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hocs/withWorkflowFilters/withWorkflowFilters.tsx b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hocs/withWorkflowFilters/withWorkflowFilters.tsx new file mode 100644 index 0000000000..d53d14a61c --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hocs/withWorkflowFilters/withWorkflowFilters.tsx @@ -0,0 +1,29 @@ +import { WorkflowFiltersProps } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/hocs/withWorkflowFilters/types'; +import { useWorkflowFilters } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowFilters'; +import { WorkflowsFiltersProvider } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/WorkflowsFiltersProvider'; + +type InputComponentProps<TProps> = Omit<TProps, keyof WorkflowFiltersProps>; + +export function withWorkflowFilters<TComponentProps extends WorkflowFiltersProps>( + Component: React.FunctionComponent<TComponentProps>, +): React.FunctionComponent<InputComponentProps<TComponentProps>> { + function Wrapper(props: InputComponentProps<TComponentProps>) { + return ( + <WorkflowsFiltersProvider> + <ContextProvider {...props} /> + </WorkflowsFiltersProvider> + ); + } + + function ContextProvider(props: InputComponentProps<TComponentProps>) { + const context = useWorkflowFilters(); + + return <Component {...({ ...props, ...context } as TComponentProps)} />; + } + + ContextProvider.displayName = 'withWorkflowFilters(ContextConsumer)'; + + Wrapper.displayName = `withWorkflowFilters(${Component.displayName})`; + + return Wrapper; +} diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowFilters/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowFilters/index.ts new file mode 100644 index 0000000000..b7b03dc54b --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowFilters/index.ts @@ -0,0 +1 @@ +export * from './useWorkflowFilters'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowFilters/useWorkflowFilters.ts b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowFilters/useWorkflowFilters.ts new file mode 100644 index 0000000000..f0273da0c8 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowFilters/useWorkflowFilters.ts @@ -0,0 +1,4 @@ +import { workflowsFilterContext } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.context'; +import { useContext } from 'react'; + +export const useWorkflowFilters = () => useContext(workflowsFilterContext); diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowsQueryParams/index.ts b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowsQueryParams/index.ts new file mode 100644 index 0000000000..d4d13f682c --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowsQueryParams/index.ts @@ -0,0 +1 @@ +export * from './useWorkflowsQueryParams'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowsQueryParams/types.ts b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowsQueryParams/types.ts new file mode 100644 index 0000000000..19bb1e8006 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowsQueryParams/types.ts @@ -0,0 +1,3 @@ +import { useWorkflowsQueryParams } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowsQueryParams/useWorkflowsQueryParams'; + +export type WorkflowsQueryParams = ReturnType<typeof useWorkflowsQueryParams>['query']; diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowsQueryParams/useWorkflowsQueryParams.ts b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowsQueryParams/useWorkflowsQueryParams.ts new file mode 100644 index 0000000000..d8898666d8 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/hooks/useWorkflowsQueryParams/useWorkflowsQueryParams.ts @@ -0,0 +1,20 @@ +import { IWorkflowStatus } from '@app/domains/workflows/api/workflow'; +import { useState } from 'react'; +import { useQueryParams, NumberParam, withDefault, ArrayParam } from 'use-query-params'; +import dayjs from 'dayjs'; + +export function useWorkflowsQueryParams() { + const [dateNow] = useState(() => dayjs().subtract(1, 'hour').toDate()); + + const [query, setQuery] = useQueryParams({ + page: withDefault(NumberParam, 1), + limit: withDefault(NumberParam, 25), + status: withDefault(ArrayParam, [] as IWorkflowStatus[]), + fromDate: withDefault(NumberParam, +dateNow), + }); + + return { + query, + setQuery, + }; +} diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.context.ts b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.context.ts new file mode 100644 index 0000000000..07ed5967fa --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.context.ts @@ -0,0 +1,4 @@ +import { WorkflowFiltersContext } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.types'; +import { createContext } from 'react'; + +export const workflowsFilterContext = createContext({} as WorkflowFiltersContext); diff --git a/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.types.ts b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.types.ts new file mode 100644 index 0000000000..ceb3e24953 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.types.ts @@ -0,0 +1,15 @@ +import { IWorkflowStatus } from '@app/domains/workflows/api/workflow'; + +export interface WorkflowFilterValues { + status?: IWorkflowStatus[]; + page?: number; + limit?: number; + fromDate?: number; +} + +export type WorkflowFiltersUpdater = (filters: Partial<WorkflowFilterValues>) => void; + +export interface WorkflowFiltersContext { + filters: WorkflowFilterValues; + updateFilters: WorkflowFiltersUpdater; +} diff --git a/apps/workflows-dashboard/src/pages/Workflows/hooks/useWorkflowDefinitionQuery/index.ts b/apps/workflows-dashboard/src/pages/Workflows/hooks/useWorkflowDefinitionQuery/index.ts new file mode 100644 index 0000000000..83bcc17332 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/hooks/useWorkflowDefinitionQuery/index.ts @@ -0,0 +1 @@ +export * from './useWorkflowDefinitionQuery'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/hooks/useWorkflowDefinitionQuery/useWorkflowDefinitionQuery.ts b/apps/workflows-dashboard/src/pages/Workflows/hooks/useWorkflowDefinitionQuery/useWorkflowDefinitionQuery.ts new file mode 100644 index 0000000000..a9629db26a --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/hooks/useWorkflowDefinitionQuery/useWorkflowDefinitionQuery.ts @@ -0,0 +1,14 @@ +import { workflowKeys } from '@app/domains/workflows'; +import { useQuery } from '@tanstack/react-query'; + +export const useWorkflowDefinitionQuery = (workflowId?: string) => { + const { data, isLoading } = useQuery({ + ...workflowKeys.workflowDefinition({ workflowId: workflowId! }), + enabled: Boolean(workflowId), + }); + + return { + data, + isLoading, + }; +}; diff --git a/apps/workflows-dashboard/src/pages/Workflows/hooks/useWorkflowsQuery/index.ts b/apps/workflows-dashboard/src/pages/Workflows/hooks/useWorkflowsQuery/index.ts new file mode 100644 index 0000000000..0d39bd8d2d --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/hooks/useWorkflowsQuery/index.ts @@ -0,0 +1 @@ +export * from './useWorkflowsQuery'; diff --git a/apps/workflows-dashboard/src/pages/Workflows/hooks/useWorkflowsQuery/useWorkflowsQuery.ts b/apps/workflows-dashboard/src/pages/Workflows/hooks/useWorkflowsQuery/useWorkflowsQuery.ts new file mode 100644 index 0000000000..a0ac6925a1 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/hooks/useWorkflowsQuery/useWorkflowsQuery.ts @@ -0,0 +1,21 @@ +import { SortingParams } from '@app/common/types/sorting-params.types'; +import { workflowKeys } from '@app/domains/workflows'; +import { WorkflowFilterValues } from '@app/pages/Workflows/components/providers/WorkflowsFiltersProvider/workflows-filters.types'; +import { useQuery } from '@tanstack/react-query'; + +export function useWorkflowsQuery(query: WorkflowFilterValues, sortingParams?: SortingParams) { + const { + isFetching, + isLoading, + data = { results: [], meta: { pages: 0, total: 0 } }, + } = useQuery({ + ...workflowKeys.list(query, sortingParams || {}), + keepPreviousData: true, + }); + + return { + isFetching, + isLoading, + data, + }; +} diff --git a/apps/workflows-dashboard/src/pages/Workflows/index.ts b/apps/workflows-dashboard/src/pages/Workflows/index.ts new file mode 100644 index 0000000000..57508c4052 --- /dev/null +++ b/apps/workflows-dashboard/src/pages/Workflows/index.ts @@ -0,0 +1 @@ +export * from './Workflows'; diff --git a/apps/workflows-dashboard/src/router.tsx b/apps/workflows-dashboard/src/router.tsx new file mode 100644 index 0000000000..52301391cc --- /dev/null +++ b/apps/workflows-dashboard/src/router.tsx @@ -0,0 +1,31 @@ +import { App } from '@app/App'; +import { withSessionProtected } from '@app/common/hocs/withSessionProtected'; +import { Overview } from '@app/pages/Overview'; +import { SignIn } from '@app/pages/SignIn'; +import { Workflows } from '@app/pages/Workflows'; +import { createBrowserRouter, Navigate } from 'react-router-dom'; + +export const router = createBrowserRouter([ + { + path: '/', + Component: App, + children: [ + { + path: '', + element: <Navigate to="/overview" replace />, + }, + { + path: '/auth/signin', + Component: SignIn, + }, + { + path: 'overview', + Component: withSessionProtected(Overview), + }, + { + path: 'workflows', + Component: withSessionProtected(Workflows), + }, + ], + }, +]); diff --git a/apps/workflows-dashboard/src/utils/calculate-hour-difference.ts b/apps/workflows-dashboard/src/utils/calculate-hour-difference.ts new file mode 100644 index 0000000000..150d6e931f --- /dev/null +++ b/apps/workflows-dashboard/src/utils/calculate-hour-difference.ts @@ -0,0 +1,8 @@ +export function calculateHourDifference(dateA: Date, dateB: Date) { + const diff = Math.abs( + Number(new Date(dateB.toISOString())) - Number(new Date(dateA.toISOString())), + ); + const hours = Math.floor(diff / (1000 * 60 * 60)); + + return hours; +} diff --git a/apps/workflows-dashboard/src/utils/get-workflow-health-status.test.ts b/apps/workflows-dashboard/src/utils/get-workflow-health-status.test.ts new file mode 100644 index 0000000000..2d8a1f089b --- /dev/null +++ b/apps/workflows-dashboard/src/utils/get-workflow-health-status.test.ts @@ -0,0 +1,50 @@ +import * as dayjs from 'dayjs'; +import { IWorkflow } from '@app/domains/workflows/api/workflow'; +import { getWorkflowHealthStatus, HealthStatus } from '@app/utils/get-workflow-health-status'; + +describe('getWorkflowHealthStatus', () => { + describe('healthy status', () => { + test('healthy when status is completed', () => { + expect(getWorkflowHealthStatus({ status: 'completed' } as IWorkflow)).toBe( + HealthStatus.healthy, + ); + }); + + test('healthy when status pending/active and process started < 2 hours ago', () => { + const PAST_DATE_3O_MIN_AGO = dayjs().subtract(30, 'minutes'); + + expect( + getWorkflowHealthStatus({ + status: 'active', + createdAt: PAST_DATE_3O_MIN_AGO.toDate(), + } as IWorkflow), + ).toBe(HealthStatus.healthy); + }); + }); + + describe('pending status', () => { + test('pending when status pending/active and process started > 2 && < 6 hours', () => { + const PAST_DATE_3HOURS_AGO = dayjs().subtract(3, 'hours'); + + expect( + getWorkflowHealthStatus({ + status: 'active', + createdAt: PAST_DATE_3HOURS_AGO.toDate(), + } as IWorkflow), + ).toBe(HealthStatus.pending); + }); + }); + + describe('pending-longterm status', () => { + test('pending-longterm when status pending/active and process started > 6 hours', () => { + const PAST_DATE_8HOURS_AGO = dayjs().subtract(8, 'hours'); + + expect( + getWorkflowHealthStatus({ + status: 'active', + createdAt: PAST_DATE_8HOURS_AGO.toDate(), + } as IWorkflow), + ).toBe(HealthStatus['pending-longterm']); + }); + }); +}); diff --git a/apps/workflows-dashboard/src/utils/get-workflow-health-status.ts b/apps/workflows-dashboard/src/utils/get-workflow-health-status.ts new file mode 100644 index 0000000000..3ee0f161d3 --- /dev/null +++ b/apps/workflows-dashboard/src/utils/get-workflow-health-status.ts @@ -0,0 +1,35 @@ +import { IWorkflow } from '@app/domains/workflows/api/workflow'; +import { calculateHourDifference } from '@app/utils/calculate-hour-difference'; + +export enum HealthStatus { + 'healthy', + 'failed', + 'pending', + 'pending-longterm', +} + +export function getWorkflowHealthStatus(workflow: IWorkflow): HealthStatus { + const { status, createdAt } = workflow; + + if (status === 'failed') return HealthStatus.failed; + + if (status === 'completed') return HealthStatus.healthy; + + const hourDifference = calculateHourDifference(new Date(createdAt), new Date()); + const TWO_HOURS = 2; + const SIX_HOURS = 6; + + if (status === 'active' && hourDifference < TWO_HOURS) { + return HealthStatus.healthy; + } + + if (status === 'active' && hourDifference > TWO_HOURS && hourDifference < SIX_HOURS) { + return HealthStatus.pending; + } + + if (status === 'active' && hourDifference > SIX_HOURS) { + return HealthStatus['pending-longterm']; + } + + return HealthStatus.failed; +} diff --git a/apps/workflows-dashboard/src/vite-env.d.ts b/apps/workflows-dashboard/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/apps/workflows-dashboard/src/vite-env.d.ts @@ -0,0 +1 @@ +/// <reference types="vite/client" /> diff --git a/apps/workflows-dashboard/tailwind.config.cjs b/apps/workflows-dashboard/tailwind.config.cjs new file mode 100644 index 0000000000..49e805d1f4 --- /dev/null +++ b/apps/workflows-dashboard/tailwind.config.cjs @@ -0,0 +1,77 @@ +const { fontFamily } = require('tailwindcss/defaultTheme'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ['class'], + content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], + theme: { + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px', + }, + }, + extend: { + colors: { + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + }, + borderRadius: { + lg: `var(--radius)`, + md: `calc(var(--radius) - 2px)`, + sm: 'calc(var(--radius) - 4px)', + }, + fontFamily: { + sans: ['var(--font-sans)', ...fontFamily.sans], + inter: ['Inter', 'sans-serif'], + }, + keyframes: { + 'accordion-down': { + from: { height: 0 }, + to: { height: 'var(--radix-accordion-content-height)' }, + }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, + to: { height: 0 }, + }, + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', + }, + }, + }, + plugins: [require('tailwindcss-animate'), require('@tailwindcss/line-clamp')], +}; diff --git a/apps/workflows-dashboard/tsconfig.json b/apps/workflows-dashboard/tsconfig.json new file mode 100644 index 0000000000..7ae5ec3710 --- /dev/null +++ b/apps/workflows-dashboard/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "Node", + "allowImportingTsExtensions": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "paths": { + "@app/*": ["./src/*"] + }, + "types": ["node", "jest", "vite-plugin-terminal/client"] + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/apps/workflows-dashboard/tsconfig.node.json b/apps/workflows-dashboard/tsconfig.node.json new file mode 100644 index 0000000000..c53fe434e9 --- /dev/null +++ b/apps/workflows-dashboard/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ES6", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/apps/workflows-dashboard/vite.config.ts b/apps/workflows-dashboard/vite.config.ts new file mode 100644 index 0000000000..acbad75473 --- /dev/null +++ b/apps/workflows-dashboard/vite.config.ts @@ -0,0 +1,33 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import { resolve } from 'path'; +import tailwindcss from 'tailwindcss'; +import checker from 'vite-plugin-checker'; +import terminal from 'vite-plugin-terminal'; + +// https://vitejs.dev/config/ +export default defineConfig({ + server: { + port: 5200, + }, + preview: { + port: 5200, + }, + build: { + sourcemap: true, + }, + plugins: [ + react(), + tailwindcss(), + checker({ typescript: true }), + terminal({ + output: ['console', 'terminal'], + strip: false, + }), + ], + resolve: { + alias: { + '@app': resolve(__dirname, './src'), + }, + }, +}); diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index a295d9e3af..4ac27d69b4 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -22,14 +22,14 @@ services: depends_on: - db db: - image: postgres:12 + image: sibedge/postgres-plv8:15.3-3.1.7 ports: - ${DB_PORT}:5432 environment: POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - - postgres:/var/lib/postgresql/data + - postgres15:/var/lib/postgresql/data healthcheck: test: - CMD @@ -43,4 +43,4 @@ services: interval: 10s retries: 10 volumes: - postgres: ~ + postgres15: ~ diff --git a/deploy/helm/.helmignore b/deploy/helm/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/deploy/helm/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/deploy/helm/Chart.yaml b/deploy/helm/Chart.yaml new file mode 100644 index 0000000000..10cb54eb75 --- /dev/null +++ b/deploy/helm/Chart.yaml @@ -0,0 +1,30 @@ +apiVersion: v2 +name: helm +description: A Helm chart for ballerine + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.0.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.4.14" +dependencies: + - condition: postgresql.enabled + name: postgresql + version: 12.0.0 + appVersion: 15.0.0 + repository: https://charts.bitnami.com/bitnami \ No newline at end of file diff --git a/deploy/helm/README.md b/deploy/helm/README.md new file mode 100644 index 0000000000..69136f3004 --- /dev/null +++ b/deploy/helm/README.md @@ -0,0 +1,91 @@ +# Install ballerine using helm chart + +Ballerine is a collection of services like workflow-service, backendoffice. +In values.yaml we have sections to enable/disable them based on the necessity like below + +``` bash +workflowService: + enabled: true +``` + +## Prerequisites + +- kubernetes cluster +- [helm](https://helm.sh/docs/intro/install/) +- [kubectl](https://storage.googleapis.com/kubernetes-release/release/v1.23.6/bin/linux/amd64/kubectl) preferably 1.24 or less upto 1.23 + +### How to install + +Move to deploy directory + +```bash +cd deploy/helm +``` + +### Setup Postgresql + +#### Install postgresql along with ballerine + +- edit values.yaml + +```bash +## Postgres params +postgresql: + enabled: true + auth: + username: admin + password: admin + postgresPassword: admin + database: postgres +# Local dev purpose +# persistence: +# existingClaim: postgresql-pv-claim +# volumePermissions: +# enabled: true +``` + +#### How to use managed postgresql along with ballerine + +- edit values.yaml + +```bash +## Postgres params +postgresql: + enabled: false +. +. +. +. + applicationConfig: + BCRYPT_SALT: "10" + JWT_SECRET_KEY: "secret" + JWT_EXPIRATION: "10d" + DB_URL: "<Managed DB_URL with databasename>" + DB_USER: "<Managed DB_USER>" + DB_PASSWORD: "<Managed DB_PASSWORD>" + DB_PORT: "5432" +``` + +### Installing Ballerine helm chart + +``` bash +helm install ballerine . -n ballerine --create-namespace -f values.yaml +``` + +### Troubleshooting + +```bash +kubectl get pods -n ballerine +``` + +- Note the pod name of service you wish to trouble shoot + +```bash +kubectl logs <pod> -n ballerine +``` + +- Accessing the application + +```bash +kubectl port-forward svc/<service> -n ballerine 3000:3000 +``` diff --git a/deploy/helm/example.values.yaml b/deploy/helm/example.values.yaml new file mode 100644 index 0000000000..1b0e48ab26 --- /dev/null +++ b/deploy/helm/example.values.yaml @@ -0,0 +1,50 @@ +## Postgres params +postgresql: + enabled: true + auth: + username: admin + password: admin + postgresPassword: admin + database: postgres + # Local dev purpose + persistence: + existingClaim: postgresql-pv-claim + volumePermissions: + enabled: true + +workflowService: + enabled: true + replicas: 1 + strategyType: RollingUpdate + updateStrategy: + maxSurge: 1 + maxUnavailable: "0" + nameOverride: workflowservice + migration: true + service: + port: 3000 + type: ClusterIP + protocol: TCP + image: + registry: index.docker.io + repository: "" + pullPolicy: Always + tag: "latest" + applicationConfig: + BCRYPT_SALT: "10" + JWT_SECRET_KEY: "secret" + JWT_EXPIRATION: "10d" + DB_URL: "" + DB_USER: "" + DB_PASSWORD: "" + DB_PORT: "5432" + PORT: "3000" + COMPOSE_PROJECT_NAME: "ballerine-x" + SESSION_SECRET: "iGdnj4A0YOhj8dHJK7IWSvQKEZsG7P70FFehuddhFPjtg/bSkzFejYILk4Xue6Ilx9y3IAwzR8pV1gb4" + BACKOFFICE_CORS_ORIGIN: "http://localhost:5137" + HEADLESS_EXAMPLE_CORS_ORIGIN: "http://localhost:5173" + API_KEY: "secret" + NODE_ENV: "development" + SENTRY_DSN: "" + WEBHOOK_URL: "" + WEBHOOK_SECRET: "webhook_secret" diff --git a/deploy/helm/services/workflows-service/templates/configmap.yaml b/deploy/helm/services/workflows-service/templates/configmap.yaml new file mode 100644 index 0000000000..aa5eb44be3 --- /dev/null +++ b/deploy/helm/services/workflows-service/templates/configmap.yaml @@ -0,0 +1,31 @@ +{{- $name := .Release.Name }} +{{- $namespace:= .Release.Namespace }} +{{- $postgresqlUser := .Values.postgresql.auth.username -}} +{{- $postgresqlPassword := .Values.postgresql.auth.password -}} +{{- $postgresqlDatabase := .Values.postgresql.auth.database -}} +{{- if .Values.workflowService.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.workflowService.nameOverride }} + namespace: {{ .Release.Namespace | quote }} + labels: + app: {{ .Values.workflowService.nameOverride }} +data: + {{- range $key, $value := .Values.workflowService.applicationConfig }} + {{- if $.Values.postgresql.enabled }} + {{- if (eq "DB_URL" $key) }} + {{ $key }}: postgres://{{ $postgresqlUser }}:{{ $postgresqlPassword }}@{{ $name }}-postgresql.{{ $namespace }}.svc.cluster.local:5432/{{ $postgresqlDatabase }} + {{- end }} + {{- if (eq "DB_USER" $key) }} + {{ $key }}: {{ $postgresqlUser }} + {{- end }} + {{- if (eq "DB_PASSWORD" $key) }} + {{ $key }}: {{ $postgresqlPassword }} + {{- end }} + {{- end }} + {{- if $value }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/deploy/helm/services/workflows-service/templates/deployment.yaml b/deploy/helm/services/workflows-service/templates/deployment.yaml new file mode 100644 index 0000000000..beac436107 --- /dev/null +++ b/deploy/helm/services/workflows-service/templates/deployment.yaml @@ -0,0 +1,60 @@ +{{- if .Values.workflowService.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.workflowService.nameOverride }} + namespace: {{ .Release.Namespace | quote }} + labels: + app: {{ .Values.workflowService.nameOverride }} +spec: + replicas: {{ .Values.workflowService.replicas }} + {{- if .Values.workflowService.strategyType }} + strategy: + type: {{ .Values.workflowService.strategyType }} + {{- end }} + {{- if .Values.workflowService.updateStrategy }} + rollingUpdate: + {{- if .Values.workflowService.updateStrategy.maxSurge }} + maxSurge: {{ .Values.workflowService.updateStrategy.maxSurge}} + {{- end }} + {{- if .Values.workflowService.updateStrategy.maxUnavailable }} + maxUnavailable: {{ .Values.workflowService.updateStrategy.maxUnavailable }} + {{- end }} + {{- end }} + selector: + matchLabels: + app: {{ .Values.workflowService.nameOverride }} + template: + metadata: + labels: + app: {{ .Values.workflowService.nameOverride }} + spec: + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + initContainers: + {{- if .Values.postgresql.enabled }} + - name: psql-init-container + image: alpine:latest + command: ['sh', '-c', "apk add postgresql-client; until pg_isready -h {{ .Release.Name }}-postgresql.{{.Release.Namespace}}.svc.cluster.local; do echo waiting for postgresql; sleep 2; done"] + {{- end }} + {{- if .Values.workflowService.migration }} + - name: migration-init-container + image: {{ .Values.workflowService.image.registry }}/{{ .Values.workflowService.image.repository }}:{{ .Values.workflowService.image.tag }} + command: ['npm','run','db:init'] + envFrom: + - configMapRef: + name: {{ .Values.workflowService.nameOverride }} + {{- end }} + containers: + - name: {{ .Values.workflowService.nameOverride }} + image: "{{ .Values.workflowService.image.registry }}/{{ .Values.workflowService.image.repository }}:{{ .Values.workflowService.image.tag }}" + imagePullPolicy: {{ .Values.workflowService.image.pullPolicy }} + command: [ "dumb-init", "npm", "run", "prod" ] + envFrom: + - configMapRef: + name: {{ .Values.workflowService.nameOverride }} + imagePullSecrets: + - name: {{ .Values.workflowService.image.pullSecrets }} +{{- end }} \ No newline at end of file diff --git a/deploy/helm/services/workflows-service/templates/service.yaml b/deploy/helm/services/workflows-service/templates/service.yaml new file mode 100644 index 0000000000..20c609d3c8 --- /dev/null +++ b/deploy/helm/services/workflows-service/templates/service.yaml @@ -0,0 +1,18 @@ +{{- if .Values.workflowService.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.workflowService.nameOverride }} + namespace: {{ .Release.Namespace | quote }} + labels: + app: {{ .Values.workflowService.nameOverride }} +spec: + ports: + - name: {{ .Values.workflowService.nameOverride }} + port: {{ .Values.workflowService.service.port }} + protocol: {{ .Values.workflowService.service.protocol }} + targetPort: {{ .Values.workflowService.service.port }} + selector: + app: {{ .Values.workflowService.nameOverride }} + type: {{ .Values.workflowService.service.type }} +{{- end }} \ No newline at end of file diff --git a/deploy/helm/templates/import.yaml b/deploy/helm/templates/import.yaml new file mode 100644 index 0000000000..1d09e4031f --- /dev/null +++ b/deploy/helm/templates/import.yaml @@ -0,0 +1,5 @@ +{{- /* Import all "templates/*.yaml" files found in the repo */ -}} +{{ range $path, $_ := .Files.Glob "services/**/templates/*.yaml" }} +{{ tpl (print "# -> " $path "\n" ((print "--- \n # -> " $path "\n") | regexReplaceAll "---" ($.Files.Get $path))) $ }} +--- +{{ end }} \ No newline at end of file diff --git a/examples/headless-example/.gitignore b/examples/headless-example/.gitignore index 7ceb59f89a..91c473a3ff 100644 --- a/examples/headless-example/.gitignore +++ b/examples/headless-example/.gitignore @@ -23,3 +23,6 @@ dist-ssr *.sln *.sw? .env +/test-results/ +/playwright-report/ +/playwright/.cache/ diff --git a/examples/headless-example/CHANGELOG.md b/examples/headless-example/CHANGELOG.md new file mode 100644 index 0000000000..011356781e --- /dev/null +++ b/examples/headless-example/CHANGELOG.md @@ -0,0 +1,25 @@ +# @ballerine/headless-example + +## 0.0.4 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.6 + - @ballerine/workflow-browser-sdk@0.4.9 + +## 0.0.3 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.5 + - @ballerine/workflow-browser-sdk@0.4.8 + +## 0.0.2 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.4 + - @ballerine/workflow-browser-sdk@0.4.7 diff --git a/examples/headless-example/e2e/smoke.spec.ts b/examples/headless-example/e2e/smoke.spec.ts new file mode 100644 index 0000000000..47e9f755b9 --- /dev/null +++ b/examples/headless-example/e2e/smoke.spec.ts @@ -0,0 +1,29 @@ +import { expect, test } from '@playwright/test'; + +test.describe('should keep basic functionality #e2e #smoke', async () => { + test('when navigating to the root page the sign up form renders', async ({ page }) => { + await page.goto('/'); + + const welcome = page.getByText(/welcome/i); + + const firstInput = page.getByLabel(/first\sname|business\sname/i); + const secondInput = page.getByLabel(/last\sname|registration\snumber/i); + + const signUpBtn = page.getByRole('button', { name: /sign\sup/i }); + + await expect(welcome).toBeVisible(); + + await expect(firstInput).toBeVisible(); + await expect(secondInput).toBeVisible(); + + await expect(signUpBtn).toBeVisible(); + }); + + test('when navigating to the root page clear user button renders', async ({ page }) => { + await page.goto('/'); + + const clearUserBtn = page.getByRole('button', { name: /clear\suser/i }); + + await expect(clearUserBtn).toBeVisible(); + }); +}); diff --git a/examples/headless-example/package.json b/examples/headless-example/package.json index 9d1abfdc33..b062ade564 100644 --- a/examples/headless-example/package.json +++ b/examples/headless-example/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/headless-example", "private": true, - "version": "0.0.1", + "version": "0.0.4", "type": "module", "scripts": { "format": "prettier --plugin-search-dir=. --write .", @@ -9,12 +9,15 @@ "dev": "vite", "build": "vite build", "preview": "vite preview", - "check": "svelte-check --tsconfig ./tsconfig.json" + "check": "svelte-check --tsconfig ./tsconfig.json", + "test:e2e": "playwright test" }, "devDependencies": { "@felte/core": "^1.3.7", + "@playwright/test": "^1.35.1", "@sveltejs/vite-plugin-svelte": "^2.0.2", "@tsconfig/svelte": "^3.0.0", + "@types/node": "^20.3.2", "@xstate/inspect": "^0.7.1", "autoprefixer": "^10.4.7", "postcss": "^8.4.21", @@ -28,7 +31,8 @@ "vite": "^4.1.0" }, "dependencies": { - "@ballerine/workflow-browser-sdk": "^0.4.2", + "@ballerine/common": "0.5.6", + "@ballerine/workflow-browser-sdk": "^0.4.9", "@felte/reporter-svelte": "^1.1.5", "@felte/validator-zod": "^1.0.13", "@fontsource/inter": "^4.5.15", diff --git a/examples/headless-example/playwright.config.ts b/examples/headless-example/playwright.config.ts new file mode 100644 index 0000000000..90aff329e4 --- /dev/null +++ b/examples/headless-example/playwright.config.ts @@ -0,0 +1,77 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './e2e', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: 'http://127.0.0.1:5173', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/examples/headless-example/public/mock-id.png b/examples/headless-example/public/mock-id-photo-CA.png similarity index 100% rename from examples/headless-example/public/mock-id.png rename to examples/headless-example/public/mock-id-photo-CA.png diff --git a/examples/headless-example/public/mock-certificate-of-incorporation.pdf b/examples/headless-example/public/mock-incorporation-pdf-CA.pdf similarity index 100% rename from examples/headless-example/public/mock-certificate-of-incorporation.pdf rename to examples/headless-example/public/mock-incorporation-pdf-CA.pdf diff --git a/examples/headless-example/public/mock-selfie.png b/examples/headless-example/public/mock-selfie-photo-CA.png similarity index 100% rename from examples/headless-example/public/mock-selfie.png rename to examples/headless-example/public/mock-selfie-photo-CA.png diff --git a/examples/headless-example/src/components/App.svelte b/examples/headless-example/src/components/App.svelte index 089541fb07..fcfe349455 100644 --- a/examples/headless-example/src/components/App.svelte +++ b/examples/headless-example/src/components/App.svelte @@ -9,7 +9,7 @@ import { makeWorkflow } from '@/utils'; import SignUp from './SignUp.svelte'; import Workflow from './Workflow.svelte'; - import { NO_AUTH_USER_KEY } from '@/constants'; + import { ENTITY_ID_STORAGE_KEY } from '@/constants'; import { writable } from 'svelte/store'; import Approved from '@/components/Approved.svelte'; import Rejected from '@/components/Rejected.svelte'; @@ -19,7 +19,14 @@ import { BallerineBackOfficeService } from '@/services/ballerine-backoffice.service'; import DevSidebar from '@/visualiser/dev-sidebar.svelte'; - let noAuthUserId = sessionStorage.getItem(NO_AUTH_USER_KEY); + let entityId = sessionStorage.getItem(ENTITY_ID_STORAGE_KEY); + let nextWorkflow; + let shouldResubmit = false; + let documentsDecisionStatuses; + let isDecided; + let isApproved; + let isRejected; + let isRevision; const { fetchEndUser, @@ -37,30 +44,37 @@ queryFn: async () => import.meta.env.VITE_EXAMPLE_TYPE === 'kyc' ? fetchEndUser(id) : fetchBusiness(id), onSuccess(data) { - const cached = sessionStorage.getItem(NO_AUTH_USER_KEY); + const cached = sessionStorage.getItem(ENTITY_ID_STORAGE_KEY); if ((cached && cached === data?.id) || !data?.id) return; - noAuthUserId = data?.id; - sessionStorage.setItem(NO_AUTH_USER_KEY, noAuthUserId); + entityId = data?.id; + sessionStorage.setItem(ENTITY_ID_STORAGE_KEY, entityId); }, onError(error) { if (error.message !== 'Not Found (404)') { throw error; } - sessionStorage.removeItem(NO_AUTH_USER_KEY); - noAuthUserId = undefined; + sessionStorage.removeItem(ENTITY_ID_STORAGE_KEY); + entityId = undefined; }, enabled: typeof id === 'string' && id.length > 0, }); + const entityType = ( + import.meta.env.VITE_EXAMPLE_TYPE === 'kyc' ? 'end-users' : 'businesses' + ) as const; const createWorkflowsQuery = ( options: CreateQueryOptions<Awaited<ReturnType<typeof fetchWorkflows>>> = {}, ) => createQuery({ - queryKey: ['workflows'], - queryFn: fetchWorkflows, - enabled: typeof noAuthUserId === 'string' && noAuthUserId.length > 0, + queryKey: ['workflows', { entityType, entityId }], + queryFn: () => + fetchWorkflows({ + entityType, + entityId, + }), + enabled: typeof entityId === 'string' && entityId.length > 0, ...options, }); const createIntentQuery = () => @@ -79,15 +93,32 @@ createWorkflowsQuery({ select: workflows => { return Array.isArray(workflows) - ? workflows?.find( - workflow => workflow?.workflowDefinition?.name === import.meta.env.VITE_EXAMPLE_TYPE, - ) + ? workflows + ?.slice() + ?.sort((a, b) => { + if ( + a?.workflowRuntimeData?.status === 'active' && + b?.workflowRuntimeData?.status !== 'active' + ) + return -1; + if ( + b?.workflowRuntimeData?.status === 'active' && + a?.workflowRuntimeData?.status !== 'active' + ) + return 1; + + return 0; + }) + ?.find( + workflow => + workflow?.workflowDefinition?.name === import.meta.env.VITE_EXAMPLE_TYPE, + ) : undefined; }, }); const createWorkflowQuery = (id: string) => createQuery({ - queryKey: ['workflows', { id }], + queryKey: ['workflows', { id, entityType, entityId }], queryFn: async () => { const data = await fetchWorkflow(id); @@ -97,8 +128,9 @@ }, refetchInterval(data) { if ( - entityState === 'REJECTED' || - entityState === 'APPROVED' || + isRejected || + isApproved || + data?.workflowRuntimeData?.status === 'active' || (entityState === 'NEW' && data?.workflowRuntimeData?.status === 'created') || (isProcessing && data?.workflowRuntimeData?.status !== 'completed') ) { @@ -107,7 +139,7 @@ return parseInt(import.meta.env.VITE_POLLING_INTERVAL) * 1000 || false; }, - enabled: typeof id === 'string' && id.length > 0, + enabled: typeof id === 'string' && id.length > 0 && !!entityType && !!entityId, }); const queryClient = useQueryClient(); const createSignUpMutation = () => @@ -115,12 +147,12 @@ mutationFn: import.meta.env.VITE_EXAMPLE_TYPE === 'kyc' ? fetchEnduserSignUp : fetchBusinessSignUp, onSuccess: data => { - sessionStorage.setItem(NO_AUTH_USER_KEY, data?.id); - noAuthUserId = data?.id; + sessionStorage.setItem(ENTITY_ID_STORAGE_KEY, data?.id); + entityId = data?.id; queryClient.invalidateQueries(); }, }); - $: entityQuery = createEntityQuery(noAuthUserId); + $: entityQuery = createEntityQuery(entityId); const firstWorkflowQuery = createFirstWorkflowQuery(); $: workflowQuery = createWorkflowQuery($firstWorkflowQuery?.data?.workflowRuntimeData?.id); const intentQuery = createIntentQuery(); @@ -151,7 +183,12 @@ }); const onSubmit = import.meta.env.VITE_EXAMPLE_TYPE === 'kyc' ? onSubmitEnduser : onSubmitBusiness; - + const clearUser = () => { + sessionStorage.removeItem(ENTITY_ID_STORAGE_KEY); + entityId = undefined; + workflow.set(undefined); + queryClient.invalidateQueries(); + }; const workflow = writable<WorkflowOptionsBrowser | undefined>(); const debugWf = writable<{ definition: unknown }>(); @@ -174,92 +211,113 @@ const mergeWorkflow = () => makeWorkflow($workflowQuery?.data || $intentQuery?.data); const handleResubmit = () => { workflow.set(mergeWorkflow()); + shouldResubmit = false; + }; + const handleNextWorkflow = ({ + entityQueryId, + workflowQueryDefinition, + intentQueryDefinition, + workflowDefinitionInitial, + }: { + entityQueryId: string; + workflowQueryDefinition: Record<string, unknown>; + intentQueryDefinition: Record<string, unknown>; + workflowDefinitionInitial: string; + }) => { + if (!entityQueryId || (!workflowQueryDefinition && !intentQueryDefinition)) { + workflow.set(undefined); + shouldResubmit = false; + + return; + } + + nextWorkflow = mergeWorkflow(); + + if ( + nextWorkflow?.definition?.initial !== workflowDefinitionInitial && + nextWorkflow?.definition?.context?.documents?.some( + ({ decision }) => decision?.status === 'revision', + ) + ) { + shouldResubmit = true; + + return; + } + + workflow.set(nextWorkflow); + shouldResubmit = false; }; - let nextWorkflow; - let shouldResubmit = false; $: isCompleted = $workflowQuery.data?.workflowRuntimeData?.status === 'completed'; - $: entityId = $entityQuery.data?.id; $: entityState = $entityQuery.data?.approvalState; $: isProcessing = entityState === 'PROCESSING'; - $: isValidWorkflow = entityId && !isCompleted; $: { - if ( - entityId && - ($workflowQuery?.data?.workflowDefinition || $intentQuery?.data?.workflowDefinition) - ) { - nextWorkflow = mergeWorkflow(); - - if ( - nextWorkflow?.definition?.initial !== $workflow?.definition?.initial && - nextWorkflow?.definition?.context?.id?.resubmissionReason - ) { - shouldResubmit = true; - } else { - workflow.set(nextWorkflow); - shouldResubmit = false; - } - } else { - workflow.set(undefined); - shouldResubmit = false; - } + handleNextWorkflow({ + intentQueryDefinition: $intentQuery?.data?.workflowDefinition?.definition, + workflowQueryDefinition: $workflowQuery?.data?.workflowDefinition?.definition, + entityQueryId: $entityQuery?.data?.id, + workflowDefinitionInitial: $workflow?.definition?.initial, + }); } - let message; - $: { - switch (entityState) { - case 'PROCESSING': - message = ''; - break; - case 'REJECTED': - message = 'Your request was declined.'; - break; - case 'APPROVED': - message = 'Your request was approved :)'; - break; - default: - message = ''; - } + documentsDecisionStatuses = nextWorkflow + ? nextWorkflow?.workflowContext?.machineContext?.documents?.map( + ({ decision }) => decision?.status, + ) + : $workflow?.workflowContext?.machineContext?.documents?.map( + ({ decision }) => decision?.status, + ); + + const hasDecisions = !!documentsDecisionStatuses?.length; + + // In JavaScript `every` returns true with empty arrays. + isApproved = hasDecisions && documentsDecisionStatuses?.every(status => status === 'approved'); + isRejected = documentsDecisionStatuses?.some(status => status === 'rejected'); + isRevision = !isCompleted && documentsDecisionStatuses?.some(status => status === 'revision'); + isDecided = isApproved || isRejected || isRevision; } </script> <div class="flex h-full flex-row items-center justify-center"> <main class="flex h-full w-full flex-col items-center justify-center p-6"> - {#if !entityId} + {#if !$entityQuery.data?.id} <SignUp {onSubmit} /> {/if} {#if $workflow && !isCompleted && !shouldResubmit} <Workflow workflow={$workflow} on:workflow-updated={workflowComponentStateUpdated} /> {/if} - {#if entityId && !$workflow && !isProcessing} - <Intent disabled={!entityId} refetch={$intentQuery.refetch} /> + {#if $entityQuery.data?.id && !$workflow && !isProcessing} + <Intent disabled={!$entityQuery.data?.id} refetch={$intentQuery.refetch} /> {/if} - {#if entityId && isProcessing && isCompleted} + {#if $entityQuery.data?.id && isCompleted && !isDecided} <ThankYou /> {/if} - {#if isValidWorkflow && shouldResubmit} + {#if isRevision && shouldResubmit} <Resubmission {handleResubmit} - reason={nextWorkflow?.definition?.context?.id?.resubmissionReason - ?.toLowerCase() - ?.replace(/_/g, ' ')} + reason={nextWorkflow?.definition?.context?.documents + ?.find(({ decision }) => decision?.status === 'revision') + ?.decision?.revisionReason?.toLowerCase()} /> {/if} - {#if entityState === 'REJECTED'} + {#if isRejected} <Rejected /> {/if} - {#if entityState === 'APPROVED'} + {#if isApproved} <Approved /> {/if} </main> {#if $debugWf} <DevSidebar workflowDefinition={$debugWf?.definition} /> {/if} + <div class={'fixed left-2 top-2'}> + <button on:click={clearUser}> Clear User</button> + </div> </div> diff --git a/examples/headless-example/src/components/DocumentPhoto.svelte b/examples/headless-example/src/components/DocumentPhoto.svelte index 0c45ee3e1d..67735d32ae 100644 --- a/examples/headless-example/src/components/DocumentPhoto.svelte +++ b/examples/headless-example/src/components/DocumentPhoto.svelte @@ -4,8 +4,9 @@ import { camelCaseToTitle, createZodForm, getWorkflowContext } from '@/utils'; import Form from './Form.svelte'; import type { TOnPrev, TOnSubmit } from '@/types'; + import { DocumentId } from '@/constants'; - export let documentName: string; + export let documentId: string; export let initialValues: z.infer<typeof schema>; export let onSubmit: TOnSubmit<typeof schema>; export let onPrev: TOnPrev<typeof schema>; @@ -13,13 +14,13 @@ const workflowService = getWorkflowContext(); const schema = z.object({ - [documentName]: z.object({ + [documentId]: z.object({ type: z.union([ z.literal('passport'), z.literal('idCard'), z.literal('driverLicense'), z.literal('selfie'), - z.literal('certificateOfIncorporation'), + z.literal('incorporation'), ]), file: z.custom<File>(v => v instanceof File), }), @@ -27,16 +28,16 @@ const zodForm = createZodForm(schema, { initialValues: { - [documentName]: { - type: initialValues[documentName].type, + [documentId]: { + type: initialValues[documentId]?.type, }, }, async onSubmit(data, ctx) { - const uploadedFile = await workflowService.uploadFile(data[documentName]); + const uploadedFile = await workflowService.uploadFile(data[documentId]); return onSubmit( { - [documentName]: uploadedFile, + [documentId]: uploadedFile, }, ctx, ); @@ -54,7 +55,7 @@ }; $: { - title = camelCaseToTitle(documentName); + title = camelCaseToTitle(documentId); } </script> @@ -66,20 +67,24 @@ <fieldset class="mb-2 flex h-full flex-col"> <legend>Upload {title}</legend> <p class="max-w-[50ch] p-1"> - {#if documentName === 'selfie'} - You can download <a download="mock-selfie.png" href="/mock-selfie.png">this selfie file</a> + {#if documentId === DocumentId.SELFIE} + You can download <a + download={`mock-${DocumentId.SELFIE}.png`} + href={`/mock-${DocumentId.SELFIE}.png`}>this selfie file</a + > and upload it here. {/if} - {#if documentName === 'id'} + {#if documentId === DocumentId.ID_CARD} Pssst... instead of uploading your own ID, you can download <a - download="mock-id.png" - href="/mock-id.png">this file</a + download={`mock-${DocumentId.ID_CARD}.png`} + href={`/mock-${DocumentId.ID_CARD}.png`}>this file</a > and upload it here. {/if} - {#if documentName === 'certificateOfIncorporation'} + {#if documentId === DocumentId.CERTIFICATE_OF_INCORPORATION} You can download <a - download="mock-certificate-of-incorporation.pdf" - href="/mock-certificate-of-incorporation.pdf">this certificate of incorporation file</a + download={`mock-${DocumentId.CERTIFICATE_OF_INCORPORATION}.pdf`} + href={`mock-${DocumentId.CERTIFICATE_OF_INCORPORATION}.pdf`} + >this certificate of incorporation file</a > and upload it here. {/if} </p> @@ -88,7 +93,7 @@ type="file" class="hidden" id="file" - name={`${documentName}.file`} + name={`${documentId}.file`} on:change={updateFileName} bind:this={fileInput} /> @@ -102,7 +107,7 @@ </div> {:else} <img - src={`/${documentName === 'selfie' ? 'selfie' : 'upload-document'}.svg`} + src={`/${documentId === DocumentId.SELFIE ? 'selfie' : 'upload-document'}.svg`} alt="Upload Document" class="mx-auto h-48 w-48" /> @@ -110,7 +115,12 @@ <button type="button" on:click={uploadFile}>Choose Document</button> </div> </fieldset> - <ValidationMessage for={`${documentName}.file`} let:messages={message}> - <div style="color: red; font-weight: bold;">{message || ''}</div> + <ValidationMessage for={`${documentId}.type`} let:messages={message}> + <div style="color: red; font-weight: bold;"> + {message ? `Type ${message}: ${JSON.stringify(zodForm.data.type)}` : ''} + </div> + </ValidationMessage> + <ValidationMessage for={`${documentId}.file`} let:messages={message}> + <div style="color: red; font-weight: bold;">{message ? `File ${message}` : ''}</div> </ValidationMessage> </Form> diff --git a/examples/headless-example/src/components/DocumentReview.svelte b/examples/headless-example/src/components/DocumentReview.svelte index d50c412b30..2d21d66d09 100644 --- a/examples/headless-example/src/components/DocumentReview.svelte +++ b/examples/headless-example/src/components/DocumentReview.svelte @@ -1,5 +1,5 @@ <script lang="ts"> - import { camelCaseToTitle, createZodForm, getWorkflowContext } from '@/utils'; + import { camelCaseToTitle, createZodForm, getSnapshotContext, getWorkflowContext } from '@/utils'; import { z } from 'zod'; import type { TOnPrev, TOnSubmit } from '@/types'; import Form from '@/components/Form.svelte'; @@ -7,11 +7,10 @@ const schema = z.object({}); - const workflowService = getWorkflowContext(); export let initialValues: z.infer<typeof schema>; export let onSubmit: TOnSubmit<typeof schema>; export let onPrev: TOnPrev<typeof schema>; - export let documentName: string; + export let documentId: string; // Defaults to 'Next' const submitText = 'Looks Good'; @@ -19,12 +18,16 @@ let id; let fileType; + const workflowService = getWorkflowContext(); const zodForm = createZodForm(schema, { initialValues, onSubmit(data, ctx) { + const context = getSnapshotContext(workflowService); + const document = context?.form?.[documentId]; + return onSubmit( { - [documentName]: workflowService.getSnapshot?.()?.context?.[documentName], + [documentId]: document, }, ctx, ); @@ -33,9 +36,10 @@ const backText = 'Re-upload'; $: { - const document = workflowService.getSnapshot?.()?.context?.[documentName]; + const context = getSnapshotContext(workflowService); + const document = context?.form?.[documentId]; - title = camelCaseToTitle(documentName); + title = camelCaseToTitle(documentId); id = document?.id; fileType = document?.fileType; } diff --git a/examples/headless-example/src/components/DocumentSelection.svelte b/examples/headless-example/src/components/DocumentSelection.svelte index 180721ce33..88ac3dbf47 100644 --- a/examples/headless-example/src/components/DocumentSelection.svelte +++ b/examples/headless-example/src/components/DocumentSelection.svelte @@ -5,9 +5,10 @@ import Form from './Form.svelte'; import type { TOnPrev, TOnSubmit } from '@/types'; import DocumentType from '@/components/DocumentType.svelte'; + import { DocumentId } from '@/constants'; const schema = z.object({ - id: z.object({ + [DocumentId.ID_CARD]: z.object({ type: z.union([z.literal('passport'), z.literal('idCard'), z.literal('driverLicense')]), }), }); @@ -18,8 +19,8 @@ const zodForm = createZodForm(schema, { initialValues: { - id: { - type: initialValues.id.type, + [DocumentId.ID_CARD]: { + type: initialValues[DocumentId.ID_CARD]?.type, }, }, onSubmit, @@ -33,20 +34,26 @@ <DocumentType id="passport" label="Passport" - name="id.type" + name={`${DocumentId.ID_CARD}.type`} value="passport" - type={$data.id.type} + type={$data[DocumentId.ID_CARD].type} + /> + <DocumentType + id="id-card" + label="ID Card" + name={`${DocumentId.ID_CARD}.type`} + value="idCard" + type={$data[DocumentId.ID_CARD].type} /> - <DocumentType id="id-card" label="ID Card" name="id.type" value="idCard" type={$data.id.type} /> <DocumentType id="driver-license" label="Driver License" - name="id.type" + name={`${DocumentId.ID_CARD}.type`} value="driverLicense" - type={$data.id.type} + type={$data[DocumentId.ID_CARD].type} /> </fieldset> - <ValidationMessage for="document" let:messages={message}> - <div style="color: red; font-weight: bold;">{message || ''}</div> + <ValidationMessage for={`${DocumentId.ID_CARD}.type`} let:messages={message}> + <div style="color: red; font-weight: bold;">{message ? `Type ${message}` : ''}</div> </ValidationMessage> </Form> diff --git a/examples/headless-example/src/components/Final.svelte b/examples/headless-example/src/components/Final.svelte index d80fe6a451..d06fcaac99 100644 --- a/examples/headless-example/src/components/Final.svelte +++ b/examples/headless-example/src/components/Final.svelte @@ -3,5 +3,3 @@ export let onSubmit = undefined; export let onPrev = undefined; </script> - -Final diff --git a/examples/headless-example/src/components/RemoteImage.svelte b/examples/headless-example/src/components/RemoteImage.svelte index 14e99921e2..2f36acb3a4 100644 --- a/examples/headless-example/src/components/RemoteImage.svelte +++ b/examples/headless-example/src/components/RemoteImage.svelte @@ -1,6 +1,7 @@ <script lang="ts"> import { onMount } from 'svelte'; - import { fetchBlob } from '@/utils'; + import { fetchBlob, fetchJson } from '@/utils'; + import { z } from 'zod'; export let id: string; export let alt: string; @@ -23,27 +24,36 @@ const isFileSourcePublic = fileInfo => { return fileInfo.uri.includes('https') && !fileInfo.fileNameInBucket; }; + const fetchFileInfoById = async (id: string) => { + const data = await fetchJson(`http://localhost:3000/api/v1/external/storage/${id}`); + + return z + .object({ + uri: z.string(), + fileNameInBucket: z.string().nullable(), + }) + .parse(data); + }; + const fetchFileContentById = async (id: string) => { + const data = await fetchBlob(`http://localhost:3000/api/v1/external/storage/content/${id}`); + + return z.instanceof(Blob).transform(blobToBase64).parseAsync(data); + }; onMount(async () => { if (!id) return; - const response = await fetch(`http://localhost:3000/api/v1/external/storage/${id}`); - if (!response.ok) { - throw new Error(`Error fetching fileInfo: ${response.statusText}`); - } - const fileInfo = await response.json(); + const fileInfo = await fetchFileInfoById(id); if (isFileSourcePublic(fileInfo)) { src = fileInfo.uri; - } else { - const streamedFile = await fetchBlob<Blob>( - `http://localhost:3000/api/v1/external/storage/content/${id}`, - ); - - const base64 = await blobToBase64(streamedFile); - src = base64?.replace(/application\/octet-stream/gi, fileType); + return; } + + const base64 = await fetchFileContentById(id); + + src = base64?.replace(/application\/octet-stream/gi, fileType); }); </script> diff --git a/examples/headless-example/src/components/Resubmission.svelte b/examples/headless-example/src/components/Resubmission.svelte index 8e6aad9d95..2fdf1d2988 100644 --- a/examples/headless-example/src/components/Resubmission.svelte +++ b/examples/headless-example/src/components/Resubmission.svelte @@ -1,5 +1,6 @@ <script lang="ts"> import Card from '@/components/Card.svelte'; + import { DocumentId } from '@/constants'; export let reason: string; export let handleResubmit = () => {}; @@ -9,7 +10,9 @@ <h1 class="w-full text-center text-2xl font-bold">Re-upload ID</h1> <p class="max-w-[50ch] p-1"> Your ID was rejected due to {reason}, please re-upload a clearer image. You can upload - <a download="mock-id.png" href="/mock-id.png">this file</a>. + <a download={`mock-${DocumentId.ID_CARD}.png`} href={`/mock-${DocumentId.ID_CARD}.png`} + >this file</a + >. </p> <img src="/re-upload-id.svg" alt="clock" class="m-auto mb-2 h-48 w-48" /> <button class="mt-auto" on:click={handleResubmit}>Re-upload ID File</button> diff --git a/examples/headless-example/src/components/SignUp.svelte b/examples/headless-example/src/components/SignUp.svelte index 89a81d3c5f..ff10003352 100644 --- a/examples/headless-example/src/components/SignUp.svelte +++ b/examples/headless-example/src/components/SignUp.svelte @@ -47,7 +47,7 @@ <input type="text" id="bname" name="bname" /> </div> <div> - <label for="rnum">Regestration Number</label> + <label for="rnum">Registration Number</label> <input type="text" id="rnum" name="rnum" /> </div> {/if} diff --git a/examples/headless-example/src/components/Workflow.svelte b/examples/headless-example/src/components/Workflow.svelte index 0d7e9b0ba9..418769ceb7 100644 --- a/examples/headless-example/src/components/Workflow.svelte +++ b/examples/headless-example/src/components/Workflow.svelte @@ -1,31 +1,11 @@ <script lang="ts"> import type { WorkflowOptionsBrowser } from '@ballerine/workflow-browser-sdk'; - import DocumentPhoto from './DocumentPhoto.svelte'; - import DocumentSelection from './DocumentSelection.svelte'; - import ErrorComponent from './Error.svelte'; - import Final from './Final.svelte'; - import Resubmission from './Resubmission.svelte'; - import Success from './Success.svelte'; import { type ObjectValues, State } from '@/types'; - import Welcome from './Welcome.svelte'; - import { initWorkflowContext } from '@/utils'; - import DocumentReview from './DocumentReview.svelte'; + import { initWorkflowContext, makeDocument, upsertDocument } from '@/utils'; import { createEventDispatcher } from 'svelte'; + import { DocumentId } from '@/constants'; + import { Step } from '@/steps'; - const Step = { - WELCOME: Welcome, - DOCUMENT_SELECTION: DocumentSelection, - DOCUMENT_PHOTO: DocumentPhoto, - DOCUMENT_REVIEW: DocumentReview, - CERTIFICATE_OF_INCORPORATION: DocumentPhoto, - CERTIFICATE_OF_INCORPORATION_REVIEW: DocumentReview, - SELFIE: DocumentPhoto, - SELFIE_REVIEW: DocumentReview, - FINAL: Final, - ERROR: ErrorComponent, - SUCCESS: Success, - RESUBMISSION: Resubmission, - } as const; export let workflow: WorkflowOptionsBrowser; const dispatch = createEventDispatcher(); const workflowUpdated = (newWf: unknown) => dispatch('workflow-updated', newWf); @@ -39,52 +19,83 @@ const onPrev = (payload: Record<PropertyKey, any>) => () => { const context = workflowService.getSnapshot()?.context; + const document = makeDocument({ + id: Object.keys(payload)[0], + payload, + }); + const updatedDocuments = upsertDocument({ + documents: context?.documents, + document, + }); workflowService.sendEvent({ type: 'USER_PREV_STEP', payload: { ...context, - ...payload, - id: { - ...context?.id, - ...payload?.id, - }, - selfie: { - ...context?.selfie, - ...payload?.selfie, - }, - certificateOfIncorporation: { - ...context?.certificateOfIncorporation, - ...payload?.certificateOfIncorporation, + documents: updatedDocuments, + form: { + [DocumentId.ID_CARD]: { + ...context?.form?.[DocumentId.ID_CARD], + ...payload?.[DocumentId.ID_CARD], + }, + [DocumentId.SELFIE]: { + ...context?.form?.[DocumentId.SELFIE], + ...payload?.[DocumentId.SELFIE], + }, + [DocumentId.CERTIFICATE_OF_INCORPORATION]: { + ...context?.form?.[DocumentId.CERTIFICATE_OF_INCORPORATION], + ...payload?.[DocumentId.CERTIFICATE_OF_INCORPORATION], + }, }, }, }); }; const onSubmit = (payload: Record<PropertyKey, any>) => { + const context = workflowService.getSnapshot()?.context; + const documentId = Object.keys(payload ?? {})?.[0]; + const document = makeDocument({ + id: documentId, + payload, + }); + const newDocuments = upsertDocument({ + documents: context?.documents, + document, + }); + workflowService.sendEvent({ type: 'USER_NEXT_STEP', - payload, + payload: { + documents: newDocuments, + form: { + ...context?.form, + [documentId]: { + ...context?.form?.[documentId], + ...payload?.[documentId], + }, + }, + }, }); }; let initialValues = { - id: { - type: snapshot?.context?.id?.type, + [DocumentId.ID_CARD]: { + type: snapshot?.context?.form?.[DocumentId.ID_CARD]?.type, }, - selfie: { - type: snapshot?.context?.selfie?.type, + [DocumentId.SELFIE]: { + type: snapshot?.context?.form?.[DocumentId.SELFIE]?.type, }, - certificateOfIncorporation: { - type: snapshot?.context?.certificateOfIncorporation?.type, + [DocumentId.CERTIFICATE_OF_INCORPORATION]: { + type: snapshot?.context?.form?.[DocumentId.CERTIFICATE_OF_INCORPORATION]?.type, }, }; - let documentName; + + let documentId; workflowService.subscribe('USER_NEXT_STEP', async data => { currentStep = data.state; if (currentStep !== 'final') return; - window.location.reload(); + setTimeout(() => window.location.reload(), 240); }); workflowService.subscribe('USER_PREV_STEP', data => { @@ -112,22 +123,22 @@ step = Step[currentStep.toUpperCase() as keyof typeof Step]; snapshot = workflowService?.getSnapshot(); workflowUpdated(snapshot); - initialValues.id.type = snapshot?.context?.id?.type; - initialValues.selfie.type = 'selfie'; - initialValues.certificateOfIncorporation.type = 'certificateOfIncorporation'; + initialValues[DocumentId.ID_CARD].type = snapshot?.context?.form?.[DocumentId.ID_CARD]?.type; + initialValues[DocumentId.SELFIE].type = 'selfie'; + initialValues[DocumentId.CERTIFICATE_OF_INCORPORATION].type = 'incorporation'; switch (currentStep) { case 'document_photo': case 'document_review': - documentName = 'id'; + documentId = DocumentId.ID_CARD; break; case 'selfie': case 'selfie_review': - documentName = 'selfie'; + documentId = DocumentId.SELFIE; break; case 'certificate_of_incorporation': case 'certificate_of_incorporation_review': - documentName = 'certificateOfIncorporation'; + documentId = DocumentId.CERTIFICATE_OF_INCORPORATION; break; default: break; @@ -149,4 +160,4 @@ {/if} </span> -<svelte:component this={step} {onPrev} {onSubmit} {initialValues} {documentName} /> +<svelte:component this={step} {onPrev} {onSubmit} {initialValues} {documentId} /> diff --git a/examples/headless-example/src/constants.ts b/examples/headless-example/src/constants.ts index b3ce4c0aa1..c2b6afc661 100644 --- a/examples/headless-example/src/constants.ts +++ b/examples/headless-example/src/constants.ts @@ -1 +1,20 @@ -export const NO_AUTH_USER_KEY = 'no_auth_user_id'; +export const ENTITY_ID_STORAGE_KEY = 'entityId' as const; + +export const Category = { + ID_CARD: 'id', + SELFIE: 'selfie', + CERTIFICATE_OF_INCORPORATION: 'incorporation', +} as const; + +export const Type = { + PHOTO: 'photo', + PDF: 'pdf', +} as const; + +export const ISSUER_COUNTRY = 'CA' as const; + +export const DocumentId = { + ID_CARD: `${Category.ID_CARD}-${Type.PHOTO}-${ISSUER_COUNTRY}`, + SELFIE: `${Category.SELFIE}-${Type.PHOTO}-${ISSUER_COUNTRY}`, + CERTIFICATE_OF_INCORPORATION: `${Category.CERTIFICATE_OF_INCORPORATION}-${Type.PDF}-${ISSUER_COUNTRY}`, +} as const; diff --git a/examples/headless-example/src/services/ballerine-backoffice.service.ts b/examples/headless-example/src/services/ballerine-backoffice.service.ts index 5c48735b99..0fe0fec3ab 100644 --- a/examples/headless-example/src/services/ballerine-backoffice.service.ts +++ b/examples/headless-example/src/services/ballerine-backoffice.service.ts @@ -1,4 +1,5 @@ import { fetchJson } from '@/utils'; +import { ENTITY_ID_STORAGE_KEY } from '@/constants'; export type WorkServiceEndpoints = { base: string; @@ -10,7 +11,13 @@ export class BallerineBackOfficeService { fetchBusiness = async (id: string) => fetchJson(`${this.baseUrl}/businesses/${id}`); fetchWorkflow = async (id: string) => fetchJson(`${this.baseUrl}/workflows/${id}`); - fetchWorkflows = async () => + fetchWorkflows = async ({ + entityType, + entityId, + }: { + entityType: 'end-users' | 'businesses'; + entityId: string; + }) => fetchJson< Array<{ workflowDefinition: { @@ -22,11 +29,14 @@ export class BallerineBackOfficeService { status: string; }; }> - >(`${this.baseUrl}/workflows`); + >(`${this.baseUrl}/${entityType}/${entityId}/workflows`); fetchIntent = async () => fetchJson<Array<Record<string, unknown>>>(`${this.baseUrl}/workflows/intent`, { method: 'POST', - body: { intentName: import.meta.env.VITE_EXAMPLE_TYPE === 'kyc' ? 'kycSignup' : 'kybSignup' }, + body: { + intentName: import.meta.env.VITE_EXAMPLE_TYPE === 'kyc' ? 'kycSignup' : 'kybSignup', + entityId: sessionStorage.getItem(ENTITY_ID_STORAGE_KEY), + }, }); fetchBusinessSignUp = async ({ diff --git a/examples/headless-example/src/steps.ts b/examples/headless-example/src/steps.ts new file mode 100644 index 0000000000..1ff6a63ef8 --- /dev/null +++ b/examples/headless-example/src/steps.ts @@ -0,0 +1,23 @@ +import Welcome from '@/components/Welcome.svelte'; +import DocumentSelection from '@/components/DocumentSelection.svelte'; +import DocumentPhoto from '@/components/DocumentPhoto.svelte'; +import DocumentReview from '@/components/DocumentReview.svelte'; +import Final from '@/components/Final.svelte'; +import ErrorComponent from '@/components/Error.svelte'; +import Success from '@/components/Success.svelte'; +import Resubmission from '@/components/Resubmission.svelte'; + +export const Step = { + WELCOME: Welcome, + DOCUMENT_SELECTION: DocumentSelection, + DOCUMENT_PHOTO: DocumentPhoto, + DOCUMENT_REVIEW: DocumentReview, + CERTIFICATE_OF_INCORPORATION: DocumentPhoto, + CERTIFICATE_OF_INCORPORATION_REVIEW: DocumentReview, + SELFIE: DocumentPhoto, + SELFIE_REVIEW: DocumentReview, + FINAL: Final, + ERROR: ErrorComponent, + SUCCESS: Success, + RESUBMISSION: Resubmission, +} as const; diff --git a/examples/headless-example/src/utils.ts b/examples/headless-example/src/utils.ts index dd1a84982e..d01aadae40 100644 --- a/examples/headless-example/src/utils.ts +++ b/examples/headless-example/src/utils.ts @@ -1,3 +1,4 @@ +import { getDocumentId } from '@ballerine/common'; import { type ClassValue, clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; import type { WorkflowBrowserSDK, WorkflowOptionsBrowser } from '@ballerine/workflow-browser-sdk'; @@ -9,7 +10,7 @@ import { createForm } from 'felte'; import { getContext, setContext } from 'svelte'; import type { z, ZodSchema } from 'zod'; import type { FetchInitWithJson, Serializable } from './types'; -import { NO_AUTH_USER_KEY } from './constants'; +import { Category } from '@/constants'; export const setWorkflowContext = (service: InstanceType<typeof WorkflowBrowserSDK>) => { setContext('workflow', service); @@ -64,6 +65,12 @@ export const makeWorkflow = (data: { }, backend: { baseUrl: 'http://localhost:3000/api/v1/external', + headers: { + Authorization: + import.meta.env.MODE === 'development' + ? `Api-Key ${import.meta.env.VITE_API_KEY}` + : undefined, + }, }, }; }; @@ -105,7 +112,7 @@ export const fetchJson = async <TData, TBody = Record<string, unknown>>( ...init, headers: { ...init?.headers, - no_auth_user_id: sessionStorage.getItem(NO_AUTH_USER_KEY) ?? '', + Authorization: `Api-Key ${import.meta.env.VITE_API_KEY}`, }, }); const data: TData = await res.json(); @@ -121,7 +128,7 @@ export const fetchBlob = async <TData, TBody = Record<string, unknown>>( ...init, headers: { ...init?.headers, - no_auth_user_id: sessionStorage.getItem(NO_AUTH_USER_KEY) ?? '', + Authorization: `Api-Key ${import.meta.env.VITE_API_KEY}`, }, }); @@ -143,6 +150,113 @@ export const handlePromise = async <TData>( export const ctw = (...classNames: Array<ClassValue>) => twMerge(clsx(classNames)); export const camelCaseToTitle = (str: string) => str - .replace(/([A-Z])/g, ' $1') - .replace(/^./, str => str.toUpperCase()) - .replace(/id/i, 'ID'); + ?.replace(/([A-Z])/g, ' $1') + ?.replace(/^./, str => str?.toUpperCase()) + ?.replace(/id/i, 'ID'); + +export const getSnapshotContext = (workflowService: InstanceType<typeof WorkflowBrowserSDK>) => + workflowService?.getSnapshot()?.context; +export const makeDocument = ({ + id, + payload, +}: { + id: string; + payload: { + [key: string]: { + id: string; + fileType: string; + }; + }; +}) => { + const [category, type, issuerCountry] = id?.split('-') ?? []; + const properties = (() => { + if (category === Category.CERTIFICATE_OF_INCORPORATION) { + return { + businessName: 'Test Business', + website: 'https://testbusiness.com', + phone: '+233 123 456 789', + email: 'test@test.com', + owner: 'Test Owner', + tin: '123456789', + }; + } + + if ( + (category === Category.ID_CARD || category === Category.SELFIE) && + import.meta.env.VITE_EXAMPLE_TYPE === 'kyc' + ) { + return { + firstName: 'John', + middleName: 'Oed', + lastName: 'Doe', + authority: 'Canada', + placeOfIssue: 'Canada', + issueDate: '2020-01-01', + expires: '2025-01-01', + dateOfBirth: '1990-01-01', + placeOfBirth: 'Canada', + sex: 'Other', + }; + } + + if ( + (category === Category.ID_CARD || category === Category.SELFIE) && + import.meta.env.VITE_EXAMPLE_TYPE === 'kyb' + ) { + return { + firstName: 'John', + middleName: 'Oed', + lastName: 'Doe', + authority: 'Canada', + placeOfIssue: 'Canada', + issueDate: '2020-01-01', + expires: '2025-01-01', + dateOfBirth: '1990-01-01', + placeOfBirth: 'Canada', + sex: 'Other', + }; + } + + throw new Error(`Invalid properties`); + })(); + + return { + category, + type, + issuer: { + country: issuerCountry, + }, + version: 1, + pages: [ + { + ballerineFileId: payload?.[id]?.id, + type: payload?.[id]?.fileType === 'application/pdf' ? 'pdf' : 'png', + provider: 'http', + uri: '', + }, + ], + properties, + }; +}; +// Update document if it exists, otherwise add a new document. +export const upsertDocument = ({ + documents, + document, +}: { + documents: Array<any>; + document: any; +}) => { + const documentExists = documents?.some( + doc => getDocumentId(doc, false) === getDocumentId(document, false), + ); + + if (!Array.isArray(documents) || !documents?.length) return [document]; + + return !documentExists + ? [...documents, document] + : documents?.map(doc => { + if (getDocumentId(doc, false) !== getDocumentId(document, false)) return doc; + + return document; + }); +}; diff --git a/examples/headless-example/tsconfig.json b/examples/headless-example/tsconfig.json index 33d7df7c2f..dfee8df293 100644 --- a/examples/headless-example/tsconfig.json +++ b/examples/headless-example/tsconfig.json @@ -25,6 +25,6 @@ "@/*": ["src/*"] } }, - "include": ["src", "public"], + "include": ["src", "public", "e2e"], "references": [{ "path": "./tsconfig.node.json" }] } diff --git a/experiments/load-testing/drill/src/writer.rs b/experiments/load-testing/drill/src/writer.rs index 8f79c119dd..62ca892a41 100644 --- a/experiments/load-testing/drill/src/writer.rs +++ b/experiments/load-testing/drill/src/writer.rs @@ -14,4 +14,4 @@ pub fn write_file(filepath: &str, content: String) { if let Err(why) = file.write_all(content.as_bytes()) { panic!("couldn't write to {}: {:?}", display, why); } -} +} diff --git a/nx.json b/nx.json index 942433093a..815af8b334 100644 --- a/nx.json +++ b/nx.json @@ -3,7 +3,7 @@ "default": { "runner": "nx/tasks-runners/default", "options": { - "cacheableOperations": ["build", "test"] + "cacheableOperations": ["build", "test", "test:unit", "test:integration", "test:e2e"] } } }, @@ -20,6 +20,15 @@ "test": { "dependsOn": [{ "projects": "self", "target": "build" }] }, + "test:unit": { + "dependsOn": [{ "projects": "self", "target": "build" }] + }, + "test:integration": { + "dependsOn": [{ "projects": "self", "target": "build" }] + }, + "test:e2e": { + "dependsOn": [{ "projects": "self", "target": "build" }] + }, "lint": { "dependsOn": [{ "projects": "self", "target": "build" }] } diff --git a/package.json b/package.json index d69525d1a7..0457a4a97c 100644 --- a/package.json +++ b/package.json @@ -28,22 +28,25 @@ ], "scripts": { "monorepo:init": "node ./scripts/init.js", - "kyc-manual-review-example": "nx run @ballerine/common:build && nx run @ballerine/workflows-service:setup && cross-env ENV_FILE_NAME=.env.example VITE_POLLING_INTERVAL=3 VITE_EXAMPLE_TYPE=kyc concurrently \"nx run @ballerine/workflows-service:dev\" \"wait-on http://localhost:3000/api/v1/_health/ready && nx run-many --target=dev --projects=@ballerine/headless-example,@ballerine/backoffice-v2\"", - "kyb-manual-review-example": "nx run @ballerine/common:build && nx run @ballerine/workflows-service:setup && cross-env ENV_FILE_NAME=.env.example VITE_POLLING_INTERVAL=3 VITE_EXAMPLE_TYPE=kyb concurrently \"nx run @ballerine/workflows-service:dev\" \"wait-on http://localhost:3000/api/v1/_health/ready && nx run-many --target=dev --projects=@ballerine/headless-example,@ballerine/backoffice-v2\"", - "api-manual-review-example": "nx run @ballerine/common:build && nx run @ballerine/workflows-service:setup && cross-env ENV_FILE_NAME=.env.example VITE_POLLING_INTERVAL=false VITE_EXAMPLE_TYPE=kyb concurrently \"nx run @ballerine/workflows-service:dev\" \"wait-on http://localhost:3000/api/v1/_health/ready && nx run-many --target=dev --projects=@ballerine/backoffice-v2\"", + "kyc-manual-review-example": "nx run @ballerine/common:build && nx run @ballerine/workflows-service:setup && cross-env ENV_FILE_NAME=.env.example VITE_POLLING_INTERVAL=3 VITE_EXAMPLE_TYPE=kyc VITE_API_KEY=secret concurrently \"nx run @ballerine/workflows-service:dev\" \"wait-on http://localhost:3000/api/v1/_health/ready && nx run-many --target=dev --projects=@ballerine/headless-example,@ballerine/backoffice-v2\"", + "kyb-manual-review-example": "nx run @ballerine/common:build && nx run @ballerine/workflows-service:setup && cross-env ENV_FILE_NAME=.env.example VITE_POLLING_INTERVAL=3 VITE_EXAMPLE_TYPE=kyb VITE_API_KEY=secret concurrently \"nx run @ballerine/workflows-service:dev\" \"wait-on http://localhost:3000/api/v1/_health/ready && nx run-many --target=dev --projects=@ballerine/headless-example,@ballerine/backoffice-v2\"", + "api-flow-example": "nx run @ballerine/common:build && nx run @ballerine/workflows-service:setup && cross-env ENV_FILE_NAME=.env.example VITE_POLLING_INTERVAL=false VITE_EXAMPLE_TYPE=kyb VITE_API_KEY=secret concurrently \"nx run @ballerine/workflows-service:dev\" \"wait-on http://localhost:3000/api/v1/_health/ready && nx run-many --target=dev --projects=@ballerine/backoffice-v2\"", "branchlint": "branchlint -u -c --prefix \"$(git config --global user.name)\"", "format": "nx run-many --target=format", "format:check": "nx run-many --target=format:check --exclude=@ballerine/backoffice-v2", "lint": "nx run-many --target=lint --projects=@ballerine/web-ui-sdk,@ballerine/workflows-service,@ballerine/workflow-browser-sdk", "test": "nx run-many --target=test --exclude=@ballerine/common", + "test:unit": "nx run-many --target=test:unit --exclude=@ballerine/common", + "test:integration": "nx run-many --target=test:integration --exclude=@ballerine/common", "test:e2e": "nx run-many --target=test:e2e", "playwright:install": "nx run-many --target=playwright:install", "dev": "nx run-many --target=dev --projects=@ballerine/workflows-service,@ballerine/backoffice-v2", "start": "nx run-many --target=start --projects=@ballerine/web-ui-sdk", - "build": "nx run-many --target=build --projects=@ballerine/workflow-browser-sdk,@ballerine/web-ui-sdk,@ballerine/workflow-core,@ballerine/workflow-node-sdk,@ballerine/rules-engine-lib,@ballerine/common,@ballerine/workflows-service", + "build": "nx run-many --target=build --projects=@ballerine/workflow-browser-sdk,@ballerine/web-ui-sdk,@ballerine/workflow-core,@ballerine/workflow-node-sdk,@ballerine/rules-engine-lib,@ballerine/common,@ballerine/workflows-service,@ballerine/websocket-service", "web-ui-sdk:dev": "nx run @ballerine/web-ui-sdk:dev", "web-ui-sdk:start": "nx run @ballerine/web-ui-sdk:start", "workflows-service:start": "nx run @ballerine/workflows-service:start", + "websocket-service:start": "nx run @ballerine/websocket-service:start", "workflow-browser-sdk:build": "nx run @ballerine/workflow-browser-sdk:build", "workflow-browser-sdk:dev": "nx run @ballerine/workflow-browser-sdk:dev", "workflow-browser-sdk:watch": "nx run @ballerine/workflow-browser-sdk:watch", diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md index f786f24511..b671de044a 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/common/CHANGELOG.md @@ -1,5 +1,51 @@ # @ballerine/common +## 0.5.6 + +### Patch Changes + +- change default context schema to ts + +## 0.5.5 + +### Patch Changes + +- Add doc types + +## 0.5.4 + +### Patch Changes + +- Schema Changes (Docs) + +## 0.5.3 + +### Patch Changes + +- Update Common + +## 0.5.2 + +### Patch Changes + +- New version + +## 0.5.1 + +### Patch Changes + +- schema changes + +## 0.5.0 + +### Minor Changes + +- be5c9bc4: added error with name validation + +### Patch Changes + +- Adding scheams + ## 0.4.3 ### Patch Changes diff --git a/packages/common/package.json b/packages/common/package.json index 83d234dc22..82e05e3eb3 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -2,7 +2,7 @@ "private": false, "name": "@ballerine/common", "author": "Ballerine <dev@ballerine.com>", - "version": "0.4.4", + "version": "0.5.6", "description": "common", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", @@ -21,6 +21,7 @@ "build": "rollup --config rollup.config.js", "watch": "concurrently --kill-others \"rollup --config rollup.config.js -w\" \"tsc -b --watch\"", "test": "vitest", + "test:unit": "vitest", "format": "prettier --write .", "format:check": "prettier --check .", "lint": "eslint . --fix", @@ -54,6 +55,7 @@ "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-functional": "^3.0.2", "eslint-plugin-import": "^2.22.0", + "eslint-plugin-unused-imports": "^2.0.0", "fs-extra": "^11.1.0", "prettier": "^2.1.1", "rimraf": "^4.1.2", @@ -66,7 +68,8 @@ "ts-node": "^10.9.1", "typescript": "4.9.5", "vite": "^4.1.1", - "vitest": "^0.28.4" + "vitest": "^0.28.4", + "zod": "^3.21.4" }, "dependencies": { "ajv": "^8.12.0", diff --git a/packages/common/src/countries/index.ts b/packages/common/src/countries/index.ts new file mode 100644 index 0000000000..72a852c6c9 --- /dev/null +++ b/packages/common/src/countries/index.ts @@ -0,0 +1,1249 @@ +const countries = [ + { + name: 'Afghanistan', + 'alpha-2': 'AF', + 'country-code': '004', + }, + { + name: 'Γ land Islands', + 'alpha-2': 'AX', + 'country-code': '248', + }, + { + name: 'Albania', + 'alpha-2': 'AL', + 'country-code': '008', + }, + { + name: 'Algeria', + 'alpha-2': 'DZ', + 'country-code': '012', + }, + { + name: 'American Samoa', + 'alpha-2': 'AS', + 'country-code': '016', + }, + { + name: 'Andorra', + 'alpha-2': 'AD', + 'country-code': '020', + }, + { + name: 'Angola', + 'alpha-2': 'AO', + 'country-code': '024', + }, + { + name: 'Anguilla', + 'alpha-2': 'AI', + 'country-code': '660', + }, + { + name: 'Antarctica', + 'alpha-2': 'AQ', + 'country-code': '010', + }, + { + name: 'Antigua and Barbuda', + 'alpha-2': 'AG', + 'country-code': '028', + }, + { + name: 'Argentina', + 'alpha-2': 'AR', + 'country-code': '032', + }, + { + name: 'Armenia', + 'alpha-2': 'AM', + 'country-code': '051', + }, + { + name: 'Aruba', + 'alpha-2': 'AW', + 'country-code': '533', + }, + { + name: 'Australia', + 'alpha-2': 'AU', + 'country-code': '036', + }, + { + name: 'Austria', + 'alpha-2': 'AT', + 'country-code': '040', + }, + { + name: 'Azerbaijan', + 'alpha-2': 'AZ', + 'country-code': '031', + }, + { + name: 'Bahamas', + 'alpha-2': 'BS', + 'country-code': '044', + }, + { + name: 'Bahrain', + 'alpha-2': 'BH', + 'country-code': '048', + }, + { + name: 'Bangladesh', + 'alpha-2': 'BD', + 'country-code': '050', + }, + { + name: 'Barbados', + 'alpha-2': 'BB', + 'country-code': '052', + }, + { + name: 'Belarus', + 'alpha-2': 'BY', + 'country-code': '112', + }, + { + name: 'Belgium', + 'alpha-2': 'BE', + 'country-code': '056', + }, + { + name: 'Belize', + 'alpha-2': 'BZ', + 'country-code': '084', + }, + { + name: 'Benin', + 'alpha-2': 'BJ', + 'country-code': '204', + }, + { + name: 'Bermuda', + 'alpha-2': 'BM', + 'country-code': '060', + }, + { + name: 'Bhutan', + 'alpha-2': 'BT', + 'country-code': '064', + }, + { + name: 'Bolivia (Plurinational State of)', + 'alpha-2': 'BO', + 'country-code': '068', + }, + { + name: 'Bonaire, Sint Eustatius and Saba', + 'alpha-2': 'BQ', + 'country-code': '535', + }, + { + name: 'Bosnia and Herzegovina', + 'alpha-2': 'BA', + 'country-code': '070', + }, + { + name: 'Botswana', + 'alpha-2': 'BW', + 'country-code': '072', + }, + { + name: 'Bouvet Island', + 'alpha-2': 'BV', + 'country-code': '074', + }, + { + name: 'Brazil', + 'alpha-2': 'BR', + 'country-code': '076', + }, + { + name: 'British Indian Ocean Territory', + 'alpha-2': 'IO', + 'country-code': '086', + }, + { + name: 'Brunei Darussalam', + 'alpha-2': 'BN', + 'country-code': '096', + }, + { + name: 'Bulgaria', + 'alpha-2': 'BG', + 'country-code': '100', + }, + { + name: 'Burkina Faso', + 'alpha-2': 'BF', + 'country-code': '854', + }, + { + name: 'Burundi', + 'alpha-2': 'BI', + 'country-code': '108', + }, + { + name: 'Cabo Verde', + 'alpha-2': 'CV', + 'country-code': '132', + }, + { + name: 'Cambodia', + 'alpha-2': 'KH', + 'country-code': '116', + }, + { + name: 'Cameroon', + 'alpha-2': 'CM', + 'country-code': '120', + }, + { + name: 'Canada', + 'alpha-2': 'CA', + 'country-code': '124', + }, + { + name: 'Cayman Islands', + 'alpha-2': 'KY', + 'country-code': '136', + }, + { + name: 'Central African Republic', + 'alpha-2': 'CF', + 'country-code': '140', + }, + { + name: 'Chad', + 'alpha-2': 'TD', + 'country-code': '148', + }, + { + name: 'Chile', + 'alpha-2': 'CL', + 'country-code': '152', + }, + { + name: 'China', + 'alpha-2': 'CN', + 'country-code': '156', + }, + { + name: 'Christmas Island', + 'alpha-2': 'CX', + 'country-code': '162', + }, + { + name: 'Cocos (Keeling) Islands', + 'alpha-2': 'CC', + 'country-code': '166', + }, + { + name: 'Colombia', + 'alpha-2': 'CO', + 'country-code': '170', + }, + { + name: 'Comoros', + 'alpha-2': 'KM', + 'country-code': '174', + }, + { + name: 'Congo', + 'alpha-2': 'CG', + 'country-code': '178', + }, + { + name: 'Congo, Democratic Republic of the', + 'alpha-2': 'CD', + 'country-code': '180', + }, + { + name: 'Cook Islands', + 'alpha-2': 'CK', + 'country-code': '184', + }, + { + name: 'Costa Rica', + 'alpha-2': 'CR', + 'country-code': '188', + }, + { + name: "CΓ΄te d'Ivoire", + 'alpha-2': 'CI', + 'country-code': '384', + }, + { + name: 'Croatia', + 'alpha-2': 'HR', + 'country-code': '191', + }, + { + name: 'Cuba', + 'alpha-2': 'CU', + 'country-code': '192', + }, + { + name: 'CuraΓ§ao', + 'alpha-2': 'CW', + 'country-code': '531', + }, + { + name: 'Cyprus', + 'alpha-2': 'CY', + 'country-code': '196', + }, + { + name: 'Czechia', + 'alpha-2': 'CZ', + 'country-code': '203', + }, + { + name: 'Denmark', + 'alpha-2': 'DK', + 'country-code': '208', + }, + { + name: 'Djibouti', + 'alpha-2': 'DJ', + 'country-code': '262', + }, + { + name: 'Dominica', + 'alpha-2': 'DM', + 'country-code': '212', + }, + { + name: 'Dominican Republic', + 'alpha-2': 'DO', + 'country-code': '214', + }, + { + name: 'Ecuador', + 'alpha-2': 'EC', + 'country-code': '218', + }, + { + name: 'Egypt', + 'alpha-2': 'EG', + 'country-code': '818', + }, + { + name: 'El Salvador', + 'alpha-2': 'SV', + 'country-code': '222', + }, + { + name: 'Equatorial Guinea', + 'alpha-2': 'GQ', + 'country-code': '226', + }, + { + name: 'Eritrea', + 'alpha-2': 'ER', + 'country-code': '232', + }, + { + name: 'Estonia', + 'alpha-2': 'EE', + 'country-code': '233', + }, + { + name: 'Eswatini', + 'alpha-2': 'SZ', + 'country-code': '748', + }, + { + name: 'Ethiopia', + 'alpha-2': 'ET', + 'country-code': '231', + }, + { + name: 'Falkland Islands (Malvinas)', + 'alpha-2': 'FK', + 'country-code': '238', + }, + { + name: 'Faroe Islands', + 'alpha-2': 'FO', + 'country-code': '234', + }, + { + name: 'Fiji', + 'alpha-2': 'FJ', + 'country-code': '242', + }, + { + name: 'Finland', + 'alpha-2': 'FI', + 'country-code': '246', + }, + { + name: 'France', + 'alpha-2': 'FR', + 'country-code': '250', + }, + { + name: 'French Guiana', + 'alpha-2': 'GF', + 'country-code': '254', + }, + { + name: 'French Polynesia', + 'alpha-2': 'PF', + 'country-code': '258', + }, + { + name: 'French Southern Territories', + 'alpha-2': 'TF', + 'country-code': '260', + }, + { + name: 'Gabon', + 'alpha-2': 'GA', + 'country-code': '266', + }, + { + name: 'Gambia', + 'alpha-2': 'GM', + 'country-code': '270', + }, + { + name: 'Georgia', + 'alpha-2': 'GE', + 'country-code': '268', + }, + { + name: 'Germany', + 'alpha-2': 'DE', + 'country-code': '276', + }, + { + name: 'Ghana', + 'alpha-2': 'GH', + 'country-code': '288', + }, + { + name: 'Gibraltar', + 'alpha-2': 'GI', + 'country-code': '292', + }, + { + name: 'Greece', + 'alpha-2': 'GR', + 'country-code': '300', + }, + { + name: 'Greenland', + 'alpha-2': 'GL', + 'country-code': '304', + }, + { + name: 'Grenada', + 'alpha-2': 'GD', + 'country-code': '308', + }, + { + name: 'Guadeloupe', + 'alpha-2': 'GP', + 'country-code': '312', + }, + { + name: 'Guam', + 'alpha-2': 'GU', + 'country-code': '316', + }, + { + name: 'Guatemala', + 'alpha-2': 'GT', + 'country-code': '320', + }, + { + name: 'Guernsey', + 'alpha-2': 'GG', + 'country-code': '831', + }, + { + name: 'Guinea', + 'alpha-2': 'GN', + 'country-code': '324', + }, + { + name: 'Guinea-Bissau', + 'alpha-2': 'GW', + 'country-code': '624', + }, + { + name: 'Guyana', + 'alpha-2': 'GY', + 'country-code': '328', + }, + { + name: 'Haiti', + 'alpha-2': 'HT', + 'country-code': '332', + }, + { + name: 'Heard Island and McDonald Islands', + 'alpha-2': 'HM', + 'country-code': '334', + }, + { + name: 'Holy See', + 'alpha-2': 'VA', + 'country-code': '336', + }, + { + name: 'Honduras', + 'alpha-2': 'HN', + 'country-code': '340', + }, + { + name: 'Hong Kong', + 'alpha-2': 'HK', + 'country-code': '344', + }, + { + name: 'Hungary', + 'alpha-2': 'HU', + 'country-code': '348', + }, + { + name: 'Iceland', + 'alpha-2': 'IS', + 'country-code': '352', + }, + { + name: 'India', + 'alpha-2': 'IN', + 'country-code': '356', + }, + { + name: 'Indonesia', + 'alpha-2': 'ID', + 'country-code': '360', + }, + { + name: 'Iran (Islamic Republic of)', + 'alpha-2': 'IR', + 'country-code': '364', + }, + { + name: 'Iraq', + 'alpha-2': 'IQ', + 'country-code': '368', + }, + { + name: 'Ireland', + 'alpha-2': 'IE', + 'country-code': '372', + }, + { + name: 'Isle of Man', + 'alpha-2': 'IM', + 'country-code': '833', + }, + { + name: 'Israel', + 'alpha-2': 'IL', + 'country-code': '376', + }, + { + name: 'Italy', + 'alpha-2': 'IT', + 'country-code': '380', + }, + { + name: 'Jamaica', + 'alpha-2': 'JM', + 'country-code': '388', + }, + { + name: 'Japan', + 'alpha-2': 'JP', + 'country-code': '392', + }, + { + name: 'Jersey', + 'alpha-2': 'JE', + 'country-code': '832', + }, + { + name: 'Jordan', + 'alpha-2': 'JO', + 'country-code': '400', + }, + { + name: 'Kazakhstan', + 'alpha-2': 'KZ', + 'country-code': '398', + }, + { + name: 'Kenya', + 'alpha-2': 'KE', + 'country-code': '404', + }, + { + name: 'Kiribati', + 'alpha-2': 'KI', + 'country-code': '296', + }, + { + name: "Korea (Democratic People's Republic of)", + 'alpha-2': 'KP', + 'country-code': '408', + }, + { + name: 'Korea, Republic of', + 'alpha-2': 'KR', + 'country-code': '410', + }, + { + name: 'Kuwait', + 'alpha-2': 'KW', + 'country-code': '414', + }, + { + name: 'Kyrgyzstan', + 'alpha-2': 'KG', + 'country-code': '417', + }, + { + name: "Lao People's Democratic Republic", + 'alpha-2': 'LA', + 'country-code': '418', + }, + { + name: 'Latvia', + 'alpha-2': 'LV', + 'country-code': '428', + }, + { + name: 'Lebanon', + 'alpha-2': 'LB', + 'country-code': '422', + }, + { + name: 'Lesotho', + 'alpha-2': 'LS', + 'country-code': '426', + }, + { + name: 'Liberia', + 'alpha-2': 'LR', + 'country-code': '430', + }, + { + name: 'Libya', + 'alpha-2': 'LY', + 'country-code': '434', + }, + { + name: 'Liechtenstein', + 'alpha-2': 'LI', + 'country-code': '438', + }, + { + name: 'Lithuania', + 'alpha-2': 'LT', + 'country-code': '440', + }, + { + name: 'Luxembourg', + 'alpha-2': 'LU', + 'country-code': '442', + }, + { + name: 'Macao', + 'alpha-2': 'MO', + 'country-code': '446', + }, + { + name: 'Madagascar', + 'alpha-2': 'MG', + 'country-code': '450', + }, + { + name: 'Malawi', + 'alpha-2': 'MW', + 'country-code': '454', + }, + { + name: 'Malaysia', + 'alpha-2': 'MY', + 'country-code': '458', + }, + { + name: 'Maldives', + 'alpha-2': 'MV', + 'country-code': '462', + }, + { + name: 'Mali', + 'alpha-2': 'ML', + 'country-code': '466', + }, + { + name: 'Malta', + 'alpha-2': 'MT', + 'country-code': '470', + }, + { + name: 'Marshall Islands', + 'alpha-2': 'MH', + 'country-code': '584', + }, + { + name: 'Martinique', + 'alpha-2': 'MQ', + 'country-code': '474', + }, + { + name: 'Mauritania', + 'alpha-2': 'MR', + 'country-code': '478', + }, + { + name: 'Mauritius', + 'alpha-2': 'MU', + 'country-code': '480', + }, + { + name: 'Mayotte', + 'alpha-2': 'YT', + 'country-code': '175', + }, + { + name: 'Mexico', + 'alpha-2': 'MX', + 'country-code': '484', + }, + { + name: 'Micronesia (Federated States of)', + 'alpha-2': 'FM', + 'country-code': '583', + }, + { + name: 'Moldova, Republic of', + 'alpha-2': 'MD', + 'country-code': '498', + }, + { + name: 'Monaco', + 'alpha-2': 'MC', + 'country-code': '492', + }, + { + name: 'Mongolia', + 'alpha-2': 'MN', + 'country-code': '496', + }, + { + name: 'Montenegro', + 'alpha-2': 'ME', + 'country-code': '499', + }, + { + name: 'Montserrat', + 'alpha-2': 'MS', + 'country-code': '500', + }, + { + name: 'Morocco', + 'alpha-2': 'MA', + 'country-code': '504', + }, + { + name: 'Mozambique', + 'alpha-2': 'MZ', + 'country-code': '508', + }, + { + name: 'Myanmar', + 'alpha-2': 'MM', + 'country-code': '104', + }, + { + name: 'Namibia', + 'alpha-2': 'NA', + 'country-code': '516', + }, + { + name: 'Nauru', + 'alpha-2': 'NR', + 'country-code': '520', + }, + { + name: 'Nepal', + 'alpha-2': 'NP', + 'country-code': '524', + }, + { + name: 'Netherlands', + 'alpha-2': 'NL', + 'country-code': '528', + }, + { + name: 'New Caledonia', + 'alpha-2': 'NC', + 'country-code': '540', + }, + { + name: 'New Zealand', + 'alpha-2': 'NZ', + 'country-code': '554', + }, + { + name: 'Nicaragua', + 'alpha-2': 'NI', + 'country-code': '558', + }, + { + name: 'Niger', + 'alpha-2': 'NE', + 'country-code': '562', + }, + { + name: 'Nigeria', + 'alpha-2': 'NG', + 'country-code': '566', + }, + { + name: 'Niue', + 'alpha-2': 'NU', + 'country-code': '570', + }, + { + name: 'Norfolk Island', + 'alpha-2': 'NF', + 'country-code': '574', + }, + { + name: 'North Macedonia', + 'alpha-2': 'MK', + 'country-code': '807', + }, + { + name: 'Northern Mariana Islands', + 'alpha-2': 'MP', + 'country-code': '580', + }, + { + name: 'Norway', + 'alpha-2': 'NO', + 'country-code': '578', + }, + { + name: 'Oman', + 'alpha-2': 'OM', + 'country-code': '512', + }, + { + name: 'Pakistan', + 'alpha-2': 'PK', + 'country-code': '586', + }, + { + name: 'Palau', + 'alpha-2': 'PW', + 'country-code': '585', + }, + { + name: 'Palestine, State of', + 'alpha-2': 'PS', + 'country-code': '275', + }, + { + name: 'Panama', + 'alpha-2': 'PA', + 'country-code': '591', + }, + { + name: 'Papua New Guinea', + 'alpha-2': 'PG', + 'country-code': '598', + }, + { + name: 'Paraguay', + 'alpha-2': 'PY', + 'country-code': '600', + }, + { + name: 'Peru', + 'alpha-2': 'PE', + 'country-code': '604', + }, + { + name: 'Philippines', + 'alpha-2': 'PH', + 'country-code': '608', + }, + { + name: 'Pitcairn', + 'alpha-2': 'PN', + 'country-code': '612', + }, + { + name: 'Poland', + 'alpha-2': 'PL', + 'country-code': '616', + }, + { + name: 'Portugal', + 'alpha-2': 'PT', + 'country-code': '620', + }, + { + name: 'Puerto Rico', + 'alpha-2': 'PR', + 'country-code': '630', + }, + { + name: 'Qatar', + 'alpha-2': 'QA', + 'country-code': '634', + }, + { + name: 'RΓ©union', + 'alpha-2': 'RE', + 'country-code': '638', + }, + { + name: 'Romania', + 'alpha-2': 'RO', + 'country-code': '642', + }, + { + name: 'Russian Federation', + 'alpha-2': 'RU', + 'country-code': '643', + }, + { + name: 'Rwanda', + 'alpha-2': 'RW', + 'country-code': '646', + }, + { + name: 'Saint BarthΓ©lemy', + 'alpha-2': 'BL', + 'country-code': '652', + }, + { + name: 'Saint Helena, Ascension and Tristan da Cunha', + 'alpha-2': 'SH', + 'country-code': '654', + }, + { + name: 'Saint Kitts and Nevis', + 'alpha-2': 'KN', + 'country-code': '659', + }, + { + name: 'Saint Lucia', + 'alpha-2': 'LC', + 'country-code': '662', + }, + { + name: 'Saint Martin (French part)', + 'alpha-2': 'MF', + 'country-code': '663', + }, + { + name: 'Saint Pierre and Miquelon', + 'alpha-2': 'PM', + 'country-code': '666', + }, + { + name: 'Saint Vincent and the Grenadines', + 'alpha-2': 'VC', + 'country-code': '670', + }, + { + name: 'Samoa', + 'alpha-2': 'WS', + 'country-code': '882', + }, + { + name: 'San Marino', + 'alpha-2': 'SM', + 'country-code': '674', + }, + { + name: 'Sao Tome and Principe', + 'alpha-2': 'ST', + 'country-code': '678', + }, + { + name: 'Saudi Arabia', + 'alpha-2': 'SA', + 'country-code': '682', + }, + { + name: 'Senegal', + 'alpha-2': 'SN', + 'country-code': '686', + }, + { + name: 'Serbia', + 'alpha-2': 'RS', + 'country-code': '688', + }, + { + name: 'Seychelles', + 'alpha-2': 'SC', + 'country-code': '690', + }, + { + name: 'Sierra Leone', + 'alpha-2': 'SL', + 'country-code': '694', + }, + { + name: 'Singapore', + 'alpha-2': 'SG', + 'country-code': '702', + }, + { + name: 'Sint Maarten (Dutch part)', + 'alpha-2': 'SX', + 'country-code': '534', + }, + { + name: 'Slovakia', + 'alpha-2': 'SK', + 'country-code': '703', + }, + { + name: 'Slovenia', + 'alpha-2': 'SI', + 'country-code': '705', + }, + { + name: 'Solomon Islands', + 'alpha-2': 'SB', + 'country-code': '090', + }, + { + name: 'Somalia', + 'alpha-2': 'SO', + 'country-code': '706', + }, + { + name: 'South Africa', + 'alpha-2': 'ZA', + 'country-code': '710', + }, + { + name: 'South Georgia and the South Sandwich Islands', + 'alpha-2': 'GS', + 'country-code': '239', + }, + { + name: 'South Sudan', + 'alpha-2': 'SS', + 'country-code': '728', + }, + { + name: 'Spain', + 'alpha-2': 'ES', + 'country-code': '724', + }, + { + name: 'Sri Lanka', + 'alpha-2': 'LK', + 'country-code': '144', + }, + { + name: 'Sudan', + 'alpha-2': 'SD', + 'country-code': '729', + }, + { + name: 'Suriname', + 'alpha-2': 'SR', + 'country-code': '740', + }, + { + name: 'Svalbard and Jan Mayen', + 'alpha-2': 'SJ', + 'country-code': '744', + }, + { + name: 'Sweden', + 'alpha-2': 'SE', + 'country-code': '752', + }, + { + name: 'Switzerland', + 'alpha-2': 'CH', + 'country-code': '756', + }, + { + name: 'Syrian Arab Republic', + 'alpha-2': 'SY', + 'country-code': '760', + }, + { + name: 'Taiwan, Province of China', + 'alpha-2': 'TW', + 'country-code': '158', + }, + { + name: 'Tajikistan', + 'alpha-2': 'TJ', + 'country-code': '762', + }, + { + name: 'Tanzania, United Republic of', + 'alpha-2': 'TZ', + 'country-code': '834', + }, + { + name: 'Thailand', + 'alpha-2': 'TH', + 'country-code': '764', + }, + { + name: 'Timor-Leste', + 'alpha-2': 'TL', + 'country-code': '626', + }, + { + name: 'Togo', + 'alpha-2': 'TG', + 'country-code': '768', + }, + { + name: 'Tokelau', + 'alpha-2': 'TK', + 'country-code': '772', + }, + { + name: 'Tonga', + 'alpha-2': 'TO', + 'country-code': '776', + }, + { + name: 'Trinidad and Tobago', + 'alpha-2': 'TT', + 'country-code': '780', + }, + { + name: 'Tunisia', + 'alpha-2': 'TN', + 'country-code': '788', + }, + { + name: 'Turkey', + 'alpha-2': 'TR', + 'country-code': '792', + }, + { + name: 'Turkmenistan', + 'alpha-2': 'TM', + 'country-code': '795', + }, + { + name: 'Turks and Caicos Islands', + 'alpha-2': 'TC', + 'country-code': '796', + }, + { + name: 'Tuvalu', + 'alpha-2': 'TV', + 'country-code': '798', + }, + { + name: 'Uganda', + 'alpha-2': 'UG', + 'country-code': '800', + }, + { + name: 'Ukraine', + 'alpha-2': 'UA', + 'country-code': '804', + }, + { + name: 'United Arab Emirates', + 'alpha-2': 'AE', + 'country-code': '784', + }, + { + name: 'United Kingdom of Great Britain and Northern Ireland', + 'alpha-2': 'GB', + 'country-code': '826', + }, + { + name: 'United States of America', + 'alpha-2': 'US', + 'country-code': '840', + }, + { + name: 'United States Minor Outlying Islands', + 'alpha-2': 'UM', + 'country-code': '581', + }, + { + name: 'Uruguay', + 'alpha-2': 'UY', + 'country-code': '858', + }, + { + name: 'Uzbekistan', + 'alpha-2': 'UZ', + 'country-code': '860', + }, + { + name: 'Vanuatu', + 'alpha-2': 'VU', + 'country-code': '548', + }, + { + name: 'Venezuela (Bolivarian Republic of)', + 'alpha-2': 'VE', + 'country-code': '862', + }, + { + name: 'Viet Nam', + 'alpha-2': 'VN', + 'country-code': '704', + }, + { + name: 'Virgin Islands (British)', + 'alpha-2': 'VG', + 'country-code': '092', + }, + { + name: 'Virgin Islands (U.S.)', + 'alpha-2': 'VI', + 'country-code': '850', + }, + { + name: 'Wallis and Futuna', + 'alpha-2': 'WF', + 'country-code': '876', + }, + { + name: 'Western Sahara', + 'alpha-2': 'EH', + 'country-code': '732', + }, + { + name: 'Yemen', + 'alpha-2': 'YE', + 'country-code': '887', + }, + { + name: 'Zambia', + 'alpha-2': 'ZM', + 'country-code': '894', + }, + { + name: 'Zimbabwe', + 'alpha-2': 'ZW', + 'country-code': '716', + }, +] as const; + +export const countryCodes = countries.map(country => country['alpha-2']); diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index fcdce33f48..8823868ddc 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -8,6 +8,21 @@ export { isNullish, isObject, noNullish, + zodErrorToReadable, sleep, uniqueArray, + type IErrorWithMessage, } from './utils'; + +export { type AnyRecord } from './types'; + +export { + type DefaultContextSchema, + type TDefaultSchemaDocumentPage, + ghanaDocuments, + certificateOfResidenceGH, + getDocumentsByCountry, + getDocumentId, + type TDocument, + defaultContextSchema, +} from './schemas'; diff --git a/services/workflows-service/src/workflow/schemas/context.d.ts b/packages/common/src/schemas/documents/context.ts similarity index 99% rename from services/workflows-service/src/workflow/schemas/context.d.ts rename to packages/common/src/schemas/documents/context.ts index f9294953ee..de0b78cc84 100644 --- a/services/workflows-service/src/workflow/schemas/context.d.ts +++ b/packages/common/src/schemas/documents/context.ts @@ -10,6 +10,7 @@ export interface DefaultContextSchema { [k: string]: unknown; }; documents: { + id?: string; category: string; type: string; issuer: { diff --git a/services/workflows-service/src/workflow/schemas/default-context-page-schema.ts b/packages/common/src/schemas/documents/default-context-page-schema.ts similarity index 60% rename from services/workflows-service/src/workflow/schemas/default-context-page-schema.ts rename to packages/common/src/schemas/documents/default-context-page-schema.ts index 9d9bd606f9..9cb8ff3e87 100644 --- a/services/workflows-service/src/workflow/schemas/default-context-page-schema.ts +++ b/packages/common/src/schemas/documents/default-context-page-schema.ts @@ -1,3 +1,3 @@ -import { DefaultContextSchema } from '@/workflow/schemas/context'; +import { DefaultContextSchema } from '../documents/context'; export type TDefaultSchemaDocumentPage = DefaultContextSchema['documents'][number]['pages'][number]; diff --git a/services/workflows-service/src/workflow/schemas/default-context-schema.json b/packages/common/src/schemas/documents/default-context-schema.json similarity index 84% rename from services/workflows-service/src/workflow/schemas/default-context-schema.json rename to packages/common/src/schemas/documents/default-context-schema.json index e272bfc552..f98a284c83 100644 --- a/services/workflows-service/src/workflow/schemas/default-context-schema.json +++ b/packages/common/src/schemas/documents/default-context-schema.json @@ -9,10 +9,13 @@ "enum": ["individual", "business"] }, "data": { - "type": "object" - }, - "additionalDetails": { - "type": "object" + "type": "object", + "properties": { + "additionalInfo": { + "type": "object" + } + }, + "additionalProperties": true }, "ballerineEntityId": { "type": "string" @@ -37,11 +40,16 @@ "items": { "type": "object", "properties": { - "category": { + "id": { "type": "string" }, + "category": { + "type": "string", + "transform": ["trim", "toLowerCase"] + }, "type": { - "type": "string" + "type": "string", + "transform": ["trim", "toLowerCase"] }, "issuer": { "type": "object", @@ -53,12 +61,13 @@ "type": "string" }, "country": { - "type": "string" + "type": "string", + "transform": ["trim", "toUpperCase"] }, "city": { "type": "string" }, - "additionalDetails": { + "additionalInfo": { "type": "object" } }, @@ -100,11 +109,19 @@ { "type": "string", "enum": [ - "Blurry image", + "Wrong document", + "Fake document", + "Spam", + "Ownership mismatch - Name", + "Ownership mismatch - National ID", + "Unknown document type", + "Bad image quality", "Missing page", "Invalid document", "Expired document", - "Unreadable document" + "Unreadable document", + "Blurry image", + "Other" ] } ] diff --git a/packages/common/src/schemas/documents/default-context-schema.ts b/packages/common/src/schemas/documents/default-context-schema.ts new file mode 100644 index 0000000000..30dfcf292e --- /dev/null +++ b/packages/common/src/schemas/documents/default-context-schema.ts @@ -0,0 +1,198 @@ +export const defaultContextSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + entity: { + type: 'object', + properties: { + type: { + enum: ['individual', 'business'], + }, + data: { + type: 'object', + properties: { + additionalInfo: { + type: 'object', + }, + }, + additionalProperties: true, + }, + ballerineEntityId: { + type: 'string', + }, + id: { + type: 'string', + }, + }, + required: ['type'], + anyOf: [ + { + required: ['id'], + }, + { + required: ['ballerineEntityId'], + }, + ], + additionalProperties: false, + }, + documents: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + }, + category: { + type: 'string', + transform: ['trim', 'toLowerCase'], + }, + type: { + type: 'string', + transform: ['trim', 'toLowerCase'], + }, + issuer: { + type: 'object', + properties: { + type: { + type: 'string', + }, + name: { + type: 'string', + }, + country: { + type: 'string', + transform: ['trim', 'toUpperCase'], + }, + city: { + type: 'string', + }, + additionalInfo: { + type: 'object', + }, + }, + required: ['country'], + additionalProperties: false, + }, + issuingVersion: { + type: 'integer', + }, + decision: { + type: 'object', + properties: { + status: { + type: 'string', + enum: ['new', 'pending', 'revision', 'approved', 'rejected'], + }, + rejectionReason: { + anyOf: [ + { + type: 'string', + }, + { + type: 'string', + enum: [ + 'Suspicious document', + 'Document does not match customer profile', + 'Potential identity theft', + 'Fake or altered document', + 'Document on watchlist or blacklist', + ], + }, + ], + }, + revisionReason: { + anyOf: [ + { + type: 'string', + }, + { + type: 'string', + enum: [ + 'Wrong document', + 'Fake document', + 'Spam', + 'Ownership mismatch - Name', + 'Ownership mismatch - National ID', + 'Unknown document type', + 'Bad image quality', + 'Missing page', + 'Invalid document', + 'Expired document', + 'Unreadable document', + 'Blurry image', + 'Other', + ], + }, + ], + }, + }, + additionalProperties: false, + }, + version: { + type: 'integer', + }, + pages: { + type: 'array', + items: { + type: 'object', + properties: { + ballerineFileId: { + type: 'string', + }, + provider: { + type: 'string', + enum: ['gcs', 'http', 'stream', 'base64', 'ftp'], + }, + uri: { + type: 'string', + format: 'uri', + }, + type: { + enum: ['pdf', 'png', 'jpg'], + }, + data: { + type: 'string', + }, + metadata: { + type: 'object', + properties: { + side: { + type: 'string', + }, + pageNumber: { + type: 'string', + }, + }, + additionalProperties: false, + }, + }, + required: ['provider', 'uri', 'type'], + additionalProperties: false, + }, + }, + properties: { + type: 'object', + properties: { + email: { + type: 'string', + format: 'email', + }, + expiryDate: { + type: 'string', + format: 'date', + }, + idNumber: { + type: 'string', + format: 'regex', + }, + }, + }, + }, + required: ['category', 'type', 'issuer', 'pages', 'properties'], + additionalProperties: false, + }, + }, + }, + required: ['entity', 'documents'], +}; diff --git a/packages/common/src/schemas/documents/workflow/documents/schemas/CA.ts b/packages/common/src/schemas/documents/workflow/documents/schemas/CA.ts new file mode 100644 index 0000000000..c25ab5e60c --- /dev/null +++ b/packages/common/src/schemas/documents/workflow/documents/schemas/CA.ts @@ -0,0 +1,127 @@ +import { TDocument } from '@/schemas'; + +export const canadaDocuments: TDocument[] = [ + { + category: 'incorporation', + type: 'pdf', + issuer: { + type: 'local_authority', + country: 'CA', + }, + issuingVersion: 1, + version: 1, + propertiesSchema: { + type: 'object', + properties: { + businessName: { + type: 'string', + }, + website: { + type: 'string', + }, + phone: { + type: 'string', + }, + email: { + type: 'string', + }, + owner: { + type: 'string', + }, + tin: { + type: 'string', + }, + }, + }, + }, + { + category: 'id', + type: 'photo', + issuer: { + type: 'local_authority', + country: 'CA', + }, + issuingVersion: 1, + version: 1, + propertiesSchema: { + type: 'object', + properties: { + firstName: { + type: 'string', + }, + middleName: { + type: 'string', + }, + lastName: { + type: 'string', + }, + authority: { + type: 'string', + }, + placeOfIssue: { + type: 'string', + }, + issueDate: { + type: 'string', + }, + expires: { + type: 'string', + }, + dateOfBirth: { + type: 'string', + }, + placeOfBirth: { + type: 'string', + }, + sex: { + type: 'string', + }, + }, + }, + }, + { + category: 'selfie', + type: 'photo', + issuer: { + type: 'local_authority', + country: 'CA', + }, + issuingVersion: 1, + version: 1, + propertiesSchema: { + type: 'object', + properties: { + firstName: { + type: 'string', + }, + middleName: { + type: 'string', + }, + lastName: { + type: 'string', + }, + authority: { + type: 'string', + }, + placeOfIssue: { + type: 'string', + }, + issueDate: { + type: 'string', + }, + expires: { + type: 'string', + }, + dateOfBirth: { + type: 'string', + }, + placeOfBirth: { + type: 'string', + }, + sex: { + type: 'string', + }, + }, + }, + }, +]; diff --git a/services/workflows-service/src/documents/schemas/GH.ts b/packages/common/src/schemas/documents/workflow/documents/schemas/GH.ts similarity index 53% rename from services/workflows-service/src/documents/schemas/GH.ts rename to packages/common/src/schemas/documents/workflow/documents/schemas/GH.ts index 06af061180..732fb351af 100644 --- a/services/workflows-service/src/documents/schemas/GH.ts +++ b/packages/common/src/schemas/documents/workflow/documents/schemas/GH.ts @@ -1,6 +1,9 @@ -import { Document } from '../types'; +import { TDocument } from '../types'; -export const certificateOfResidenceGH: Document = { +const ghNationalIdNumber = '^$|^GHA-\\d{9}-\\d{1}$'; +const alphaNumeric = '^[a-zA-Z0-9]*$'; + +export const certificateOfResidenceGH: TDocument = { category: 'proof_of_address', type: 'water_bill', issuer: { @@ -16,10 +19,11 @@ export const certificateOfResidenceGH: Document = { properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, }, userAddress: { type: 'string', @@ -38,7 +42,46 @@ export const certificateOfResidenceGH: Document = { }, }; -export const ghanaDocuments: Document[] = [ +export const ghanaDocuments: TDocument[] = [ + { + category: 'financial_information', + type: 'mtn_statement', + issuer: { + country: 'GH', + }, + issuingVersion: 1, + version: 1, + propertiesSchema: { + type: 'object', + properties: { + accountNameHolder: { + type: 'string', + }, + msisdn: { + type: 'string', + pattern: '^233[0-9]{9}$', + }, + from: { + type: 'string', + format: 'date', + }, + to: { + type: 'string', + format: 'date', + }, + timeRun: { + type: 'string', + format: 'date', + }, + accountHolderName: { + type: 'string', + }, + maxBalanceRecorded: { + type: 'number', + }, + }, + }, + }, { category: 'proof_of_address', type: 'water_bill', @@ -53,10 +96,11 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, }, userAddress: { type: 'string', @@ -90,10 +134,11 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, }, userAddress: { type: 'string', @@ -125,10 +170,11 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, }, userAddress: { type: 'string', @@ -160,10 +206,11 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, }, employeeName: { type: 'string', @@ -195,10 +242,11 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, }, employeeName: { type: 'string', @@ -227,10 +275,11 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, }, userAddress: { type: 'string', @@ -259,10 +308,11 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, }, userAddress: { type: 'string', @@ -291,10 +341,11 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, }, userAddress: { type: 'string', @@ -323,10 +374,11 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, }, userAddress: { type: 'string', @@ -359,10 +411,11 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, }, employeeName: { type: 'string', @@ -391,10 +444,47 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, + }, + employeeName: { + type: 'string', + }, + position: { + type: 'string', + }, + employerName: { + type: 'string', + }, + issuingDate: { + type: 'string', + format: 'date', + }, + }, + }, + }, + { + category: 'proof_of_employment', + type: 'form_a', + issuer: { + type: 'private', + country: 'GH', + }, + issuingVersion: 1, + version: 1, + propertiesSchema: { + type: 'object', + properties: { + nationalIdNumber: { + type: 'string', + pattern: ghNationalIdNumber, + }, + docNumber: { + type: 'string', + pattern: alphaNumeric, }, employeeName: { type: 'string', @@ -426,10 +516,11 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, }, userAddress: { type: 'string', @@ -444,6 +535,42 @@ export const ghanaDocuments: Document[] = [ }, }, }, + { + category: 'proof_of_employment', + type: 'form_3', + issuer: { + type: 'private', + country: 'GH', + }, + issuingVersion: 1, + version: 1, + propertiesSchema: { + type: 'object', + properties: { + nationalIdNumber: { + type: 'string', + pattern: ghNationalIdNumber, + }, + docNumber: { + type: 'string', + pattern: alphaNumeric, + }, + employeeName: { + type: 'string', + }, + position: { + type: 'string', + }, + employerName: { + type: 'string', + }, + issuingDate: { + type: 'string', + format: 'date', + }, + }, + }, + }, { category: 'proof_of_address', type: 'form_3', @@ -458,10 +585,44 @@ export const ghanaDocuments: Document[] = [ properties: { nationalIdNumber: { type: 'string', - pattern: '^GHA-\\d{9}-\\d{1}$', + pattern: ghNationalIdNumber, }, docNumber: { - type: 'number', + type: 'string', + pattern: alphaNumeric, + }, + userAddress: { + type: 'string', + }, + physicalAddress: { + type: 'string', + }, + issuingDate: { + type: 'string', + format: 'date', + }, + }, + }, + }, + { + category: 'proof_of_address', + type: 'form_4', + issuer: { + type: 'government', + country: 'GH', + }, + issuingVersion: 1, + version: 1, + propertiesSchema: { + type: 'object', + properties: { + nationalIdNumber: { + type: 'string', + pattern: ghNationalIdNumber, + }, + docNumber: { + type: 'string', + pattern: alphaNumeric, }, userAddress: { type: 'string', @@ -476,4 +637,147 @@ export const ghanaDocuments: Document[] = [ }, }, }, + { + category: 'proof_of_employment', + type: 'form_4', + issuer: { + type: 'private', + country: 'GH', + }, + issuingVersion: 1, + version: 1, + propertiesSchema: { + type: 'object', + properties: { + nationalIdNumber: { + type: 'string', + pattern: ghNationalIdNumber, + }, + docNumber: { + type: 'string', + pattern: alphaNumeric, + }, + employeeName: { + type: 'string', + }, + position: { + type: 'string', + }, + employerName: { + type: 'string', + }, + issuingDate: { + type: 'string', + format: 'date', + }, + }, + }, + }, + { + category: 'proof_of_registration', + type: 'certificate_of_registration', + issuer: { + country: 'GH', + }, + issuingVersion: 1, + version: 1, + propertiesSchema: { + type: 'object', + required: ['companyName', 'taxIdNumber', 'registrationNumber'], + properties: { + companyName: { + type: 'string', + }, + taxIdNumber: { + type: 'string', + pattern: alphaNumeric, + }, + registrationNumber: { + type: 'string', + pattern: alphaNumeric, + }, + issuingDate: { + type: 'string', + format: 'date', + }, + }, + }, + }, + { + category: 'proof_of_registration', + type: 'district_assembly_certificate', + issuer: { + country: 'GH', + }, + issuingVersion: 1, + version: 1, + propertiesSchema: { + type: 'object', + required: ['certificateNo', 'companyName'], + properties: { + certificateNo: { + type: 'string', + pattern: alphaNumeric, + }, + registrationNumber: { + type: 'string', + pattern: alphaNumeric, + }, + companyName: { + type: 'string', + }, + issuingDate: { + type: 'string', + format: 'date', + }, + }, + }, + }, + { + category: 'proof_of_registration', + type: 'form_a', + issuer: { + country: 'GH', + }, + issuingVersion: 1, + version: 1, + propertiesSchema: { + type: 'object', + required: ['registrationNumber', 'taxIdNumber'], + properties: { + registrationNumber: { + type: 'string', + pattern: alphaNumeric, + }, + taxIdNumber: { + type: 'string', + pattern: alphaNumeric, + }, + }, + }, + }, + { + category: 'proof_of_registration', + type: 'shareholder_details', + issuer: { + country: 'GH', + }, + issuingVersion: 1, + version: 1, + propertiesSchema: { + type: 'object', + required: ['firstName', 'lastName'], + properties: { + firstName: { + type: 'string', + }, + middleName: { + type: 'string', + }, + lastName: { + type: 'string', + }, + }, + }, + }, ]; diff --git a/packages/common/src/schemas/documents/workflow/documents/schemas/index.ts b/packages/common/src/schemas/documents/workflow/documents/schemas/index.ts new file mode 100644 index 0000000000..8ae114d17d --- /dev/null +++ b/packages/common/src/schemas/documents/workflow/documents/schemas/index.ts @@ -0,0 +1,29 @@ +import { ghanaDocuments } from './GH'; +import { TDocument } from '../types'; +import { countryCodes } from '@/countries'; +import { DefaultContextSchema } from '@/schemas'; +import { canadaDocuments } from './CA'; + +const documentIdsByCountry: Partial<Record<(typeof countryCodes)[number], TDocument[]>> = { + GH: ghanaDocuments, + CA: canadaDocuments, +}; + +export const getDocumentsByCountry = (countryCode: (typeof countryCodes)[number]): TDocument[] => { + return documentIdsByCountry[countryCode] || []; +}; + +export const getDocumentId = ( + document: TDocument | DefaultContextSchema['documents'][number], + useUuid = true, +) => { + if (useUuid && document?.id) return document.id; + + let id = `${document?.category}-${document?.type}-${document?.issuer?.country}`; + + if (document.version) { + id = `${id}-v${document.version}`; + } + + return id.toLowerCase(); +}; diff --git a/packages/common/src/schemas/documents/workflow/documents/types.ts b/packages/common/src/schemas/documents/workflow/documents/types.ts new file mode 100644 index 0000000000..60189da314 --- /dev/null +++ b/packages/common/src/schemas/documents/workflow/documents/types.ts @@ -0,0 +1,5 @@ +import { DefaultContextSchema } from '../../context'; + +export type TDocument = Omit<DefaultContextSchema['documents'][number], 'pages' | 'properties'> & { + propertiesSchema: any; +}; diff --git a/packages/common/src/schemas/index.ts b/packages/common/src/schemas/index.ts index 4da7acb80e..c8066a7024 100644 --- a/packages/common/src/schemas/index.ts +++ b/packages/common/src/schemas/index.ts @@ -1,130 +1,9 @@ -const userRolesSchema = { - type: 'array', - items: { - type: 'string', - }, -}; - -const endUserAdditionalInfoSchema = { - type: 'object', - properties: { - // Define your additional info properties here - }, - additionalProperties: false, -}; - -const businessShareholderStructureSchema = { - type: 'array', - items: { - type: 'object', - properties: { - name: { type: 'string' }, - ownershipPercentage: { type: 'number' }, - }, - required: ['name', 'ownershipPercentage'], - additionalProperties: false, - }, -}; - -const businessDocumentsSchema = { - type: 'array', - items: { - type: 'object', - properties: { - documentType: { type: 'string' }, - fileUrl: { type: 'string' }, - }, - required: ['documentType', 'fileUrl'], - additionalProperties: false, - }, -}; - -const workflowDefinitionDefinitionSchema = { - type: 'object', - properties: { - // Define your workflow definition properties here - }, - additionalProperties: false, -}; - -const workflowDefinitionSupportedPlatformsSchema = { - type: 'object', - properties: { - // Define your supported platforms properties here - }, - additionalProperties: false, -}; - -const workflowDefinitionExtensionsSchema = { - type: 'object', - properties: { - // Define your extensions properties here - }, - additionalProperties: false, -}; - -const workflowDefinitionBackendSchema = { - type: 'object', - properties: { - // Define your backend properties here - }, - additionalProperties: false, -}; - -const workflowDefinitionPersistStatesSchema = { - type: 'object', - properties: { - // Define your persist states properties here - }, - additionalProperties: false, -}; - -const workflowDefinitionSubmitStatesSchema = { - type: 'object', - properties: { - // Define your submit states properties here - }, - additionalProperties: false, -}; - -const workflowRuntimeDataContextSchema = { - type: 'object', - properties: { - // Define your workflow runtime data context properties here - }, - additionalProperties: false, -}; - -const policyTasksSchema = { - type: 'array', - items: { - type: 'object', - properties: { - taskName: { type: 'string' }, - // Define other task properties here - }, - required: ['taskName'], - additionalProperties: false, - }, -}; - -const policyRulesSetsSchema = { - type: 'array', - items: { - type: 'object', - properties: { - rules: { type: 'array' }, - result: { - type: 'object', - properties: { - status: { type: 'string' }, - fidoScore: { type: 'string' }, - }, - required: ['status', 'riskScore'], - additionalProperties: false, - }, - }, - required: ['rules', 'result'], - additionalProperties: false, - }, -}; +export { type DefaultContextSchema } from './documents/context'; +export { type TDefaultSchemaDocumentPage } from './documents/default-context-page-schema'; +export { defaultContextSchema } from './documents/default-context-schema'; +export { + ghanaDocuments, + certificateOfResidenceGH, +} from './documents/workflow/documents/schemas/GH'; +export { getDocumentsByCountry, getDocumentId } from './documents/workflow/documents/schemas/index'; +export { type TDocument } from './documents/workflow/documents/types'; diff --git a/packages/common/src/schemas/policy-schema.ts b/packages/common/src/schemas/policy-schema.ts new file mode 100644 index 0000000000..b3b5bc0ae4 --- /dev/null +++ b/packages/common/src/schemas/policy-schema.ts @@ -0,0 +1,130 @@ +const userRolesSchema = { + type: 'array', + items: { + type: 'string', + }, +}; + +const endUserAdditionalInfoSchema = { + type: 'object', + properties: { + // Define your additional info properties here + }, + additionalProperties: false, +}; + +const businessShareholderStructureSchema = { + type: 'array', + items: { + type: 'object', + properties: { + name: { type: 'string' }, + ownershipPercentage: { type: 'number' }, + }, + required: ['name', 'ownershipPercentage'], + additionalProperties: false, + }, +}; + +const businessDocumentsSchema = { + type: 'array', + items: { + type: 'object', + properties: { + documentType: { type: 'string' }, + fileUrl: { type: 'string' }, + }, + required: ['documentType', 'fileUrl'], + additionalProperties: false, + }, +}; + +const workflowDefinitionDefinitionSchema = { + type: 'object', + properties: { + // Define your workflow definition properties here + }, + additionalProperties: false, +}; + +const workflowDefinitionSupportedPlatformsSchema = { + type: 'object', + properties: { + // Define your supported platforms properties here + }, + additionalProperties: false, +}; + +const workflowDefinitionExtensionsSchema = { + type: 'object', + properties: { + // Define your extensions properties here + }, + additionalProperties: false, +}; + +const workflowDefinitionBackendSchema = { + type: 'object', + properties: { + // Define your backend properties here + }, + additionalProperties: false, +}; + +const workflowDefinitionPersistStatesSchema = { + type: 'object', + properties: { + // Define your persist states properties here + }, + additionalProperties: false, +}; + +const workflowDefinitionSubmitStatesSchema = { + type: 'object', + properties: { + // Define your submit states properties here + }, + additionalProperties: false, +}; + +const workflowRuntimeDataContextSchema = { + type: 'object', + properties: { + // Define your workflow runtime data context properties here + }, + additionalProperties: false, +}; + +const policyTasksSchema = { + type: 'array', + items: { + type: 'object', + properties: { + taskName: { type: 'string' }, + // Define other task properties here + }, + required: ['taskName'], + additionalProperties: false, + }, +}; + +const policyRulesSetsSchema = { + type: 'array', + items: { + type: 'object', + properties: { + rules: { type: 'array' }, + result: { + type: 'object', + properties: { + status: { type: 'string' }, + score: { type: 'string' }, + }, + required: ['status', 'riskScore'], + additionalProperties: false, + }, + }, + required: ['rules', 'result'], + additionalProperties: false, + }, +}; diff --git a/packages/common/src/types/any-record/any-record.ts b/packages/common/src/types/any-record/any-record.ts new file mode 100644 index 0000000000..6cabbc71bf --- /dev/null +++ b/packages/common/src/types/any-record/any-record.ts @@ -0,0 +1 @@ +export type AnyRecord = Record<PropertyKey, unknown>; diff --git a/packages/common/src/types/any-record/index.ts b/packages/common/src/types/any-record/index.ts new file mode 100644 index 0000000000..6845178874 --- /dev/null +++ b/packages/common/src/types/any-record/index.ts @@ -0,0 +1 @@ +export { AnyRecord } from './any-record'; diff --git a/packages/common/src/types/index.ts b/packages/common/src/types/index.ts new file mode 100644 index 0000000000..6845178874 --- /dev/null +++ b/packages/common/src/types/index.ts @@ -0,0 +1 @@ +export { AnyRecord } from './any-record'; diff --git a/packages/common/src/utils/index.ts b/packages/common/src/utils/index.ts index 31e6c55d6b..fb48b9acca 100644 --- a/packages/common/src/utils/index.ts +++ b/packages/common/src/utils/index.ts @@ -9,3 +9,5 @@ export { isObject } from './is-object'; export { noNullish } from './no-nullish'; export { sleep } from './sleep'; export { uniqueArray } from './unique-array'; +export { zodErrorToReadable } from './zod-error-to-readable'; +export { type IErrorWithMessage } from './is-error-with-message'; diff --git a/packages/common/src/utils/is-error-with-message/index.ts b/packages/common/src/utils/is-error-with-message/index.ts index ec16ad5b03..b6718529c4 100644 --- a/packages/common/src/utils/is-error-with-message/index.ts +++ b/packages/common/src/utils/is-error-with-message/index.ts @@ -1 +1,2 @@ export { isErrorWithMessage } from './is-error-with-message'; +export { type IErrorWithMessage } from './interfaces'; diff --git a/packages/common/src/utils/zod-error-to-readable/index.ts b/packages/common/src/utils/zod-error-to-readable/index.ts new file mode 100644 index 0000000000..8380d9c577 --- /dev/null +++ b/packages/common/src/utils/zod-error-to-readable/index.ts @@ -0,0 +1 @@ +export { zodErrorToReadable } from './zod-error-to-readable'; diff --git a/packages/common/src/utils/zod-error-to-readable/zod-error-to-readable.ts b/packages/common/src/utils/zod-error-to-readable/zod-error-to-readable.ts new file mode 100644 index 0000000000..8b5473281d --- /dev/null +++ b/packages/common/src/utils/zod-error-to-readable/zod-error-to-readable.ts @@ -0,0 +1,9 @@ +import { ZodError } from 'zod'; + +export const zodErrorToReadable = (error: ZodError) => { + return error.issues + .map(err => { + return `${err.path?.join(`.`)}: ${err.message}`; + }) + .join('\n'); +}; diff --git a/packages/common/src/validations/workflow-validators/api-plugin.validator.test.ts b/packages/common/src/validations/workflow-validators/api-plugin.validator.test.ts new file mode 100644 index 0000000000..591639c02d --- /dev/null +++ b/packages/common/src/validations/workflow-validators/api-plugin.validator.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, it } from 'vitest'; +import { validate } from './api-plugin.validator'; +import { ZodError } from 'zod'; +describe('Api Validator', () => { + describe('validate Api Plugin', () => { + const apiPlugin = { + name: 'ballerineEnrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.json', + method: 'GET', + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + request: { + transform: { + transformer: 'jq', + mapping: '{data: .entity.id}', + }, + }, + response: { + transform: { transformer: 'jq', mapping: '{result: .}' }, + }, + }; + + describe('when api plugin is valid', () => { + it('does not throw validation exception', async () => { + expect(() => validate(apiPlugin)).not.toThrow(); + }); + }); + + describe('when invalid plugin is valid - invalid request', () => { + it('does not throw validation exception', async () => { + let failingPlugin = structuredClone(apiPlugin); + failingPlugin.request.transform.transformer = { someObjectKey: 'someObjectValue' }; + expect(() => validate(failingPlugin)).toThrowError(ZodError); + }); + }); + + describe('when invalid plugin is valid - missing callback', () => { + it('does not throw validation exception', async () => { + let failingPlugin = structuredClone(apiPlugin); + failingPlugin.errorAction = undefined; + expect(() => validate(failingPlugin)).toThrowError(ZodError); + }); + }); + + describe('when invalid plugin is valid - stateNames is string', () => { + it('does not throw validation exception', async () => { + let failingPlugin = structuredClone(apiPlugin); + failingPlugin.stateNames = 'someState'; + expect(() => validate(failingPlugin)).toThrowError(ZodError); + }); + }); + }); +}); diff --git a/packages/common/src/validations/workflow-validators/api-plugin.validator.ts b/packages/common/src/validations/workflow-validators/api-plugin.validator.ts new file mode 100644 index 0000000000..03c9e1e6e1 --- /dev/null +++ b/packages/common/src/validations/workflow-validators/api-plugin.validator.ts @@ -0,0 +1,53 @@ +import { z } from 'zod'; +import { AnyRecord } from '@/types'; + +const RequestSchema = z.object({ + transform: z.object({ + transformer: z.string(), + mapping: z.string(), + }), + schema: z + .object({ + $schema: z.string(), + type: z.string(), + properties: z.object({}), + required: z.array(z.string()).optional(), + }) + .optional(), +}); + +const ResponseSchema = z + .object({ + transform: z.object({ + transformer: z.string(), + mapping: z.string(), + }), + schema: z + .object({ + $schema: z.string(), + type: z.string(), + properties: z.object({}), + required: z.array(z.string()).optional(), + }) + .optional(), + }) + .optional(); + +export const ApiPluginSchema = z + .object({ + name: z.string(), + url: z.string().url(), + logo: z.string().optional(), + method: z.string(), + headers: z.record(z.string()).optional(), + stateNames: z.array(z.string()), + successAction: z.string(), + errorAction: z.string(), + request: RequestSchema, + response: ResponseSchema, + }) + .strict(); + +export const validate = (apiPlugin: AnyRecord) => { + return ApiPluginSchema.parse(apiPlugin); +}; diff --git a/packages/common/src/validations/workflow-validators/rule.validator.ts b/packages/common/src/validations/workflow-validators/rule.validator.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/common/src/validations/workflow-validators/state.validator.ts b/packages/common/src/validations/workflow-validators/state.validator.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/common/src/validations/workflow-validators/webhook-plugin.validator.test.ts b/packages/common/src/validations/workflow-validators/webhook-plugin.validator.test.ts new file mode 100644 index 0000000000..a24ab6309b --- /dev/null +++ b/packages/common/src/validations/workflow-validators/webhook-plugin.validator.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from 'vitest'; +import { validate } from './webhook-plugin.validator'; +import { ZodError } from 'zod'; +describe('Webhook Validator', () => { + describe('validate Webhook Plugin', () => { + const webhookPlugin = { + name: 'ballerineEnrichmentHook', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.json', + method: 'GET', + headers: { some_header: 'some_value' }, + stateNames: ['checkBusinessScore'], + request: { + transform: { + transformer: 'jq', + mapping: '{data: .entity.id}', + }, + }, + }; + + describe('when webhook plugin is valid', () => { + it('does not throw validation exception', async () => { + expect(() => validate(webhookPlugin)).not.toThrow(); + }); + }); + + describe('when webhook has response - throws invalid', () => { + let failingWebhookPlugin = structuredClone(webhookPlugin); + failingWebhookPlugin.response = { transform: { transformer: 'jq', mapping: '{result: .}' } }; + + it('does not throw validation exception', async () => { + expect(() => validate(failingWebhookPlugin)).toThrowError(ZodError); + }); + }); + + describe('when webhook no request - throws invalid', () => { + let failingWebhookPlugin = structuredClone(webhookPlugin); + failingWebhookPlugin.request = undefined; + + it('does not throw validation exception', async () => { + expect(() => validate(failingWebhookPlugin)).toThrowError(ZodError); + }); + }); + }); +}); diff --git a/packages/common/src/validations/workflow-validators/webhook-plugin.validator.ts b/packages/common/src/validations/workflow-validators/webhook-plugin.validator.ts new file mode 100644 index 0000000000..22e9294015 --- /dev/null +++ b/packages/common/src/validations/workflow-validators/webhook-plugin.validator.ts @@ -0,0 +1,15 @@ +import { ApiPluginSchema } from './api-plugin.validator'; +import { AnyRecord } from '@/types'; + +export const WebhookPluginSchema = ApiPluginSchema.pick({ + name: true, + url: true, + method: true, + headers: true, + stateNames: true, + request: true, +}).strict(); + +export const validate = (webhookPlugin: AnyRecord) => { + return WebhookPluginSchema.parse(webhookPlugin); +}; diff --git a/packages/common/src/validations/workflow-validators/workflow-definition-validator.test.ts b/packages/common/src/validations/workflow-validators/workflow-definition-validator.test.ts new file mode 100644 index 0000000000..87d59a7be3 --- /dev/null +++ b/packages/common/src/validations/workflow-validators/workflow-definition-validator.test.ts @@ -0,0 +1,93 @@ +import { describe, expect, it } from 'vitest'; +import { validateWorkflowDefinition } from './workflow-definition-validator'; +describe('WorkflowDefinitionValidator', () => { + describe('validate Api Plugin', () => { + const definition = { + id: 'kyb_example_v1', + predictableActionArguments: true, + context: { + documents: [], + }, + initial: 'initial', + states: { + initial: { + on: { + CHECK_BUSINESS_SCORE: { + target: 'checkBusinessScore', + }, + }, + }, + checkBusinessScore: { + on: { + API_CALL_SUCCESS: 'checkBusinessScoreSuccess', + API_CALL_FAILURE: 'testManually', + }, + }, + checkBusinessScoreSuccess: { + type: 'final', + }, + testManually: { + type: 'final', + }, + }, + }; + + const apiPluginsSchemas = [ + { + name: 'ballerineEnrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.json', + method: 'GET', + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + request: { + transform: { + transformer: 'jq', + mapping: '{data: .entity.id}', + }, + }, + response: { + transform: { transformer: 'jq', mapping: '{result: .}' }, + }, + }, + { + name: 'ballerineEnrichmentHook', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.json', + method: 'GET', + headers: { some_header: 'some_value' }, + stateNames: ['checkBusinessScore'], + request: { + transform: { + transformer: 'jq', + mapping: '{data: .entity.id}', + }, + }, + }, + ]; + + const workflowDefinition = { + ...definition, + ...{ extensions: { apiPlugins: apiPluginsSchemas } }, + }; + + describe('when api plugin is valid', () => { + it('returns valid response', async () => { + const validationResponse = validateWorkflowDefinition(workflowDefinition); + + expect(validationResponse).toEqual({ isValid: true, error: undefined }); + }); + }); + + describe('when api plugin is invalid', () => { + it('it returns invalid response', async () => { + workflowDefinition.extensions.apiPlugins[0].request = 'dwadwad'; + const validationResponse = validateWorkflowDefinition(workflowDefinition); + + expect(validationResponse).toEqual({ + isValid: false, + error: 'extensions.apiPlugins.0: Invalid input', + }); + }); + }); + }); +}); diff --git a/packages/common/src/validations/workflow-validators/workflow-definition-validator.ts b/packages/common/src/validations/workflow-validators/workflow-definition-validator.ts new file mode 100644 index 0000000000..415552e836 --- /dev/null +++ b/packages/common/src/validations/workflow-validators/workflow-definition-validator.ts @@ -0,0 +1,32 @@ +import { z } from 'zod'; +import { ApiPluginSchema } from './api-plugin.validator'; +import { WebhookPluginSchema } from './webhook-plugin.validator'; +import { AnyRecord } from '@/types'; +import { zodErrorToReadable } from '../../utils/zod-error-to-readable'; + +const ApiOrWebhookSchema = z.union([ApiPluginSchema, WebhookPluginSchema]); + +const WorkflowDefinitionSchema = z + .object({ + extensions: z + .object({ + statePlugins: z.array(z.record(z.any())).optional(), + apiPlugins: z.array(ApiOrWebhookSchema).optional(), + }) + .optional(), + }) + .optional(); +export const validateWorkflowDefinition = (workflowDefinition: AnyRecord) => { + const parseResult = WorkflowDefinitionSchema.safeParse(workflowDefinition); + + if (!parseResult.success) { + return { isValid: false, error: zodErrorToReadable(parseResult.error) }; + } + return { isValid: true, error: undefined }; +}; + +export const validWorkflowDefinitionOrThrow = (workflowDefinition: AnyRecord) => { + const parseResult = WorkflowDefinitionSchema.parse(workflowDefinition); + + return parseResult; +}; diff --git a/packages/config/eslintrc.base.cjs b/packages/config/eslintrc.base.cjs index e503353380..6c9eac541e 100644 --- a/packages/config/eslintrc.base.cjs +++ b/packages/config/eslintrc.base.cjs @@ -13,5 +13,10 @@ module.exports = { env: { es6: true, }, - plugins: ['@typescript-eslint'], + plugins: ['@typescript-eslint', 'unused-imports'], + rules: { + 'no-unused-vars': 'off', // We use the unused-imports plugin instead + 'unused-imports/no-unused-imports': 'error', + 'unused-imports/no-unused-vars': ['warn', { vars: 'all', args: 'after-used' }], + }, }; diff --git a/packages/rules-engine/package.json b/packages/rules-engine/package.json index 89ebf5e430..1d9c43a934 100644 --- a/packages/rules-engine/package.json +++ b/packages/rules-engine/package.json @@ -16,7 +16,8 @@ "scripts": { "build": "rollup --config rollup.config.js", "watch": "concurrently --kill-others \"rollup --config rollup.config.js -w\" \"tsc -b --watch\"", - "test": "vitest run" + "test": "vitest run", + "test:unit": "vitest run" }, "engines": { "node": ">=12" @@ -46,6 +47,7 @@ "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-functional": "^3.0.2", "eslint-plugin-import": "^2.22.0", + "eslint-plugin-unused-imports": "^2.0.0", "fs-extra": "^11.1.0", "prettier": "^2.1.1", "rollup": "2.70.2", diff --git a/packages/rules-engine/src/lib/rule-engine.ts b/packages/rules-engine/src/lib/rule-engine.ts index f7d077fb4f..6ee107c601 100644 --- a/packages/rules-engine/src/lib/rule-engine.ts +++ b/packages/rules-engine/src/lib/rule-engine.ts @@ -1,6 +1,5 @@ import * as jsonLogic from 'json-logic-js'; - export interface RuleEngineOptions { Provider: 'json-logic' | 'json-rule-engine'; } @@ -12,21 +11,19 @@ export interface RuleEngine { } export interface LogicRule { - evaluate: (data: any) => boolean + evaluate: (data: any) => boolean; } export class JsonLogicRule { - #__rule: any; - constructor(rule: any) { - this.#__rule = rule - } - - evaluate(data: any) { - return jsonLogic.apply( - this.#__rule, // Rule - data // Data - ); - } + #__rule: any; + constructor(rule: any) { + this.#__rule = rule; + } + + evaluate(data: any) { + return jsonLogic.apply( + this.#__rule, // Rule + data, // Data + ); } - - \ No newline at end of file +} diff --git a/packages/ui/.eslintrc.cjs b/packages/ui/.eslintrc.cjs new file mode 100644 index 0000000000..b177750a4f --- /dev/null +++ b/packages/ui/.eslintrc.cjs @@ -0,0 +1,47 @@ +const config = require('../../packages/config/eslintrc.base.cjs'); + +/* eslint-env node */ + +module.exports = { + ...config, + env: { + ...config.env, + browser: true, + }, + parserOptions: { + ...config.parserOptions, + project: './tsconfig.eslint.json', + tsconfigRootDir: __dirname, + }, + plugins: ['tailwindcss', ...(config.plugins ?? [])], + extends: [ + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', + 'plugin:tailwindcss/recommended', + ...(config.extends ?? []), + ], + rules: { + ...config.rules, + '@typescript-eslint/ban-ts-comment': 'warn', + 'comma-dangle': 'off', + 'multiline-ternary': 'off', + 'no-use-before-define': 'off', + 'space-before-function-paren': 'off', + 'react/prop-types': 'off', + 'react/no-unescaped-entities': 'off', + 'react/display-name': 'off', + 'react/react-in-jsx-scope': 'off', + 'tailwindcss/classnames-order': 'off', + 'tailwindcss/no-custom-classname': 'off', + }, + settings: { + ...config.settings, + tailwindcss: { + callees: ['ctw'], + }, + react: { + version: 'detect', + }, + }, + ignorePatterns: ['.eslintrc.cjs', ...(config.ignorePatterns ?? [])], +}; diff --git a/packages/ui/.gitignore b/packages/ui/.gitignore new file mode 100644 index 0000000000..6b959615a8 --- /dev/null +++ b/packages/ui/.gitignore @@ -0,0 +1,25 @@ +# 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? +dist diff --git a/packages/ui/.storybook/main.ts b/packages/ui/.storybook/main.ts new file mode 100644 index 0000000000..3ab0a221f9 --- /dev/null +++ b/packages/ui/.storybook/main.ts @@ -0,0 +1,21 @@ +import type { StorybookConfig } from '@storybook/react-vite'; +const config: StorybookConfig = { + core: { + builder: '@storybook/builder-vite', // π The builder enabled here. + }, + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-interactions', + '@storybook/addon-styling', + ], + framework: { + name: '@storybook/react-vite', + options: {}, + }, + docs: { + autodocs: 'tag', + }, +}; +export default config; diff --git a/packages/ui/.storybook/preview.ts b/packages/ui/.storybook/preview.ts new file mode 100644 index 0000000000..731f7e0754 --- /dev/null +++ b/packages/ui/.storybook/preview.ts @@ -0,0 +1,16 @@ +import type { Preview } from '@storybook/react'; +import '../src/global.css'; + +const preview: Preview = { + parameters: { + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + }, +}; + +export default preview; diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md new file mode 100644 index 0000000000..54344390d3 --- /dev/null +++ b/packages/ui/CHANGELOG.md @@ -0,0 +1,13 @@ +# @ballerine/ui + +## 0.1.0 + +### Minor Changes + +- Update UI components + +## 0.0.2 + +### Patch Changes + +- First release diff --git a/packages/ui/index.html b/packages/ui/index.html new file mode 100644 index 0000000000..b8967f12ad --- /dev/null +++ b/packages/ui/index.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <link rel="icon" type="image/svg+xml" href="/vite.svg" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Vite + React + TS</title> + </head> + <script> + window.global = window; + </script> + <body> + <div id="root"></div> + <script type="module" src="/src/main.tsx"></script> + </body> +</html> diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 0000000000..2b44d7414f --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,75 @@ +{ + "name": "@ballerine/ui", + "private": false, + "version": "0.1.0", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "./components/*": "./dist/components/*", + "./dist/style.css": "./dist/style.css" + }, + "scripts": { + "dev": "vite build --watch", + "build": "tsc && vite build", + "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build", + "test": "vitest run" + }, + "dependencies": { + "@radix-ui/react-dialog": "^1.0.2", + "@radix-ui/react-dropdown-menu": "^2.0.5", + "@radix-ui/react-label": "^2.0.1", + "@radix-ui/react-scroll-area": "^1.0.2", + "@radix-ui/react-slot": "^1.0.1", + "@tanstack/react-table": "^8.9.2", + "class-variance-authority": "^0.6.0", + "clsx": "^1.2.1", + "dayjs": "^1.11.6", + "lodash": "^4.17.21", + "lucide-react": "^0.144.0", + "react-json-view": "^1.21.3", + "tailwind-merge": "^1.10.0" + }, + "devDependencies": { + "@storybook/addon-essentials": "^7.0.26", + "@storybook/addon-interactions": "^7.0.26", + "@storybook/addon-links": "^7.0.26", + "@storybook/addon-styling": "^1.3.2", + "@storybook/blocks": "^7.0.26", + "@storybook/builder-vite": "^7.0.26", + "@storybook/react": "^7.0.26", + "@storybook/react-vite": "^7.0.26", + "@storybook/testing-library": "^0.0.14-next.2", + "@tailwindcss/line-clamp": "^0.4.4", + "@types/lodash": "^4.14.191", + "@types/node": "^20.4.1", + "@types/react": "^18.0.14", + "@types/react-dom": "^18.0.5", + "@typescript-eslint/eslint-plugin": "^5.61.0", + "@typescript-eslint/parser": "^5.61.0", + "@vitejs/plugin-react": "^4.0.1", + "autoprefixer": "^10.4.14", + "eslint": "^8.44.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.1", + "eslint-plugin-storybook": "^0.6.6", + "fast-glob": "^3.3.0", + "prop-types": "^15.8.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "storybook": "^7.0.26", + "tailwindcss": "^3.3.2", + "tailwindcss-animate": "^1.0.5", + "typescript": "^4.9.5", + "vite": "^4.4.0", + "vite-plugin-dts": "^1.6.6", + "vitest": "^0.33.0" + } +} diff --git a/packages/ui/postcss.config.cjs b/packages/ui/postcss.config.cjs new file mode 100644 index 0000000000..12a703d900 --- /dev/null +++ b/packages/ui/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/ui/public/vite.svg b/packages/ui/public/vite.svg new file mode 100644 index 0000000000..e7b8dfb1b2 --- /dev/null +++ b/packages/ui/public/vite.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg> \ No newline at end of file diff --git a/packages/ui/src/common/enums/index.ts b/packages/ui/src/common/enums/index.ts new file mode 100644 index 0000000000..f7ae3a95e0 --- /dev/null +++ b/packages/ui/src/common/enums/index.ts @@ -0,0 +1 @@ +export * from './workflow-health-status'; diff --git a/packages/ui/src/common/enums/workflow-health-status.ts b/packages/ui/src/common/enums/workflow-health-status.ts new file mode 100644 index 0000000000..ec5895960e --- /dev/null +++ b/packages/ui/src/common/enums/workflow-health-status.ts @@ -0,0 +1,8 @@ +export const WorkflowHealthStatus = { + healthy: 'healthy', + failed: 'failed', + pending: 'pending', + 'pending-longterm': 'pending-longterm', +} as const; + +export type IWorkflowHealthStatus = keyof typeof WorkflowHealthStatus; diff --git a/packages/ui/src/common/index.ts b/packages/ui/src/common/index.ts new file mode 100644 index 0000000000..14790857ae --- /dev/null +++ b/packages/ui/src/common/index.ts @@ -0,0 +1 @@ +export * from './enums'; diff --git a/packages/ui/src/components.ts b/packages/ui/src/components.ts new file mode 100644 index 0000000000..4b02de003d --- /dev/null +++ b/packages/ui/src/components.ts @@ -0,0 +1,15 @@ +import './global.css'; + +// Atoms start +export * from '@components/atoms/Button'; +export * from '@components/atoms/Dialog'; +export * from '@components/atoms/Dropdown'; +export * from '@components/atoms/HealthIndicator'; +export * from '@components/atoms/Input'; +export * from '@components/atoms/Label'; +export * from '@components/atoms/Table'; +// Atoms end + +// Organisms start +export * from './components/organisms/WorkflowsTable'; +// Organims end diff --git a/packages/ui/src/components/atoms/Button/Button.stories.tsx b/packages/ui/src/components/atoms/Button/Button.stories.tsx new file mode 100644 index 0000000000..6d7f5aa64a --- /dev/null +++ b/packages/ui/src/components/atoms/Button/Button.stories.tsx @@ -0,0 +1,9 @@ +import { Button } from '@components/atoms/Button/Button'; + +export default { + component: Button, +}; + +export const Default = { + render: () => <Button>Click me</Button>, +}; diff --git a/packages/ui/src/components/atoms/Button/Button.tsx b/packages/ui/src/components/atoms/Button/Button.tsx new file mode 100644 index 0000000000..b17d7c6795 --- /dev/null +++ b/packages/ui/src/components/atoms/Button/Button.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { cva, type VariantProps } from 'class-variance-authority'; +import { ctw } from '@utils/ctw'; + +const buttonVariants = cva( + 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90', + destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90', + outline: + 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground', + secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'text-primary underline-offset-4 hover:underline', + }, + size: { + default: 'h-9 px-4 py-2', + sm: 'h-8 rounded-md px-3 text-xs', + lg: 'h-10 rounded-md px-8', + icon: 'h-9 w-9', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes<HTMLButtonElement>, + VariantProps<typeof buttonVariants> { + asChild?: boolean; +} + +const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : 'button'; + return ( + <Comp className={ctw(buttonVariants({ variant, size, className }))} ref={ref} {...props} /> + ); + }, +); +Button.displayName = 'Button'; + +export { Button, buttonVariants }; diff --git a/packages/ui/src/components/atoms/Button/index.ts b/packages/ui/src/components/atoms/Button/index.ts new file mode 100644 index 0000000000..8b166a86e4 --- /dev/null +++ b/packages/ui/src/components/atoms/Button/index.ts @@ -0,0 +1 @@ +export * from './Button'; diff --git a/packages/ui/src/components/atoms/Dialog/Dialog.stories.tsx b/packages/ui/src/components/atoms/Dialog/Dialog.stories.tsx new file mode 100644 index 0000000000..a2f43a2db7 --- /dev/null +++ b/packages/ui/src/components/atoms/Dialog/Dialog.stories.tsx @@ -0,0 +1,13 @@ +import { Dialog, DialogContent } from '@components/atoms/Dialog/Dialog'; + +export default { + component: Dialog, +}; + +export const Default = { + render: () => ( + <Dialog open> + <DialogContent>Hello World</DialogContent> + </Dialog> + ), +}; diff --git a/packages/ui/src/components/atoms/Dialog/Dialog.tsx b/packages/ui/src/components/atoms/Dialog/Dialog.tsx new file mode 100644 index 0000000000..70a8da1326 --- /dev/null +++ b/packages/ui/src/components/atoms/Dialog/Dialog.tsx @@ -0,0 +1,108 @@ +import * as React from 'react'; +import * as DialogPrimitive from '@radix-ui/react-dialog'; +import { X } from 'lucide-react'; +import { ctw } from '@utils/ctw'; + +const Dialog = DialogPrimitive.Root; + +const DialogTrigger = DialogPrimitive.Trigger; + +const DialogPortal = ({ className, children, ...props }: DialogPrimitive.DialogPortalProps) => ( + <DialogPrimitive.Portal className={ctw(className)} {...props}> + <div className="fixed inset-0 z-50 flex items-start justify-center sm:items-center"> + {children} + </div> + </DialogPrimitive.Portal> +); +DialogPortal.displayName = DialogPrimitive.Portal.displayName; + +const DialogOverlay = React.forwardRef< + React.ElementRef<typeof DialogPrimitive.Overlay>, + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> +>(({ className, ...props }, ref) => ( + <DialogPrimitive.Overlay + ref={ref} + className={ctw( + 'bg-background/80 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in fixed inset-0 z-50 backdrop-blur-sm transition-all duration-100', + className, + )} + {...props} + /> +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; + +const DialogContent = React.forwardRef< + React.ElementRef<typeof DialogPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> +>(({ className, children, ...props }, ref) => ( + <DialogPortal> + <DialogOverlay /> + <DialogPrimitive.Content + ref={ref} + className={ctw( + 'bg-background animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0 fixed z-50 grid w-full gap-4 rounded-b-lg border p-6 shadow-lg sm:max-w-lg sm:rounded-lg', + className, + )} + {...props} + > + {children} + <DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none"> + <X className="h-4 w-4" /> + <span className="sr-only">Close</span> + </DialogPrimitive.Close> + </DialogPrimitive.Content> + </DialogPortal> +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; + +const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => ( + <div + className={ctw('flex flex-col space-y-1.5 text-center sm:text-left', className)} + {...props} + /> +); +DialogHeader.displayName = 'DialogHeader'; + +const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => ( + <div + className={ctw('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)} + {...props} + /> +); +DialogFooter.displayName = 'DialogFooter'; + +const DialogTitle = React.forwardRef< + React.ElementRef<typeof DialogPrimitive.Title>, + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> +>(({ className, ...props }, ref) => ( + <DialogPrimitive.Title + ref={ref} + className={ctw('text-lg font-semibold leading-none tracking-tight', className)} + {...props} + /> +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; + +const DialogDescription = React.forwardRef< + React.ElementRef<typeof DialogPrimitive.Description>, + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> +>(({ className, ...props }, ref) => ( + <DialogPrimitive.Description + ref={ref} + className={ctw('text-muted-foreground text-sm', className)} + {...props} + /> +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; + +export { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +}; + +export type DialogProps = DialogPrimitive.DialogProps; diff --git a/packages/ui/src/components/atoms/Dialog/index.ts b/packages/ui/src/components/atoms/Dialog/index.ts new file mode 100644 index 0000000000..a5d3159726 --- /dev/null +++ b/packages/ui/src/components/atoms/Dialog/index.ts @@ -0,0 +1 @@ +export * from './Dialog'; diff --git a/packages/ui/src/components/atoms/Dropdown/Dropdown.tsx b/packages/ui/src/components/atoms/Dropdown/Dropdown.tsx new file mode 100644 index 0000000000..09b254184b --- /dev/null +++ b/packages/ui/src/components/atoms/Dropdown/Dropdown.tsx @@ -0,0 +1,184 @@ +import * as React from 'react'; +import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; +import { Check, ChevronRight, Circle } from 'lucide-react'; +import { ctw } from '@utils/ctw'; + +const DropdownMenu = DropdownMenuPrimitive.Root; + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; + +const DropdownMenuGroup = DropdownMenuPrimitive.Group; + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal; + +const DropdownMenuSub = DropdownMenuPrimitive.Sub; + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { + inset?: boolean; + } +>(({ className, inset, children, ...props }, ref) => ( + <DropdownMenuPrimitive.SubTrigger + ref={ref} + className={ctw( + 'focus:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none', + inset && 'pl-8', + className, + )} + {...props} + > + {children} + <ChevronRight className="ml-auto h-4 w-4" /> + </DropdownMenuPrimitive.SubTrigger> +)); +DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName; + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.SubContent>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> +>(({ className, ...props }, ref) => ( + <DropdownMenuPrimitive.SubContent + ref={ref} + className={ctw( + 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg', + className, + )} + {...props} + /> +)); +DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName; + +const DropdownMenuContent = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> +>(({ className, sideOffset = 4, ...props }, ref) => ( + <DropdownMenuPrimitive.Portal> + <DropdownMenuPrimitive.Content + ref={ref} + sideOffset={sideOffset} + className={ctw( + 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border bg-white p-1 shadow-md', + className, + )} + {...props} + /> + </DropdownMenuPrimitive.Portal> +)); +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; + +const DropdownMenuItem = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Item>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + <DropdownMenuPrimitive.Item + ref={ref} + className={ctw( + 'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50', + inset && 'pl-8', + className, + )} + {...props} + /> +)); +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> +>(({ className, children, checked, ...props }, ref) => ( + <DropdownMenuPrimitive.CheckboxItem + ref={ref} + className={ctw( + 'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50', + className, + )} + checked={checked} + {...props} + > + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> + <DropdownMenuPrimitive.ItemIndicator> + <Check className="h-4 w-4" /> + </DropdownMenuPrimitive.ItemIndicator> + </span> + {children} + </DropdownMenuPrimitive.CheckboxItem> +)); +DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName; + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> +>(({ className, children, ...props }, ref) => ( + <DropdownMenuPrimitive.RadioItem + ref={ref} + className={ctw( + 'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50', + className, + )} + {...props} + > + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> + <DropdownMenuPrimitive.ItemIndicator> + <Circle className="h-2 w-2 fill-current" /> + </DropdownMenuPrimitive.ItemIndicator> + </span> + {children} + </DropdownMenuPrimitive.RadioItem> +)); +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Label>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + <DropdownMenuPrimitive.Label + ref={ref} + className={ctw('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)} + {...props} + /> +)); +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Separator>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> +>(({ className, ...props }, ref) => ( + <DropdownMenuPrimitive.Separator + ref={ref} + className={ctw('bg-muted -mx-1 my-1 h-px', className)} + {...props} + /> +)); +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; + +const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => { + return ( + <span className={ctw('ml-auto text-xs tracking-widest opacity-60', className)} {...props} /> + ); +}; +DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'; + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +}; diff --git a/packages/ui/src/components/atoms/Dropdown/index.ts b/packages/ui/src/components/atoms/Dropdown/index.ts new file mode 100644 index 0000000000..2f29bad4e6 --- /dev/null +++ b/packages/ui/src/components/atoms/Dropdown/index.ts @@ -0,0 +1 @@ +export * from './Dropdown'; diff --git a/packages/ui/src/components/atoms/HealthIndicator/HealthIndicator.stories.tsx b/packages/ui/src/components/atoms/HealthIndicator/HealthIndicator.stories.tsx new file mode 100644 index 0000000000..54b2f86791 --- /dev/null +++ b/packages/ui/src/components/atoms/HealthIndicator/HealthIndicator.stories.tsx @@ -0,0 +1,22 @@ +import { WorkflowHealthStatus } from '@common/enums'; +import { HealthIndicator } from '@components/atoms/HealthIndicator/HealthIndicator'; + +export default { + component: HealthIndicator, +}; + +export const Healthy = { + render: () => <HealthIndicator healthStatus={WorkflowHealthStatus.healthy} />, +}; + +export const Failed = { + render: () => <HealthIndicator healthStatus={WorkflowHealthStatus.failed} />, +}; + +export const Pending = { + render: () => <HealthIndicator healthStatus={WorkflowHealthStatus.pending} />, +}; + +export const PendingLongterm = { + render: () => <HealthIndicator healthStatus={WorkflowHealthStatus['pending-longterm']} />, +}; diff --git a/packages/ui/src/components/atoms/HealthIndicator/HealthIndicator.tsx b/packages/ui/src/components/atoms/HealthIndicator/HealthIndicator.tsx new file mode 100644 index 0000000000..12a3470484 --- /dev/null +++ b/packages/ui/src/components/atoms/HealthIndicator/HealthIndicator.tsx @@ -0,0 +1,21 @@ +import { IWorkflowHealthStatus, WorkflowHealthStatus } from '@common/enums'; +import { ctw } from '@utils/ctw'; + +interface Props { + healthStatus: IWorkflowHealthStatus; + size?: number; +} + +export const HealthIndicator = ({ healthStatus, size = 20 }: Props) => { + return ( + <span + style={{ width: `${size}px`, height: `${size}px` }} + className={ctw('block', 'rounded-full', { + ['bg-green-400']: healthStatus === WorkflowHealthStatus.healthy, + ['bg-red-400']: healthStatus === WorkflowHealthStatus.failed, + ['bg-yellow-400']: healthStatus === WorkflowHealthStatus.pending, + ['bg-orange-400']: healthStatus === WorkflowHealthStatus['pending-longterm'], + })} + ></span> + ); +}; diff --git a/packages/ui/src/components/atoms/HealthIndicator/index.ts b/packages/ui/src/components/atoms/HealthIndicator/index.ts new file mode 100644 index 0000000000..0bd6afe835 --- /dev/null +++ b/packages/ui/src/components/atoms/HealthIndicator/index.ts @@ -0,0 +1 @@ +export * from './HealthIndicator'; diff --git a/packages/ui/src/components/atoms/Input/Input.stories.tsx b/packages/ui/src/components/atoms/Input/Input.stories.tsx new file mode 100644 index 0000000000..6d85d67272 --- /dev/null +++ b/packages/ui/src/components/atoms/Input/Input.stories.tsx @@ -0,0 +1,9 @@ +import { Input } from '@components/atoms/Input/Input'; + +export default { + component: Input, +}; + +export const Default = { + render: () => <Input />, +}; diff --git a/packages/ui/src/components/atoms/Input/Input.tsx b/packages/ui/src/components/atoms/Input/Input.tsx new file mode 100644 index 0000000000..f248cd2df6 --- /dev/null +++ b/packages/ui/src/components/atoms/Input/Input.tsx @@ -0,0 +1,21 @@ +import { ctw } from '@utils/ctw'; +import * as React from 'react'; + +const Input = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>( + ({ className, type, ...props }, ref) => { + return ( + <input + type={type} + className={ctw( + 'border-input bg-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50', + className, + )} + ref={ref} + {...props} + /> + ); + }, +); +Input.displayName = 'Input'; + +export { Input }; diff --git a/packages/ui/src/components/atoms/Input/index.ts b/packages/ui/src/components/atoms/Input/index.ts new file mode 100644 index 0000000000..ba9fe7ebc6 --- /dev/null +++ b/packages/ui/src/components/atoms/Input/index.ts @@ -0,0 +1 @@ +export * from './Input'; diff --git a/packages/ui/src/components/atoms/Label/Label.stories.tsx b/packages/ui/src/components/atoms/Label/Label.stories.tsx new file mode 100644 index 0000000000..3fca48aadb --- /dev/null +++ b/packages/ui/src/components/atoms/Label/Label.stories.tsx @@ -0,0 +1,9 @@ +import { Label } from '@components/atoms/Label/Label'; + +export default { + component: Label, +}; + +export const Default = { + render: () => <Label>Hello World</Label>, +}; diff --git a/packages/ui/src/components/atoms/Label/Label.tsx b/packages/ui/src/components/atoms/Label/Label.tsx new file mode 100644 index 0000000000..3ac317c9a1 --- /dev/null +++ b/packages/ui/src/components/atoms/Label/Label.tsx @@ -0,0 +1,19 @@ +'use client'; + +import * as React from 'react'; +import * as LabelPrimitive from '@radix-ui/react-label'; +import { cva, type VariantProps } from 'class-variance-authority'; +import { ctw } from '@utils/ctw'; + +const labelVariants = cva( + 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', +); + +const Label = React.forwardRef< + React.ElementRef<typeof LabelPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants> +>(({ className, ...props }, ref) => ( + <LabelPrimitive.Root ref={ref} className={ctw(labelVariants(), className)} {...props} /> +)); + +export { Label }; diff --git a/packages/ui/src/components/atoms/Label/index.ts b/packages/ui/src/components/atoms/Label/index.ts new file mode 100644 index 0000000000..ca58c61a25 --- /dev/null +++ b/packages/ui/src/components/atoms/Label/index.ts @@ -0,0 +1 @@ +export * from './Label'; diff --git a/packages/ui/src/components/atoms/ScrollArea/ScrollArea.tsx b/packages/ui/src/components/atoms/ScrollArea/ScrollArea.tsx new file mode 100644 index 0000000000..e35b630e6b --- /dev/null +++ b/packages/ui/src/components/atoms/ScrollArea/ScrollArea.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'; +import { ctw } from '@utils/ctw'; +import { ScrollBar } from '@components/atoms/ScrollArea'; + +interface Props extends ScrollAreaPrimitive.ScrollAreaProps { + orientation: 'vertical' | 'horizontal' | 'both'; +} + +export const ScrollArea = React.forwardRef< + React.ElementRef<React.FC<Props>>, + React.ComponentPropsWithoutRef<React.FC<Props>> +>(({ className, children, orientation, ...props }, ref) => ( + <ScrollAreaPrimitive.Root + ref={ref} + className={ctw('relative overflow-hidden', className)} + {...props} + > + <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]"> + {children} + </ScrollAreaPrimitive.Viewport> + <ScrollBar orientation="vertical" /> + <ScrollBar orientation="horizontal" /> + <ScrollAreaPrimitive.Corner /> + </ScrollAreaPrimitive.Root> +)); +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName; diff --git a/packages/ui/src/components/atoms/ScrollArea/Scrollbar.tsx b/packages/ui/src/components/atoms/ScrollArea/Scrollbar.tsx new file mode 100644 index 0000000000..d44f6fd028 --- /dev/null +++ b/packages/ui/src/components/atoms/ScrollArea/Scrollbar.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'; +import { ctw } from '@utils/ctw'; + +export const ScrollBar = React.forwardRef< + React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>, + React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar> +>(({ className, orientation = 'vertical', ...props }, ref) => ( + <ScrollAreaPrimitive.ScrollAreaScrollbar + ref={ref} + orientation={orientation} + className={ctw( + 'touch-none select-none transition-colors', + orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-[1px]', + orientation === 'horizontal' && 'flex h-2.5 border-t border-t-transparent p-[1px]', + className, + )} + {...props} + > + <ScrollAreaPrimitive.ScrollAreaThumb className="bg-border rounded-full" /> + </ScrollAreaPrimitive.ScrollAreaScrollbar> +)); +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName; diff --git a/packages/ui/src/components/atoms/ScrollArea/index.ts b/packages/ui/src/components/atoms/ScrollArea/index.ts new file mode 100644 index 0000000000..8332e9acd5 --- /dev/null +++ b/packages/ui/src/components/atoms/ScrollArea/index.ts @@ -0,0 +1,2 @@ +export * from './ScrollArea'; +export * from './Scrollbar'; diff --git a/packages/ui/src/components/atoms/Table/Table.tsx b/packages/ui/src/components/atoms/Table/Table.tsx new file mode 100644 index 0000000000..a11b3c2e8d --- /dev/null +++ b/packages/ui/src/components/atoms/Table/Table.tsx @@ -0,0 +1,9 @@ +import * as React from 'react'; +import { ctw } from '@utils/ctw'; + +export const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>( + ({ className, ...props }, ref) => ( + <table ref={ref} className={ctw('caption-bottom w-full text-sm', className)} {...props} /> + ), +); +Table.displayName = 'Table'; diff --git a/packages/ui/src/components/atoms/Table/TableBody.tsx b/packages/ui/src/components/atoms/Table/TableBody.tsx new file mode 100644 index 0000000000..7b7f19e58b --- /dev/null +++ b/packages/ui/src/components/atoms/Table/TableBody.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { ctw } from '@utils/ctw'; + +export const TableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes<HTMLTableSectionElement> +>(({ className, ...props }, ref) => ( + <tbody ref={ref} className={ctw('[&_tr:last-child]:border-0', className)} {...props} /> +)); +TableBody.displayName = 'TableBody'; diff --git a/packages/ui/src/components/atoms/Table/TableCaption.tsx b/packages/ui/src/components/atoms/Table/TableCaption.tsx new file mode 100644 index 0000000000..188d818fb6 --- /dev/null +++ b/packages/ui/src/components/atoms/Table/TableCaption.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { ctw } from '@utils/ctw'; + +export const TableCaption = React.forwardRef< + HTMLTableCaptionElement, + React.HTMLAttributes<HTMLTableCaptionElement> +>(({ className, ...props }, ref) => ( + <caption ref={ref} className={ctw('text-muted-foreground mt-4 text-sm', className)} {...props} /> +)); +TableCaption.displayName = 'TableCaption'; diff --git a/packages/ui/src/components/atoms/Table/TableCell.tsx b/packages/ui/src/components/atoms/Table/TableCell.tsx new file mode 100644 index 0000000000..47585b645a --- /dev/null +++ b/packages/ui/src/components/atoms/Table/TableCell.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { ctw } from '@utils/ctw'; + +export const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes<HTMLTableCellElement> +>(({ className, ...props }, ref) => ( + <td + ref={ref} + className={ctw('font-inter p-4 align-middle [&:has([role=checkbox])]:pr-0', className)} + {...props} + /> +)); +TableCell.displayName = 'TableCell'; diff --git a/packages/ui/src/components/atoms/Table/TableFooter.tsx b/packages/ui/src/components/atoms/Table/TableFooter.tsx new file mode 100644 index 0000000000..749490d8af --- /dev/null +++ b/packages/ui/src/components/atoms/Table/TableFooter.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { ctw } from '@utils/ctw'; + +export const TableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes<HTMLTableSectionElement> +>(({ className, ...props }, ref) => ( + <tfoot + ref={ref} + className={ctw('bg-primary text-primary-foreground font-medium', className)} + {...props} + /> +)); +TableFooter.displayName = 'TableFooter'; diff --git a/packages/ui/src/components/atoms/Table/TableHead.tsx b/packages/ui/src/components/atoms/Table/TableHead.tsx new file mode 100644 index 0000000000..6644a9d498 --- /dev/null +++ b/packages/ui/src/components/atoms/Table/TableHead.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { ctw } from '@utils/ctw'; + +export const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes<HTMLTableCellElement> +>(({ className, ...props }, ref) => ( + <th + ref={ref} + className={ctw( + 'font-inter text-muted-foreground h-12 px-4 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0', + className, + )} + {...props} + /> +)); +TableHead.displayName = 'TableHead'; diff --git a/packages/ui/src/components/atoms/Table/TableHeader.tsx b/packages/ui/src/components/atoms/Table/TableHeader.tsx new file mode 100644 index 0000000000..f2f8ac87ff --- /dev/null +++ b/packages/ui/src/components/atoms/Table/TableHeader.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { ctw } from '@utils/ctw'; + +export const TableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes<HTMLTableSectionElement> +>(({ className, ...props }, ref) => ( + <thead ref={ref} className={ctw('[&_tr]:border-b', className)} {...props} /> +)); +TableHeader.displayName = 'TableHeader'; diff --git a/packages/ui/src/components/atoms/Table/TableRow.tsx b/packages/ui/src/components/atoms/Table/TableRow.tsx new file mode 100644 index 0000000000..04ace97475 --- /dev/null +++ b/packages/ui/src/components/atoms/Table/TableRow.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { ctw } from '@utils/ctw'; + +export const TableRow = React.forwardRef< + HTMLTableRowElement, + React.HTMLAttributes<HTMLTableRowElement> +>(({ className, ...props }, ref) => ( + <tr + ref={ref} + className={ctw( + 'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors', + className, + )} + {...props} + /> +)); +TableRow.displayName = 'TableRow'; diff --git a/packages/ui/src/components/atoms/Table/index.ts b/packages/ui/src/components/atoms/Table/index.ts new file mode 100644 index 0000000000..7a860f8a35 --- /dev/null +++ b/packages/ui/src/components/atoms/Table/index.ts @@ -0,0 +1,8 @@ +export * from './Table'; +export * from './TableBody'; +export * from './TableRow'; +export * from './TableCell'; +export * from './TableFooter'; +export * from './TableCaption'; +export * from './TableHead'; +export * from './TableHeader'; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/WorkflowsTable.stories.tsx b/packages/ui/src/components/organisms/WorkflowsTable/WorkflowsTable.stories.tsx new file mode 100644 index 0000000000..ad15c45674 --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/WorkflowsTable.stories.tsx @@ -0,0 +1,69 @@ +import { WorkflowsTable } from './WorkflowsTable'; +import { WorkflowTableItem } from './types'; + +export default { + component: WorkflowsTable, +}; + +function createTableData(count: number): WorkflowTableItem[] { + return new Array(count).fill(null).map((_, index) => ({ + id: `element-id-${index}`, + workflowDefinitionName: `Workflow-Name-${index}`, + workflowDefinitionId: `Workflow-ID-${index}`, + status: 'active', + state: null, + assignee: null, + context: { + some_context: 'hello world', + }, + createdAt: new Date(), + updatedAt: new Date(), + resolvedAt: new Date(), + })); +} + +export const Default = { + render: () => <WorkflowsTable items={createTableData(10)} onSort={() => {}} />, +}; + +export const Rounded = { + render: () => ( + <WorkflowsTable.Container> + <WorkflowsTable items={createTableData(10)} onSort={() => {}} /> + </WorkflowsTable.Container> + ), +}; + +export const StickyHeader = { + render: () => ( + <div className="h-[500px]"> + <WorkflowsTable.Container> + <WorkflowsTable items={createTableData(10)} onSort={() => {}} /> + </WorkflowsTable.Container> + </div> + ), +}; + +export const FancyScrollbars = { + render: () => ( + <div className="h-[500px]"> + <WorkflowsTable.Container> + <WorkflowsTable.ScrollContainer> + <WorkflowsTable items={createTableData(10)} onSort={() => {}} /> + </WorkflowsTable.ScrollContainer> + </WorkflowsTable.Container> + </div> + ), +}; + +export const FetchingData = { + render: () => ( + <WorkflowsTable.Container isFetching> + <WorkflowsTable items={createTableData(10)} onSort={() => {}} /> + </WorkflowsTable.Container> + ), +}; + +export const Empty = { + render: () => <WorkflowsTable items={[]} onSort={() => {}} isFetching={false} />, +}; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/WorkflowsTable.tsx b/packages/ui/src/components/organisms/WorkflowsTable/WorkflowsTable.tsx new file mode 100644 index 0000000000..55210b5408 --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/WorkflowsTable.tsx @@ -0,0 +1,122 @@ +import { Table } from '@components/atoms/Table/Table'; +import { TableBody } from '@components/atoms/Table/TableBody'; +import { TableCell } from '@components/atoms/Table/TableCell'; +import { TableHead } from '@components/atoms/Table/TableHead'; +import { TableHeader } from '@components/atoms/Table/TableHeader'; +import { TableRow } from '@components/atoms/Table/TableRow'; +import { useMemo } from 'react'; +import { useReactTable, flexRender, getCoreRowModel } from '@tanstack/react-table'; +import { defaultColumns } from './columns'; +import { InputColumn, WorkflowsTableSorting, WorkflowTableColumnDef } from './types'; +import keyBy from 'lodash/keyBy'; +import { mergeColumns } from './utils/merge-columns'; +import { WorkflowTableItem } from './types'; +import { TableContainer } from './components/TableContainer'; +import { ScrollContainer } from './components/ScrollContainer'; + +interface Props { + items: WorkflowTableItem[]; + sorting?: WorkflowsTableSorting; + isFetching?: boolean; + columns?: InputColumn[]; + onSort: (key: string, direction: 'asc' | 'desc') => void; +} + +export function WorkflowsTable({ items, isFetching, sorting, columns, onSort }: Props) { + // merging column parameters if provided + const tableColumns = useMemo((): WorkflowTableColumnDef<WorkflowTableItem>[] => { + if (!Array.isArray(columns) || !columns.length) return defaultColumns; + + const columnsMap = keyBy(columns, 'id'); + + return defaultColumns.map(defaultColumn => { + const columnParams = columnsMap[defaultColumn.accessorKey]; + + if (!columnParams) return defaultColumn; + + return mergeColumns<WorkflowTableItem>(defaultColumn, columnParams); + }); + }, [columns]); + + const table = useReactTable({ + columns: tableColumns, + data: items, + enableColumnResizing: true, + manualSorting: false, + state: { + sorting: sorting + ? [ + { + id: sorting.key, + desc: sorting.direction === 'desc', + }, + ] + : [], + }, + onSortingChange: updater => { + if (typeof updater === 'function') { + const newSortingValue = updater(table.getState().sorting); + table.setSorting(newSortingValue); + } else { + const sortingState = updater; + onSort(sortingState[0].id, sortingState[0].desc ? 'desc' : 'asc'); + } + }, + getCoreRowModel: getCoreRowModel(), + }); + + const isEmpty = !items.length && !isFetching; + + return ( + <Table> + <TableHeader> + {table.getHeaderGroups().map(({ id: headerRowId, headers }) => { + return ( + <TableRow key={headerRowId}> + {headers.map(header => ( + <TableHead key={header.id} className="font-inter sticky top-0 w-1/4 bg-white"> + {flexRender(header.column.columnDef.header, header.getContext())} + </TableHead> + ))} + </TableRow> + ); + })} + </TableHeader> + <TableBody> + {isEmpty ? ( + <TableRow> + <TableCell colSpan={table.getAllColumns().length} className="font-inter text-center"> + Workflows not found. + </TableCell> + </TableRow> + ) : ( + table.getRowModel().rows.map(row => { + return ( + <TableRow key={row.id}> + {row.getVisibleCells().map(cell => { + return ( + <TableCell + key={cell.id} + className="max-w-1/4 w-1/4 whitespace-nowrap" + title={String(cell.getValue())} + style={{ + minWidth: `${cell.column.getSize()}px`, + }} + > + <div className="line-clamp-1 overflow-hidden text-ellipsis break-all"> + {flexRender(cell.column.columnDef.cell, cell.getContext())} + </div> + </TableCell> + ); + })} + </TableRow> + ); + }) + )} + </TableBody> + </Table> + ); +} + +WorkflowsTable.Container = TableContainer; +WorkflowsTable.ScrollContainer = ScrollContainer; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/columns.tsx b/packages/ui/src/components/organisms/WorkflowsTable/columns.tsx new file mode 100644 index 0000000000..3c79823565 --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/columns.tsx @@ -0,0 +1,69 @@ +import { WorkflowTableColumnDef, WorkflowTableItem } from './types'; +import { getWorkflowHealthStatus } from './utils/get-workflow-health-status'; +import { formatDate } from './utils/format-date'; +import { DataTableColumnHeader } from './components/DataTableColumnHeader'; +import { HealthIndicator } from '@components/atoms/HealthIndicator'; +import { JsonDialog } from './components/JsonDialog'; + +export const defaultColumns: WorkflowTableColumnDef<WorkflowTableItem>[] = [ + { + accessorKey: 'id', + cell: info => info.getValue<string>(), + header: () => 'ID', + }, + { + accessorKey: 'workflowDefinitionName', + cell: info => info.getValue<string>(), + header: ({ column }) => ( + <DataTableColumnHeader column={column} title="Workflow Definition Name" /> + ), + }, + { + accessorKey: 'status', + cell: info => ( + <div className="font-inter flex flex-row flex-nowrap gap-4 font-medium capitalize"> + <HealthIndicator healthStatus={getWorkflowHealthStatus(info.row.original)} /> + {info.getValue<string>() || ''} + </div> + ), + header: ({ column }) => <DataTableColumnHeader column={column} title="Status" />, + }, + { + accessorKey: 'state', + cell: info => info.getValue<string>(), + header: ({ column }) => <DataTableColumnHeader column={column} title="State" />, + }, + { + accessorKey: 'assignee', + accessorFn: row => (row.assignee ? `${row.assignee.firstName} ${row.assignee.lastName}` : '-'), + cell: info => info.getValue<string>(), + header: ({ column }) => <DataTableColumnHeader column={column} title="Assign To" />, + }, + { + accessorKey: 'context', + accessorFn: row => JSON.stringify(row.context), + cell: info => <JsonDialog context={info.getValue<string>()} />, + header: () => 'Context', + }, + { + accessorKey: 'view-workflow', + accessorFn: row => row.id, + cell: () => '-', + header: () => 'Workflow', + }, + { + accessorKey: 'resolvedAt', + cell: info => (info.getValue<Date>() ? formatDate(info.getValue<Date>()) : '-'), + header: ({ column }) => <DataTableColumnHeader column={column} title="Resolved At" />, + }, + { + accessorKey: 'createdBy', + cell: info => info.getValue<string>(), + header: ({ column }) => <DataTableColumnHeader column={column} title="Created By" />, + }, + { + accessorKey: 'createdAt', + cell: info => formatDate(info.getValue<Date>()), + header: ({ column }) => <DataTableColumnHeader column={column} title="Created At" />, + }, +]; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/components/DataTableColumnHeader/DataTableColumnHeader.tsx b/packages/ui/src/components/organisms/WorkflowsTable/components/DataTableColumnHeader/DataTableColumnHeader.tsx new file mode 100644 index 0000000000..385e33eaba --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/components/DataTableColumnHeader/DataTableColumnHeader.tsx @@ -0,0 +1,60 @@ +import { Column } from '@tanstack/react-table'; +import { ChevronsUpDown, EyeOff, SortAsc, SortDesc } from 'lucide-react'; +import { Button } from '@components/atoms/Button'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@components/atoms/Dropdown'; +import { ctw } from '@utils/ctw'; + +interface DataTableColumnHeaderProps<TData, TValue> extends React.HTMLAttributes<HTMLDivElement> { + column: Column<TData, TValue>; + title: string; +} + +export function DataTableColumnHeader<TData, TValue>({ + column, + title, + className, +}: DataTableColumnHeaderProps<TData, TValue>) { + if (!column.getCanSort()) { + return <div className={ctw(className)}>{title}</div>; + } + + return ( + <div className={ctw('flex items-center space-x-2 whitespace-nowrap', className)}> + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button variant="ghost" size="sm" className="data-[state=open]:bg-accent -ml-3 h-8"> + <span>{title}</span> + {column.getIsSorted() === 'desc' ? ( + <SortDesc className="ml-2 h-4 w-4" /> + ) : column.getIsSorted() === 'asc' ? ( + <SortAsc className="ml-2 h-4 w-4" /> + ) : ( + <ChevronsUpDown className="ml-2 h-4 w-4" /> + )} + </Button> + </DropdownMenuTrigger> + <DropdownMenuContent align="start"> + <DropdownMenuItem onClick={() => column.toggleSorting(false)}> + <SortAsc className="text-muted-foreground/70 mr-2 h-3.5 w-3.5" /> + Asc + </DropdownMenuItem> + <DropdownMenuItem onClick={() => column.toggleSorting(true)}> + <SortDesc className="text-muted-foreground/70 mr-2 h-3.5 w-3.5" /> + Desc + </DropdownMenuItem> + <DropdownMenuSeparator /> + <DropdownMenuItem onClick={() => column.toggleVisibility(false)}> + <EyeOff className="text-muted-foreground/70 mr-2 h-3.5 w-3.5" /> + Hide + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + </div> + ); +} diff --git a/packages/ui/src/components/organisms/WorkflowsTable/components/DataTableColumnHeader/index.ts b/packages/ui/src/components/organisms/WorkflowsTable/components/DataTableColumnHeader/index.ts new file mode 100644 index 0000000000..6aa469ad2b --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/components/DataTableColumnHeader/index.ts @@ -0,0 +1 @@ +export * from './DataTableColumnHeader'; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/components/JsonDialog/JsonDialog.tsx b/packages/ui/src/components/organisms/WorkflowsTable/components/JsonDialog/JsonDialog.tsx new file mode 100644 index 0000000000..8fb000dd2c --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/components/JsonDialog/JsonDialog.tsx @@ -0,0 +1,29 @@ +import { Button } from '@components/atoms/Button'; +import { Dialog, DialogContent, DialogTrigger } from '@components/atoms/Dialog'; +import { ScrollArea } from '@components/atoms/ScrollArea'; +import { CodeIcon } from 'lucide-react'; +import ReactJson from 'react-json-view'; + +interface Props { + context: string; +} + +export const JsonDialog = ({ context }: Props) => { + return ( + <Dialog> + <DialogTrigger asChild> + <Button className="flex items-center gap-2"> + <CodeIcon size="16" /> + View context + </Button> + </DialogTrigger> + <DialogContent className="h-[80vh] min-w-[80%] bg-white"> + <div className="pr-4"> + <ScrollArea orientation="both"> + <ReactJson src={context ? (JSON.parse(context) as object) : {}} /> + </ScrollArea> + </div> + </DialogContent> + </Dialog> + ); +}; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/components/JsonDialog/index.ts b/packages/ui/src/components/organisms/WorkflowsTable/components/JsonDialog/index.ts new file mode 100644 index 0000000000..6c7847cec8 --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/components/JsonDialog/index.ts @@ -0,0 +1 @@ +export * from './JsonDialog'; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/components/ScrollContainer/ScrollContainer.tsx b/packages/ui/src/components/organisms/WorkflowsTable/components/ScrollContainer/ScrollContainer.tsx new file mode 100644 index 0000000000..136fdf5d3a --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/components/ScrollContainer/ScrollContainer.tsx @@ -0,0 +1,17 @@ +// import Scrollbars from 'react-custom-scrollbars'; + +import { ScrollArea } from '@components/atoms/ScrollArea'; + +interface Props { + children: React.ReactNode; +} + +export function ScrollContainer({ children }: Props) { + return ( + <ScrollArea className="h-full" orientation="both"> + {children} + </ScrollArea> + ); +} + +ScrollContainer.displayName = 'ScrollContainer'; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/components/ScrollContainer/index.ts b/packages/ui/src/components/organisms/WorkflowsTable/components/ScrollContainer/index.ts new file mode 100644 index 0000000000..0d367b8b9d --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/components/ScrollContainer/index.ts @@ -0,0 +1 @@ +export * from './ScrollContainer'; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/components/TableContainer/TableContainer.tsx b/packages/ui/src/components/organisms/WorkflowsTable/components/TableContainer/TableContainer.tsx new file mode 100644 index 0000000000..30da825ec5 --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/components/TableContainer/TableContainer.tsx @@ -0,0 +1,21 @@ +import { ctw } from '@utils/ctw'; + +interface Props { + isFetching?: boolean; + children: React.ReactNode; +} + +export function TableContainer({ children, isFetching }: Props) { + return ( + <div + className={ctw('relative w-full overflow-auto bg-white', 'h-full rounded-md border', { + ['opacity-40']: isFetching, + ['pointer-events-none']: isFetching, + })} + > + {children} + </div> + ); +} + +TableContainer.displayName = 'TableContainer'; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/components/TableContainer/index.ts b/packages/ui/src/components/organisms/WorkflowsTable/components/TableContainer/index.ts new file mode 100644 index 0000000000..2ca68880b4 --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/components/TableContainer/index.ts @@ -0,0 +1 @@ +export * from './TableContainer'; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/index.ts b/packages/ui/src/components/organisms/WorkflowsTable/index.ts new file mode 100644 index 0000000000..0d82108883 --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/index.ts @@ -0,0 +1 @@ +export * from './WorkflowsTable'; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/types.ts b/packages/ui/src/components/organisms/WorkflowsTable/types.ts new file mode 100644 index 0000000000..412c8dc197 --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/types.ts @@ -0,0 +1,43 @@ +import { AccessorFnColumnDef, ColumnDef } from '@tanstack/react-table'; + +export interface WorkflowTableItem { + id: string; + workflowDefinitionName: string; + workflowDefinitionId: string; + status: string; + state: string | null; + assignee: { + firstName: string; + lastName: string; + } | null; + context: object; + createdAt: Date; + updatedAt: Date; + resolvedAt: Date | null; +} + +export interface WorkflowsTableSorting { + key: string; + direction: 'asc' | 'desc'; +} + +export type WorkflowTableColumnKeys = + | 'id' + | 'workflowDefinitionName' + | 'status' + | 'state' + | 'assignee' + | 'context' + | 'view-workflow' + | 'resolvedAt' + | 'createdBy' + | 'createdAt'; + +export type WorkflowTableColumnDef<TData> = Omit<ColumnDef<TData>, 'accessorKey'> & { + accessorFn?: AccessorFnColumnDef<TData>['accessorFn']; + accessorKey: WorkflowTableColumnKeys; +}; + +export type InputColumn<TData = WorkflowTableItem> = Partial<WorkflowTableColumnDef<TData>> & { + id: WorkflowTableColumnKeys; +}; diff --git a/packages/ui/src/components/organisms/WorkflowsTable/utils/calculate-hour-difference.ts b/packages/ui/src/components/organisms/WorkflowsTable/utils/calculate-hour-difference.ts new file mode 100644 index 0000000000..b979318701 --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/utils/calculate-hour-difference.ts @@ -0,0 +1,5 @@ +import dayjs from 'dayjs'; + +export function calculateHourDifference(dateA: Date, dateB: Date) { + return Math.abs(dayjs(dateB).diff(dateA, 'hour', false)); +} diff --git a/packages/ui/src/components/organisms/WorkflowsTable/utils/format-date.ts b/packages/ui/src/components/organisms/WorkflowsTable/utils/format-date.ts new file mode 100644 index 0000000000..b078ba7457 --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/utils/format-date.ts @@ -0,0 +1,3 @@ +export function formatDate(date: Date): string { + return new Date(date).toLocaleString(); +} diff --git a/packages/ui/src/components/organisms/WorkflowsTable/utils/get-workflow-health-status.ts b/packages/ui/src/components/organisms/WorkflowsTable/utils/get-workflow-health-status.ts new file mode 100644 index 0000000000..99b26c5426 --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/utils/get-workflow-health-status.ts @@ -0,0 +1,29 @@ +import { calculateHourDifference } from './calculate-hour-difference'; +import { WorkflowTableItem } from '../types'; +import { IWorkflowHealthStatus, WorkflowHealthStatus } from '@common/enums'; + +export function getWorkflowHealthStatus(workflow: WorkflowTableItem): IWorkflowHealthStatus { + const { status, createdAt } = workflow; + + if (status === 'failed') return WorkflowHealthStatus.failed; + + if (status === 'completed') return WorkflowHealthStatus.healthy; + + const hourDifference = calculateHourDifference(new Date(createdAt), new Date()); + const TWO_HOURS = 2; + const SIX_HOURS = 6; + + if (status === 'active' && hourDifference < TWO_HOURS) { + return WorkflowHealthStatus.healthy; + } + + if (status === 'active' && hourDifference > TWO_HOURS && hourDifference < SIX_HOURS) { + return WorkflowHealthStatus.pending; + } + + if (status === 'active' && hourDifference > SIX_HOURS) { + return WorkflowHealthStatus['pending-longterm']; + } + + return WorkflowHealthStatus.failed; +} diff --git a/packages/ui/src/components/organisms/WorkflowsTable/utils/get-workflow-health-status.unit.test.ts b/packages/ui/src/components/organisms/WorkflowsTable/utils/get-workflow-health-status.unit.test.ts new file mode 100644 index 0000000000..e52e5c0db7 --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/utils/get-workflow-health-status.unit.test.ts @@ -0,0 +1,52 @@ +import { describe, test, expect } from 'vitest'; +import { WorkflowTableItem } from '../types'; +import { getWorkflowHealthStatus } from '../utils/get-workflow-health-status'; +import dayjs from 'dayjs'; +import { WorkflowHealthStatus } from '@common/enums'; + +describe('getWorkflowHealthStatus', () => { + describe('healthy status', () => { + test('healthy when status is completed', () => { + expect(getWorkflowHealthStatus({ status: 'completed' } as WorkflowTableItem)).toBe( + WorkflowHealthStatus.healthy, + ); + }); + + test('healthy when status pending/active and process started < 2 hours ago', () => { + const PAST_DATE_3O_MIN_AGO = dayjs().subtract(30, 'minutes'); + + expect( + getWorkflowHealthStatus({ + status: 'active', + createdAt: PAST_DATE_3O_MIN_AGO.toDate(), + } as WorkflowTableItem), + ).toBe(WorkflowHealthStatus.healthy); + }); + }); + + describe('pending status', () => { + test('pending when status pending/active and process started > 2 && < 6 hours', () => { + const PAST_DATE_3HOURS_AGO = dayjs().subtract(3, 'hours'); + + expect( + getWorkflowHealthStatus({ + status: 'active', + createdAt: PAST_DATE_3HOURS_AGO.toDate(), + } as WorkflowTableItem), + ).toBe(WorkflowHealthStatus.pending); + }); + }); + + describe('pending-longterm status', () => { + test('pending-longterm when status pending/active and process started > 6 hours', () => { + const PAST_DATE_8HOURS_AGO = dayjs().subtract(8, 'hours'); + + expect( + getWorkflowHealthStatus({ + status: 'active', + createdAt: PAST_DATE_8HOURS_AGO.toDate(), + } as WorkflowTableItem), + ).toBe(WorkflowHealthStatus['pending-longterm']); + }); + }); +}); diff --git a/packages/ui/src/components/organisms/WorkflowsTable/utils/merge-columns.ts b/packages/ui/src/components/organisms/WorkflowsTable/utils/merge-columns.ts new file mode 100644 index 0000000000..fcb3b8adee --- /dev/null +++ b/packages/ui/src/components/organisms/WorkflowsTable/utils/merge-columns.ts @@ -0,0 +1,11 @@ +import { InputColumn, WorkflowTableColumnDef } from '../types'; + +export function mergeColumns<TColumnData>( + leftColumn: WorkflowTableColumnDef<TColumnData>, + rightColumn: InputColumn<TColumnData>, +): WorkflowTableColumnDef<TColumnData> { + return { + ...leftColumn, + ...rightColumn, + }; +} diff --git a/packages/ui/src/global.css b/packages/ui/src/global.css new file mode 100644 index 0000000000..ec7ec28c42 --- /dev/null +++ b/packages/ui/src/global.css @@ -0,0 +1,82 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap'); +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + html, + body, + #root { + height: 100%; + @apply font-inter; + } + + :root { + --background: 0 0% 100%; + --foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 47.4% 11.2%; + + --card: 0 0% 100%; + --card-foreground: 222.2 47.4% 11.2%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 359 86% 63%; + --destructive-foreground: 359 86% 93%; + + --success: 152 51% 54%; + --success-foreground: 152 51% 84%; + + --ring: 215 20.2% 65.1%; + + --radius: 0.5rem; + } + + .dark { + --background: 224 71% 4%; + --foreground: 213 31% 91%; + + --muted: 223 47% 11%; + --muted-foreground: 215.4 16.3% 56.9%; + + --popover: 224 71% 4%; + --popover-foreground: 215 20.2% 65.1%; + + --card: 224 71% 4%; + --card-foreground: 213 31% 91%; + + --border: 216 34% 17%; + --input: 216 34% 17%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 1.2%; + + --secondary: 222.2 47.4% 11.2%; + --secondary-foreground: 210 40% 98%; + + --accent: 216 34% 17%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 63% 31%; + --destructive-foreground: 210 40% 98%; + + --ring: 216 34% 17%; + + --radius: 0.5rem; + } +} diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts new file mode 100644 index 0000000000..07635cbbc8 --- /dev/null +++ b/packages/ui/src/index.ts @@ -0,0 +1 @@ +export * from './components'; diff --git a/packages/ui/src/utils/ctw.ts b/packages/ui/src/utils/ctw.ts new file mode 100644 index 0000000000..f344e541d2 --- /dev/null +++ b/packages/ui/src/utils/ctw.ts @@ -0,0 +1,4 @@ +import { ClassValue, clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export const ctw = (...classNames: Array<ClassValue>) => twMerge(clsx(classNames)); diff --git a/packages/ui/src/vite-env.d.ts b/packages/ui/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/packages/ui/src/vite-env.d.ts @@ -0,0 +1 @@ +/// <reference types="vite/client" /> diff --git a/packages/ui/tailwind.config.cjs b/packages/ui/tailwind.config.cjs new file mode 100644 index 0000000000..49e805d1f4 --- /dev/null +++ b/packages/ui/tailwind.config.cjs @@ -0,0 +1,77 @@ +const { fontFamily } = require('tailwindcss/defaultTheme'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ['class'], + content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], + theme: { + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px', + }, + }, + extend: { + colors: { + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + }, + borderRadius: { + lg: `var(--radius)`, + md: `calc(var(--radius) - 2px)`, + sm: 'calc(var(--radius) - 4px)', + }, + fontFamily: { + sans: ['var(--font-sans)', ...fontFamily.sans], + inter: ['Inter', 'sans-serif'], + }, + keyframes: { + 'accordion-down': { + from: { height: 0 }, + to: { height: 'var(--radix-accordion-content-height)' }, + }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, + to: { height: 0 }, + }, + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', + }, + }, + }, + plugins: [require('tailwindcss-animate'), require('@tailwindcss/line-clamp')], +}; diff --git a/packages/ui/tsconfig.eslint.json b/packages/ui/tsconfig.eslint.json new file mode 100644 index 0000000000..95fceb7782 --- /dev/null +++ b/packages/ui/tsconfig.eslint.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["src", "e2e", "vite.config.ts"] +} diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json new file mode 100644 index 0000000000..50ed2e5203 --- /dev/null +++ b/packages/ui/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": false, + "types": ["node"], + "paths": { + "@components/*": ["./src/components/*"], + "@utils/*": ["./src/utils/*"], + "@common/*": ["./src/common/*"], + "class-variance-authority": ["node_modules/class-variance-authority"] + } + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/packages/ui/tsconfig.node.json b/packages/ui/tsconfig.node.json new file mode 100644 index 0000000000..c53fe434e9 --- /dev/null +++ b/packages/ui/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ES6", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/packages/ui/vite.config.ts b/packages/ui/vite.config.ts new file mode 100644 index 0000000000..c77a8af09e --- /dev/null +++ b/packages/ui/vite.config.ts @@ -0,0 +1,67 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import dts from 'vite-plugin-dts'; +import fg from 'fast-glob'; +import { resolve } from 'path'; +import tailwindcss from 'tailwindcss'; + +// Defines an array of entry points to be used to search for files. +const entryPoints = ['src/components/**/*.ts']; + +// Searches for files that match the patterns defined in the array of input points. +// Returns an array of absolute file paths. +const files = fg.sync(entryPoints, { absolute: true }); + +// Maps the file paths in the "files" array to an array of key-value pair. +const entities = files + //excluding test files + .filter(file => Boolean(file.match(/(?<=src\/)(?!.*\.test).*$/))) + .map(file => { + // Extract the part of the file path after the "src" folder and before the file extension. + const [key] = file.match(/(?<=src\/).*$/) || []; + + // Remove the file extension from the key. + const keyWithoutExt = key.replace(/\.[^.]*$/, ''); + + return [keyWithoutExt, file]; + }); + +// Convert the array of key-value pairs to an object using the Object.fromEntries() method. +// Returns an object where each key is the file name without the extension and the value is the absolute file path. +const entries = Object.fromEntries(entities); +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react(), dts(), tailwindcss()], + resolve: { + alias: { + '@components': resolve(__dirname, './src/components'), + '@common': resolve(__dirname, './src/common'), + '@utils': resolve(__dirname, './src/utils'), + }, + }, + test: { + exclude: ['node_modules', 'dist'], + }, + build: { + outDir: 'dist', + lib: { + entry: { + ...entries, + index: './src/index.ts', + }, + formats: ['es'], + name: 'ui', + }, + rollupOptions: { + external: ['react', 'react-dom', 'react/jsx-runtime'], + output: { + globals: { + react: 'React', + 'react-dom': 'ReactDOM', + 'react/jsx-runtime': 'react/jsx-runtime', + }, + }, + }, + minify: true, + }, +}); diff --git a/packages/workflow-core/CHANGELOG.md b/packages/workflow-core/CHANGELOG.md index 35f3858c44..2af246f407 100644 --- a/packages/workflow-core/CHANGELOG.md +++ b/packages/workflow-core/CHANGELOG.md @@ -1,5 +1,40 @@ # @ballerine/workflow-core +## 0.4.16 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.6 + +## 0.4.15 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.5 + +## 0.4.14 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.4 + +## 0.4.13 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.3 + +## 0.4.12 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.2 + ## 0.4.11 ### Patch Changes diff --git a/packages/workflow-core/package.json b/packages/workflow-core/package.json index f9af9ab65c..469f54a9d4 100644 --- a/packages/workflow-core/package.json +++ b/packages/workflow-core/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/workflow-core", "author": "Ballerine <dev@ballerine.com>", - "version": "0.4.11", + "version": "0.4.16", "description": "workflow-core", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", @@ -21,13 +21,17 @@ "build": "pnpm clean && rollup --config rollup.config.js", "watch": "tsc -w", "dev": "concurrently --kill-others \"pnpm build -w\" \"pnpm watch\"", - "test": "vitest run" + "test": "vitest run", + "test:watch": "vitest", + "test:unit": "vitest run" }, "engines": { "node": ">=12" }, "dependencies": { - "@ballerine/common": "0.4.3", + "@ballerine/common": "0.5.6", + "ajv": "^8.12.0", + "jmespath": "^0.16.0", "json-logic-js": "^2.0.2", "xstate": "^4.35.2" }, @@ -37,10 +41,12 @@ "@babel/preset-typescript": "7.16.7", "@rollup/plugin-babel": "5.3.1", "@rollup/plugin-commonjs": "^24.0.1", + "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "13.2.1", "@rollup/plugin-replace": "4.0.0", "@rollup/plugin-terser": "^0.4.0", "@types/babel__core": "^7.20.0", + "@types/jmespath": "^0.15.0", "@types/fs-extra": "^11.0.1", "@types/json-logic-js": "^2.0.1", "@types/node": "^18.14.0", @@ -54,7 +60,10 @@ "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-functional": "^3.0.2", "eslint-plugin-import": "^2.22.0", + "eslint-plugin-unused-imports": "^2.0.0", "fs-extra": "^11.1.0", + "msw": "^1.2.2", + "node-fetch": "^3.3.1", "plugin-babel": "link:@types/@rollup/plugin-babel", "plugin-terser": "link:@types/@rollup/plugin-terser", "prettier": "^2.1.1", diff --git a/packages/workflow-core/rollup.config.ts b/packages/workflow-core/rollup.config.ts index fccb9ff4e8..1d6fe39d1f 100644 --- a/packages/workflow-core/rollup.config.ts +++ b/packages/workflow-core/rollup.config.ts @@ -10,6 +10,7 @@ import commonjs from '@rollup/plugin-commonjs'; import path from 'path'; import dts from 'rollup-plugin-dts'; import { readJsonSync } from 'fs-extra'; +import json from '@rollup/plugin-json'; type Options = { input: string; @@ -61,8 +62,7 @@ function buildConfigs(opts: { }): RollupOptions[] { const input = path.resolve('./', opts.entryFile); - const packageJson = - readJsonSync(path.resolve(process.cwd(), 'package.json')) ?? {}; + const packageJson = readJsonSync(path.resolve(process.cwd(), 'package.json')) ?? {}; const banner = createBanner(opts.name); @@ -100,7 +100,7 @@ function esm({ input, packageDir, external, banner }: Options): RollupOptions { banner, preserveModules: true, }, - plugins: [babelPlugin, nodeResolve({ extensions: ['.ts'] })], + plugins: [babelPlugin, json(), nodeResolve({ extensions: ['.ts'] })], }; } @@ -117,17 +117,11 @@ function cjs({ input, external, packageDir, banner }: Options): RollupOptions { exports: 'named', banner, }, - plugins: [babelPlugin, commonjs(), nodeResolve({ extensions: ['.ts'] })], + plugins: [babelPlugin, json(), commonjs(), nodeResolve({ extensions: ['.ts'] })], }; } -function umdDev({ - input, - umdExternal, - packageDir, - banner, - jsName, -}: Options): RollupOptions { +function umdDev({ input, umdExternal, packageDir, banner, jsName }: Options): RollupOptions { return { // UMD (Dev) external: umdExternal, @@ -142,19 +136,14 @@ function umdDev({ plugins: [ babelPlugin, commonjs(), + json(), nodeResolve({ extensions: ['.ts'] }), umdDevPlugin('development'), ], }; } -function umdProd({ - input, - umdExternal, - packageDir, - banner, - jsName, -}: Options): RollupOptions { +function umdProd({ input, umdExternal, packageDir, banner, jsName }: Options): RollupOptions { return { // UMD (Prod) external: umdExternal, @@ -169,6 +158,7 @@ function umdProd({ plugins: [ babelPlugin, commonjs(), + json(), nodeResolve({ extensions: ['.ts'] }), umdDevPlugin('production'), terser(), @@ -181,12 +171,7 @@ function umdProd({ }; } -function types({ - input, - packageDir, - external, - banner, -}: Options): RollupOptions { +function types({ input, packageDir, external, banner }: Options): RollupOptions { return { // TYPES external, @@ -196,7 +181,7 @@ function types({ file: `${packageDir}/dist/types/index.d.ts`, banner, }, - plugins: [dts()], + plugins: [dts(), json()], }; } diff --git a/packages/workflow-core/src/lib/index.ts b/packages/workflow-core/src/lib/index.ts index fb0f9818fc..f0818a2e03 100644 --- a/packages/workflow-core/src/lib/index.ts +++ b/packages/workflow-core/src/lib/index.ts @@ -3,12 +3,12 @@ export { HttpError } from './errors'; export { Error, Errors, - StatePlugin, WorkflowEvent, WorkflowEventWithoutState, WorkflowOptions, WorkflowRunnerArgs, - PluginAction, - ExtensionRunOrder, - WorkflowContext + WorkflowContext, } from './types'; +export { StatePlugin } from './plugins/types'; +export { PluginAction } from './plugins/types'; +export { ExtensionRunOrder } from './plugins/types'; diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.test.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.test.ts new file mode 100644 index 0000000000..0fc6de5537 --- /dev/null +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.test.ts @@ -0,0 +1,165 @@ +import { describe, expect, it } from 'vitest'; +import { WorkflowRunner } from '../../workflow-runner'; +import { IApiPluginParams } from './api-plugin'; +import { WorkflowRunnerArgs } from '../../types'; + +function createWorkflowRunner( + definition: WorkflowRunnerArgs['definition'], + apiPluginsSchemas: IApiPluginParams[], +) { + return new WorkflowRunner({ + definition, + extensions: { + apiPlugins: apiPluginsSchemas, + }, + workflowContext: { machineContext: { entity: { id: 'some_id' } } }, + }); +} + +describe('workflow-runner', () => { + describe('api plugins', () => { + const definition = { + initial: 'initial', + states: { + initial: { + on: { + CHECK_BUSINESS_SCORE: { + target: 'checkBusinessScore', + }, + }, + }, + checkBusinessScore: { + on: { + API_CALL_SUCCESS: 'checkBusinessScoreSuccess', + API_CALL_FAILURE: 'testManually', + }, + }, + checkBusinessScoreSuccess: { + type: 'final', + }, + testManually: { + type: 'final', + }, + }, + }; + + const apiPluginsSchemas = [ + { + name: 'ballerineEnrichment', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_us.json', + method: 'GET', + stateNames: ['checkBusinessScore'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_FAILURE', + request: { + transform: { + transformer: 'jmespath', + mapping: '{data: entity.id}', + }, + }, + response: { + transform: { transformer: 'jmespath', mapping: '{result: @}' }, + }, + }, + ]; + + describe('when api plugin tranforms and makes a request to an external api', () => { + const workflow = createWorkflowRunner(definition, apiPluginsSchemas); + it('it transitions to successAction and persist response to context', async () => { + await workflow.sendEvent('CHECK_BUSINESS_SCORE'); + + expect(workflow.state).toEqual('checkBusinessScoreSuccess'); + expect(workflow.context.pluginsOutput).toEqual({ + ballerineEnrichment: { + result: { + companyInfo: { + companyName: 'TestCorp Ltd', + industry: 'Software', + location: 'New York, USA', + country: 'US', + yearEstablished: 1995, + numberOfEmployees: 500, + ceo: 'John Doe', + products: ['Product A', 'Product B', 'Product C'], + website: 'www.testcorpltd.com', + }, + }, + }, + }); + }); + }); + + describe('when api invalid jmespath transformation of request', () => { + const apiPluginsSchemasCopy = structuredClone(apiPluginsSchemas); + apiPluginsSchemasCopy[0].request.transform.mapping = 'dsa: .unknwonvalue.id}'; + const workflow = createWorkflowRunner(definition, apiPluginsSchemasCopy); + it('it returns error for transformation and transition to testManually', async () => { + await workflow.sendEvent('CHECK_BUSINESS_SCORE'); + + expect(workflow.state).toEqual('testManually'); + expect(workflow.context.pluginsOutput).toEqual({ + ballerineEnrichment: { + error: + 'Error transforming data: Unexpected token type: Colon, value: : for transformer mapping: dsa: .unknwonvalue.id}', + }, + }); + }); + }); + + describe('when api plugin has schema', () => { + describe('when api request invalid for schema', () => { + const apiPluginsSchemasCopy = structuredClone(apiPluginsSchemas); + apiPluginsSchemasCopy[0].request.schema = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + business_name: { + type: 'string', + }, + registration_number: { + type: 'string', + }, + }, + required: ['business_name', 'registration_number'], + }; + const workflow = createWorkflowRunner(definition, apiPluginsSchemasCopy); + + it('it returns error for transformation and transition to testManually', async () => { + await workflow.sendEvent('CHECK_BUSINESS_SCORE'); + + expect(workflow.state).toEqual('testManually'); + expect(workflow.context.pluginsOutput).toEqual({ + ballerineEnrichment: { + error: + "must have required property 'business_name' | must have required property 'registration_number'", + }, + }); + }); + }); + + describe('when api request valid schema', () => { + const apiPluginsSchemasCopy = structuredClone(apiPluginsSchemas); + apiPluginsSchemasCopy[0].request.schema = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + data: { + type: 'string', + }, + }, + required: ['data'], + }; + const workflow = createWorkflowRunner(definition, apiPluginsSchemasCopy); + + it('it transitions to successAction and persist success (response) to context', async () => { + await workflow.sendEvent('CHECK_BUSINESS_SCORE'); + + expect(workflow.state).toEqual('checkBusinessScoreSuccess'); + expect(Object.keys(workflow.context.pluginsOutput.ballerineEnrichment)[0]).toEqual( + 'result', + ); + }); + }); + }); + }); +}); diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.ts b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.ts new file mode 100644 index 0000000000..63485f2267 --- /dev/null +++ b/packages/workflow-core/src/lib/plugins/external-plugin/api-plugin.ts @@ -0,0 +1,167 @@ +import { TContext, TTransformers, TValidators } from '../../utils/types'; +import { AnyRecord, isErrorWithMessage } from '@ballerine/common'; +import * as process from 'process'; + +export interface IApiPluginParams { + name: string; + stateNames: Array<string>; + url: string; + method: 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'GET'; + request: { transformer: TTransformers; schemaValidator?: TValidators }; + response?: { transformer: TTransformers; schemaValidator?: TValidators }; + headers?: HeadersInit; + successAction?: string; + errorAction?: string; +} +export class ApiPlugin { + name: string; + stateNames: Array<string>; + url: string; + method: IApiPluginParams['method']; + headers: IApiPluginParams['headers']; + request: IApiPluginParams['request']; + response?: IApiPluginParams['response']; + successAction?: string; + errorAction?: string; + + constructor(pluginParams: IApiPluginParams) { + this.name = pluginParams.name; + this.stateNames = pluginParams.stateNames; + this.url = pluginParams.url; + this.method = pluginParams.method; + this.headers = { + 'Content-Type': 'application/json', + ...(pluginParams.headers || {}), + } as HeadersInit; + this.request = pluginParams.request; + this.response = pluginParams.response; + this.successAction = pluginParams.successAction; + this.errorAction = pluginParams.errorAction; + } + async callApi(context: TContext) { + try { + const requestPayload = await this.transformData(this.request.transformer, context); + const { isValidRequest, errorMessage } = await this.validateContent( + this.request.schemaValidator, + requestPayload, + 'Request', + ); + if (!isValidRequest) return this.returnErrorResponse(errorMessage!); + + const apiResponse = await this.makeApiRequest( + this.replaceValuePlaceholders(this.url, context), + this.method, + requestPayload, + this.composeRequestHeaders(this.headers!, context), + ); + + if (apiResponse.ok) { + const result = await apiResponse.json(); + const responseBody = await this.transformData( + this.response!.transformer, + result as AnyRecord, + ); + + const { isValidResponse, errorMessage } = await this.validateContent( + this.response!.schemaValidator, + responseBody, + 'Response', + ); + if (!isValidResponse) return this.returnErrorResponse(errorMessage!); + + return { callbackAction: this.successAction, responseBody }; + } else { + return this.returnErrorResponse('Request Failed: ' + apiResponse.statusText); + } + } catch (error) { + return this.returnErrorResponse(isErrorWithMessage(error) ? error.message : ''); + } + } + returnErrorResponse(errorMessage: string) { + return { callbackAction: this.errorAction, error: errorMessage }; + } + + async makeApiRequest( + url: string, + method: ApiPlugin['method'], + payload: AnyRecord, + headers: HeadersInit, + ) { + const requestParams = { + method: method, + headers: headers, + }; + + if (this.method.toUpperCase() === 'POST') { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + requestParams.body = JSON.stringify(payload); + } else if (this.method.toUpperCase() === 'GET' && payload) { + const queryParams = new URLSearchParams(payload as Record<string, string>).toString(); + url = `${url}?${queryParams}`; + } + + return await fetch(url, requestParams); + } + + async transformData(transformer: TTransformers, record: AnyRecord) { + try { + return (await transformer.transform(record, { input: 'json', output: 'json' })) as AnyRecord; + } catch (error) { + throw new Error( + `Error transforming data: ${ + isErrorWithMessage(error) ? error.message : '' + } for transformer mapping: ${transformer.mapping}`, + ); + } + } + + async validateContent<TValidationContext extends 'Request' | 'Response'>( + schemaValidator: TValidators | undefined, + transformedRequest: AnyRecord, + validationContext: TValidationContext, + ) { + const returnArgKey = `isValid${validationContext}`; + if (!schemaValidator) return { [returnArgKey]: true }; + + const { isValid, errorMessage } = await schemaValidator.validate(transformedRequest); + return { [returnArgKey]: isValid, errorMessage }; + } + + composeRequestHeaders(headers: HeadersInit, context: TContext) { + return Object.fromEntries( + Object.entries(headers).map(header => [ + header[0], + this.replaceValuePlaceholders(header[1], context), + ]), + ); + } + replaceValuePlaceholders(content: string, context: TContext) { + const placeholders = content.match(/{(.*?)}/g); + if (!placeholders) return content; + + let replacedContent = content; + placeholders.forEach(placeholder => { + const variableKey = placeholder.replace(/{|}/g, ''); + const isPlaceholderSecret = variableKey.includes('secret.'); + const placeholderValue = isPlaceholderSecret + ? `${process.env[variableKey.replace('secret.', '')]}` + : `${this.fetchObjectPlaceholderValue(context, variableKey)}`; + replacedContent = replacedContent.replace(placeholder, placeholderValue); + }); + + return replacedContent; + } + + fetchObjectPlaceholderValue(record: AnyRecord, path: string) { + let pathToValue = path.split('.'); + + return pathToValue.reduce((acc: unknown, pathKey: string) => { + if (typeof acc === 'object' && acc !== null && acc.hasOwnProperty(pathKey)) { + return (acc as AnyRecord)[pathKey]; + } else { + return undefined; + } + }, record as unknown); + } +} diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/webhook-plugin.test.ts b/packages/workflow-core/src/lib/plugins/external-plugin/webhook-plugin.test.ts new file mode 100644 index 0000000000..920b2ce0d6 --- /dev/null +++ b/packages/workflow-core/src/lib/plugins/external-plugin/webhook-plugin.test.ts @@ -0,0 +1,84 @@ +import { beforeEach, afterEach, describe, expect, it, test } from 'vitest'; +import { WorkflowRunner } from '../../workflow-runner'; +import { WorkflowRunnerArgs } from '../../types'; +import { WebhookPlugin, WebhookPluginParams } from './webhook-plugin'; +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; + +function createWorkflowRunner( + definition: WorkflowRunnerArgs['definition'], + webhookPluginsSchemas: WebhookPluginParams[], +) { + return new WorkflowRunner({ + definition, + extensions: { + apiPlugins: webhookPluginsSchemas, + }, + workflowContext: { machineContext: { entity: { id: 'some_id' } } }, + }); +} + +describe('workflow-runner', () => { + describe('webhook plugins', () => { + const definition = { + initial: 'initial', + states: { + initial: { + on: { + ALL_GOOD: { + target: 'success', + }, + }, + }, + success: { + type: 'final', + }, + fail: { + type: 'final', + }, + }, + }; + + let webhookUrl = 'https://SomeTestUrl.com/ballerine/test/url/123'; + let webhookPluginsSchemas = [ + { + name: 'ballerineEnrichment', + url: webhookUrl, + method: 'GET', + stateNames: ['success', 'type'], + request: { + transform: { + transformer: 'jmespath', + mapping: '{id: entity.id}', + }, + }, + }, + ]; + + describe('when webhook plugin hits state', () => { + const server = setupServer(); + + beforeEach(() => { + server.listen(); + }); + afterEach(() => { + server.close(); + }); + + let serverRequesUrl; + server.use( + rest.get(webhookUrl, (req, res, ctx) => { + serverRequesUrl = req.url.toString(); + return res(ctx.json({ result: 'someResult' })); + }), + ); + const workflow = createWorkflowRunner(definition, webhookPluginsSchemas); + it('it transitions to successAction and persist response to context', async () => { + await workflow.sendEvent('ALL_GOOD'); + expect(serverRequesUrl).toEqual( + 'https://sometesturl.com/ballerine/test/url/123?id=some_id', + ); + }); + }); + }); +}); diff --git a/packages/workflow-core/src/lib/plugins/external-plugin/webhook-plugin.ts b/packages/workflow-core/src/lib/plugins/external-plugin/webhook-plugin.ts new file mode 100644 index 0000000000..f9d5110e1e --- /dev/null +++ b/packages/workflow-core/src/lib/plugins/external-plugin/webhook-plugin.ts @@ -0,0 +1,28 @@ +import { ApiPlugin, IApiPluginParams } from './api-plugin'; +import { TContext } from '../../utils/types'; + +export interface WebhookPluginParams { + name: string; + stateNames: Array<string>; + url: string; + method: IApiPluginParams['method']; + headers: IApiPluginParams['headers']; + request: Omit<IApiPluginParams['request'], 'schemaValidator'>; +} + +export class WebhookPlugin extends ApiPlugin { + constructor(pluginParams: IApiPluginParams) { + super(pluginParams); + } + async callApi(context: TContext) { + const requestPayload = await this.transformData(this.request.transformer, context); + + try { + await this.makeApiRequest(this.url, this.method, requestPayload, this.headers); + } catch (e) { + console.error(e); + } + + return {}; + } +} diff --git a/packages/workflow-core/src/lib/plugins/types.ts b/packages/workflow-core/src/lib/plugins/types.ts new file mode 100644 index 0000000000..6a636baac9 --- /dev/null +++ b/packages/workflow-core/src/lib/plugins/types.ts @@ -0,0 +1,30 @@ +import { ApiPlugin } from './external-plugin/api-plugin'; + +export type PluginAction = { workflowId: string; context: any; event: any; state: any }; + +export type ExtensionRunOrder = 'pre' | 'post'; + +export interface WorkflowPlugin { + when: ExtensionRunOrder; + action: (options: PluginAction) => Promise<void>; +} + +export interface StatePlugin extends WorkflowPlugin { + /** + * The actions key to inject an action function into. + * E.g. { actions: { [plugin.name]: plugin.action } } + */ + name: string; + + /** + * Should the plugin be executed in a blocking manner or async + */ + isBlocking: boolean; + /** + * States already defined in the statechart + */ + stateNames: Array<string>; +} + +export type StatePlugins = StatePlugin[]; +export type ApiPlugins = ApiPlugin[]; diff --git a/packages/workflow-core/src/lib/types.ts b/packages/workflow-core/src/lib/types.ts index 99e495966b..fd66e760dd 100644 --- a/packages/workflow-core/src/lib/types.ts +++ b/packages/workflow-core/src/lib/types.ts @@ -1,4 +1,6 @@ import type { MachineConfig, MachineOptions } from 'xstate'; +import { ApiPlugins, StatePlugins } from './plugins/types'; +import { IApiPluginParams } from './plugins/external-plugin/api-plugin'; export type ObjectValues<TObject extends Record<any, any>> = TObject[keyof TObject]; @@ -8,32 +10,6 @@ export interface Workflow { getSnapshot: () => Record<PropertyKey, any>; } -export type PluginAction = { workflowId: string; context: any; event: any; state: any }; - -export type ExtensionRunOrder = 'pre' | 'post'; - -export interface WorkflowPlugin { - when: ExtensionRunOrder; - action: (options: PluginAction) => Promise<void>; -} - -export interface StatePlugin extends WorkflowPlugin { - /** - * The actions key to inject an action function into. - * E.g. { actions: { [plugin.name]: plugin.action } } - */ - name: string; - - /** - * Should the plugin be executed in a blocking manner or async - */ - isBlocking: boolean; - /** - * States already defined in the statechart - */ - stateNames: Array<string>; -} - export interface WorkflowEvent { type: string; state: string; @@ -42,9 +18,9 @@ export interface WorkflowEvent { } export interface WorkflowExtensions { - statePlugins: StatePlugins; + statePlugins?: StatePlugins; + apiPlugins?: ApiPlugins | IApiPluginParams[]; } - export interface WorkflowContext { id?: string; state?: any; @@ -70,8 +46,6 @@ export interface WorkflowRunnerArgs { export type WorkflowEventWithoutState = Omit<WorkflowEvent, 'state'>; -export type StatePlugins = StatePlugin[]; - export type TCreateWorkflow = (options: WorkflowOptions) => Workflow; export const Error = { diff --git a/packages/workflow-core/src/lib/utils/context-transformers/jmespath-transformer.ts b/packages/workflow-core/src/lib/utils/context-transformers/jmespath-transformer.ts new file mode 100644 index 0000000000..eb8e01a3db --- /dev/null +++ b/packages/workflow-core/src/lib/utils/context-transformers/jmespath-transformer.ts @@ -0,0 +1,18 @@ +import { BaseContextTransformer, TTransformationLogic } from './types'; +import { TContext } from '../types'; +import { search } from 'jmespath'; +export class JmespathTransformer extends BaseContextTransformer { + name = 'jmespath-transformer'; + mapping: TTransformationLogic; + + constructor(mapping: TTransformationLogic) { + super(); + this.mapping = mapping; + } + + async transform(context: TContext, options: {}) { + const response = await search(context, this.mapping); + + return response; + } +} diff --git a/packages/workflow-core/src/lib/utils/context-transformers/types.ts b/packages/workflow-core/src/lib/utils/context-transformers/types.ts new file mode 100644 index 0000000000..3657e1b7ec --- /dev/null +++ b/packages/workflow-core/src/lib/utils/context-transformers/types.ts @@ -0,0 +1,9 @@ +import { TContext } from '../types'; + +export type TTransformationLogic = string; +export abstract class BaseContextTransformer { + abstract name: string; + type = 'context-transformer'; + + abstract transform(context: TContext, options: {}): Promise<any>; +} diff --git a/packages/workflow-core/src/lib/utils/context-validator/json-schema-validator.ts b/packages/workflow-core/src/lib/utils/context-validator/json-schema-validator.ts new file mode 100644 index 0000000000..1c5f0b4e0f --- /dev/null +++ b/packages/workflow-core/src/lib/utils/context-validator/json-schema-validator.ts @@ -0,0 +1,28 @@ +import { BaseSchemaValidator, TSchemaValidatorResponse, TValidationLogic } from './types'; +import Ajv from 'ajv'; +import { AnyRecord } from '@ballerine/common'; + +export class JsonSchemaValidator extends BaseSchemaValidator { + name = 'json-schema-validator'; + schema: TValidationLogic; + + constructor(schema: TValidationLogic) { + super(); + this.schema = schema; + } + async validate( + data: AnyRecord, + options: AnyRecord = { allErrors: true }, + errorMessage?: string, + ): TSchemaValidatorResponse { + const validator = new Ajv(options); + const validationResult = validator.validate(this.schema, data); + + if (!validationResult) { + const validationErrorMessage = validator.errors?.map(error => error.message).join(' | '); + return { isValid: false, errorMessage: validationErrorMessage }; + } + + return { isValid: true }; + } +} diff --git a/packages/workflow-core/src/lib/utils/context-validator/types.ts b/packages/workflow-core/src/lib/utils/context-validator/types.ts new file mode 100644 index 0000000000..300d64efc7 --- /dev/null +++ b/packages/workflow-core/src/lib/utils/context-validator/types.ts @@ -0,0 +1,15 @@ +import { AnyRecord } from '@ballerine/common'; + +export type TSchemaValidatorResponse = Promise<{ isValid: boolean; errorMessage?: string }>; +export type TJsonSchema = AnyRecord; +export type TValidationLogic = TJsonSchema; +export abstract class BaseSchemaValidator { + abstract name: string; + type = 'schema-validator'; + + abstract validate( + data: AnyRecord, + options: AnyRecord, + errorMessage?: string, + ): TSchemaValidatorResponse; +} diff --git a/packages/workflow-core/src/lib/utils/types.ts b/packages/workflow-core/src/lib/utils/types.ts new file mode 100644 index 0000000000..7ed33cce66 --- /dev/null +++ b/packages/workflow-core/src/lib/utils/types.ts @@ -0,0 +1,7 @@ +import { JsonSchemaValidator } from './context-validator/json-schema-validator'; +import { AnyRecord } from '@ballerine/common'; +import { JmespathTransformer } from './context-transformers/jmespath-transformer'; + +export type TContext = AnyRecord; +export type TTransformers = JmespathTransformer; +export type TValidators = JsonSchemaValidator; diff --git a/packages/workflow-core/src/lib/workflow-runner.test.ts b/packages/workflow-core/src/lib/workflow-runner.test.ts index acbdcf7135..9c8e3873f7 100644 --- a/packages/workflow-core/src/lib/workflow-runner.test.ts +++ b/packages/workflow-core/src/lib/workflow-runner.test.ts @@ -1,4 +1,6 @@ -import { beforeEach, describe, expect, it, test } from 'vitest'; +/* eslint-disable */ + +import { afterEach, describe, expect, it } from 'vitest'; import { WorkflowRunner } from './workflow-runner'; import { sleep } from '@ballerine/common'; @@ -182,9 +184,9 @@ describe('workflow-runner', () => { ]); }); - it('raises an exception if any of stateNames is not defined', async => { + it('raises an exception if any of stateNames is not defined', () => { expect(() => { - const workflow = new WorkflowRunner({ + new WorkflowRunner({ definition: TWO_STATES_MACHINE_DEFINITION, extensions: { statePlugins: [ @@ -218,7 +220,7 @@ describe('workflow-runner', () => { when: 'pre', isBlocking: true, stateNames: ['initial'], - action: async () => { + action: () => { throw new Error('some error'); }, }, @@ -333,3 +335,86 @@ describe('workflow-runner', () => { expect(done).toEqual(true); }); }); + +describe('Workflows with conditions', () => { + const createCondMachine = score => ({ + workflowContext: { + machineContext: { + external_request_example: { + data: { + name_fuzziness_score: 0.85, // or whatever value you want to assign + }, + }, + }, + }, + definition: { + initial: 'initial', + states: { + initial: { + on: { + EVENT: [ + { + target: 'final', + cond: { + type: 'json-logic', + options: { + rule: { + '>': [{ var: 'external_request_example.data.name_fuzziness_score' }, score], + }, + assignOnFailure: { manualReviewReason: 'name not matching ... ' }, + }, + }, + }, + ], + }, + }, + middle: { + on: { EVENT2: { target: 'final', cond: 'isTrue' } }, + }, + final: { + type: 'final', + }, + }, + }, + }); + it('should not proceed with transition if json logic condition falsy', async () => { + const workflow = createEventCollectingWorkflow(createCondMachine(0.9)); + await workflow.sendEvent({ type: 'EVENT' }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(workflow.events[0].state).toEqual('initial'); + }); + it('should proceed with transition if json logic condition truthy', async () => { + const workflowArgs = createCondMachine(0.5); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + const workflow = createEventCollectingWorkflow(workflowArgs); + await workflow.sendEvent({ type: 'EVENT' }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(workflow.events[0].state).toEqual('final'); + // expect(workflow.#__context).toContain({ manualReviewReason: 'name not matching ... ' }); + }); + it('should proceed with transition if json logic condition truthy, and default transition is set', async () => { + const workflowArgs = createCondMachine(0.5); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + workflowArgs.definition.states.initial.on.EVENT.push({ target: 'middle' }); + const workflow = createEventCollectingWorkflow(workflowArgs); + await workflow.sendEvent({ type: 'EVENT' }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(workflow.events[0].state).toEqual('final'); + // expect(workflow.#__context).toContain({ manualReviewReason: 'name not matching ... ' }); + }); + it('should not proceed with transition if json logic condition truthy, but transition to a default state THIS TEST SHOULD BE REVISIONED', async () => { + const workflowArgs = createCondMachine(0.9); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + workflowArgs.definition.states.initial.on.EVENT.push({ target: 'middle' }); + console.log(JSON.stringify(workflowArgs.definition, null, 2)); + + const workflow = createEventCollectingWorkflow(workflowArgs); + await workflow.sendEvent({ type: 'EVENT' }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(workflow.events[0].state).toEqual('initial'); + // expect(workflow.#__context).toContain({ manualReviewReason: 'name not matching ... ' }); + }); +}); diff --git a/packages/workflow-core/src/lib/workflow-runner.ts b/packages/workflow-core/src/lib/workflow-runner.ts index 0e7ad7848e..708a149e21 100644 --- a/packages/workflow-core/src/lib/workflow-runner.ts +++ b/packages/workflow-core/src/lib/workflow-runner.ts @@ -2,17 +2,21 @@ import { uniqueArray } from '@ballerine/common'; import * as jsonLogic from 'json-logic-js'; import type { ActionFunction, MachineOptions, StateMachine } from 'xstate'; -import { createMachine, interpret } from 'xstate'; +import { createMachine, interpret, assign } from 'xstate'; import { HttpError } from './errors'; import type { ObjectValues, - StatePlugin, WorkflowEvent, WorkflowEventWithoutState, WorkflowExtensions, WorkflowRunnerArgs, } from './types'; import { Error as ErrorEnum } from './types'; +import { JmespathTransformer } from './utils/context-transformers/jmespath-transformer'; +import { JsonSchemaValidator } from './utils/context-validator/json-schema-validator'; +import { StatePlugin } from './plugins/types'; +import { ApiPlugin, IApiPluginParams } from './plugins/external-plugin/api-plugin'; +import { WebhookPlugin } from './plugins/external-plugin/webhook-plugin'; export class WorkflowRunner { #__subscription: Array<(event: WorkflowEvent) => void> = []; @@ -22,25 +26,31 @@ export class WorkflowRunner { #__callback: ((event: WorkflowEvent) => void) | null = null; #__extensions: WorkflowExtensions; #__debugMode: boolean; + events: any; public get workflow() { return this.#__workflow; } + public get context() { + return this.#__context; + } public get state() { return this.#__currentState; } constructor( { definition, workflowActions, workflowContext, extensions }: WorkflowRunnerArgs, - debugMode = true, + debugMode = false, ) { // global and state specific extensions - this.#__extensions = extensions ?? { - statePlugins: [], - }; + this.#__extensions = extensions ?? {}; + this.#__extensions.statePlugins ??= []; this.#__debugMode = debugMode; + this.#__extensions.apiPlugins = this.initiateApiPlugins(this.#__extensions.apiPlugins); + // this.#__defineApiPluginsStatesAsEntryActions(definition, apiPlugins); + this.#__workflow = this.#__extendedWorkflow({ definition, workflowActions, @@ -56,6 +66,52 @@ export class WorkflowRunner { this.#__currentState = workflowContext?.state ? workflowContext.state : definition.initial; } + initiateApiPlugins(apiPluginSchemas: IApiPluginParams[]) { + return apiPluginSchemas?.map(apiPluginSchema => { + const requestTransformerLogic = apiPluginSchema.request.transform; + const requestSchema = apiPluginSchema.request.schema; + const responseTransformerLogic = apiPluginSchema.response?.transform; + const responseSchema = apiPluginSchema.response?.schema; + const requestTransformer = this.fetchTransformer(requestTransformerLogic); + const responseTransformer = + responseTransformerLogic && this.fetchTransformer(responseTransformerLogic); + const requestValidator = this.fetchValidator('json-schema', requestSchema); + const responseValidator = this.fetchValidator('json-schema', responseSchema); + + let isApiPlugin = this.isApiPlugin(apiPluginSchema); + const apiPluginClass = isApiPlugin ? ApiPlugin : WebhookPlugin; + const apiPlugin = new apiPluginClass({ + name: apiPluginSchema.name, + stateNames: apiPluginSchema.stateNames, + url: apiPluginSchema.url, + method: apiPluginSchema.method, + headers: apiPluginSchema.headers, + request: { transformer: requestTransformer, schemaValidator: requestValidator }, + response: { transformer: responseTransformer, schemaValidator: responseValidator }, + successAction: apiPluginSchema.successAction, + errorAction: apiPluginSchema.errorAction, + }); + + return apiPlugin; + }); + } + + private isApiPlugin(apiPluginSchema: IApiPluginParams) { + return !!apiPluginSchema.successAction && !!apiPluginSchema.errorAction; + } + + fetchTransformer(transformer) { + if (transformer.transformer == 'jmespath') return new JmespathTransformer(transformer.mapping); + + throw new Error(`Transformer ${transformer.name} is not supported`); + } + fetchValidator(validatorName, schema) { + if (!schema) return; + if (validatorName === 'json-schema') return new JsonSchemaValidator(schema); + + throw new Error(`Validator ${validatorName} is not supported`); + } + #__handleAction({ type, plugin, @@ -131,7 +187,9 @@ export class WorkflowRunner { * * @see {@link WorfklowRunner.sendEvent} * */ - const nonBlockingPlugins = this.#__extensions.statePlugins.filter(plugin => !plugin.isBlocking); + const nonBlockingPlugins = this.#__extensions.statePlugins?.filter( + plugin => !plugin.isBlocking, + ); for (const statePlugin of nonBlockingPlugins) { const when = statePlugin.when === 'pre' ? 'entry' : 'exit'; @@ -163,12 +221,25 @@ export class WorkflowRunner { }; const guards: MachineOptions<any, any>['guards'] = { - 'json-rule': (ctx, { payload }, { cond }) => { - const data = { ...ctx, ...payload }; - return jsonLogic.apply( - cond.name, // Rule + 'json-logic': (ctx, event, metadata) => { + const data = { ...ctx, ...event.payload }; + // @ts-expect-error + const options = metadata.cond.options; + + const ruleResult = jsonLogic.apply( + options.rule, // Rule data, // Data ); + if (!ruleResult && options.assignOnFailure) { + this.#__callback?.({ + type: 'RULE_EVALUATION_FAILURE', + state: this.#__currentState, + payload: { + ...options, + }, + }); + } + return ruleResult; }, }; @@ -211,6 +282,7 @@ export class WorkflowRunner { plugin.when === 'pre' && plugin.stateNames.includes(this.#__currentState), ); + const snapshot = service.getSnapshot(); for (const prePlugin of prePlugins) { @@ -225,6 +297,21 @@ export class WorkflowRunner { this.#__context = service.getSnapshot().context; + if (this.#__extensions.apiPlugins) { + for (const apiPlugin of this.#__extensions.apiPlugins) { + if (!apiPlugin.stateNames.includes(this.#__currentState)) continue; + + const { callbackAction, responseBody, error } = await apiPlugin.callApi(this.#__context); + if (!this.isApiPlugin(apiPlugin)) continue; + + this.#__context.pluginsOutput = { + ...(this.#__context.pluginsOutput || {}), + ...{ [apiPlugin.name]: responseBody ? responseBody : { error: error } }, + }; + await this.sendEvent(callbackAction); + } + } + if (this.#__debugMode) { console.log('context:', this.#__context); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 07e0bfdf4d..c7ddda332f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + importers: .: @@ -28,10 +32,10 @@ importers: version: 2.26.1 '@commitlint/cli': specifier: ^17.5.0 - version: 17.6.0 + version: 17.5.0 '@commitlint/config-conventional': specifier: ^17.4.4 - version: 17.6.0 + version: 17.4.4 commitizen: specifier: ^4.3.0 version: 4.3.0 @@ -57,10 +61,10 @@ importers: apps/backoffice-v2: dependencies: '@ballerine/common': - specifier: ^0.4.3 + specifier: 0.5.6 version: link:../../packages/common '@ballerine/workflow-browser-sdk': - specifier: ^0.4.3 + specifier: ^0.4.9 version: link:../../sdks/workflow-browser-sdk '@ballerine/workflow-node-sdk': specifier: ^0.4.3 @@ -73,19 +77,19 @@ importers: version: 3.1.0(react-hook-form@7.43.9) '@lukemorales/query-key-factory': specifier: ^1.0.3 - version: 1.2.0(@tanstack/query-core@4.29.1) + version: 1.2.0(@tanstack/query-core@4.27.0) '@radix-ui/react-checkbox': specifier: ^1.0.1 version: 1.0.3(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dialog': specifier: ^1.0.2 - version: 1.0.3(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.3(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dropdown-menu': - specifier: ^2.0.4 - version: 2.0.4(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0) + specifier: ^2.0.5 + version: 2.0.5(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-hover-card': specifier: ^1.0.2 - version: 1.0.5(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.5(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-label': specifier: ^2.0.1 version: 2.0.1(react-dom@18.2.0)(react@18.2.0) @@ -94,7 +98,7 @@ importers: version: 1.0.3(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-select': specifier: ^1.2.1 - version: 1.2.1(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0) + version: 1.2.1(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-separator': specifier: ^1.0.2 version: 1.0.2(react-dom@18.2.0)(react@18.2.0) @@ -103,7 +107,7 @@ importers: version: 1.0.1(react@18.2.0) '@tanstack/react-query': specifier: ^4.19.1 - version: 4.29.1(react-dom@18.2.0)(react@18.2.0) + version: 4.28.0(react-dom@18.2.0)(react@18.2.0) class-variance-authority: specifier: ^0.6.0 version: 0.6.0(typescript@4.9.5) @@ -115,7 +119,7 @@ importers: version: 1.11.7 eslint-plugin-tailwindcss: specifier: ^3.8.0 - version: 3.11.0(tailwindcss@3.3.1) + version: 3.10.1(tailwindcss@3.2.7) face-api.js: specifier: ^0.22.2 version: 0.22.2 @@ -124,13 +128,16 @@ importers: version: 8.5.5(react-dom@18.2.0)(react@18.2.0) i18next: specifier: ^22.4.9 - version: 22.4.14 + version: 22.4.13 i18next-browser-languagedetector: specifier: ^7.0.1 version: 7.0.1 i18next-http-backend: specifier: ^2.1.1 version: 2.2.0 + leaflet: + specifier: ^1.9.4 + version: 1.9.4 lucide-react: specifier: ^0.144.0 version: 0.144.0(react@18.2.0) @@ -154,25 +161,31 @@ importers: version: 7.43.9(react@18.2.0) react-hot-toast: specifier: ^2.4.0 - version: 2.4.0(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0) + version: 2.4.0(csstype@3.1.1)(react-dom@18.2.0)(react@18.2.0) react-i18next: specifier: ^12.1.4 - version: 12.2.0(i18next@22.4.14)(react-dom@18.2.0)(react@18.2.0) + version: 12.2.0(i18next@22.4.13)(react-dom@18.2.0)(react@18.2.0) react-image-crop: specifier: ^10.0.9 version: 10.0.9(react@18.2.0) + react-leaflet: + specifier: ^4.2.1 + version: 4.2.1(leaflet@1.9.4)(react-dom@18.2.0)(react@18.2.0) react-router-dom: specifier: ^6.11.2 version: 6.11.2(react-dom@18.2.0)(react@18.2.0) + react-zoom-pan-pinch: + specifier: ^3.0.8 + version: 3.0.8(react-dom@18.2.0)(react@18.2.0) tailwind-merge: specifier: ^1.10.0 - version: 1.12.0 + version: 1.10.0 tailwindcss-animate: specifier: ^1.0.5 - version: 1.0.5(tailwindcss@3.3.1) + version: 1.0.5(tailwindcss@3.2.7) tesseract.js: specifier: ^4.0.1 - version: 4.0.3(eslint@8.22.0) + version: 4.0.2(eslint@8.22.0) vite-plugin-terminal: specifier: ^1.1.0 version: 1.1.0(vite@4.2.1) @@ -185,7 +198,7 @@ importers: version: 7.6.0 '@playwright/test': specifier: ^1.32.1 - version: 1.32.3 + version: 1.32.1 '@storybook/addon-a11y': specifier: ^6.5.16 version: 6.5.16(react-dom@18.2.0)(react@18.2.0) @@ -212,22 +225,25 @@ importers: version: 0.0.14-next.1 '@tanstack/react-query-devtools': specifier: 4.22.0 - version: 4.22.0(@tanstack/react-query@4.29.1)(react-dom@18.2.0)(react@18.2.0) + version: 4.22.0(@tanstack/react-query@4.28.0)(react-dom@18.2.0)(react@18.2.0) '@testing-library/jest-dom': specifier: ^5.16.4 version: 5.16.5 '@testing-library/react': specifier: ^13.3.0 version: 13.4.0(react-dom@18.2.0)(react@18.2.0) + '@types/leaflet': + specifier: ^1.9.3 + version: 1.9.3 '@types/node': specifier: ^18.11.13 - version: 18.15.11 + version: 18.15.10 '@types/qs': specifier: ^6.9.7 version: 6.9.7 '@types/react': specifier: ^18.0.14 - version: 18.0.35 + version: 18.0.29 '@types/react-dom': specifier: ^18.0.5 version: 18.0.11 @@ -236,19 +252,19 @@ importers: version: 5.14.5 '@typescript-eslint/eslint-plugin': specifier: ^5.30.0 - version: 5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.22.0)(typescript@4.9.5) + version: 5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.22.0)(typescript@4.9.5) '@typescript-eslint/parser': specifier: ^5.30.0 - version: 5.58.0(eslint@8.22.0)(typescript@4.9.5) + version: 5.56.0(eslint@8.22.0)(typescript@4.9.5) '@vitejs/plugin-react-swc': specifier: ^3.0.1 - version: 3.3.0(vite@4.2.1) + version: 3.2.0(vite@4.2.1) autoprefixer: specifier: ^10.4.7 version: 10.4.14(postcss@8.4.21) daisyui: specifier: ^2.46.1 - version: 2.51.5(autoprefixer@10.4.14)(postcss@8.4.21) + version: 2.51.5(autoprefixer@10.4.14)(postcss@8.4.21)(ts-node@10.9.1) eslint: specifier: 8.22.0 version: 8.22.0 @@ -257,7 +273,7 @@ importers: version: 8.8.0(eslint@8.22.0) eslint-plugin-import: specifier: ^2.26.0 - version: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint@8.22.0) + version: 2.27.5(@typescript-eslint/parser@5.56.0)(eslint@8.22.0) eslint-plugin-react: specifier: ^7.30.1 version: 7.32.2(eslint@8.22.0) @@ -267,6 +283,9 @@ importers: eslint-plugin-storybook: specifier: ^0.6.6 version: 0.6.11(eslint@8.22.0)(typescript@4.9.5) + eslint-plugin-unused-imports: + specifier: ^2.0.0 + version: 2.0.0(@typescript-eslint/eslint-plugin@5.56.0)(eslint@8.22.0) postcss: specifier: ^8.4.14 version: 8.4.21 @@ -275,31 +294,221 @@ importers: version: 2.8.7 prettier-plugin-tailwindcss: specifier: ^0.2.1 - version: 0.2.7(prettier@2.8.7) + version: 0.2.5(prettier@2.8.7) storybook: specifier: ^7.0.0-rc.10 version: 7.0.0-rc.10 tailwindcss: specifier: ^3.2.4 - version: 3.3.1(postcss@8.4.21) + version: 3.2.7(postcss@8.4.21)(ts-node@10.9.1) typescript: specifier: ^4.9.3 version: 4.9.5 vite: specifier: ^4.0.4 - version: 4.2.1(@types/node@18.15.11) + version: 4.2.1(@types/node@18.15.10) vitest: specifier: ^0.29.8 version: 0.29.8 + apps/workflows-dashboard: + dependencies: + '@lukemorales/query-key-factory': + specifier: ^1.0.3 + version: 1.2.0(@tanstack/query-core@4.27.0) + '@radix-ui/react-avatar': + specifier: ^1.0.3 + version: 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dialog': + specifier: ^1.0.2 + version: 1.0.3(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dropdown-menu': + specifier: ^2.0.5 + version: 2.0.5(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-icons': + specifier: ^1.3.0 + version: 1.3.0(react@18.2.0) + '@radix-ui/react-label': + specifier: ^2.0.1 + version: 2.0.1(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-popover': + specifier: ^1.0.6 + version: 1.0.6(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-select': + specifier: ^1.2.1 + version: 1.2.1(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-separator': + specifier: ^1.0.2 + version: 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': + specifier: ^1.0.1 + version: 1.0.1(react@18.2.0) + '@radix-ui/react-tabs': + specifier: ^1.0.4 + version: 1.0.4(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@tailwindcss/line-clamp': + specifier: ^0.4.4 + version: 0.4.4(tailwindcss@3.2.7) + '@tanstack/react-query': + specifier: ^4.28.0 + version: 4.28.0(react-dom@18.2.0)(react@18.2.0) + '@tanstack/react-table': + specifier: ^8.9.2 + version: 8.9.2(react-dom@18.2.0)(react@18.2.0) + '@xstate/inspect': + specifier: ^0.7.1 + version: 0.7.1(ws@8.13.0)(xstate@4.38.0) + '@xstate/react': + specifier: ^3.2.2 + version: 3.2.2(@types/react@18.0.37)(react@18.2.0)(xstate@4.38.0) + axios: + specifier: ^1.4.0 + version: 1.4.0 + class-variance-authority: + specifier: ^0.6.0 + version: 0.6.0(typescript@5.0.2) + classnames: + specifier: ^2.3.2 + version: 2.3.2 + clsx: + specifier: ^1.2.1 + version: 1.2.1 + cmdk: + specifier: ^0.2.0 + version: 0.2.0(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + dayjs: + specifier: ^1.11.6 + version: 1.11.7 + install: + specifier: ^0.13.0 + version: 0.13.0 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + lucide-react: + specifier: ^0.144.0 + version: 0.144.0(react@18.2.0) + react: + specifier: ^18.2.0 + version: 18.2.0 + react-custom-scrollbars: + specifier: ^4.2.1 + version: 4.2.1(react-dom@18.2.0)(react@18.2.0) + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + react-hook-form: + specifier: ^7.43.9 + version: 7.43.9(react@18.2.0) + react-json-view: + specifier: ^1.21.3 + version: 1.21.3(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + react-router-dom: + specifier: ^6.11.2 + version: 6.11.2(react-dom@18.2.0)(react@18.2.0) + recharts: + specifier: ^2.7.2 + version: 2.7.2(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0) + tailwind-merge: + specifier: ^1.13.2 + version: 1.13.2 + tailwindcss-animate: + specifier: ^1.0.5 + version: 1.0.5(tailwindcss@3.2.7) + use-query-params: + specifier: ^2.2.1 + version: 2.2.1(react-dom@18.2.0)(react-router-dom@6.11.2)(react@18.2.0) + vite-plugin-terminal: + specifier: ^1.1.0 + version: 1.1.0(vite@4.3.9) + xstate: + specifier: ^4.38.0 + version: 4.38.0 + zod: + specifier: ^3.21.4 + version: 3.21.4 + devDependencies: + '@types/axios': + specifier: ^0.14.0 + version: 0.14.0 + '@types/classnames': + specifier: ^2.3.1 + version: 2.3.1 + '@types/jest': + specifier: ^26.0.19 + version: 26.0.24 + '@types/lodash': + specifier: ^4.14.191 + version: 4.14.191 + '@types/moment': + specifier: ^2.13.0 + version: 2.13.0 + '@types/node': + specifier: ^20.3.1 + version: 20.3.1 + '@types/react': + specifier: ^18.0.37 + version: 18.0.37 + '@types/react-custom-scrollbars': + specifier: ^4.0.10 + version: 4.0.10 + '@types/react-dom': + specifier: ^18.0.11 + version: 18.0.11 + '@typescript-eslint/eslint-plugin': + specifier: ^5.59.0 + version: 5.59.0(@typescript-eslint/parser@5.59.0)(eslint@8.38.0)(typescript@5.0.2) + '@typescript-eslint/parser': + specifier: ^5.59.0 + version: 5.59.0(eslint@8.38.0)(typescript@5.0.2) + '@vitejs/plugin-react': + specifier: ^4.0.0 + version: 4.0.0(vite@4.3.9) + autoprefixer: + specifier: ^10.4.14 + version: 10.4.14(postcss@8.4.24) + eslint: + specifier: ^8.38.0 + version: 8.38.0 + eslint-plugin-react-hooks: + specifier: ^4.6.0 + version: 4.6.0(eslint@8.38.0) + eslint-plugin-react-refresh: + specifier: ^0.3.4 + version: 0.3.4(eslint@8.38.0) + jest: + specifier: ^29.5.0 + version: 29.5.0(@types/node@20.3.1)(ts-node@10.9.1) + postcss: + specifier: ^8.4.24 + version: 8.4.24 + tailwindcss: + specifier: ^3.2.7 + version: 3.2.7(postcss@8.4.24)(ts-node@10.9.1) + ts-jest: + specifier: ^29.1.0 + version: 29.1.0(@babel/core@7.22.5)(jest@29.5.0)(typescript@5.0.2) + typescript: + specifier: ^5.0.2 + version: 5.0.2 + vite: + specifier: ^4.3.9 + version: 4.3.9(@types/node@20.3.1) + vite-plugin-checker: + specifier: ^0.6.1 + version: 0.6.1(eslint@8.38.0)(typescript@5.0.2)(vite@4.3.9) + examples/headless-example: dependencies: + '@ballerine/common': + specifier: 0.5.6 + version: link:../../packages/common '@ballerine/workflow-browser-sdk': - specifier: ^0.4.2 + specifier: ^0.4.9 version: link:../../sdks/workflow-browser-sdk '@felte/reporter-svelte': specifier: ^1.1.5 - version: 1.1.5(svelte@3.58.0) + version: 1.1.5(svelte@3.57.0) '@felte/validator-zod': specifier: ^1.0.13 version: 1.0.13(zod@3.21.4) @@ -308,22 +517,22 @@ importers: version: 4.5.15 '@tanstack/svelte-query': specifier: ^4.27.0 - version: 4.29.1(svelte@3.58.0) + version: 4.27.0(svelte@3.57.0) '@xstate/svelte': specifier: ^2.0.1 - version: 2.0.1(svelte@3.58.0)(xstate@4.37.1) + version: 2.0.1(svelte@3.57.0)(xstate@4.37.1) clsx: specifier: ^1.2.1 version: 1.2.1 felte: specifier: ^1.2.7 - version: 1.2.7(svelte@3.58.0) + version: 1.2.7(svelte@3.57.0) tailwind-merge: specifier: ^1.8.1 - version: 1.12.0 + version: 1.10.0 vite-tsconfig-paths: specifier: ^4.0.7 - version: 4.2.0(typescript@4.9.5)(vite@4.2.1) + version: 4.0.7(typescript@4.9.5)(vite@4.2.1) xstate: specifier: 4.37.1 version: 4.37.1 @@ -334,12 +543,18 @@ importers: '@felte/core': specifier: ^1.3.7 version: 1.3.7 + '@playwright/test': + specifier: ^1.35.1 + version: 1.35.1 '@sveltejs/vite-plugin-svelte': specifier: ^2.0.2 - version: 2.0.4(svelte@3.58.0)(vite@4.2.1) + version: 2.0.3(svelte@3.57.0)(vite@4.2.1) '@tsconfig/svelte': specifier: ^3.0.0 version: 3.0.0 + '@types/node': + specifier: ^20.3.2 + version: 20.3.2 '@xstate/inspect': specifier: ^0.7.1 version: 0.7.1(ws@8.13.0)(xstate@4.37.1) @@ -354,16 +569,16 @@ importers: version: 2.8.8 prettier-plugin-svelte: specifier: ^2.8.0 - version: 2.10.0(prettier@2.8.8)(svelte@3.58.0) + version: 2.10.0(prettier@2.8.8)(svelte@3.57.0) svelte: specifier: ^3.55.1 - version: 3.58.0 + version: 3.57.0 svelte-check: specifier: ^2.10.3 - version: 2.10.3(@babel/core@7.21.4)(postcss@8.4.21)(svelte@3.58.0) + version: 2.10.3(@babel/core@7.21.3)(postcss@8.4.21)(svelte@3.57.0) tailwindcss: specifier: ^3.2.4 - version: 3.3.1(postcss@8.4.21)(ts-node@10.9.1) + version: 3.2.7(postcss@8.4.21)(ts-node@10.9.1) tslib: specifier: ^2.5.0 version: 2.5.0 @@ -372,7 +587,7 @@ importers: version: 4.9.5 vite: specifier: ^4.1.0 - version: 4.2.1(@types/node@18.15.11) + version: 4.2.1(@types/node@20.3.2) packages/common: dependencies: @@ -397,7 +612,7 @@ importers: version: 5.3.1(@babel/core@7.17.9)(@types/babel__core@7.20.0)(rollup@2.70.2) '@rollup/plugin-commonjs': specifier: ^24.0.1 - version: 24.1.0(rollup@2.70.2) + version: 24.0.1(rollup@2.70.2) '@rollup/plugin-node-resolve': specifier: 13.2.1 version: 13.2.1(rollup@2.70.2) @@ -415,13 +630,13 @@ importers: version: 2.0.1 '@types/node': specifier: ^18.14.0 - version: 18.15.11 + version: 18.15.10 '@typescript-eslint/eslint-plugin': specifier: ^5.48.1 - version: 5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.38.0)(typescript@4.9.5) + version: 5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.36.0)(typescript@4.9.5) '@typescript-eslint/parser': specifier: ^5.48.1 - version: 5.58.0(eslint@8.38.0)(typescript@4.9.5) + version: 5.56.0(eslint@8.36.0)(typescript@4.9.5) '@vitest/coverage-istanbul': specifier: ^0.28.4 version: 0.28.5(jsdom@20.0.3) @@ -433,19 +648,22 @@ importers: version: 3.3.0 eslint: specifier: ^8.32.0 - version: 8.38.0 + version: 8.36.0 eslint-config-prettier: specifier: ^6.11.0 - version: 6.15.0(eslint@8.38.0) + version: 6.15.0(eslint@8.36.0) eslint-plugin-eslint-comments: specifier: ^3.2.0 - version: 3.2.0(eslint@8.38.0) + version: 3.2.0(eslint@8.36.0) eslint-plugin-functional: specifier: ^3.0.2 - version: 3.7.2(eslint@8.38.0)(typescript@4.9.5) + version: 3.7.2(eslint@8.36.0)(typescript@4.9.5) eslint-plugin-import: specifier: ^2.22.0 - version: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0) + version: 2.27.5(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-typescript@3.5.3)(eslint@8.36.0) + eslint-plugin-unused-imports: + specifier: ^2.0.0 + version: 2.0.0(@typescript-eslint/eslint-plugin@5.56.0)(eslint@8.36.0) fs-extra: specifier: ^11.1.0 version: 11.1.1 @@ -475,16 +693,19 @@ importers: version: 5.6.0(rollup@2.70.2) ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) + version: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) typescript: specifier: 4.9.5 version: 4.9.5 vite: specifier: ^4.1.1 - version: 4.2.1(@types/node@18.15.11) + version: 4.2.1(@types/node@18.15.10) vitest: specifier: ^0.28.4 version: 0.28.5(jsdom@20.0.3) + zod: + specifier: ^3.21.4 + version: 3.21.4 packages/rules-engine: dependencies: @@ -509,7 +730,7 @@ importers: version: 5.3.1(@babel/core@7.17.9)(@types/babel__core@7.20.0)(rollup@2.70.2) '@rollup/plugin-commonjs': specifier: ^24.0.1 - version: 24.1.0(rollup@2.70.2) + version: 24.0.1(rollup@2.70.2) '@rollup/plugin-node-resolve': specifier: 13.2.1 version: 13.2.1(rollup@2.70.2) @@ -527,10 +748,10 @@ importers: version: 2.0.1 '@typescript-eslint/eslint-plugin': specifier: ^5.48.1 - version: 5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.38.0)(typescript@4.9.5) + version: 5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.36.0)(typescript@4.9.5) '@typescript-eslint/parser': specifier: ^5.48.1 - version: 5.58.0(eslint@8.38.0)(typescript@4.9.5) + version: 5.56.0(eslint@8.36.0)(typescript@4.9.5) '@vitest/coverage-istanbul': specifier: ^0.28.4 version: 0.28.5(jsdom@20.0.3) @@ -542,19 +763,22 @@ importers: version: 3.3.0 eslint: specifier: ^8.32.0 - version: 8.38.0 + version: 8.36.0 eslint-config-prettier: specifier: ^6.11.0 - version: 6.15.0(eslint@8.38.0) + version: 6.15.0(eslint@8.36.0) eslint-plugin-eslint-comments: specifier: ^3.2.0 - version: 3.2.0(eslint@8.38.0) + version: 3.2.0(eslint@8.36.0) eslint-plugin-functional: specifier: ^3.0.2 - version: 3.7.2(eslint@8.38.0)(typescript@4.9.5) + version: 3.7.2(eslint@8.36.0)(typescript@4.9.5) eslint-plugin-import: specifier: ^2.22.0 - version: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0) + version: 2.27.5(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-typescript@3.5.3)(eslint@8.36.0) + eslint-plugin-unused-imports: + specifier: ^2.0.0 + version: 2.0.0(@typescript-eslint/eslint-plugin@5.56.0)(eslint@8.36.0) fs-extra: specifier: ^11.1.0 version: 11.1.1 @@ -578,22 +802,170 @@ importers: version: 5.6.0(rollup@2.70.2) ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) + version: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) typescript: specifier: 4.9.5 version: 4.9.5 vite: specifier: ^4.1.1 - version: 4.2.1(@types/node@18.15.11) + version: 4.2.1(@types/node@18.15.10) vitest: specifier: ^0.28.4 version: 0.28.5(jsdom@20.0.3) + packages/ui: + dependencies: + '@radix-ui/react-dialog': + specifier: ^1.0.2 + version: 1.0.3(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dropdown-menu': + specifier: ^2.0.5 + version: 2.0.5(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-label': + specifier: ^2.0.1 + version: 2.0.1(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-scroll-area': + specifier: ^1.0.2 + version: 1.0.3(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': + specifier: ^1.0.1 + version: 1.0.1(react@18.2.0) + '@tanstack/react-table': + specifier: ^8.9.2 + version: 8.9.2(react-dom@18.2.0)(react@18.2.0) + class-variance-authority: + specifier: ^0.6.0 + version: 0.6.0(typescript@4.9.5) + clsx: + specifier: ^1.2.1 + version: 1.2.1 + dayjs: + specifier: ^1.11.6 + version: 1.11.7 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + lucide-react: + specifier: ^0.144.0 + version: 0.144.0(react@18.2.0) + react-json-view: + specifier: ^1.21.3 + version: 1.21.3(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + tailwind-merge: + specifier: ^1.10.0 + version: 1.10.0 + devDependencies: + '@storybook/addon-essentials': + specifier: ^7.0.26 + version: 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-interactions': + specifier: ^7.0.26 + version: 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-links': + specifier: ^7.0.26 + version: 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-styling': + specifier: ^1.3.2 + version: 1.3.2(less@4.1.3)(postcss@8.4.24)(react-dom@18.2.0)(react@18.2.0)(webpack@5.76.2) + '@storybook/blocks': + specifier: ^7.0.26 + version: 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/builder-vite': + specifier: ^7.0.26 + version: 7.0.26(typescript@4.9.5)(vite@4.4.0) + '@storybook/react': + specifier: ^7.0.26 + version: 7.0.26(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5) + '@storybook/react-vite': + specifier: ^7.0.26 + version: 7.0.26(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5)(vite@4.4.0) + '@storybook/testing-library': + specifier: ^0.0.14-next.2 + version: 0.0.14-next.2 + '@tailwindcss/line-clamp': + specifier: ^0.4.4 + version: 0.4.4(tailwindcss@3.3.2) + '@types/lodash': + specifier: ^4.14.191 + version: 4.14.191 + '@types/node': + specifier: ^20.4.1 + version: 20.4.1 + '@types/react': + specifier: ^18.0.14 + version: 18.0.37 + '@types/react-dom': + specifier: ^18.0.5 + version: 18.0.11 + '@typescript-eslint/eslint-plugin': + specifier: ^5.61.0 + version: 5.61.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@4.9.5) + '@typescript-eslint/parser': + specifier: ^5.61.0 + version: 5.61.0(eslint@8.44.0)(typescript@4.9.5) + '@vitejs/plugin-react': + specifier: ^4.0.1 + version: 4.0.1(vite@4.4.0) + autoprefixer: + specifier: ^10.4.14 + version: 10.4.14(postcss@8.4.24) + eslint: + specifier: ^8.44.0 + version: 8.44.0 + eslint-plugin-react-hooks: + specifier: ^4.6.0 + version: 4.6.0(eslint@8.44.0) + eslint-plugin-react-refresh: + specifier: ^0.4.1 + version: 0.4.1(eslint@8.44.0) + eslint-plugin-storybook: + specifier: ^0.6.6 + version: 0.6.11(eslint@8.44.0)(typescript@4.9.5) + fast-glob: + specifier: ^3.3.0 + version: 3.3.0 + prop-types: + specifier: ^15.8.1 + version: 15.8.1 + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + storybook: + specifier: ^7.0.26 + version: 7.0.26 + tailwindcss: + specifier: ^3.3.2 + version: 3.3.2(ts-node@10.9.1) + tailwindcss-animate: + specifier: ^1.0.5 + version: 1.0.5(tailwindcss@3.3.2) + typescript: + specifier: ^4.9.5 + version: 4.9.5 + vite: + specifier: ^4.4.0 + version: 4.4.0(@types/node@20.4.1)(less@4.1.3) + vite-plugin-dts: + specifier: ^1.6.6 + version: 1.7.3(@types/node@20.4.1)(vite@4.4.0) + vitest: + specifier: ^0.33.0 + version: 0.33.0(less@4.1.3) + packages/workflow-core: dependencies: '@ballerine/common': - specifier: 0.4.3 - version: 0.4.3 + specifier: 0.5.6 + version: link:../common + ajv: + specifier: ^8.12.0 + version: 8.12.0 + jmespath: + specifier: ^0.16.0 + version: 0.16.0 json-logic-js: specifier: ^2.0.2 version: 2.0.2 @@ -615,7 +987,10 @@ importers: version: 5.3.1(@babel/core@7.17.9)(@types/babel__core@7.20.0)(rollup@2.70.2) '@rollup/plugin-commonjs': specifier: ^24.0.1 - version: 24.1.0(rollup@2.70.2) + version: 24.0.1(rollup@2.70.2) + '@rollup/plugin-json': + specifier: ^6.0.0 + version: 6.0.0(rollup@2.70.2) '@rollup/plugin-node-resolve': specifier: 13.2.1 version: 13.2.1(rollup@2.70.2) @@ -624,25 +999,28 @@ importers: version: 4.0.0(rollup@2.70.2) '@rollup/plugin-terser': specifier: ^0.4.0 - version: 0.4.1(rollup@2.70.2) + version: 0.4.0(rollup@2.70.2) '@types/babel__core': specifier: ^7.20.0 version: 7.20.0 '@types/fs-extra': specifier: ^11.0.1 version: 11.0.1 + '@types/jmespath': + specifier: ^0.15.0 + version: 0.15.0 '@types/json-logic-js': specifier: ^2.0.1 version: 2.0.1 '@types/node': specifier: ^18.14.0 - version: 18.15.11 + version: 18.15.10 '@typescript-eslint/eslint-plugin': specifier: ^5.48.1 - version: 5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.38.0)(typescript@4.9.5) + version: 5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.36.0)(typescript@4.9.5) '@typescript-eslint/parser': specifier: ^5.48.1 - version: 5.58.0(eslint@8.38.0)(typescript@4.9.5) + version: 5.56.0(eslint@8.36.0)(typescript@4.9.5) '@vitest/coverage-istanbul': specifier: ^0.28.4 version: 0.28.5(jsdom@20.0.3) @@ -654,22 +1032,31 @@ importers: version: 3.3.0 eslint: specifier: ^8.32.0 - version: 8.38.0 + version: 8.36.0 eslint-config-prettier: specifier: ^6.11.0 - version: 6.15.0(eslint@8.38.0) + version: 6.15.0(eslint@8.36.0) eslint-plugin-eslint-comments: specifier: ^3.2.0 - version: 3.2.0(eslint@8.38.0) + version: 3.2.0(eslint@8.36.0) eslint-plugin-functional: specifier: ^3.0.2 - version: 3.7.2(eslint@8.38.0)(typescript@4.9.5) + version: 3.7.2(eslint@8.36.0)(typescript@4.9.5) eslint-plugin-import: specifier: ^2.22.0 - version: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0) + version: 2.27.5(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-typescript@3.5.3)(eslint@8.36.0) + eslint-plugin-unused-imports: + specifier: ^2.0.0 + version: 2.0.0(@typescript-eslint/eslint-plugin@5.56.0)(eslint@8.36.0) fs-extra: specifier: ^11.1.0 version: 11.1.1 + msw: + specifier: ^1.2.2 + version: 1.2.2(typescript@4.9.5) + node-fetch: + specifier: ^3.3.1 + version: 3.3.1 plugin-babel: specifier: link:@types/@rollup/plugin-babel version: link:@types/@rollup/plugin-babel @@ -696,13 +1083,13 @@ importers: version: 5.6.0(rollup@2.70.2) ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) + version: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) typescript: specifier: 4.9.5 version: 4.9.5 vite: specifier: ^4.1.1 - version: 4.2.1(@types/node@18.15.11) + version: 4.2.1(@types/node@18.15.10) vitest: specifier: ^0.28.4 version: 0.28.5(jsdom@20.0.3) @@ -710,11 +1097,11 @@ importers: sdks/web-ui-sdk: dependencies: '@ballerine/common': - specifier: 0.4.3 - version: 0.4.3 + specifier: 0.5.6 + version: link:../../packages/common '@zerodevx/svelte-toast': specifier: ^0.8.0 - version: 0.8.2 + version: 0.8.0 compressorjs: specifier: ^1.1.1 version: 1.2.1 @@ -730,19 +1117,19 @@ importers: devDependencies: '@babel/core': specifier: ^7.18.5 - version: 7.21.4 + version: 7.21.3 '@playwright/test': specifier: ^1.27.1 - version: 1.32.3 + version: 1.32.1 '@sveltejs/vite-plugin-svelte': specifier: 1.0.8 - version: 1.0.8(svelte@3.58.0)(vite@4.0.3) + version: 1.0.8(svelte@3.57.0)(vite@4.0.3) '@testing-library/jest-dom': specifier: ^5.16.5 version: 5.16.5 '@testing-library/svelte': specifier: ^3.2.2 - version: 3.2.2(svelte@3.58.0) + version: 3.2.2(svelte@3.57.0) '@tsconfig/svelte': specifier: ^2.0.1 version: 2.0.1 @@ -754,16 +1141,16 @@ importers: version: 4.6.7 '@types/node': specifier: ^18.11.9 - version: 18.15.11 + version: 18.15.10 '@types/testing-library__jest-dom': specifier: ^5.14.5 version: 5.14.5 '@typescript-eslint/eslint-plugin': specifier: ^5.41.0 - version: 5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.22.0)(typescript@4.9.5) + version: 5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.22.0)(typescript@4.9.5) '@typescript-eslint/parser': specifier: ^5.41.0 - version: 5.58.0(eslint@8.22.0)(typescript@4.9.5) + version: 5.56.0(eslint@8.22.0)(typescript@4.9.5) editorconfig: specifier: ^1.0.1 version: 1.0.2 @@ -778,7 +1165,10 @@ importers: version: 0.6.11(eslint@8.22.0)(typescript@4.9.5) eslint-plugin-svelte3: specifier: ^4.0.0 - version: 4.0.0(eslint@8.22.0)(svelte@3.58.0) + version: 4.0.0(eslint@8.22.0)(svelte@3.57.0) + eslint-plugin-unused-imports: + specifier: ^2.0.0 + version: 2.0.0(@typescript-eslint/eslint-plugin@5.56.0)(eslint@8.22.0) jsdom: specifier: ^20.0.2 version: 20.0.3 @@ -790,34 +1180,34 @@ importers: version: 2.8.7 prettier-plugin-svelte: specifier: ^2.8.0 - version: 2.10.0(prettier@2.8.7)(svelte@3.58.0) + version: 2.10.0(prettier@2.8.7)(svelte@3.57.0) rollup-plugin-visualizer: specifier: ^5.8.3 version: 5.9.0 svelte: specifier: ^3.39.0 - version: 3.58.0 + version: 3.57.0 svelte-check: specifier: ^2.2.7 - version: 2.10.3(@babel/core@7.21.4)(postcss@8.4.21)(svelte@3.58.0) + version: 2.10.3(@babel/core@7.21.3)(postcss@8.4.21)(svelte@3.57.0) svelte-preprocess: specifier: ^4.9.8 - version: 4.10.7(@babel/core@7.21.4)(postcss@8.4.21)(svelte@3.58.0)(typescript@4.9.5) + version: 4.10.7(@babel/core@7.21.3)(postcss@8.4.21)(svelte@3.57.0)(typescript@4.9.5) typedoc: specifier: ^0.23.23 version: 0.23.28(typescript@4.9.5) typedoc-plugin-markdown: specifier: ^3.14.0 - version: 3.15.1(typedoc@0.23.28) + version: 3.14.0(typedoc@0.23.28) typescript: specifier: ^4.5.4 version: 4.9.5 vite: specifier: 4.0.3 - version: 4.0.3(@types/node@18.15.11) + version: 4.0.3(@types/node@18.15.10) vite-plugin-dts: specifier: ^1.6.6 - version: 1.7.3(@types/node@18.15.11)(vite@4.0.3) + version: 1.7.3(@types/node@18.15.10)(vite@4.0.3) vite-plugin-html: specifier: ^3.2.0 version: 3.2.0(vite@4.0.3) @@ -828,11 +1218,11 @@ importers: sdks/workflow-browser-sdk: dependencies: '@ballerine/common': - specifier: ^0.4.3 + specifier: 0.5.6 version: link:../../packages/common '@ballerine/workflow-core': - specifier: ^0.4.9 - version: link:../../packages/workflow-core + specifier: ^0.4.16 + version: 0.4.19 xstate: specifier: ^4.37.0 version: 4.37.1 @@ -851,7 +1241,10 @@ importers: version: 5.3.1(@babel/core@7.17.9)(@types/babel__core@7.20.0)(rollup@2.70.2) '@rollup/plugin-commonjs': specifier: ^24.0.1 - version: 24.1.0(rollup@2.70.2) + version: 24.0.1(rollup@2.70.2) + '@rollup/plugin-json': + specifier: ^6.0.0 + version: 6.0.0(rollup@2.70.2) '@rollup/plugin-node-resolve': specifier: 13.2.1 version: 13.2.1(rollup@2.70.2) @@ -860,7 +1253,7 @@ importers: version: 4.0.0(rollup@2.70.2) '@rollup/plugin-terser': specifier: ^0.4.0 - version: 0.4.1(rollup@2.70.2) + version: 0.4.0(rollup@2.70.2) '@types/babel__core': specifier: ^7.20.0 version: 7.20.0 @@ -869,13 +1262,13 @@ importers: version: 11.0.1 '@types/node': specifier: ^18.14.0 - version: 18.15.11 + version: 18.15.10 '@typescript-eslint/eslint-plugin': specifier: ^5.48.1 - version: 5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.38.0)(typescript@4.9.5) + version: 5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.36.0)(typescript@4.9.5) '@typescript-eslint/parser': specifier: ^5.48.1 - version: 5.58.0(eslint@8.38.0)(typescript@4.9.5) + version: 5.56.0(eslint@8.36.0)(typescript@4.9.5) '@vitest/coverage-istanbul': specifier: ^0.28.4 version: 0.28.5(jsdom@20.0.3) @@ -890,19 +1283,22 @@ importers: version: 3.3.0 eslint: specifier: ^8.32.0 - version: 8.38.0 + version: 8.36.0 eslint-config-prettier: specifier: ^6.11.0 - version: 6.15.0(eslint@8.38.0) + version: 6.15.0(eslint@8.36.0) eslint-plugin-eslint-comments: specifier: ^3.2.0 - version: 3.2.0(eslint@8.38.0) + version: 3.2.0(eslint@8.36.0) eslint-plugin-functional: specifier: ^3.0.2 - version: 3.7.2(eslint@8.38.0)(typescript@4.9.5) + version: 3.7.2(eslint@8.36.0)(typescript@4.9.5) eslint-plugin-import: specifier: ^2.22.0 - version: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0) + version: 2.27.5(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-typescript@3.5.3)(eslint@8.36.0) + eslint-plugin-unused-imports: + specifier: ^2.0.0 + version: 2.0.0(@typescript-eslint/eslint-plugin@5.56.0)(eslint@8.36.0) fs-extra: specifier: ^11.1.0 version: 11.1.1 @@ -938,13 +1334,13 @@ importers: version: 5.6.0(rollup@2.70.2) ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) + version: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) typescript: specifier: 4.9.5 version: 4.9.5 vite: specifier: ^4.1.1 - version: 4.2.1(@types/node@18.15.11) + version: 4.2.1(@types/node@18.15.10) vitest: specifier: ^0.28.4 version: 0.28.5(jsdom@20.0.3) @@ -952,8 +1348,8 @@ importers: sdks/workflow-node-sdk: dependencies: '@ballerine/workflow-core': - specifier: ^0.4.9 - version: link:../../packages/workflow-core + specifier: 0.4.9 + version: 0.4.9 json-logic-js: specifier: ^2.0.2 version: 2.0.2 @@ -975,7 +1371,10 @@ importers: version: 5.3.1(@babel/core@7.17.9)(@types/babel__core@7.20.0)(rollup@2.70.2) '@rollup/plugin-commonjs': specifier: ^24.0.1 - version: 24.1.0(rollup@2.70.2) + version: 24.0.1(rollup@2.70.2) + '@rollup/plugin-json': + specifier: ^6.0.0 + version: 6.0.0(rollup@2.70.2) '@rollup/plugin-node-resolve': specifier: 13.2.1 version: 13.2.1(rollup@2.70.2) @@ -993,10 +1392,10 @@ importers: version: 2.0.1 '@typescript-eslint/eslint-plugin': specifier: ^5.48.1 - version: 5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.38.0)(typescript@4.9.5) + version: 5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.36.0)(typescript@4.9.5) '@typescript-eslint/parser': specifier: ^5.48.1 - version: 5.58.0(eslint@8.38.0)(typescript@4.9.5) + version: 5.56.0(eslint@8.36.0)(typescript@4.9.5) '@vitest/coverage-istanbul': specifier: ^0.28.4 version: 0.28.5(jsdom@20.0.3) @@ -1008,19 +1407,22 @@ importers: version: 3.3.0 eslint: specifier: ^8.32.0 - version: 8.38.0 + version: 8.36.0 eslint-config-prettier: specifier: ^6.11.0 - version: 6.15.0(eslint@8.38.0) + version: 6.15.0(eslint@8.36.0) eslint-plugin-eslint-comments: specifier: ^3.2.0 - version: 3.2.0(eslint@8.38.0) + version: 3.2.0(eslint@8.36.0) eslint-plugin-functional: specifier: ^3.0.2 - version: 3.7.2(eslint@8.38.0)(typescript@4.9.5) + version: 3.7.2(eslint@8.36.0)(typescript@4.9.5) eslint-plugin-import: specifier: ^2.22.0 - version: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0) + version: 2.27.5(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-typescript@3.5.3)(eslint@8.36.0) + eslint-plugin-unused-imports: + specifier: ^2.0.0 + version: 2.0.0(@typescript-eslint/eslint-plugin@5.56.0)(eslint@8.36.0) fs-extra: specifier: ^11.1.0 version: 11.1.1 @@ -1044,99 +1446,202 @@ importers: version: 5.6.0(rollup@2.70.2) ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) + version: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) typescript: specifier: 4.9.5 version: 4.9.5 vite: specifier: ^4.1.1 - version: 4.2.1(@types/node@18.15.11) + version: 4.2.1(@types/node@18.15.10) vitest: specifier: ^0.28.5 version: 0.28.5(jsdom@20.0.3) - services/workflows-service: + services/websocket-service: dependencies: - '@aws-sdk/client-s3': - specifier: 3.325.0 - version: 3.325.0 - '@aws-sdk/lib-storage': - specifier: 3.325.0 - version: 3.325.0(@aws-sdk/abort-controller@3.329.0)(@aws-sdk/client-s3@3.325.0) - '@ballerine/common': - specifier: 0.4.4 - version: link:../../packages/common - '@ballerine/workflow-node-sdk': - specifier: ^0.4.2 - version: link:../../sdks/workflow-node-sdk - '@faker-js/faker': - specifier: ^7.6.0 - version: 7.6.0 - '@nestjs/axios': - specifier: ^2.0.0 - version: 2.0.0(@nestjs/common@9.4.0)(axios@1.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) '@nestjs/common': specifier: ^9.3.12 - version: 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/config': - specifier: 2.3.1 - version: 2.3.1(@nestjs/common@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + version: 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) '@nestjs/core': specifier: ^9.3.12 - version: 9.4.0(@nestjs/common@9.4.0)(@nestjs/platform-express@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/devtools-integration': - specifier: ^0.1.4 - version: 0.1.4(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(reflect-metadata@0.1.13) - '@nestjs/event-emitter': - specifier: ^1.4.1 - version: 1.4.1(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(reflect-metadata@0.1.13) - '@nestjs/graphql': - specifier: ^10.0.0 - version: 10.0.0(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(class-transformer@0.5.1)(class-validator@0.14.0)(graphql@16.6.0)(reflect-metadata@0.1.13) - '@nestjs/jwt': - specifier: 10.0.3 - version: 10.0.3(@nestjs/common@9.4.0) - '@nestjs/passport': - specifier: 9.0.3 - version: 9.0.3(@nestjs/common@9.4.0)(passport@0.6.0) + version: 9.3.12(@nestjs/common@9.3.12)(@nestjs/platform-express@9.3.12)(@nestjs/websockets@9.4.2)(reflect-metadata@0.1.13)(rxjs@7.8.0) '@nestjs/platform-express': specifier: ^9.3.12 - version: 9.4.0(@nestjs/common@9.4.0)(@nestjs/core@9.4.0) - '@nestjs/serve-static': - specifier: 3.0.1 - version: 3.0.1(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(express@4.18.2) - '@nestjs/testing': - specifier: ^9.3.12 - version: 9.4.0(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(@nestjs/platform-express@9.4.0) - '@prisma/client': - specifier: ^4.13.0 - version: 4.13.0(prisma@4.13.0) - '@sentry/cli': - specifier: ^2.17.5 - version: 2.17.5 - '@sentry/integrations': - specifier: ^7.52.1 - version: 7.52.1 - '@sentry/node': - specifier: ^7.52.1 - version: 7.52.1 + version: 9.3.12(@nestjs/common@9.3.12)(@nestjs/core@9.3.12) + '@nestjs/platform-ws': + specifier: ^9.4.2 + version: 9.4.2(@nestjs/common@9.3.12)(@nestjs/websockets@9.4.2)(rxjs@7.8.0) + '@nestjs/websockets': + specifier: ^9.4.2 + version: 9.4.2(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(reflect-metadata@0.1.13)(rxjs@7.8.0) '@t3-oss/env-core': specifier: ^0.3.1 version: 0.3.1(typescript@4.9.5)(zod@3.21.4) - '@types/tmp': - specifier: ^0.2.3 - version: 0.2.3 - accesscontrol: - specifier: ^2.2.1 - version: 2.2.1 - ajv: - specifier: ^8.12.0 - version: 8.12.0 - ajv-formats: - specifier: ^2.1.1 - version: 2.1.1(ajv@8.12.0) - axios: - specifier: ^1.4.0 + '@types/ws': + specifier: ^8.5.4 + version: 8.5.4 + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.13 + rxjs: + specifier: ^7.8.0 + version: 7.8.0 + ws: + specifier: ^8.13.0 + version: 8.13.0 + zod: + specifier: ^3.21.4 + version: 3.21.4 + devDependencies: + '@nestjs/cli': + specifier: ^9.3.0 + version: 9.3.0 + '@nestjs/schematics': + specifier: ^9.0.0 + version: 9.0.4(chokidar@3.5.3)(typescript@4.9.5) + '@nestjs/testing': + specifier: ^9.3.12 + version: 9.3.12(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(@nestjs/platform-express@9.3.12) + '@types/express': + specifier: 4.17.9 + version: 4.17.9 + '@types/jest': + specifier: ^26.0.19 + version: 26.0.24 + '@types/node': + specifier: ^18.14.6 + version: 18.15.10 + '@types/supertest': + specifier: 2.0.11 + version: 2.0.11 + '@typescript-eslint/eslint-plugin': + specifier: ^5.54.1 + version: 5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.36.0)(typescript@4.9.5) + '@typescript-eslint/parser': + specifier: ^5.54.1 + version: 5.56.0(eslint@8.36.0)(typescript@4.9.5) + eslint: + specifier: ^8.35.0 + version: 8.36.0 + eslint-config-prettier: + specifier: ^8.7.0 + version: 8.8.0(eslint@8.36.0) + eslint-import-resolver-typescript: + specifier: ^3.5.3 + version: 3.5.3(eslint-plugin-import@2.27.5)(eslint@8.36.0) + eslint-plugin-import: + specifier: ^2.27.5 + version: 2.27.5(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-typescript@3.5.3)(eslint@8.36.0) + jest: + specifier: 29.5.0 + version: 29.5.0(@types/node@18.15.10)(ts-node@10.9.1) + prettier: + specifier: ^2.8.4 + version: 2.8.8 + supertest: + specifier: ^6.1.3 + version: 6.1.3 + ts-jest: + specifier: 29.1.0 + version: 29.1.0(@babel/core@7.22.5)(jest@29.5.0)(typescript@4.9.5) + ts-loader: + specifier: ^9.2.3 + version: 9.2.3(typescript@4.9.5)(webpack@5.76.2) + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) + tsconfig-paths: + specifier: 4.2.0 + version: 4.2.0 + typescript: + specifier: ^4.9.3 + version: 4.9.5 + + services/workflows-service: + dependencies: + '@aws-sdk/client-s3': + specifier: 3.325.0 + version: 3.325.0 + '@aws-sdk/lib-storage': + specifier: 3.325.0 + version: 3.325.0(@aws-sdk/abort-controller@3.347.0)(@aws-sdk/client-s3@3.325.0) + '@ballerine/common': + specifier: 0.5.6 + version: link:../../packages/common + '@ballerine/workflow-node-sdk': + specifier: 0.4.3 + version: link:../../sdks/workflow-node-sdk + '@faker-js/faker': + specifier: ^7.6.0 + version: 7.6.0 + '@nestjs/axios': + specifier: ^2.0.0 + version: 2.0.0(@nestjs/common@9.3.12)(axios@1.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/common': + specifier: ^9.3.12 + version: 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/config': + specifier: 2.3.1 + version: 2.3.1(@nestjs/common@9.3.12)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/core': + specifier: ^9.3.12 + version: 9.3.12(@nestjs/common@9.3.12)(@nestjs/platform-express@9.3.12)(@nestjs/websockets@9.4.2)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/devtools-integration': + specifier: ^0.1.4 + version: 0.1.4(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(reflect-metadata@0.1.13) + '@nestjs/event-emitter': + specifier: ^1.4.1 + version: 1.4.1(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(reflect-metadata@0.1.13) + '@nestjs/graphql': + specifier: ^10.0.0 + version: 10.0.0(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(class-transformer@0.5.1)(class-validator@0.14.0)(graphql@16.6.0)(reflect-metadata@0.1.13) + '@nestjs/jwt': + specifier: 10.0.3 + version: 10.0.3(@nestjs/common@9.3.12) + '@nestjs/passport': + specifier: 9.0.3 + version: 9.0.3(@nestjs/common@9.3.12)(passport@0.6.0) + '@nestjs/platform-express': + specifier: ^9.3.12 + version: 9.3.12(@nestjs/common@9.3.12)(@nestjs/core@9.3.12) + '@nestjs/serve-static': + specifier: 3.0.1 + version: 3.0.1(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(express@4.18.2) + '@nestjs/testing': + specifier: ^9.3.12 + version: 9.3.12(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(@nestjs/platform-express@9.3.12) + '@prisma/client': + specifier: ^4.13.0 + version: 4.13.0(prisma@4.13.0) + '@sentry/cli': + specifier: ^2.17.5 + version: 2.17.5 + '@sentry/integrations': + specifier: ^7.52.1 + version: 7.52.1 + '@sentry/node': + specifier: ^7.52.1 + version: 7.52.1 + '@t3-oss/env-core': + specifier: ^0.3.1 + version: 0.3.1(typescript@4.9.5)(zod@3.21.4) + '@types/tmp': + specifier: ^0.2.3 + version: 0.2.3 + accesscontrol: + specifier: ^2.2.1 + version: 2.2.1 + ajv: + specifier: ^8.12.0 + version: 8.12.0 + ajv-formats: + specifier: ^2.1.1 + version: 2.1.1(ajv@8.12.0) + ajv-keywords: + specifier: ^5.1.0 + version: 5.1.0(ajv@8.12.0) + axios: + specifier: ^1.4.0 version: 1.4.0 bcrypt: specifier: 5.1.0 @@ -1155,19 +1660,22 @@ importers: version: 4.17.1 helmet: specifier: ^6.0.1 - version: 6.1.5 + version: 6.0.1 lodash: specifier: ^4.17.21 version: 4.17.21 multer-s3: specifier: 3.0.1 - version: 3.0.1(@aws-sdk/abort-controller@3.329.0)(@aws-sdk/client-s3@3.325.0) + version: 3.0.1(@aws-sdk/abort-controller@3.347.0)(@aws-sdk/client-s3@3.325.0) nest-access-control: specifier: 2.2.0 version: 2.2.0(@nestjs/graphql@10.0.0) + nestjs-cls: + specifier: ^3.5.0 + version: 3.5.0(@nestjs/common@9.3.12)(@nestjs/core@9.3.12) nestjs-prisma: specifier: 0.20.0 - version: 0.20.0(@nestjs/common@9.4.0)(@prisma/client@4.13.0)(prisma@4.13.0) + version: 0.20.0(@nestjs/common@9.3.12)(@prisma/client@4.13.0)(prisma@4.13.0) passport: specifier: 0.6.0 version: 0.6.0 @@ -1192,6 +1700,9 @@ importers: tmp: specifier: ^0.2.1 version: 0.2.1 + winston: + specifier: ^3.9.0 + version: 3.9.0 zod: specifier: ^3.21.4 version: 3.21.4 @@ -1201,7 +1712,7 @@ importers: version: 9.3.0 '@nestjs/swagger': specifier: 6.2.1 - version: 6.2.1(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) + version: 6.2.1(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) '@types/bcrypt': specifier: 5.0.0 version: 5.0.0 @@ -1216,7 +1727,7 @@ importers: version: 26.0.24 '@types/lodash': specifier: ^4.14.191 - version: 4.14.192 + version: 4.14.191 '@types/multer': specifier: ^1.4.7 version: 1.4.7 @@ -1225,7 +1736,7 @@ importers: version: 3.0.0 '@types/node': specifier: ^18.14.6 - version: 18.15.11 + version: 18.15.10 '@types/normalize-path': specifier: 3.0.0 version: 3.0.0 @@ -1246,25 +1757,31 @@ importers: version: 2.0.11 '@typescript-eslint/eslint-plugin': specifier: ^5.54.1 - version: 5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.38.0)(typescript@4.9.5) + version: 5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.36.0)(typescript@4.9.5) '@typescript-eslint/parser': specifier: ^5.54.1 - version: 5.58.0(eslint@8.38.0)(typescript@4.9.5) + version: 5.56.0(eslint@8.36.0)(typescript@4.9.5) + dayjs: + specifier: ^1.11.6 + version: 1.11.7 dotenv: specifier: ^16.0.3 version: 16.0.3 eslint: specifier: ^8.35.0 - version: 8.38.0 + version: 8.36.0 eslint-config-prettier: specifier: ^8.7.0 - version: 8.8.0(eslint@8.38.0) + version: 8.8.0(eslint@8.36.0) eslint-import-resolver-typescript: specifier: ^3.5.3 - version: 3.5.5(@typescript-eslint/parser@5.58.0)(eslint-plugin-import@2.27.5)(eslint@8.38.0) + version: 3.5.3(eslint-plugin-import@2.27.5)(eslint@8.36.0) eslint-plugin-import: specifier: ^2.27.5 - version: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0) + version: 2.27.5(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-typescript@3.5.3)(eslint@8.36.0) + eslint-plugin-unused-imports: + specifier: ^2.0.0 + version: 2.0.0(@typescript-eslint/eslint-plugin@5.56.0)(eslint@8.36.0) jest: specifier: 27.0.6 version: 27.0.6(ts-node@10.9.1) @@ -1283,12 +1800,15 @@ importers: supertest: specifier: 4.0.2 version: 4.0.2 + testcontainers: + specifier: ^9.8.0 + version: 9.8.0 ts-jest: specifier: 27.0.3 version: 27.0.3(jest@27.0.6)(typescript@4.9.5) ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) + version: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) type-fest: specifier: 0.11.0 version: 0.11.0 @@ -1300,46 +1820,55 @@ importers: dependencies: '@algolia/client-search': specifier: ^4.13.1 - version: 4.17.0 + version: 4.13.1 '@astrojs/mdx': - specifier: ^0.18.3 - version: 0.18.3(astro@1.9.2)(rollup@2.70.2) + specifier: 0.19.7 + version: 0.19.7(astro@2.7.2) '@astrojs/react': - specifier: ^1.2.2 - version: 1.2.2(@types/react-dom@18.0.11)(@types/react@17.0.58)(react-dom@18.2.0)(react@18.2.0) + specifier: ^2.2.1 + version: 2.2.1(@types/react-dom@18.0.11)(@types/react@17.0.45)(react-dom@18.2.0)(react@18.2.0) '@astrojs/solid-js': specifier: ^1.2.3 - version: 1.2.3(@babel/core@7.21.4)(solid-js@1.7.3)(vite@3.2.5) + version: 1.2.3(@babel/core@7.22.5)(solid-js@1.4.3)(vite@3.2.5) + '@astrojs/tailwind': + specifier: ^4.0.0 + version: 4.0.0(astro@2.7.2)(tailwindcss@3.3.2)(ts-node@10.9.1) '@ballerine/common': - specifier: 0.0.1 - version: 0.0.1 + specifier: 0.5.6 + version: link:../../packages/common '@docsearch/css': specifier: ^3.1.0 - version: 3.3.3 + version: 3.1.0 '@docsearch/react': specifier: ^3.1.0 - version: 3.3.3(@algolia/client-search@4.17.0)(@types/react@17.0.58)(react-dom@18.2.0)(react@18.2.0) + version: 3.1.0(@types/react@17.0.45)(react-dom@18.2.0)(react@18.2.0) '@types/node': specifier: ^18.0.0 - version: 18.15.11 + version: 18.15.10 '@types/react': specifier: ^17.0.45 - version: 17.0.58 + version: 17.0.45 '@types/react-dom': specifier: ^18.0.0 version: 18.0.11 astro: - specifier: ^1.6.10 - version: 1.9.2(@types/node@18.15.11)(ts-node@10.9.1) + specifier: ^2.6.6 + version: 2.7.2(@types/node@18.15.10) + react: + specifier: ^18.2.0 + version: 18.2.0 react-dom: - specifier: ^18.1.0 + specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + shiki: + specifier: ^0.14.2 + version: 0.14.2 solid-js: specifier: ^1.4.3 - version: 1.7.3 + version: 1.4.3 vite: specifier: ~3.2.5 - version: 3.2.5(@types/node@18.15.11) + version: 3.2.5(@types/node@18.15.10) devDependencies: '@types/html-escaper': specifier: ^3.0.0 @@ -1349,149 +1878,185 @@ importers: version: 1.0.2 eslint: specifier: ^8.28.0 - version: 8.38.0 + version: 8.44.0 eslint-config-prettier: specifier: ^8.5.0 - version: 8.8.0(eslint@8.38.0) + version: 8.8.0(eslint@8.44.0) eslint-config-standard-with-typescript: specifier: ^23.0.0 - version: 23.0.0(@typescript-eslint/eslint-plugin@5.58.0)(eslint-plugin-import@2.27.5)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.38.0)(typescript@4.9.5) + version: 23.0.0(@typescript-eslint/eslint-plugin@5.61.0)(eslint-plugin-import@2.27.5)(eslint-plugin-n@15.6.1)(eslint-plugin-promise@6.1.1)(eslint@8.44.0)(typescript@4.9.5) eslint-plugin-astro: specifier: ^0.21.0 - version: 0.21.1(eslint@8.38.0) + version: 0.21.0(eslint@8.44.0) + eslint-plugin-unused-imports: + specifier: ^2.0.0 + version: 2.0.0(@typescript-eslint/eslint-plugin@5.61.0)(eslint@8.44.0) html-escaper: specifier: ^3.0.3 version: 3.0.3 prettier: specifier: ^2.8.0 - version: 2.8.7 + version: 2.8.8 prettier-plugin-astro: - specifier: ^0.7.0 - version: 0.7.2 + specifier: ^0.10.0 + version: 0.10.0 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.2.0: resolution: {integrity: sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==} dev: true - /@algolia/autocomplete-core@1.7.4: - resolution: {integrity: sha512-daoLpQ3ps/VTMRZDEBfU8ixXd+amZcNJ4QSP3IERGyzqnL5Ch8uSRFt/4G8pUvW9c3o6GA4vtVv4I4lmnkdXyg==} + /@algolia/autocomplete-core@1.6.3: + resolution: {integrity: sha512-dqQqRt01fX3YuVFrkceHsoCnzX0bLhrrg8itJI1NM68KjrPYQPYsE+kY8EZTCM4y8VDnhqJErR73xe/ZsV+qAA==} dependencies: - '@algolia/autocomplete-shared': 1.7.4 + '@algolia/autocomplete-shared': 1.6.3 dev: false - /@algolia/autocomplete-preset-algolia@1.7.4(@algolia/client-search@4.17.0)(algoliasearch@4.17.0): - resolution: {integrity: sha512-s37hrvLEIfcmKY8VU9LsAXgm2yfmkdHT3DnA3SgHaY93yjZ2qL57wzb5QweVkYuEBZkT2PIREvRoLXC2sxTbpQ==} - peerDependencies: - '@algolia/client-search': '>= 4.9.1 < 6' - algoliasearch: '>= 4.9.1 < 6' + /@algolia/autocomplete-shared@1.6.3: + resolution: {integrity: sha512-UV46bnkTztyADFaETfzFC5ryIdGVb2zpAoYgu0tfcuYWjhg1KbLXveFffZIrGVoboqmAk1b+jMrl6iCja1i3lg==} + dev: false + + /@algolia/cache-browser-local-storage@4.18.0: + resolution: {integrity: sha512-rUAs49NLlO8LVLgGzM4cLkw8NJLKguQLgvFmBEe3DyzlinoqxzQMHfKZs6TSq4LZfw/z8qHvRo8NcTAAUJQLcw==} dependencies: - '@algolia/autocomplete-shared': 1.7.4 - '@algolia/client-search': 4.17.0 - algoliasearch: 4.17.0 + '@algolia/cache-common': 4.18.0 dev: false - /@algolia/autocomplete-shared@1.7.4: - resolution: {integrity: sha512-2VGCk7I9tA9Ge73Km99+Qg87w0wzW4tgUruvWAn/gfey1ZXgmxZtyIRBebk35R1O8TbK77wujVtCnpsGpRy1kg==} + /@algolia/cache-common@4.13.1: + resolution: {integrity: sha512-7Vaf6IM4L0Jkl3sYXbwK+2beQOgVJ0mKFbz/4qSxKd1iy2Sp77uTAazcX+Dlexekg1fqGUOSO7HS4Sx47ZJmjA==} dev: false - /@algolia/cache-browser-local-storage@4.17.0: - resolution: {integrity: sha512-myRSRZDIMYB8uCkO+lb40YKiYHi0fjpWRtJpR/dgkaiBlSD0plRyB6lLOh1XIfmMcSeBOqDE7y9m8xZMrXYfyQ==} + /@algolia/cache-common@4.18.0: + resolution: {integrity: sha512-BmxsicMR4doGbeEXQu8yqiGmiyvpNvejYJtQ7rvzttEAMxOPoWEHrWyzBQw4x7LrBY9pMrgv4ZlUaF8PGzewHg==} + dev: false + + /@algolia/cache-in-memory@4.18.0: + resolution: {integrity: sha512-evD4dA1nd5HbFdufBxLqlJoob7E2ozlqJZuV3YlirNx5Na4q1LckIuzjNYZs2ddLzuTc/Xd5O3Ibf7OwPskHxw==} dependencies: - '@algolia/cache-common': 4.17.0 + '@algolia/cache-common': 4.18.0 dev: false - /@algolia/cache-common@4.17.0: - resolution: {integrity: sha512-g8mXzkrcUBIPZaulAuqE7xyHhLAYAcF2xSch7d9dABheybaU3U91LjBX6eJTEB7XVhEsgK4Smi27vWtAJRhIKQ==} + /@algolia/client-account@4.18.0: + resolution: {integrity: sha512-XsDnlROr3+Z1yjxBJjUMfMazi1V155kVdte6496atvBgOEtwCzTs3A+qdhfsAnGUvaYfBrBkL0ThnhMIBCGcew==} + dependencies: + '@algolia/client-common': 4.18.0 + '@algolia/client-search': 4.18.0 + '@algolia/transporter': 4.18.0 dev: false - /@algolia/cache-in-memory@4.17.0: - resolution: {integrity: sha512-PT32ciC/xI8z919d0oknWVu3kMfTlhQn3MKxDln3pkn+yA7F7xrxSALysxquv+MhFfNAcrtQ/oVvQVBAQSHtdw==} + /@algolia/client-analytics@4.18.0: + resolution: {integrity: sha512-chEUSN4ReqU7uRQ1C8kDm0EiPE+eJeAXiWcBwLhEynfNuTfawN9P93rSZktj7gmExz0C8XmkbBU19IQ05wCNrQ==} dependencies: - '@algolia/cache-common': 4.17.0 + '@algolia/client-common': 4.18.0 + '@algolia/client-search': 4.18.0 + '@algolia/requester-common': 4.18.0 + '@algolia/transporter': 4.18.0 dev: false - /@algolia/client-account@4.17.0: - resolution: {integrity: sha512-sSEHx9GA6m7wrlsSMNBGfyzlIfDT2fkz2u7jqfCCd6JEEwmxt8emGmxAU/0qBfbhRSuGvzojoLJlr83BSZAKjA==} + /@algolia/client-common@4.13.1: + resolution: {integrity: sha512-LcDoUE0Zz3YwfXJL6lJ2OMY2soClbjrrAKB6auYVMNJcoKZZ2cbhQoFR24AYoxnGUYBER/8B+9sTBj5bj/Gqbg==} dependencies: - '@algolia/client-common': 4.17.0 - '@algolia/client-search': 4.17.0 - '@algolia/transporter': 4.17.0 + '@algolia/requester-common': 4.13.1 + '@algolia/transporter': 4.13.1 dev: false - /@algolia/client-analytics@4.17.0: - resolution: {integrity: sha512-84ooP8QA3mQ958hQ9wozk7hFUbAO+81CX1CjAuerxBqjKIInh1fOhXKTaku05O/GHBvcfExpPLIQuSuLYziBXQ==} + /@algolia/client-common@4.18.0: + resolution: {integrity: sha512-7N+soJFP4wn8tjTr3MSUT/U+4xVXbz4jmeRfWfVAzdAbxLAQbHa0o/POSdTvQ8/02DjCLelloZ1bb4ZFVKg7Wg==} dependencies: - '@algolia/client-common': 4.17.0 - '@algolia/client-search': 4.17.0 - '@algolia/requester-common': 4.17.0 - '@algolia/transporter': 4.17.0 + '@algolia/requester-common': 4.18.0 + '@algolia/transporter': 4.18.0 dev: false - /@algolia/client-common@4.17.0: - resolution: {integrity: sha512-jHMks0ZFicf8nRDn6ma8DNNsdwGgP/NKiAAL9z6rS7CymJ7L0+QqTJl3rYxRW7TmBhsUH40wqzmrG6aMIN/DrQ==} + /@algolia/client-personalization@4.18.0: + resolution: {integrity: sha512-+PeCjODbxtamHcPl+couXMeHEefpUpr7IHftj4Y4Nia1hj8gGq4VlIcqhToAw8YjLeCTfOR7r7xtj3pJcYdP8A==} dependencies: - '@algolia/requester-common': 4.17.0 - '@algolia/transporter': 4.17.0 + '@algolia/client-common': 4.18.0 + '@algolia/requester-common': 4.18.0 + '@algolia/transporter': 4.18.0 dev: false - /@algolia/client-personalization@4.17.0: - resolution: {integrity: sha512-RMzN4dZLIta1YuwT7QC9o+OeGz2cU6eTOlGNE/6RcUBLOU3l9tkCOdln5dPE2jp8GZXPl2yk54b2nSs1+pAjqw==} + /@algolia/client-search@4.13.1: + resolution: {integrity: sha512-YQKYA83MNRz3FgTNM+4eRYbSmHi0WWpo019s5SeYcL3HUan/i5R09VO9dk3evELDFJYciiydSjbsmhBzbpPP2A==} dependencies: - '@algolia/client-common': 4.17.0 - '@algolia/requester-common': 4.17.0 - '@algolia/transporter': 4.17.0 + '@algolia/client-common': 4.13.1 + '@algolia/requester-common': 4.13.1 + '@algolia/transporter': 4.13.1 dev: false - /@algolia/client-search@4.17.0: - resolution: {integrity: sha512-x4P2wKrrRIXszT8gb7eWsMHNNHAJs0wE7/uqbufm4tZenAp+hwU/hq5KVsY50v+PfwM0LcDwwn/1DroujsTFoA==} + /@algolia/client-search@4.18.0: + resolution: {integrity: sha512-F9xzQXTjm6UuZtnsLIew6KSraXQ0AzS/Ee+OD+mQbtcA/K1sg89tqb8TkwjtiYZ0oij13u3EapB3gPZwm+1Y6g==} dependencies: - '@algolia/client-common': 4.17.0 - '@algolia/requester-common': 4.17.0 - '@algolia/transporter': 4.17.0 + '@algolia/client-common': 4.18.0 + '@algolia/requester-common': 4.18.0 + '@algolia/transporter': 4.18.0 + dev: false + + /@algolia/logger-common@4.13.1: + resolution: {integrity: sha512-L6slbL/OyZaAXNtS/1A8SAbOJeEXD5JcZeDCPYDqSTYScfHu+2ePRTDMgUTY4gQ7HsYZ39N1LujOd8WBTmM2Aw==} dev: false - /@algolia/logger-common@4.17.0: - resolution: {integrity: sha512-DGuoZqpTmIKJFDeyAJ7M8E/LOenIjWiOsg1XJ1OqAU/eofp49JfqXxbfgctlVZVmDABIyOz8LqEoJ6ZP4DTyvw==} + /@algolia/logger-common@4.18.0: + resolution: {integrity: sha512-46etYgSlkoKepkMSyaoriSn2JDgcrpc/nkOgou/lm0y17GuMl9oYZxwKKTSviLKI5Irk9nSKGwnBTQYwXOYdRg==} dev: false - /@algolia/logger-console@4.17.0: - resolution: {integrity: sha512-zMPvugQV/gbXUvWBCzihw6m7oxIKp48w37QBIUu/XqQQfxhjoOE9xyfJr1KldUt5FrYOKZJVsJaEjTsu+bIgQg==} + /@algolia/logger-console@4.18.0: + resolution: {integrity: sha512-3P3VUYMl9CyJbi/UU1uUNlf6Z8N2ltW3Oqhq/nR7vH0CjWv32YROq3iGWGxB2xt3aXobdUPXs6P0tHSKRmNA6g==} dependencies: - '@algolia/logger-common': 4.17.0 + '@algolia/logger-common': 4.18.0 dev: false - /@algolia/requester-browser-xhr@4.17.0: - resolution: {integrity: sha512-aSOX/smauyTkP21Pf52pJ1O2LmNFJ5iHRIzEeTh0mwBeADO4GdG94cAWDILFA9rNblq/nK3EDh3+UyHHjplZ1A==} + /@algolia/requester-browser-xhr@4.18.0: + resolution: {integrity: sha512-/AcWHOBub2U4TE/bPi4Gz1XfuLK6/7dj4HJG+Z2SfQoS1RjNLshZclU3OoKIkFp8D2NC7+BNsPvr9cPLyW8nyQ==} dependencies: - '@algolia/requester-common': 4.17.0 + '@algolia/requester-common': 4.18.0 + dev: false + + /@algolia/requester-common@4.13.1: + resolution: {integrity: sha512-eGVf0ID84apfFEuXsaoSgIxbU3oFsIbz4XiotU3VS8qGCJAaLVUC5BUJEkiFENZIhon7hIB4d0RI13HY4RSA+w==} + dev: false + + /@algolia/requester-common@4.18.0: + resolution: {integrity: sha512-xlT8R1qYNRBCi1IYLsx7uhftzdfsLPDGudeQs+xvYB4sQ3ya7+ppolB/8m/a4F2gCkEO6oxpp5AGemM7kD27jA==} dev: false - /@algolia/requester-common@4.17.0: - resolution: {integrity: sha512-XJjmWFEUlHu0ijvcHBoixuXfEoiRUdyzQM6YwTuB8usJNIgShua8ouFlRWF8iCeag0vZZiUm4S2WCVBPkdxFgg==} + /@algolia/requester-node-http@4.18.0: + resolution: {integrity: sha512-TGfwj9aeTVgOUhn5XrqBhwUhUUDnGIKlI0kCBMdR58XfXcfdwomka+CPIgThRbfYw04oQr31A6/95ZH2QVJ9UQ==} + dependencies: + '@algolia/requester-common': 4.18.0 dev: false - /@algolia/requester-node-http@4.17.0: - resolution: {integrity: sha512-bpb/wDA1aC6WxxM8v7TsFspB7yBN3nqCGs2H1OADolQR/hiAIjAxusbuMxVbRFOdaUvAIqioIIkWvZdpYNIn8w==} + /@algolia/transporter@4.13.1: + resolution: {integrity: sha512-pICnNQN7TtrcYJqqPEXByV8rJ8ZRU2hCiIKLTLRyNpghtQG3VAFk6fVtdzlNfdUGZcehSKGarPIZEHlQXnKjgw==} dependencies: - '@algolia/requester-common': 4.17.0 + '@algolia/cache-common': 4.13.1 + '@algolia/logger-common': 4.13.1 + '@algolia/requester-common': 4.13.1 dev: false - /@algolia/transporter@4.17.0: - resolution: {integrity: sha512-6xL6H6fe+Fi0AEP3ziSgC+G04RK37iRb4uUUqVAH9WPYFI8g+LYFq6iv5HS8Cbuc5TTut+Bwj6G+dh/asdb9uA==} + /@algolia/transporter@4.18.0: + resolution: {integrity: sha512-xbw3YRUGtXQNG1geYFEDDuFLZt4Z8YNKbamHPkzr3rWc6qp4/BqEeXcI2u/P/oMq2yxtXgMxrCxOPA8lyIe5jw==} dependencies: - '@algolia/cache-common': 4.17.0 - '@algolia/logger-common': 4.17.0 - '@algolia/requester-common': 4.17.0 + '@algolia/cache-common': 4.18.0 + '@algolia/logger-common': 4.18.0 + '@algolia/requester-common': 4.18.0 dev: false - /@ampproject/remapping@2.2.1: - resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + /@ampproject/remapping@2.2.0: + resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} engines: {node: '>=6.0.0'} dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/gen-mapping': 0.1.1 + '@jridgewell/trace-mapping': 0.3.17 /@angular-devkit/core@13.3.11: resolution: {integrity: sha512-rfqoLMRYhlz0wzKlHx7FfyIyQq8dKTsmbCoIVU1cEIH0gyTMVY7PbVzwRRcO6xp5waY+0hA+0Brriujpuhkm4w==} @@ -1510,6 +2075,23 @@ packages: source-map: 0.7.3 dev: false + /@angular-devkit/core@15.0.4(chokidar@3.5.3): + resolution: {integrity: sha512-4ITpRAevd652SxB+qNesIQ9qfbm7wT5UBU5kJOPPwGL77I21g8CQpkmV1n5VSacPvC9Zbz90feOWexf7w7JzcA==} + engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^3.5.2 + peerDependenciesMeta: + chokidar: + optional: true + dependencies: + ajv: 8.11.0 + ajv-formats: 2.1.1(ajv@8.11.0) + chokidar: 3.5.3 + jsonc-parser: 3.2.0 + rxjs: 6.6.7 + source-map: 0.7.4 + dev: true + /@angular-devkit/core@15.2.4(chokidar@3.5.3): resolution: {integrity: sha512-yl+0j1bMwJLKShsyCXw77tbJG8Sd21+itisPLL2MgEpLNAO252kr9zG4TLlFRJyKVftm2l1h78KjqvM5nbOXNg==} engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} @@ -1555,6 +2137,19 @@ packages: - chokidar dev: false + /@angular-devkit/schematics@15.0.4(chokidar@3.5.3): + resolution: {integrity: sha512-/gXiLFS0+xFdx6wPoBpe/c6/K9I5edMpaASqPf4XheKtrsSvL+qTlIi3nsbfItzOiDXbaBmlbxGfkMHz/yg0Ig==} + engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + dependencies: + '@angular-devkit/core': 15.0.4(chokidar@3.5.3) + jsonc-parser: 3.2.0 + magic-string: 0.26.7 + ora: 5.4.1 + rxjs: 6.6.7 + transitivePeerDependencies: + - chokidar + dev: true + /@angular-devkit/schematics@15.2.4(chokidar@3.5.3): resolution: {integrity: sha512-/W7/vvn59PAVLzhcvD4/N/E8RDhub8ny1A7I96LTRjC5o+yvVV16YJ4YJzolrRrIEN01KmLVQJ9A58VCaweMgw==} engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} @@ -1577,22 +2172,27 @@ packages: js-yaml: 4.1.0 dev: false - /@astrojs/compiler@0.31.4: - resolution: {integrity: sha512-6bBFeDTtPOn4jZaiD3p0f05MEGQL9pw2Zbfj546oFETNmjJFWO3nzHz6/m+P53calknCvyVzZ5YhoBLIvzn5iw==} - /@astrojs/compiler@0.32.0: resolution: {integrity: sha512-QL5qMGkfsC1/kDjJF4RRagz8/hACBUb19cHWrQ8AROphS42qXM6JhoO1Og5FohV3p2VfT5CdEJspn4uNsgZvmw==} dev: true - /@astrojs/language-server@0.28.3: - resolution: {integrity: sha512-fPovAX/X46eE2w03jNRMpQ7W9m2mAvNt4Ay65lD9wl1Z5vIQYxlg7Enp9qP225muTr4jSVB5QiLumFJmZMAaVA==} + /@astrojs/compiler@1.5.1: + resolution: {integrity: sha512-iIGKu/uzB8sJ5VveQf0eHrVPPFEcrvSlp4qShYMOuY2aMmK2RVXQlX9dUjtmBQ+NAokfIOb7fwCutvH+p13l+g==} + + /@astrojs/internal-helpers@0.1.1: + resolution: {integrity: sha512-+LySbvFbjv2nO2m/e78suleQOGEru4Cnx73VsZbrQgB2u7A4ddsQg3P2T0zC0e10jgcT+c6nNlKeLpa6nRhQIg==} + dev: false + + /@astrojs/language-server@1.0.8: + resolution: {integrity: sha512-gssRxLGb8XnvKpqSzrDW5jdzdFnXD7eBXVkPCkkt2hv7Qzb+SAzv6hVgMok3jDCxpR1aeB+XNd9Qszj2h29iog==} hasBin: true dependencies: + '@astrojs/compiler': 1.5.1 + '@jridgewell/trace-mapping': 0.3.17 '@vscode/emmet-helper': 2.8.6 events: 3.3.0 prettier: 2.8.8 - prettier-plugin-astro: 0.7.2 - source-map: 0.7.4 + prettier-plugin-astro: 0.9.1 vscode-css-languageservice: 6.2.4 vscode-html-languageservice: 5.0.4 vscode-languageserver: 8.1.0 @@ -1602,44 +2202,13 @@ packages: vscode-uri: 3.0.7 dev: false - /@astrojs/markdown-remark@1.2.0: - resolution: {integrity: sha512-Cb+uhSuukyfERknfJ8K4iJLeKJaiZWi1BTwPS4fzw0bc9kGKe5VeTRzd2E25+vaMnRTk0tN/y6QfYEMMN3Q97g==} - dependencies: - '@astrojs/micromark-extension-mdx-jsx': 1.0.3 - '@astrojs/prism': 1.0.2 - acorn: 8.8.2 - acorn-jsx: 5.3.2(acorn@8.8.2) - github-slugger: 1.5.0 - hast-util-to-html: 8.0.4 - import-meta-resolve: 2.2.2 - mdast-util-from-markdown: 1.3.0 - mdast-util-mdx-expression: 1.3.2 - mdast-util-mdx-jsx: 1.2.0 - micromark-extension-mdx-expression: 1.0.4 - micromark-extension-mdx-md: 1.0.0 - micromark-util-combine-extensions: 1.0.0 - rehype-raw: 6.1.1 - rehype-stringify: 9.0.3 - remark-gfm: 3.0.1 - remark-parse: 10.0.1 - remark-rehype: 10.1.0 - remark-smartypants: 2.0.0 - shiki: 0.11.1 - unified: 10.1.2 - unist-util-map: 3.1.3 - unist-util-visit: 4.1.2 - vfile: 5.3.7 - transitivePeerDependencies: - - supports-color - dev: false - - /@astrojs/markdown-remark@2.1.4(astro@1.9.2): - resolution: {integrity: sha512-z5diCcFo2xkBAJ11KySAIKpZZkULZmzUvWsZ2VWIOrR6QrEgEfVl5jTpgPSedx4m+xUPuemlUviOotGB7ItNsQ==} + /@astrojs/markdown-remark@2.2.1(astro@2.7.2): + resolution: {integrity: sha512-VF0HRv4GpC1XEMLnsKf6jth7JSmlt9qpqP0josQgA2eSpCIAC/Et+y94mgdBIZVBYH/yFnMoIxgKVe93xfO2GA==} peerDependencies: - astro: ^2.3.0 + astro: ^2.5.0 dependencies: - '@astrojs/prism': 2.1.1 - astro: 1.9.2(@types/node@18.15.11)(ts-node@10.9.1) + '@astrojs/prism': 2.1.2 + astro: 2.7.2(@types/node@18.15.10) github-slugger: 1.5.0 import-meta-resolve: 2.2.2 rehype-raw: 6.1.1 @@ -1648,7 +2217,7 @@ packages: remark-parse: 10.0.1 remark-rehype: 10.1.0 remark-smartypants: 2.0.0 - shiki: 0.11.1 + shiki: 0.14.3 unified: 10.1.2 unist-util-visit: 4.1.2 vfile: 5.3.7 @@ -1656,73 +2225,52 @@ packages: - supports-color dev: false - /@astrojs/mdx@0.18.3(astro@1.9.2)(rollup@2.70.2): - resolution: {integrity: sha512-fFkzYthnFqxmdp6IesvzU6FDHdAGo9bf4dbMOPCREcBfEhATqSpT9gjK/HdJ5s1MfZI8jjYeSC3yzhmNlq62qA==} + /@astrojs/mdx@0.19.7(astro@2.7.2): + resolution: {integrity: sha512-mfEbBD7oi8yBHhcJucEjnrquREkJ3os+jioURP8BR2B8tOV2rV2j8trvmLUgfS+P/+HevGObxCTjcRYxn6T7eg==} engines: {node: '>=16.12.0'} dependencies: - '@astrojs/markdown-remark': 2.1.4(astro@1.9.2) - '@astrojs/prism': 2.1.1 + '@astrojs/markdown-remark': 2.2.1(astro@2.7.2) + '@astrojs/prism': 2.1.2 '@mdx-js/mdx': 2.3.0 - '@mdx-js/rollup': 2.3.0(rollup@2.70.2) - acorn: 8.8.2 - es-module-lexer: 1.2.1 + acorn: 8.9.0 + es-module-lexer: 1.3.0 estree-util-visit: 1.2.1 github-slugger: 1.5.0 gray-matter: 4.0.3 + hast-util-to-html: 8.0.4 kleur: 4.1.5 rehype-raw: 6.1.1 remark-frontmatter: 4.0.1 remark-gfm: 3.0.1 remark-smartypants: 2.0.0 - shiki: 0.11.1 + shiki: 0.14.3 + source-map: 0.7.4 unist-util-visit: 4.1.2 vfile: 5.3.7 transitivePeerDependencies: - astro - - rollup - supports-color dev: false - /@astrojs/micromark-extension-mdx-jsx@1.0.3: - resolution: {integrity: sha512-O15+i2DGG0qb1R/1SYbFXgOKDGbYdV8iJMtuboVb1S9YFQfMOJxaCMco0bhXQI7PmZcQ4pZWIjT5oZ64dXUtRA==} - dependencies: - '@types/acorn': 4.0.6 - estree-util-is-identifier-name: 2.1.0 - micromark-factory-mdx-expression: 1.0.7 - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.6 - vfile-message: 3.1.4 - dev: false - - /@astrojs/prism@1.0.2: - resolution: {integrity: sha512-o3cUVoAuALDqdN5puNlsN2eO4Yi1kDh68YO8V7o6U4Ts+J/mMayzlJ7JsgYAmob0xrf/XnADVgu8khfMv/w3uA==} - engines: {node: ^14.18.0 || >=16.12.0} - dependencies: - prismjs: 1.29.0 - dev: false - - /@astrojs/prism@2.1.1: - resolution: {integrity: sha512-Gnwnlb1lGJzCQEg89r4/WqgfCGPNFC7Kuh2D/k289Cbdi/2PD7Lrdstz86y1itDvcb2ijiRqjqWnJ5rsfu/QOA==} + /@astrojs/prism@2.1.2: + resolution: {integrity: sha512-3antim1gb34689GHRQFJ88JEo93HuZKQBnmxDT5W/nxiNz1p/iRxnCTEhIbJhqMOTRbbo5h2ldm5qSxx+TMFQA==} engines: {node: '>=16.12.0'} dependencies: prismjs: 1.29.0 dev: false - /@astrojs/react@1.2.2(@types/react-dom@18.0.11)(@types/react@17.0.58)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ab9fYvzkC74J7N+M3DWQuZgwu7sYjW0aLO3sEAdCX/jZZz+0BhrqS8m9QjtGJyQK/niF4tgJjpPfadopxKc56g==} - engines: {node: ^14.18.0 || >=16.12.0} + /@astrojs/react@2.2.1(@types/react-dom@18.0.11)(@types/react@17.0.45)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-nq5Zr8iWdwjSp5fh1NReaCplwsnL4w5PXAY5XWu1jE/frxEfF/ycGHrrhwWW0uJHX9G+kUtmQLR0GBhlR4FmAw==} + engines: {node: '>=16.12.0'} peerDependencies: '@types/react': ^17.0.50 || ^18.0.21 '@types/react-dom': ^17.0.17 || ^18.0.6 react: ^17.0.2 || ^18.0.0 react-dom: ^17.0.2 || ^18.0.0 dependencies: - '@babel/core': 7.21.4 - '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.21.4) - '@types/react': 17.0.58 + '@babel/core': 7.22.5 + '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.5) + '@types/react': 17.0.45 '@types/react-dom': 18.0.11 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1730,23 +2278,38 @@ packages: - supports-color dev: false - /@astrojs/solid-js@1.2.3(@babel/core@7.21.4)(solid-js@1.7.3)(vite@3.2.5): + /@astrojs/solid-js@1.2.3(@babel/core@7.22.5)(solid-js@1.4.3)(vite@3.2.5): resolution: {integrity: sha512-YyWQVcIeUMGKTnK3myXmBrG6dfYL5qiQNn8fv50jV0nPgahLeSUTIVxtt5WtqmbFr5kGTwDyk63TZoUvCUWJXA==} engines: {node: ^14.18.0 || >=16.12.0} peerDependencies: solid-js: ^1.4.3 dependencies: - babel-preset-solid: 1.7.3(@babel/core@7.21.4) - solid-js: 1.7.3 + babel-preset-solid: 1.7.7(@babel/core@7.22.5) + solid-js: 1.4.3 vitefu: 0.2.4(vite@3.2.5) transitivePeerDependencies: - '@babel/core' - vite dev: false - /@astrojs/telemetry@1.0.1: - resolution: {integrity: sha512-SJVfZHp00f8VZsT1fsx1+6acJGUNt/84xZytV5znPzzNE8RXjlE0rv03llgTsEeUHYZc6uJah91jNojS7RldFg==} - engines: {node: ^14.18.0 || >=16.12.0} + /@astrojs/tailwind@4.0.0(astro@2.7.2)(tailwindcss@3.3.2)(ts-node@10.9.1): + resolution: {integrity: sha512-HmCAXFFes7MUBt5ihdfH1goa8QyGkHejIpz6Z4XBKK9VNYY9G2E3brCn8+pNn5zAOzcwl3FYcuH2AiOa/NGoMQ==} + peerDependencies: + astro: ^2.6.5 + tailwindcss: ^3.0.24 + dependencies: + astro: 2.7.2(@types/node@18.15.10) + autoprefixer: 10.4.14(postcss@8.4.24) + postcss: 8.4.24 + postcss-load-config: 4.0.1(postcss@8.4.24)(ts-node@10.9.1) + tailwindcss: 3.3.2(ts-node@10.9.1) + transitivePeerDependencies: + - ts-node + dev: false + + /@astrojs/telemetry@2.1.1: + resolution: {integrity: sha512-4pRhyeQr0MLB5PKYgkdu+YE8sSpMbHL8dUuslBWBIdgcYjtD1SufPMBI8pgXJ+xlwrQJHKKfK2X1KonHYuOS9A==} + engines: {node: '>=16.12.0'} dependencies: ci-info: 3.8.0 debug: 4.3.4(supports-color@8.1.1) @@ -1754,17 +2317,16 @@ packages: dset: 3.1.2 is-docker: 3.0.0 is-wsl: 2.2.0 - node-fetch: 3.3.1 + undici: 5.22.1 which-pm-runs: 1.1.0 transitivePeerDependencies: - supports-color dev: false - /@astrojs/webapi@1.1.1: - resolution: {integrity: sha512-yeUvP27PoiBK/WCxyQzC4HLYZo4Hg6dzRd/dTsL50WGlAQVCwWcqzVJrIZKvzNDNaW/fIXutZTmdj6nec0PIGg==} + /@astrojs/webapi@2.2.0: + resolution: {integrity: sha512-mHAOApWyjqSe5AQMOUD9rsZJqbMQqe3Wosb1a40JV6Okvyxj1G6GTlthwYadWCymq/lbgwh0PLiY8Fr4eFxtuQ==} dependencies: - global-agent: 3.0.0 - node-fetch: 3.3.1 + undici: 5.22.1 dev: false /@aw-web-design/x-default-browser@1.4.88: @@ -1840,20 +2402,20 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 - /@aws-sdk/abort-controller@3.329.0: - resolution: {integrity: sha512-hzrjPNQcJoSPe0oS20V5i98oiEZSM3mKNiR6P3xHTHTPI/F23lyjGZ+/CSkCmJbSWfGZ5sHZZcU6AWuS7xBdTw==} + /@aws-sdk/abort-controller@3.347.0: + resolution: {integrity: sha512-P/2qE6ntYEmYG4Ez535nJWZbXqgbkJx8CMz7ChEuEg3Gp3dvVYEKg+iEUEvlqQ2U5dWP5J3ehw5po9t86IsVPQ==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.329.0 - tslib: 2.5.0 + '@aws-sdk/types': 3.347.0 + tslib: 2.5.2 dev: false /@aws-sdk/chunked-blob-reader@3.310.0: resolution: {integrity: sha512-CrJS3exo4mWaLnWxfCH+w88Ou0IcAZSIkk4QbmxiHl/5Dq705OLoxf4385MVyExpqpeVJYOYQ2WaD8i/pQZ2fg==} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/client-s3@3.325.0: resolution: {integrity: sha512-S88q84bJ1Wba3jnpIrWxMD4AbDsG7l45Ey4ZDdyriEceEgIWvBCA90ZOgSdaObhFNa5pNFZG9C6UbiaCz3F3bA==} @@ -1952,7 +2514,7 @@ packages: '@aws-sdk/util-user-agent-browser': 3.310.0 '@aws-sdk/util-user-agent-node': 3.310.0 '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 transitivePeerDependencies: - aws-crt @@ -1991,7 +2553,7 @@ packages: '@aws-sdk/util-user-agent-browser': 3.310.0 '@aws-sdk/util-user-agent-node': 3.310.0 '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 transitivePeerDependencies: - aws-crt @@ -2034,7 +2596,7 @@ packages: '@aws-sdk/util-user-agent-node': 3.310.0 '@aws-sdk/util-utf8': 3.310.0 fast-xml-parser: 4.1.2 - tslib: 2.5.0 + tslib: 2.5.2 transitivePeerDependencies: - aws-crt @@ -2045,7 +2607,7 @@ packages: '@aws-sdk/types': 3.310.0 '@aws-sdk/util-config-provider': 3.310.0 '@aws-sdk/util-middleware': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/credential-provider-env@3.310.0: resolution: {integrity: sha512-vvIPQpI16fj95xwS7M3D48F7QhZJBnnCgB5lR+b7So+vsG9ibm1mZRVGzVpdxCvgyOhHFbvrby9aalNJmmIP1A==} @@ -2053,7 +2615,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/credential-provider-imds@3.310.0: resolution: {integrity: sha512-baxK7Zp6dai5AGW01FIW27xS2KAaPUmKLIXv5SvFYsUgXXvNW55im4uG3b+2gA0F7V+hXvVBH08OEqmwW6we5w==} @@ -2063,7 +2625,7 @@ packages: '@aws-sdk/property-provider': 3.310.0 '@aws-sdk/types': 3.310.0 '@aws-sdk/url-parser': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/credential-provider-ini@3.325.0: resolution: {integrity: sha512-jvNEHU4zEBbtvf2JqiC2ENb0Y55BurA5X6KVBP1vA3mvn7+zIGH9qD1nAkpvrRelzuorbC5Ey7AmZshR+AugTg==} @@ -2077,7 +2639,7 @@ packages: '@aws-sdk/property-provider': 3.310.0 '@aws-sdk/shared-ini-file-loader': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 transitivePeerDependencies: - aws-crt @@ -2094,7 +2656,7 @@ packages: '@aws-sdk/property-provider': 3.310.0 '@aws-sdk/shared-ini-file-loader': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 transitivePeerDependencies: - aws-crt @@ -2105,7 +2667,7 @@ packages: '@aws-sdk/property-provider': 3.310.0 '@aws-sdk/shared-ini-file-loader': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/credential-provider-sso@3.325.0: resolution: {integrity: sha512-Te7jxJwjVGJAPWN3jCq2xcYrX8d4WkZFIqSIWSmrHqGKgWPc8+QgUkpRQkHaM+4NeNBJadRG1XbJNfu22gjDWA==} @@ -2116,7 +2678,7 @@ packages: '@aws-sdk/shared-ini-file-loader': 3.310.0 '@aws-sdk/token-providers': 3.325.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 transitivePeerDependencies: - aws-crt @@ -2126,7 +2688,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/eventstream-codec@3.310.0: resolution: {integrity: sha512-clIeSgWbZbxwtsxZ/yoedNM0/kJFSIjjHPikuDGhxhqc+vP6TN3oYyVMFrYwFaTFhk2+S5wZcWYMw8Op1pWo+A==} @@ -2134,7 +2696,7 @@ packages: '@aws-crypto/crc32': 3.0.0 '@aws-sdk/types': 3.310.0 '@aws-sdk/util-hex-encoding': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/eventstream-serde-browser@3.310.0: resolution: {integrity: sha512-3S6ziuQVALgEyz0TANGtYDVeG8ArK4Y05mcgrs8qUTmsvlDIXX37cR/DvmVbNB76M4IrsZeSAIajL9644CywkA==} @@ -2142,14 +2704,14 @@ packages: dependencies: '@aws-sdk/eventstream-serde-universal': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/eventstream-serde-config-resolver@3.310.0: resolution: {integrity: sha512-8s1Qdn9STj+sV75nUp9yt0W6fHS4BZ2jTm4Z/1Pcbvh2Gqs0WjH5n2StS+pDW5Y9J/HSGBl0ogmUr5lC5bXFHg==} engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/eventstream-serde-node@3.310.0: resolution: {integrity: sha512-kSnRomCgW43K9TmQYuwN9+AoYPnhyOKroanUMyZEzJk7rpCPMj4OzaUpXfDYOvznFNYn7NLaH6nHLJAr0VPlJA==} @@ -2157,7 +2719,7 @@ packages: dependencies: '@aws-sdk/eventstream-serde-universal': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/eventstream-serde-universal@3.310.0: resolution: {integrity: sha512-Qyjt5k/waV5cDukpgT824ISZAz5U0pwzLz5ztR409u85AGNkF/9n7MS+LSyBUBSb0WJ5pUeSD47WBk+nLq9Nhw==} @@ -2165,7 +2727,7 @@ packages: dependencies: '@aws-sdk/eventstream-codec': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/fetch-http-handler@3.310.0: resolution: {integrity: sha512-Bi9vIwzdkw1zMcvi/zGzlWS9KfIEnAq4NNhsnCxbQ4OoIRU9wvU+WGZdBBhxg0ZxZmpp1j1aZhU53lLjA07MHw==} @@ -2174,14 +2736,14 @@ packages: '@aws-sdk/querystring-builder': 3.310.0 '@aws-sdk/types': 3.310.0 '@aws-sdk/util-base64': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/hash-blob-browser@3.310.0: resolution: {integrity: sha512-OoR8p0cbypToysLT0v3o2oyjy6+DKrY7GNCAzHOHJK9xmqXCt+DsjKoPeiY7o1sWX2aN6Plmvubj/zWxMKEn/A==} dependencies: '@aws-sdk/chunked-blob-reader': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/hash-node@3.310.0: resolution: {integrity: sha512-NvE2fhRc8GRwCXBfDehxVAWCmVwVMILliAKVPAEr4yz2CkYs0tqU51S48x23dtna07H4qHtgpeNqVTthcIQOEQ==} @@ -2190,7 +2752,7 @@ packages: '@aws-sdk/types': 3.310.0 '@aws-sdk/util-buffer-from': 3.310.0 '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/hash-stream-node@3.310.0: resolution: {integrity: sha512-ZoXdybNgvMz1Hl6k/e32xVL3jmG5p2IEk5mTtLfFEuskTJ74Z+VMYKkkF1whyy7KQfH83H+TQGnsGtlRCchQKw==} @@ -2198,28 +2760,28 @@ packages: dependencies: '@aws-sdk/types': 3.310.0 '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/invalid-dependency@3.310.0: resolution: {integrity: sha512-1s5RG5rSPXoa/aZ/Kqr5U/7lqpx+Ry81GprQ2bxWqJvWQIJ0IRUwo5pk8XFxbKVr/2a+4lZT/c3OGoBOM1yRRA==} dependencies: '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/is-array-buffer@3.310.0: resolution: {integrity: sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 - /@aws-sdk/lib-storage@3.325.0(@aws-sdk/abort-controller@3.329.0)(@aws-sdk/client-s3@3.325.0): + /@aws-sdk/lib-storage@3.325.0(@aws-sdk/abort-controller@3.347.0)(@aws-sdk/client-s3@3.325.0): resolution: {integrity: sha512-QobsFsDDoOs26nLmxo8nTTCwjJmO4jmElqU4R2r2mlf4g3ClSm5+W79jbqbfDKlEGbN4TtDr+FzWYiULOl/TIg==} engines: {node: '>=14.0.0'} peerDependencies: '@aws-sdk/abort-controller': ^3.0.0 '@aws-sdk/client-s3': ^3.0.0 dependencies: - '@aws-sdk/abort-controller': 3.329.0 + '@aws-sdk/abort-controller': 3.347.0 '@aws-sdk/client-s3': 3.325.0 '@aws-sdk/middleware-endpoint': 3.325.0 '@aws-sdk/smithy-client': 3.325.0 @@ -2234,7 +2796,7 @@ packages: dependencies: '@aws-sdk/types': 3.310.0 '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-bucket-endpoint@3.310.0: resolution: {integrity: sha512-uJJfHI7v4AgbJZRLtyI8ap2QRWkBokGc3iyUoQ+dVNT3/CE2ZCu694A6W+H0dRqg79dIE+f9CRNdtLGa/Ehhvg==} @@ -2244,7 +2806,7 @@ packages: '@aws-sdk/types': 3.310.0 '@aws-sdk/util-arn-parser': 3.310.0 '@aws-sdk/util-config-provider': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-content-length@3.325.0: resolution: {integrity: sha512-t38VBKCpNqSKqSu0OfWMJs7cwaRHFGQxIF9lV8JMCM/2lyUpN4JcfuzSTK+MFN2eDZEHp5DiNg8w07GXXusRYg==} @@ -2252,7 +2814,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-endpoint@3.325.0: resolution: {integrity: sha512-3CavuOHCKiWUnCtzrUFbhbEP26qIgzzRs5C3vpOJhDUhugBubIWgPGGRLpbnIro+P4XJPwM3pMziNzhKVuSDlQ==} @@ -2262,7 +2824,7 @@ packages: '@aws-sdk/types': 3.310.0 '@aws-sdk/url-parser': 3.310.0 '@aws-sdk/util-middleware': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-expect-continue@3.325.0: resolution: {integrity: sha512-Hj4D+zeet4gdUpSiMeHZfIzcnXkZI2krGyUw4U1psPzCqOp7WP5307g+1NWXOlVu3H3tF5r3rEgthQOQj2zNfA==} @@ -2270,7 +2832,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-flexible-checksums@3.325.0: resolution: {integrity: sha512-kNcm5psgNT/aSCG+OcRHWYD6HGqaqDhlwhcwPBr03qg4UDBLlNG8qCT5NbXUh/CnrBhPRs7LIwWp85qy2eK7Hw==} @@ -2282,7 +2844,7 @@ packages: '@aws-sdk/protocol-http': 3.310.0 '@aws-sdk/types': 3.310.0 '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-host-header@3.325.0: resolution: {integrity: sha512-IN28gsxcRy4J+FxxCHvzb2NORBx8uMA+h9QYS4BBZfpKVYIZh+mudHgYcdNHWlKXmlTGjhWBNWTeByhzuSKAiA==} @@ -2290,21 +2852,21 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-location-constraint@3.325.0: resolution: {integrity: sha512-T2OrpXXY9I1nHvIGSlQD6qj1FDG3WDFSu65+Bh4pMl+zVh0IqIEajiK++TfrdQl+sJxRGQd/euoeXXL4JYw9JA==} engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-logger@3.325.0: resolution: {integrity: sha512-S8rWgTpN2b/+UDDm+yZMFM6rw1zwO8KT0GAIQbAhB96shyD5eKen/UfihCTB7YMvbD2piebymwJTvxv6bn1VqQ==} engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-recursion-detection@3.325.0: resolution: {integrity: sha512-2l1ABF7KePsoKz8KaNvD2uxo1zHqkFHK4PL/wW/FbcwOcE08f0R7qX++st/bPpVjXX/j/5vWTnNNgJOIOrZhyw==} @@ -2312,7 +2874,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-retry@3.325.0: resolution: {integrity: sha512-oQM5AI3vkNQuCakBMgdohOcvRnVYcBBlv+KzCCj07ue9gk0x2dHOZY2pqTQ2CYilRqS/X1PtLogJXoyHP5Wvwg==} @@ -2323,7 +2885,7 @@ packages: '@aws-sdk/types': 3.310.0 '@aws-sdk/util-middleware': 3.310.0 '@aws-sdk/util-retry': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 uuid: 8.3.2 /@aws-sdk/middleware-sdk-s3@3.325.0: @@ -2333,7 +2895,7 @@ packages: '@aws-sdk/protocol-http': 3.310.0 '@aws-sdk/types': 3.310.0 '@aws-sdk/util-arn-parser': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-sdk-sts@3.325.0: resolution: {integrity: sha512-deRK1ZuNueQ6OOTEhBZ9bLmjPema/N3cwbtO+tDVwpi7MipjE4EZDXX8WL0xza5YLRnz9kxcHuyfL47vvKgO3A==} @@ -2341,14 +2903,14 @@ packages: dependencies: '@aws-sdk/middleware-signing': 3.325.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-serde@3.325.0: resolution: {integrity: sha512-QAZYaFfAw1a06Vg39JiYIq0kSJ6EuUPOiKfK/Goj0cBv78lrXWuKdf04UF3U8Rqk/4mamnsTqUSwf4NoKkF0hw==} engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-signing@3.325.0: resolution: {integrity: sha512-SOwPwaCE3vSCGwFzkIlnOUSkeCUzKTyIQnFVjlQkqGuMxMX/iDaQQGaX+HUbuGIuULCEQqjZH4dLKZcor8eVZw==} @@ -2359,20 +2921,20 @@ packages: '@aws-sdk/signature-v4': 3.310.0 '@aws-sdk/types': 3.310.0 '@aws-sdk/util-middleware': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-ssec@3.325.0: resolution: {integrity: sha512-hxmvvWVfVrbfUw8pDEPlsR6Sb+IUdhq0cOJc7SL5XO9ddRXJ5DjT2Z2ao9FB424hJgAcOrqIO5ECjdIRs+O4FQ==} engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-stack@3.325.0: resolution: {integrity: sha512-cZWehA4grGvX1IKlY9atJgD0bq3ew7YRJgY7GA6DSgsU7GrZ61Qvi+H7IuGx5AdeAwaTnbnTGN4qCaA2EfxNhA==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/middleware-user-agent@3.325.0: resolution: {integrity: sha512-2aIdGId4o8eIStm1J1aWZwNDf6nvrwg5Nx7BomLAxKZ4lkH8knzXDtxaZR4ElcTsBlBcYxz2gbsrScMyKRDTGA==} @@ -2381,7 +2943,7 @@ packages: '@aws-sdk/protocol-http': 3.310.0 '@aws-sdk/types': 3.310.0 '@aws-sdk/util-endpoints': 3.319.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/node-config-provider@3.310.0: resolution: {integrity: sha512-T/Pp6htc6hq/Cq+MLNDSyiwWCMVF6GqbBbXKVlO5L8rdHx4sq9xPdoPveZhGWrxvkanjA6eCwUp6E0riBOSVng==} @@ -2390,7 +2952,7 @@ packages: '@aws-sdk/property-provider': 3.310.0 '@aws-sdk/shared-ini-file-loader': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/node-http-handler@3.321.1: resolution: {integrity: sha512-DdQBrtFFDNtzphJIN3s93Vf+qd9LHSzH6WTQRrWoXhTDMHDzSI2Cn+c5KWfk89Nggp/n3+OTwUPQeCiBT5EBuw==} @@ -2400,21 +2962,21 @@ packages: '@aws-sdk/protocol-http': 3.310.0 '@aws-sdk/querystring-builder': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/property-provider@3.310.0: resolution: {integrity: sha512-3lxDb0akV6BBzmFe4nLPaoliQbAifyWJhuvuDOu7e8NzouvpQXs0275w9LePhhcgjKAEVXUIse05ZW2DLbxo/g==} engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/protocol-http@3.310.0: resolution: {integrity: sha512-fgZ1aw/irQtnrsR58pS8ThKOWo57Py3xX6giRvwSgZDEcxHfVzuQjy9yPuV++v04fdmdtgpbGf8WfvAAJ11yXQ==} engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/querystring-builder@3.310.0: resolution: {integrity: sha512-ZHH8GV/80+pWGo7DzsvwvXR5xVxUHXUvPJPFAkhr6nCf78igdoF8gR10ScFoEKbtEapoNTaZlKHPXxpD8aPG7A==} @@ -2422,14 +2984,14 @@ packages: dependencies: '@aws-sdk/types': 3.310.0 '@aws-sdk/util-uri-escape': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/querystring-parser@3.310.0: resolution: {integrity: sha512-YkIznoP6lsiIUHinx++/lbb3tlMURGGqMpo0Pnn32zYzGrJXA6eC3D0as2EcMjo55onTfuLcIiX4qzXes2MYOA==} engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/service-error-classification@3.310.0: resolution: {integrity: sha512-PuyC7k3qfIKeH2LCnDwbttMOKq3qAx4buvg0yfnJtQOz6t1AR8gsnAq0CjKXXyfkXwNKWTqCpE6lVNUIkXgsMw==} @@ -2440,7 +3002,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/signature-v4-multi-region@3.310.0: resolution: {integrity: sha512-q8W+RIomTS/q85Ntgks/CoDElwqkC9+4OCicee5YznNHjQ4gtNWhUkYIyIRWRmXa/qx/AUreW9DM8FAecCOdng==} @@ -2454,7 +3016,7 @@ packages: '@aws-sdk/protocol-http': 3.310.0 '@aws-sdk/signature-v4': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/signature-v4@3.310.0: resolution: {integrity: sha512-1M60P1ZBNAjCFv9sYW29OF6okktaeibWyW3lMXqzoHF70lHBZh+838iUchznXUA5FLabfn4jBFWMRxlAXJUY2Q==} @@ -2466,7 +3028,7 @@ packages: '@aws-sdk/util-middleware': 3.310.0 '@aws-sdk/util-uri-escape': 3.310.0 '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/smithy-client@3.325.0: resolution: {integrity: sha512-sqDFuhjxd8+Q9qI8MmXe/g1/FgoViwetv14K+bpHK7pGlOIvDyT7TboDNClfgqSLdgTDCEaoC3JRSi9Y5RgbmA==} @@ -2474,7 +3036,7 @@ packages: dependencies: '@aws-sdk/middleware-stack': 3.325.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/token-providers@3.325.0: resolution: {integrity: sha512-h/ecgqXaMwEkUWo+SZJNcOMWJkAknjonWWpK/vQ4uz+qE9rBqRugsIeIiey+Ij7zCtwh6WhtpfCpt5RbxRHe6g==} @@ -2484,7 +3046,7 @@ packages: '@aws-sdk/property-provider': 3.310.0 '@aws-sdk/shared-ini-file-loader': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 transitivePeerDependencies: - aws-crt @@ -2492,13 +3054,13 @@ packages: resolution: {integrity: sha512-j8eamQJ7YcIhw7fneUfs8LYl3t01k4uHi4ZDmNRgtbmbmTTG3FZc2MotStZnp3nZB6vLiPF1o5aoJxWVvkzS6A==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 - /@aws-sdk/types@3.329.0: - resolution: {integrity: sha512-wFBW4yciDfzQBSFmWNaEvHShnSGLMxSu9Lls6EUf6xDMavxSB36bsrVRX6CyAo/W0NeIIyEOW1LclGPgJV1okg==} + /@aws-sdk/types@3.347.0: + resolution: {integrity: sha512-GkCMy79mdjU9OTIe5KT58fI/6uqdf8UmMdWqVHmFJ+UpEzOci7L/uw4sOXWo7xpPzLs6cJ7s5ouGZW4GRPmHFA==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 dev: false /@aws-sdk/url-parser@3.310.0: @@ -2506,44 +3068,44 @@ packages: dependencies: '@aws-sdk/querystring-parser': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-arn-parser@3.310.0: resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-base64@3.310.0: resolution: {integrity: sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==} engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/util-buffer-from': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-body-length-browser@3.310.0: resolution: {integrity: sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-body-length-node@3.310.0: resolution: {integrity: sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-buffer-from@3.310.0: resolution: {integrity: sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==} engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/is-array-buffer': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-config-provider@3.310.0: resolution: {integrity: sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-defaults-mode-browser@3.325.0: resolution: {integrity: sha512-gcowpXTo8E8N3jxD2KW+csiicJ7HPkhWnpL925xgwe0oq091OpATsKFrBOL18h72VfRWf4FAsR9lVwxSQ78zSA==} @@ -2552,7 +3114,7 @@ packages: '@aws-sdk/property-provider': 3.310.0 '@aws-sdk/types': 3.310.0 bowser: 2.11.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-defaults-mode-node@3.325.0: resolution: {integrity: sha512-/5uoOrgNxoUxv3AwsdXjMA3f6KJA6fi69otA0RiINjilCdcbOxq5GI11AFEyRio/+e+imriX4+UYjsguUR+f4g==} @@ -2563,39 +3125,39 @@ packages: '@aws-sdk/node-config-provider': 3.310.0 '@aws-sdk/property-provider': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-endpoints@3.319.0: resolution: {integrity: sha512-3I64UMoYA2e2++oOUJXRcFtYLpLylnZFRltWfPo1B3dLlf+MIWat9djT+mMus+hW1ntLsvAIVu1hLVePJC0gvw==} engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-hex-encoding@3.310.0: resolution: {integrity: sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-locate-window@3.310.0: resolution: {integrity: sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-middleware@3.310.0: resolution: {integrity: sha512-FTSUKL/eRb9X6uEZClrTe27QFXUNNp7fxYrPndZwk1hlaOP5ix+MIHBcI7pIiiY/JPfOUmPyZOu+HetlFXjWog==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-retry@3.310.0: resolution: {integrity: sha512-FwWGhCBLfoivTMUHu1LIn4NjrN9JLJ/aX5aZmbcPIOhZVFJj638j0qDgZXyfvVqBuBZh7M8kGq0Oahy3dp69OA==} engines: {node: '>= 14.0.0'} dependencies: '@aws-sdk/service-error-classification': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-stream-browser@3.310.0: resolution: {integrity: sha512-bysXZHwFwvbqOTCScCdCnoLk1K3GCo0HRIYEZuL7O7MHrQmfaYRXcaft/p22+GUv9VeFXS/eJJZ5r4u32az94w==} @@ -2605,7 +3167,7 @@ packages: '@aws-sdk/util-base64': 3.310.0 '@aws-sdk/util-hex-encoding': 3.310.0 '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-stream-node@3.321.1: resolution: {integrity: sha512-jvfff1zeA8q16hQWSC0BGwcHJPCwoh+bwiuAjihfl9q1tFLYuqaTzJzzkL1bntUsbW+y/ac5DO7fWcYPq0jWkw==} @@ -2614,20 +3176,20 @@ packages: '@aws-sdk/node-http-handler': 3.321.1 '@aws-sdk/types': 3.310.0 '@aws-sdk/util-buffer-from': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-uri-escape@3.310.0: resolution: {integrity: sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-user-agent-browser@3.310.0: resolution: {integrity: sha512-yU/4QnHHuQ5z3vsUqMQVfYLbZGYwpYblPiuZx4Zo9+x0PBkNjYMqctdDcrpoH9Z2xZiDN16AmQGK1tix117ZKw==} dependencies: '@aws-sdk/types': 3.310.0 bowser: 2.11.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-user-agent-node@3.310.0: resolution: {integrity: sha512-Ra3pEl+Gn2BpeE7KiDGpi4zj7WJXZA5GXnGo3mjbi9+Y3zrbuhJAbdZO3mO/o7xDgMC6ph4xCTbaSGzU6b6EDg==} @@ -2640,19 +3202,19 @@ packages: dependencies: '@aws-sdk/node-config-provider': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-utf8-browser@3.259.0: resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-utf8@3.310.0: resolution: {integrity: sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==} engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/util-buffer-from': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/util-waiter@3.310.0: resolution: {integrity: sha512-AV5j3guH/Y4REu+Qh3eXQU9igljHuU4XjX2sADAgf54C0kkhcCCkkiuzk3IsX089nyJCqIcj5idbjdvpnH88Vw==} @@ -2660,40 +3222,52 @@ packages: dependencies: '@aws-sdk/abort-controller': 3.310.0 '@aws-sdk/types': 3.310.0 - tslib: 2.5.0 + tslib: 2.5.2 /@aws-sdk/xml-builder@3.310.0: resolution: {integrity: sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 - /@babel/code-frame@7.21.4: - resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} + /@babel/code-frame@7.18.6: + resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.18.6 + dev: true + + /@babel/code-frame@7.22.5: + resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.5 + + /@babel/compat-data@7.21.0: + resolution: {integrity: sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==} + engines: {node: '>=6.9.0'} + dev: true - /@babel/compat-data@7.21.4: - resolution: {integrity: sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==} + /@babel/compat-data@7.22.5: + resolution: {integrity: sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==} engines: {node: '>=6.9.0'} /@babel/core@7.17.9: resolution: {integrity: sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==} engines: {node: '>=6.9.0'} dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.17.9) - '@babel/helper-module-transforms': 7.21.2 - '@babel/helpers': 7.21.0 - '@babel/parser': 7.21.4 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@ampproject/remapping': 2.2.0 + '@babel/code-frame': 7.22.5 + '@babel/generator': 7.22.5 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.17.9) + '@babel/helper-module-transforms': 7.22.5 + '@babel/helpers': 7.22.5 + '@babel/parser': 7.22.5 + '@babel/template': 7.22.5 + '@babel/traverse': 7.22.5 + '@babel/types': 7.22.5 convert-source-map: 1.9.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.0 @@ -2701,58 +3275,91 @@ packages: - supports-color dev: true - /@babel/core@7.21.4: - resolution: {integrity: sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==} + /@babel/core@7.21.3: + resolution: {integrity: sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==} engines: {node: '>=6.9.0'} dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) + '@ampproject/remapping': 2.2.0 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.21.3 + '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.21.3) '@babel/helper-module-transforms': 7.21.2 '@babel/helpers': 7.21.0 - '@babel/parser': 7.21.4 + '@babel/parser': 7.21.3 '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 + convert-source-map: 1.9.0 + debug: 4.3.4(supports-color@8.1.1) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/core@7.22.5: + resolution: {integrity: sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.0 + '@babel/code-frame': 7.22.5 + '@babel/generator': 7.22.5 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.22.5) + '@babel/helper-module-transforms': 7.22.5 + '@babel/helpers': 7.22.5 + '@babel/parser': 7.22.5 + '@babel/template': 7.22.5 + '@babel/traverse': 7.22.5 + '@babel/types': 7.22.5 convert-source-map: 1.9.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.0 transitivePeerDependencies: - supports-color - /@babel/generator@7.21.4: - resolution: {integrity: sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==} + /@babel/generator@7.21.3: + resolution: {integrity: sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.5 + '@jridgewell/gen-mapping': 0.3.2 + '@jridgewell/trace-mapping': 0.3.17 + jsesc: 2.5.2 + dev: true + + /@babel/generator@7.22.5: + resolution: {integrity: sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 + '@babel/types': 7.22.5 + '@jridgewell/gen-mapping': 0.3.2 + '@jridgewell/trace-mapping': 0.3.17 jsesc: 2.5.2 - /@babel/helper-annotate-as-pure@7.18.6: - resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} + /@babel/helper-annotate-as-pure@7.22.5: + resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 /@babel/helper-builder-binary-assignment-operator-visitor@7.18.9: resolution: {integrity: sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-explode-assignable-expression': 7.18.6 - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true - /@babel/helper-compilation-targets@7.21.4(@babel/core@7.17.9): - resolution: {integrity: sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==} + /@babel/helper-compilation-targets@7.20.7(@babel/core@7.17.9): + resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.21.4 + '@babel/compat-data': 7.21.0 '@babel/core': 7.17.9 '@babel/helper-validator-option': 7.21.0 browserslist: 4.21.5 @@ -2760,76 +3367,162 @@ packages: semver: 6.3.0 dev: true - /@babel/helper-compilation-targets@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==} + /@babel/helper-compilation-targets@7.20.7(@babel/core@7.21.3): + resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.21.0 + '@babel/core': 7.21.3 + '@babel/helper-validator-option': 7.21.0 + browserslist: 4.21.5 + lru-cache: 5.1.1 + semver: 6.3.0 + dev: true + + /@babel/helper-compilation-targets@7.20.7(@babel/core@7.22.5): + resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.21.4 + '@babel/compat-data': 7.21.0 + '@babel/core': 7.22.5 '@babel/helper-validator-option': 7.21.0 browserslist: 4.21.5 lru-cache: 5.1.1 semver: 6.3.0 + dev: true - /@babel/helper-create-class-features-plugin@7.21.4(@babel/core@7.17.9): - resolution: {integrity: sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q==} + /@babel/helper-compilation-targets@7.22.5(@babel/core@7.17.9): + resolution: {integrity: sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: + '@babel/compat-data': 7.22.5 '@babel/core': 7.17.9 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 + '@babel/helper-validator-option': 7.22.5 + browserslist: 4.21.5 + lru-cache: 5.1.1 + semver: 6.3.0 + dev: true + + /@babel/helper-compilation-targets@7.22.5(@babel/core@7.21.3): + resolution: {integrity: sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.22.5 + '@babel/core': 7.21.3 + '@babel/helper-validator-option': 7.22.5 + browserslist: 4.21.5 + lru-cache: 5.1.1 + semver: 6.3.0 + dev: true + + /@babel/helper-compilation-targets@7.22.5(@babel/core@7.22.5): + resolution: {integrity: sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.22.5 + '@babel/core': 7.22.5 + '@babel/helper-validator-option': 7.22.5 + browserslist: 4.21.5 + lru-cache: 5.1.1 + semver: 6.3.0 + + /@babel/helper-create-class-features-plugin@7.21.0(@babel/core@7.17.9): + resolution: {integrity: sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.17.9 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-function-name': 7.22.5 '@babel/helper-member-expression-to-functions': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/helper-replace-supers': 7.20.7 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-split-export-declaration': 7.22.5 transitivePeerDependencies: - supports-color dev: true - /@babel/helper-create-class-features-plugin@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q==} + /@babel/helper-create-class-features-plugin@7.21.0(@babel/core@7.21.3): + resolution: {integrity: sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 + '@babel/core': 7.21.3 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-function-name': 7.22.5 '@babel/helper-member-expression-to-functions': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/helper-replace-supers': 7.20.7 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-split-export-declaration': 7.22.5 transitivePeerDependencies: - supports-color dev: true - /@babel/helper-create-regexp-features-plugin@7.21.4(@babel/core@7.17.9): - resolution: {integrity: sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA==} + /@babel/helper-create-class-features-plugin@7.21.0(@babel/core@7.22.5): + resolution: {integrity: sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-function-name': 7.22.5 + '@babel/helper-member-expression-to-functions': 7.21.0 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-replace-supers': 7.20.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 + '@babel/helper-split-export-declaration': 7.22.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-create-regexp-features-plugin@7.21.0(@babel/core@7.17.9): + resolution: {integrity: sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-annotate-as-pure': 7.22.5 + regexpu-core: 5.3.2 + dev: true + + /@babel/helper-create-regexp-features-plugin@7.21.0(@babel/core@7.21.3): + resolution: {integrity: sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-annotate-as-pure': 7.22.5 regexpu-core: 5.3.2 dev: true - /@babel/helper-create-regexp-features-plugin@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA==} + /@babel/helper-create-regexp-features-plugin@7.21.0(@babel/core@7.22.5): + resolution: {integrity: sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/core': 7.22.5 + '@babel/helper-annotate-as-pure': 7.22.5 regexpu-core: 5.3.2 dev: true @@ -2839,8 +3532,8 @@ packages: '@babel/core': ^7.4.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.17.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.17.9) + '@babel/helper-plugin-utils': 7.22.5 debug: 4.3.4(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.2 @@ -2849,15 +3542,31 @@ packages: - supports-color dev: true - /@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.21.4): + /@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.21.3): resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==} peerDependencies: '@babel/core': ^7.4.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - debug: 4.3.4 + '@babel/core': 7.21.3 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.21.3) + '@babel/helper-plugin-utils': 7.22.5 + debug: 4.3.4(supports-color@8.1.1) + lodash.debounce: 4.0.8 + resolve: 1.22.2 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.22.5): + resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==} + peerDependencies: + '@babel/core': ^7.4.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.22.5) + '@babel/helper-plugin-utils': 7.22.5 + debug: 4.3.4(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.2 semver: 6.3.0 @@ -2868,59 +3577,86 @@ packages: /@babel/helper-environment-visitor@7.18.9: resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-environment-visitor@7.22.5: + resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==} + engines: {node: '>=6.9.0'} /@babel/helper-explode-assignable-expression@7.18.6: resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true - /@babel/helper-function-name@7.21.0: - resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} + /@babel/helper-function-name@7.22.5: + resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.4 + '@babel/template': 7.22.5 + '@babel/types': 7.22.5 /@babel/helper-hoist-variables@7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 + dev: true + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.5 /@babel/helper-member-expression-to-functions@7.21.0: resolution: {integrity: sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-module-imports@7.18.6: resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 - dev: false + '@babel/types': 7.22.5 - /@babel/helper-module-imports@7.21.4: - resolution: {integrity: sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==} + /@babel/helper-module-imports@7.22.5: + resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 /@babel/helper-module-transforms@7.21.2: resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-module-imports': 7.21.4 + '@babel/helper-module-imports': 7.18.6 '@babel/helper-simple-access': 7.20.2 '@babel/helper-split-export-declaration': 7.18.6 '@babel/helper-validator-identifier': 7.19.1 '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/traverse': 7.21.3 + '@babel/types': 7.22.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-module-transforms@7.22.5: + resolution: {integrity: sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-module-imports': 7.22.5 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.5 + '@babel/helper-validator-identifier': 7.22.5 + '@babel/template': 7.22.5 + '@babel/traverse': 7.22.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color @@ -2928,12 +3664,17 @@ packages: resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-plugin-utils@7.20.2: resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.17.9): resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} @@ -2942,25 +3683,40 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.5 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true - /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.21.4): + /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.21.3): resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-environment-visitor': 7.18.9 + '@babel/core': 7.21.3 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-wrap-function': 7.20.5 + '@babel/types': 7.22.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.22.5): + resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.5 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2969,12 +3725,12 @@ packages: resolution: {integrity: sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-environment-visitor': 7.22.5 '@babel/helper-member-expression-to-functions': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/template': 7.22.5 + '@babel/traverse': 7.22.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2983,41 +3739,68 @@ packages: resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.5 /@babel/helper-skip-transparent-expression-wrappers@7.20.0: resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-split-export-declaration@7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 + dev: true + + /@babel/helper-split-export-declaration@7.22.5: + resolution: {integrity: sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.5 /@babel/helper-string-parser@7.19.4: resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} engines: {node: '>=6.9.0'} + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + /@babel/helper-validator-identifier@7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} + /@babel/helper-validator-identifier@7.22.5: + resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} + engines: {node: '>=6.9.0'} + /@babel/helper-validator-option@7.21.0: resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.22.5: + resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} + engines: {node: '>=6.9.0'} /@babel/helper-wrap-function@7.20.5: resolution: {integrity: sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-function-name': 7.21.0 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/helper-function-name': 7.22.5 + '@babel/template': 7.22.5 + '@babel/traverse': 7.22.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -3027,8 +3810,19 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/traverse': 7.21.3 + '@babel/types': 7.22.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helpers@7.22.5: + resolution: {integrity: sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.5 + '@babel/traverse': 7.22.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color @@ -3036,16 +3830,32 @@ packages: resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.22.5 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/highlight@7.22.5: + resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.5 chalk: 2.4.2 js-tokens: 4.0.0 - /@babel/parser@7.21.4: - resolution: {integrity: sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==} + /@babel/parser@7.21.3: + resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 + + /@babel/parser@7.22.5: + resolution: {integrity: sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.22.5 /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.17.9): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} @@ -3054,17 +3864,27 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.21.4): + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.21.3): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.22.5): + resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 dev: true /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.20.7(@babel/core@7.17.9): @@ -3074,21 +3894,33 @@ packages: '@babel/core': ^7.13.0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.17.9) dev: true - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.20.7(@babel/core@7.21.4): + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.20.7(@babel/core@7.21.3): resolution: {integrity: sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.3) + dev: true + + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.20.7(@babel/core@7.22.5): + resolution: {integrity: sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.4) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.22.5) dev: true /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.17.9): @@ -3098,51 +3930,79 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.17.9) '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.17.9) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.21.4): + /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.21.3): resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.4) + '@babel/core': 7.21.3 + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.3) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.17.9): - resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.22.5): + resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.17.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.22.5 + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.22.5) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.5) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.21.4): + /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.17.9): resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.17.9 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.17.9) + '@babel/helper-plugin-utils': 7.22.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.21.3) + '@babel/helper-plugin-utils': 7.22.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.22.5): + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.22.5) + '@babel/helper-plugin-utils': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -3154,23 +4014,37 @@ packages: '@babel/core': ^7.12.0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.17.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.17.9) + '@babel/helper-plugin-utils': 7.22.5 '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.17.9) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-class-static-block@7.21.0(@babel/core@7.21.4): + /@babel/plugin-proposal-class-static-block@7.21.0(@babel/core@7.21.3): resolution: {integrity: sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.4) + '@babel/core': 7.21.3 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.21.3) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.3) + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-class-static-block@7.21.0(@babel/core@7.22.5): + resolution: {integrity: sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.22.5) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.22.5) transitivePeerDependencies: - supports-color dev: true @@ -3186,15 +4060,26 @@ packages: '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.17.9) dev: true - /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.21.4): + /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.21.3): resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.3) + dev: true + + /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.22.5): + resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.22.5) dev: true /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.17.9): @@ -3208,15 +4093,26 @@ packages: '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.17.9) dev: true - /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.21.4): + /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.21.3): + resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.3) + dev: true + + /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.22.5): resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.22.5) dev: true /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.17.9): @@ -3230,15 +4126,26 @@ packages: '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.17.9) dev: true - /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.21.4): + /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.21.3): resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.3) + dev: true + + /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.22.5): + resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.5) dev: true /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.17.9): @@ -3252,15 +4159,26 @@ packages: '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.17.9) dev: true - /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.21.4): + /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.21.3): + resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.3) + dev: true + + /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.22.5): resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.5) dev: true /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.17.9): @@ -3274,15 +4192,26 @@ packages: '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.17.9) dev: true - /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.21.4): + /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.21.3): resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.3) + dev: true + + /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.22.5): + resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.5) dev: true /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.17.9): @@ -3296,15 +4225,26 @@ packages: '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.17.9) dev: true - /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.21.4): + /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.3) + dev: true + + /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.22.5): resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.5) dev: true /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.17.9): @@ -3313,26 +4253,40 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.21.4 + '@babel/compat-data': 7.21.0 '@babel/core': 7.17.9 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.17.9) + '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.17.9) '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.17.9) '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.17.9) dev: true - /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.21.4): + /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.21.3): resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) + '@babel/compat-data': 7.21.0 + '@babel/core': 7.21.3 + '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.21.3) '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.3) + dev: true + + /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.22.5): + resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.21.0 + '@babel/core': 7.22.5 + '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.22.5) + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.22.5) dev: true /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.17.9): @@ -3346,15 +4300,26 @@ packages: '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.17.9) dev: true - /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.21.4): + /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.3) + dev: true + + /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.22.5): resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.5) dev: true /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.17.9): @@ -3364,21 +4329,33 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.17.9) dev: true - /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.21.4): + /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.21.3): resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.3) + dev: true + + /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.22.5): + resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.5) dev: true /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.17.9): @@ -3388,20 +4365,33 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.17.9) + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.17.9) + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.21.3) '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.21.4): + /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.22.5): resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) + '@babel/core': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.22.5) '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color @@ -3414,25 +4404,40 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.17.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.17.9) + '@babel/helper-plugin-utils': 7.22.5 '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.17.9) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-private-property-in-object@7.21.0(@babel/core@7.21.4): + /@babel/plugin-proposal-private-property-in-object@7.21.0(@babel/core@7.21.3): resolution: {integrity: sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.4) + '@babel/core': 7.21.3 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.21.3) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.3) + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-private-property-in-object@7.21.0(@babel/core@7.22.5): + resolution: {integrity: sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.22.5) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.22.5) transitivePeerDependencies: - supports-color dev: true @@ -3444,18 +4449,29 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.17.9) + '@babel/helper-create-regexp-features-plugin': 7.21.0(@babel/core@7.17.9) + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} + engines: {node: '>=4'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-create-regexp-features-plugin': 7.21.0(@babel/core@7.21.3) '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.21.4): + /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.22.5): resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} engines: {node: '>=4'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.21.4) + '@babel/core': 7.22.5 + '@babel/helper-create-regexp-features-plugin': 7.21.0(@babel/core@7.22.5) '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3468,24 +4484,33 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.4): + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.3): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.21.4): - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.22.5): + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.22.5): + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.17.9): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: @@ -3495,12 +4520,21 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.4): + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.3): + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.22.5): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3514,13 +4548,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.21.4): + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.21.3): + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.22.5): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3533,12 +4577,21 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.21.4): + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.21.3): + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.22.5): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3551,42 +4604,70 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.21.4): + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.21.3): resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-flow@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw==} - engines: {node: '>=6.9.0'} + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.22.5): + resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.21.4): + /@babel/plugin-syntax-flow@7.18.6(@babel/core@7.22.5): + resolution: {integrity: sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.21.3): resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.22.5): + resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.21.4): + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.21.3): resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.22.5): + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 dev: true /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.17.9): @@ -3598,23 +4679,32 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.4): + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.3): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-jsx@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==} - engines: {node: '>=6.9.0'} + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.22.5): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.5): + resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.17.9): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} @@ -3625,12 +4715,21 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.4): + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.3): + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.22.5): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3643,12 +4742,21 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.4): + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.3): + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.22.5): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3661,12 +4769,21 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.4): + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.3): + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.22.5): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3679,12 +4796,21 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.4): + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.3): + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.22.5): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3697,12 +4823,21 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.4): + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.3): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.22.5): + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3715,12 +4850,21 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.4): + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.3): + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.22.5): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3734,13 +4878,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.21.4): + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.21.3): + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.22.5): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3754,34 +4908,44 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.4): + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.3): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-typescript@7.21.4(@babel/core@7.17.9): - resolution: {integrity: sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==} + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.22.5): + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-typescript@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==} + /@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.17.9): + resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.17.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.22.5): + resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 dev: true /@babel/plugin-transform-arrow-functions@7.20.7(@babel/core@7.17.9): @@ -3794,16 +4958,36 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-arrow-functions@7.20.7(@babel/core@7.21.4): + /@babel/plugin-transform-arrow-functions@7.20.7(@babel/core@7.21.3): resolution: {integrity: sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-arrow-functions@7.20.7(@babel/core@7.22.5): + resolution: {integrity: sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-arrow-functions@7.22.5(@babel/core@7.21.3): + resolution: {integrity: sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-transform-async-to-generator@7.20.7(@babel/core@7.17.9): resolution: {integrity: sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==} engines: {node: '>=6.9.0'} @@ -3811,23 +4995,37 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-module-imports': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-module-imports': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.17.9) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-async-to-generator@7.20.7(@babel/core@7.21.4): + /@babel/plugin-transform-async-to-generator@7.20.7(@babel/core@7.21.3): resolution: {integrity: sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-module-imports': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.21.4) + '@babel/core': 7.21.3 + '@babel/helper-module-imports': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.21.3) + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-async-to-generator@7.20.7(@babel/core@7.22.5): + resolution: {integrity: sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-module-imports': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.22.5) transitivePeerDependencies: - supports-color dev: true @@ -3842,13 +5040,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.21.4): + /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.22.5): resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3862,13 +5070,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-block-scoping@7.21.0(@babel/core@7.21.4): + /@babel/plugin-transform-block-scoping@7.21.0(@babel/core@7.21.3): + resolution: {integrity: sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-block-scoping@7.21.0(@babel/core@7.22.5): resolution: {integrity: sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3879,34 +5097,54 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.17.9) - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.17.9) + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-function-name': 7.22.5 '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-split-export-declaration': 7.22.5 globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-classes@7.21.0(@babel/core@7.21.4): + /@babel/plugin-transform-classes@7.21.0(@babel/core@7.21.3): resolution: {integrity: sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 + '@babel/core': 7.21.3 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.21.3) + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-function-name': 7.22.5 '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-split-export-declaration': 7.22.5 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-classes@7.21.0(@babel/core@7.22.5): + resolution: {integrity: sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.22.5) + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-function-name': 7.22.5 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-replace-supers': 7.20.7 + '@babel/helper-split-export-declaration': 7.22.5 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -3923,17 +5161,39 @@ packages: '@babel/template': 7.20.7 dev: true - /@babel/plugin-transform-computed-properties@7.20.7(@babel/core@7.21.4): + /@babel/plugin-transform-computed-properties@7.20.7(@babel/core@7.21.3): + resolution: {integrity: sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/template': 7.20.7 + dev: true + + /@babel/plugin-transform-computed-properties@7.20.7(@babel/core@7.22.5): resolution: {integrity: sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 '@babel/template': 7.20.7 dev: true + /@babel/plugin-transform-computed-properties@7.22.5(@babel/core@7.21.3): + resolution: {integrity: sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/template': 7.22.5 + dev: true + /@babel/plugin-transform-destructuring@7.21.3(@babel/core@7.17.9): resolution: {integrity: sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==} engines: {node: '>=6.9.0'} @@ -3944,13 +5204,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-destructuring@7.21.3(@babel/core@7.21.4): + /@babel/plugin-transform-destructuring@7.21.3(@babel/core@7.21.3): + resolution: {integrity: sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-destructuring@7.21.3(@babel/core@7.22.5): resolution: {integrity: sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -3961,19 +5231,30 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.17.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-create-regexp-features-plugin': 7.21.0(@babel/core@7.17.9) + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-dotall-regex@7.18.6(@babel/core@7.21.4): + /@babel/plugin-transform-dotall-regex@7.18.6(@babel/core@7.21.3): resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.21.3 + '@babel/helper-create-regexp-features-plugin': 7.21.0(@babel/core@7.21.3) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-dotall-regex@7.18.6(@babel/core@7.22.5): + resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-create-regexp-features-plugin': 7.21.0(@babel/core@7.22.5) + '@babel/helper-plugin-utils': 7.22.5 dev: true /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.17.9): @@ -3986,13 +5267,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.21.4): + /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.21.3): + resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.22.5): resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -4004,29 +5295,40 @@ packages: dependencies: '@babel/core': 7.17.9 '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-exponentiation-operator@7.18.6(@babel/core@7.21.4): + /@babel/plugin-transform-exponentiation-operator@7.18.6(@babel/core@7.21.3): resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-exponentiation-operator@7.18.6(@babel/core@7.22.5): + resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-flow-strip-types@7.21.0(@babel/core@7.21.4): + /@babel/plugin-transform-flow-strip-types@7.21.0(@babel/core@7.22.5): resolution: {integrity: sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-flow': 7.21.4(@babel/core@7.21.4) + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-flow': 7.18.6(@babel/core@7.22.5) dev: true /@babel/plugin-transform-for-of@7.21.0(@babel/core@7.17.9): @@ -4039,16 +5341,36 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-for-of@7.21.0(@babel/core@7.21.4): + /@babel/plugin-transform-for-of@7.21.0(@babel/core@7.21.3): + resolution: {integrity: sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-for-of@7.21.0(@babel/core@7.22.5): resolution: {integrity: sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-for-of@7.22.5(@babel/core@7.21.3): + resolution: {integrity: sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.17.9): resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} engines: {node: '>=6.9.0'} @@ -4056,21 +5378,33 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.17.9) - '@babel/helper-function-name': 7.21.0 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.17.9) + '@babel/helper-function-name': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.21.4): + /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.21.3): resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) - '@babel/helper-function-name': 7.21.0 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.21.3 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.21.3) + '@babel/helper-function-name': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.22.5): + resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.22.5) + '@babel/helper-function-name': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 dev: true /@babel/plugin-transform-literals@7.18.9(@babel/core@7.17.9): @@ -4083,13 +5417,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-literals@7.18.9(@babel/core@7.21.4): + /@babel/plugin-transform-literals@7.18.9(@babel/core@7.21.3): + resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-literals@7.18.9(@babel/core@7.22.5): resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -4103,13 +5447,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.21.4): + /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.22.5): resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -4126,13 +5480,26 @@ packages: - supports-color dev: true - /@babel/plugin-transform-modules-amd@7.20.11(@babel/core@7.21.4): + /@babel/plugin-transform-modules-amd@7.20.11(@babel/core@7.21.3): + resolution: {integrity: sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-amd@7.20.11(@babel/core@7.22.5): resolution: {integrity: sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: @@ -4153,13 +5520,13 @@ packages: - supports-color dev: true - /@babel/plugin-transform-modules-commonjs@7.21.2(@babel/core@7.21.4): + /@babel/plugin-transform-modules-commonjs@7.21.2(@babel/core@7.21.3): resolution: {integrity: sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-simple-access': 7.20.2 @@ -4167,6 +5534,34 @@ packages: - supports-color dev: true + /@babel/plugin-transform-modules-commonjs@7.21.2(@babel/core@7.22.5): + resolution: {integrity: sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-simple-access': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-commonjs@7.22.5(@babel/core@7.21.3): + resolution: {integrity: sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-module-transforms': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-simple-access': 7.22.5 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-modules-systemjs@7.20.11(@babel/core@7.17.9): resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==} engines: {node: '>=6.9.0'} @@ -4182,17 +5577,32 @@ packages: - supports-color dev: true - /@babel/plugin-transform-modules-systemjs@7.20.11(@babel/core@7.21.4): + /@babel/plugin-transform-modules-systemjs@7.20.11(@babel/core@7.21.3): resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.22.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-systemjs@7.20.11(@babel/core@7.22.5): + resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-validator-identifier': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -4210,13 +5620,26 @@ packages: - supports-color dev: true - /@babel/plugin-transform-modules-umd@7.18.6(@babel/core@7.21.4): + /@babel/plugin-transform-modules-umd@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-umd@7.18.6(@babel/core@7.22.5): resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: @@ -4230,19 +5653,30 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.17.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-create-regexp-features-plugin': 7.21.0(@babel/core@7.17.9) + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-named-capturing-groups-regex@7.20.5(@babel/core@7.21.4): + /@babel/plugin-transform-named-capturing-groups-regex@7.20.5(@babel/core@7.21.3): resolution: {integrity: sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.21.3 + '@babel/helper-create-regexp-features-plugin': 7.21.0(@babel/core@7.21.3) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-named-capturing-groups-regex@7.20.5(@babel/core@7.22.5): + resolution: {integrity: sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-create-regexp-features-plugin': 7.21.0(@babel/core@7.22.5) + '@babel/helper-plugin-utils': 7.22.5 dev: true /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.17.9): @@ -4255,13 +5689,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.21.4): + /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.22.5): resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -4272,20 +5716,33 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-replace-supers': 7.20.7 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.21.4): + /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.21.3): resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-replace-supers': 7.20.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.22.5): + resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-replace-supers': 7.20.7 transitivePeerDependencies: - supports-color @@ -4301,108 +5758,180 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-parameters@7.21.3(@babel/core@7.21.4): + /@babel/plugin-transform-parameters@7.21.3(@babel/core@7.21.3): resolution: {integrity: sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.17.9): - resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} + /@babel/plugin-transform-parameters@7.21.3(@babel/core@7.22.5): + resolution: {integrity: sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.21.4): + /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.17.9): resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.17.9 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-react-jsx-self@7.21.0(@babel/core@7.21.4): - resolution: {integrity: sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==} + /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-react-jsx-source@7.19.6(@babel/core@7.21.4): - resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==} + /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.22.5): + resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-react-jsx@7.21.0(@babel/core@7.21.4): - resolution: {integrity: sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg==} + /@babel/plugin-transform-react-jsx-self@7.21.0(@babel/core@7.22.5): + resolution: {integrity: sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-module-imports': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.4) - '@babel/types': 7.21.4 + dev: true - /@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.17.9): - resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==} + /@babel/plugin-transform-react-jsx-self@7.22.5(@babel/core@7.22.5): + resolution: {integrity: sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 - '@babel/helper-plugin-utils': 7.20.2 - regenerator-transform: 0.15.1 + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.21.4): - resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==} + /@babel/plugin-transform-react-jsx-source@7.19.6(@babel/core@7.22.5): + resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 - regenerator-transform: 0.15.1 dev: true - /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.17.9): - resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} + /@babel/plugin-transform-react-jsx-source@7.22.5(@babel/core@7.22.5): + resolution: {integrity: sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-react-jsx@7.22.5(@babel/core@7.22.5): + resolution: {integrity: sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-module-imports': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.5) + '@babel/types': 7.22.5 + + /@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.17.9): + resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.9 + '@babel/helper-plugin-utils': 7.22.5 + regenerator-transform: 0.15.1 + dev: true + + /@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.21.3): + resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.22.5 + regenerator-transform: 0.15.1 + dev: true + + /@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.22.5): + resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + regenerator-transform: 0.15.1 + dev: true + + /@babel/plugin-transform-regenerator@7.22.5(@babel/core@7.21.3): + resolution: {integrity: sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.22.5 + regenerator-transform: 0.15.1 + dev: true + + /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.17.9): + resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.21.4): + /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.22.5): resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -4416,13 +5945,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.21.4): + /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.22.5): resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -4437,13 +5976,24 @@ packages: '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 dev: true - /@babel/plugin-transform-spread@7.20.7(@babel/core@7.21.4): + /@babel/plugin-transform-spread@7.20.7(@babel/core@7.21.3): + resolution: {integrity: sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 + dev: true + + /@babel/plugin-transform-spread@7.20.7(@babel/core@7.22.5): resolution: {integrity: sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 dev: true @@ -4458,13 +6008,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-sticky-regex@7.18.6(@babel/core@7.21.4): + /@babel/plugin-transform-sticky-regex@7.18.6(@babel/core@7.21.3): + resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-sticky-regex@7.18.6(@babel/core@7.22.5): resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -4478,13 +6038,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.21.4): + /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.21.3): resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.22.5): + resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -4498,13 +6068,23 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-typeof-symbol@7.18.9(@babel/core@7.21.4): + /@babel/plugin-transform-typeof-symbol@7.18.9(@babel/core@7.21.3): + resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-typeof-symbol@7.18.9(@babel/core@7.22.5): resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -4515,25 +6095,25 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.17.9) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.17.9) + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.17.9) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.17.9) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-typescript@7.21.3(@babel/core@7.21.4): + /@babel/plugin-transform-typescript@7.21.3(@babel/core@7.22.5): resolution: {integrity: sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.21.4) + '@babel/core': 7.22.5 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.22.5) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.22.5) transitivePeerDependencies: - supports-color dev: true @@ -4548,16 +6128,36 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-unicode-escapes@7.18.10(@babel/core@7.21.4): + /@babel/plugin-transform-unicode-escapes@7.18.10(@babel/core@7.21.3): + resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-unicode-escapes@7.18.10(@babel/core@7.22.5): resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-unicode-escapes@7.22.5(@babel/core@7.21.3): + resolution: {integrity: sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.17.9): resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==} engines: {node: '>=6.9.0'} @@ -4565,18 +6165,29 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.17.9) + '@babel/helper-create-regexp-features-plugin': 7.21.0(@babel/core@7.17.9) '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.21.4): + /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.21.3): resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.21.4) + '@babel/core': 7.21.3 + '@babel/helper-create-regexp-features-plugin': 7.21.0(@babel/core@7.21.3) + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.22.5): + resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-create-regexp-features-plugin': 7.21.0(@babel/core@7.22.5) '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -4586,11 +6197,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.21.4 + '@babel/compat-data': 7.22.5 '@babel/core': 7.17.9 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.17.9) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.17.9) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.22.5 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.17.9) '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.17.9) '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.17.9) @@ -4655,198 +6266,285 @@ packages: '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.17.9) '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.17.9) '@babel/preset-modules': 0.1.5(@babel/core@7.17.9) - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.17.9) babel-plugin-polyfill-corejs3: 0.5.3(@babel/core@7.17.9) babel-plugin-polyfill-regenerator: 0.3.1(@babel/core@7.17.9) - core-js-compat: 3.30.0 + core-js-compat: 3.29.1 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-env@7.20.2(@babel/core@7.21.4): + /@babel/preset-env@7.20.2(@babel/core@7.21.3): resolution: {integrity: sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.4) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.4) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.4) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.21.4) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.4) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.4) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.4) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.4) - '@babel/plugin-transform-arrow-functions': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-computed-properties': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.21.4) - '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-for-of': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.21.4) - '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.21.4) - '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.21.4) - '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.21.4) - '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.4) - '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-regenerator': 7.20.5(@babel/core@7.21.4) - '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.21.4) - '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.21.4) - '@babel/preset-modules': 0.1.5(@babel/core@7.21.4) - '@babel/types': 7.21.4 - babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.4) - babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.21.4) - babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.21.4) - core-js-compat: 3.30.0 + '@babel/compat-data': 7.22.5 + '@babel/core': 7.21.3 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.21.3) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.22.5 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.21.3) + '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.3) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.21.3) + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.3) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.3) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.3) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.21.3) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.3) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.3) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.3) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.3) + '@babel/plugin-transform-arrow-functions': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.21.3) + '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.21.3) + '@babel/plugin-transform-computed-properties': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.21.3) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-for-of': 7.21.0(@babel/core@7.21.3) + '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.21.3) + '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.21.3) + '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.21.3) + '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.21.3) + '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.3) + '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-regenerator': 7.20.5(@babel/core@7.21.3) + '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.21.3) + '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.21.3) + '@babel/preset-modules': 0.1.5(@babel/core@7.21.3) + '@babel/types': 7.22.5 + babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.3) + babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.21.3) + babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.21.3) + core-js-compat: 3.29.1 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-env@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw==} + /@babel/preset-env@7.20.2(@babel/core@7.22.5): + resolution: {integrity: sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.4) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.4) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.4) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.21.4) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.4) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.4) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.4) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.4) - '@babel/plugin-transform-arrow-functions': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-computed-properties': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.21.4) - '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-for-of': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.21.4) - '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.21.4) - '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.21.4) - '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.21.4) - '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.4) - '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-regenerator': 7.20.5(@babel/core@7.21.4) - '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.21.4) - '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.21.4) - '@babel/preset-modules': 0.1.5(@babel/core@7.21.4) - '@babel/types': 7.21.4 - babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.4) - babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.21.4) - babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.21.4) - core-js-compat: 3.30.0 + '@babel/compat-data': 7.22.5 + '@babel/core': 7.22.5 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.22.5) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.22.5 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.22.5) + '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.22.5) + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.22.5) + '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.22.5) + '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.22.5) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.22.5) + '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.22.5) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.22.5) + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.5) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.22.5) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.22.5) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.22.5) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.5) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.5) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.22.5) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.5) + '@babel/plugin-transform-arrow-functions': 7.20.7(@babel/core@7.22.5) + '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.22.5) + '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.22.5) + '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.22.5) + '@babel/plugin-transform-computed-properties': 7.20.7(@babel/core@7.22.5) + '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.22.5) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.22.5) + '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-for-of': 7.21.0(@babel/core@7.22.5) + '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.22.5) + '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.22.5) + '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.22.5) + '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.22.5) + '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.22.5) + '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.22.5) + '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.22.5) + '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-regenerator': 7.20.5(@babel/core@7.22.5) + '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.22.5) + '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.22.5) + '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.22.5) + '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.22.5) + '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.22.5) + '@babel/preset-modules': 0.1.5(@babel/core@7.22.5) + '@babel/types': 7.22.5 + babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.22.5) + babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.22.5) + babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.22.5) + core-js-compat: 3.29.1 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-flow@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-F24cSq4DIBmhq4OzK3dE63NHagb27OPE3eWR+HLekt4Z3Y5MzIIUGF3LlLgV0gN8vzbDViSY7HnrReNVCJXTeA==} + /@babel/preset-env@7.21.5(@babel/core@7.21.3): + resolution: {integrity: sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-transform-flow-strip-types': 7.21.0(@babel/core@7.21.4) + '@babel/compat-data': 7.22.5 + '@babel/core': 7.21.3 + '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.21.3) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.22.5 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.21.3) + '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.3) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.21.3) + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.3) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.3) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.3) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.21.3) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.21.3) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.3) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.3) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.3) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.3) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.3) + '@babel/plugin-transform-arrow-functions': 7.22.5(@babel/core@7.21.3) + '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.21.3) + '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.21.3) + '@babel/plugin-transform-computed-properties': 7.22.5(@babel/core@7.21.3) + '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.21.3) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-for-of': 7.22.5(@babel/core@7.21.3) + '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.21.3) + '@babel/plugin-transform-modules-commonjs': 7.22.5(@babel/core@7.21.3) + '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.21.3) + '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.21.3) + '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.3) + '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-regenerator': 7.22.5(@babel/core@7.21.3) + '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.21.3) + '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.21.3) + '@babel/plugin-transform-unicode-escapes': 7.22.5(@babel/core@7.21.3) + '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.21.3) + '@babel/preset-modules': 0.1.5(@babel/core@7.21.3) + '@babel/types': 7.22.5 + babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.3) + babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.21.3) + babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.21.3) + core-js-compat: 3.29.1 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/preset-flow@7.18.6(@babel/core@7.22.5): + resolution: {integrity: sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.22.5 + '@babel/plugin-transform-flow-strip-types': 7.21.0(@babel/core@7.22.5) dev: true /@babel/preset-modules@0.1.5(@babel/core@7.17.9): @@ -4858,20 +6556,33 @@ packages: '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.17.9) '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.17.9) - '@babel/types': 7.21.4 + '@babel/types': 7.21.3 + esutils: 2.0.3 + dev: true + + /@babel/preset-modules@0.1.5(@babel/core@7.21.3): + resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.3) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.3) + '@babel/types': 7.21.3 esutils: 2.0.3 dev: true - /@babel/preset-modules@0.1.5(@babel/core@7.21.4): + /@babel/preset-modules@0.1.5(@babel/core@7.22.5): resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.4) - '@babel/types': 7.21.4 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.22.5) + '@babel/types': 7.21.3 esutils: 2.0.3 dev: true @@ -4882,34 +6593,34 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.22.5 '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.17.9) transitivePeerDependencies: - supports-color dev: true - /@babel/preset-typescript@7.16.7(@babel/core@7.21.4): + /@babel/preset-typescript@7.16.7(@babel/core@7.22.5): resolution: {integrity: sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.21.4) + '@babel/core': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.22.5 + '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.22.5) transitivePeerDependencies: - supports-color dev: true - /@babel/register@7.21.0(@babel/core@7.21.4): + /@babel/register@7.21.0(@babel/core@7.22.5): resolution: {integrity: sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 clone-deep: 4.0.1 find-cache-dir: 2.1.0 make-dir: 2.1.0 @@ -4931,45 +6642,108 @@ packages: resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.21.4 - '@babel/parser': 7.21.4 - '@babel/types': 7.21.4 + '@babel/code-frame': 7.18.6 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 + dev: true - /@babel/traverse@7.21.4: - resolution: {integrity: sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==} + /@babel/template@7.22.5: + resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.21.4 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.21.4 - '@babel/types': 7.21.4 - debug: 4.3.4 + '@babel/code-frame': 7.22.5 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 + + /@babel/traverse@7.21.3: + resolution: {integrity: sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.5 + '@babel/generator': 7.22.5 + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-function-name': 7.22.5 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.5 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 + debug: 4.3.4(supports-color@8.1.1) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/traverse@7.22.5: + resolution: {integrity: sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.5 + '@babel/generator': 7.22.5 + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-function-name': 7.22.5 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.5 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 + debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color - /@babel/types@7.21.4: - resolution: {integrity: sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==} + /@babel/types@7.21.3: + resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.19.4 '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 - /@ballerine/common@0.0.1: - resolution: {integrity: sha512-j4mZ3rWjT0y7HVCnuXZbSp06ML7wP7XRC8Eiga29UCXgKoAxys95zB5nuDjE0t20nAgEoC90G5TN/jkd20on6w==} - engines: {node: '>=12'} - dev: false + /@babel/types@7.22.5: + resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.5 + to-fast-properties: 2.0.0 + + /@balena/dockerignore@1.0.2: + resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} + dev: true /@ballerine/common@0.4.3: resolution: {integrity: sha512-MV7i5eYQ4yAg0ZI3p0oel1Erz4W+nZ7mjc8tXyr0XZszckvxkWuTPCnTDggIKOsf7c26I0JMsbHlY9vpBFcSHQ==} engines: {node: '>=12'} dev: false + /@ballerine/common@0.5.9: + resolution: {integrity: sha512-5tPfpsQ0ibg/H2qSOQ1aeRDDaAig7A+p8nsX6CNTEMUZ/8Pa4Hs02DgRjCWGlQjmczOWblCfwA0+AVj25Ht5qg==} + engines: {node: '>=12'} + dependencies: + ajv: 8.12.0 + json-schema-to-zod: 0.6.3 + dev: false + + /@ballerine/workflow-core@0.4.19: + resolution: {integrity: sha512-F+VeepJVsEp83S7R6F2JoDQzpEq4dgIxPdbqBm2pK7idJo+z9W+yEODsNRURuWuy68GxEmAP5AFFDgRs7HcJ6Q==} + engines: {node: '>=12'} + dependencies: + '@ballerine/common': 0.5.9 + ajv: 8.12.0 + i18n-iso-countries: 7.6.0 + jmespath: 0.16.0 + json-logic-js: 2.0.2 + xstate: 4.38.0 + dev: false + + /@ballerine/workflow-core@0.4.9: + resolution: {integrity: sha512-+GKxU0ZK2TtBRUfroivNzxF8avm99oJKriZKq0IWwWNkFndG9ot9CBH43zY5Do55yuFBAzQqgFx6crtyT5c6Qg==} + engines: {node: '>=12'} + dependencies: + '@ballerine/common': 0.4.3 + json-logic-js: 2.0.2 + xstate: 4.38.0 + dev: false + /@base2/pretty-print-object@1.0.1: resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==} dev: true @@ -5201,18 +6975,16 @@ packages: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} requiresBuild: true - dev: true - optional: true - /@commitlint/cli@17.6.0: - resolution: {integrity: sha512-JaZeZ1p6kfkSiZlDoQjK09AuiI9zYQMiIUJzTOM8qNRHFOXOPmiTM56nI67yzeUSNTFu6M/DRqjmdjtA5q3hEg==} + /@commitlint/cli@17.5.0: + resolution: {integrity: sha512-yNW3+M7UM1ioK28LKTrryIVB5qGpXlEv8+rJQiWPMZNayy9/1XR5+lL8qBTNlgopYtZWWnIm5RETcAN29ZTL/A==} engines: {node: '>=v14'} hasBin: true dependencies: '@commitlint/format': 17.4.4 - '@commitlint/lint': 17.6.0 + '@commitlint/lint': 17.4.4 '@commitlint/load': 17.5.0 - '@commitlint/read': 17.5.1 + '@commitlint/read': 17.4.4 '@commitlint/types': 17.4.4 execa: 5.1.1 lodash.isfunction: 3.0.9 @@ -5224,8 +6996,8 @@ packages: - '@swc/wasm' dev: true - /@commitlint/config-conventional@17.6.0: - resolution: {integrity: sha512-2Y9M7MN942bTK5h70fJGknhXA02+OtWCkKeIzTSwsdwz1V7y6bxYv24x052E9XHKtZHJfvM3iLuTOsjRvLqWtA==} + /@commitlint/config-conventional@17.4.4: + resolution: {integrity: sha512-u6ztvxqzi6NuhrcEDR7a+z0yrh11elY66nRrQIpqsqW6sZmpxYkDLtpRH8jRML+mmxYQ8s4qqF06Q/IQx5aJeQ==} engines: {node: '>=v14'} dependencies: conventional-changelog-conventionalcommits: 5.0.0 @@ -5272,13 +7044,13 @@ packages: semver: 7.3.8 dev: true - /@commitlint/lint@17.6.0: - resolution: {integrity: sha512-6cEXxpxZd7fbtYMxeosOum/Nnwu3VdSuZcrFSqP9lWNsrHRv4ijVsnLeomvo6WHPchGOeEWAazAI7Q6Ap22fJw==} + /@commitlint/lint@17.4.4: + resolution: {integrity: sha512-qgkCRRFjyhbMDWsti/5jRYVJkgYZj4r+ZmweZObnbYqPUl5UKLWMf9a/ZZisOI4JfiPmRktYRZ2JmqlSvg+ccw==} engines: {node: '>=v14'} dependencies: '@commitlint/is-ignored': 17.4.4 '@commitlint/parse': 17.4.4 - '@commitlint/rules': 17.6.0 + '@commitlint/rules': 17.4.4 '@commitlint/types': 17.4.4 dev: true @@ -5290,15 +7062,15 @@ packages: '@commitlint/execute-rule': 17.4.0 '@commitlint/resolve-extends': 17.4.4 '@commitlint/types': 17.4.4 - '@types/node': 18.15.11 + '@types/node': 18.15.10 chalk: 4.1.2 cosmiconfig: 8.1.3 - cosmiconfig-typescript-loader: 4.3.0(@types/node@18.15.11)(cosmiconfig@8.1.3)(ts-node@10.9.1)(typescript@4.9.5) + cosmiconfig-typescript-loader: 4.3.0(@types/node@18.15.10)(cosmiconfig@8.1.3)(ts-node@10.9.1)(typescript@4.9.5) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 resolve-from: 5.0.0 - ts-node: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) + ts-node: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - '@swc/core' @@ -5319,8 +7091,8 @@ packages: conventional-commits-parser: 3.2.4 dev: true - /@commitlint/read@17.5.1: - resolution: {integrity: sha512-7IhfvEvB//p9aYW09YVclHbdf1u7g7QhxeYW9ZHSO8Huzp8Rz7m05aCO1mFG7G8M+7yfFnXB5xOmG18brqQIBg==} + /@commitlint/read@17.4.4: + resolution: {integrity: sha512-B2TvUMJKK+Svzs6eji23WXsRJ8PAD+orI44lVuVNsm5zmI7O8RSGJMvdEZEikiA4Vohfb+HevaPoWZ7PiFZ3zA==} engines: {node: '>=v14'} dependencies: '@commitlint/top-level': 17.4.0 @@ -5342,8 +7114,8 @@ packages: resolve-global: 1.0.0 dev: true - /@commitlint/rules@17.6.0: - resolution: {integrity: sha512-Ka7AsRFvkKMYYE7itgo7hddRGCiV+0BgbTIAq4PWmnkHAECxYpdqMVzW5jaATmXZfwfRRTB57e7KZWj6EPmK1A==} + /@commitlint/rules@17.4.4: + resolution: {integrity: sha512-0tgvXnHi/mVcyR8Y8mjTFZIa/FEQXA4uEutXS/imH2v1UNkYDSEMsK/68wiXRpfW1euSgEdwRkvE1z23+yhNrQ==} engines: {node: '>=v14'} dependencies: '@commitlint/ensure': 17.4.4 @@ -5378,54 +7150,52 @@ packages: dependencies: '@jridgewell/trace-mapping': 0.3.9 + /@dabh/diagnostics@2.0.3: + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + dev: false + /@discoveryjs/json-ext@0.5.7: resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} dev: true - /@docsearch/css@3.3.3: - resolution: {integrity: sha512-6SCwI7P8ao+se1TUsdZ7B4XzL+gqeQZnBc+2EONZlcVa0dVrk0NjETxozFKgMv0eEGH8QzP1fkN+A1rH61l4eg==} + /@docsearch/css@3.1.0: + resolution: {integrity: sha512-bh5IskwkkodbvC0FzSg1AxMykfDl95hebEKwxNoq4e5QaGzOXSBgW8+jnMFZ7JU4sTBiB04vZWoUSzNrPboLZA==} dev: false - /@docsearch/react@3.3.3(@algolia/client-search@4.17.0)(@types/react@17.0.58)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-pLa0cxnl+G0FuIDuYlW+EBK6Rw2jwLw9B1RHIeS4N4s2VhsfJ/wzeCi3CWcs5yVfxLd5ZK50t//TMA5e79YT7Q==} + /@docsearch/react@3.1.0(@types/react@17.0.45)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-bjB6ExnZzf++5B7Tfoi6UXgNwoUnNOfZ1NyvnvPhWgCMy5V/biAtLL4o7owmZSYdAKeFSvZ5Lxm0is4su/dBWg==} peerDependencies: '@types/react': '>= 16.8.0 < 19.0.0' react: '>= 16.8.0 < 19.0.0' react-dom: '>= 16.8.0 < 19.0.0' - peerDependenciesMeta: - '@types/react': - optional: true - react: - optional: true - react-dom: - optional: true dependencies: - '@algolia/autocomplete-core': 1.7.4 - '@algolia/autocomplete-preset-algolia': 1.7.4(@algolia/client-search@4.17.0)(algoliasearch@4.17.0) - '@docsearch/css': 3.3.3 - '@types/react': 17.0.58 - algoliasearch: 4.17.0 + '@algolia/autocomplete-core': 1.6.3 + '@docsearch/css': 3.1.0 + '@types/react': 17.0.45 + algoliasearch: 4.18.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - transitivePeerDependencies: - - '@algolia/client-search' dev: false - /@emmetio/abbreviation@2.3.1: - resolution: {integrity: sha512-QXgYlXZGprqb6aCBJPPWVBN/Jb69khJF73GGJkOk//PoMgSbPGuaHn1hCRolctnzlBHjCIC6Om97Pw46/1A23g==} + /@emmetio/abbreviation@2.2.3: + resolution: {integrity: sha512-87pltuCPt99aL+y9xS6GPZ+Wmmyhll2WXH73gG/xpGcQ84DRnptBsI2r0BeIQ0EB/SQTOe2ANPqFqj3Rj5FOGA==} dependencies: - '@emmetio/scanner': 1.0.2 + '@emmetio/scanner': 1.0.0 dev: false - /@emmetio/css-abbreviation@2.1.6: - resolution: {integrity: sha512-bvuPogt0OvwcILRg+ZD/oej1H72xwOhUDPWOmhCWLJrZZ8bMTazsWnvw8a8noaaVqUhOE9PsC0tYgGVv5N7fsw==} + /@emmetio/css-abbreviation@2.1.4: + resolution: {integrity: sha512-qk9L60Y+uRtM5CPbB0y+QNl/1XKE09mSO+AhhSauIfr2YOx/ta3NJw2d8RtCFxgzHeRqFRr8jgyzThbu+MZ4Uw==} dependencies: - '@emmetio/scanner': 1.0.2 + '@emmetio/scanner': 1.0.0 dev: false - /@emmetio/scanner@1.0.2: - resolution: {integrity: sha512-1ESCGgXRgn1r29hRmz8K0G4Ywr5jDWezMgRnICComBCWmg3znLWU8+tmakuM1og1Vn4W/sauvlABl/oq2pve8w==} + /@emmetio/scanner@1.0.0: + resolution: {integrity: sha512-8HqW8EVqjnCmWXVpqAOZf+EGESdkR27odcMMMGefgKXtar00SoYNSryGv//TELI4T3QFsECo78p+0lmalk/CFA==} dev: false /@emotion/is-prop-valid@0.8.8: @@ -5458,8 +7228,24 @@ packages: dev: true optional: true - /@esbuild/android-arm64@0.17.16: - resolution: {integrity: sha512-QX48qmsEZW+gcHgTmAj+x21mwTz8MlYQBnzF6861cNdQGvj2jzzFjqH0EBabrIa/WVZ2CHolwMoqxVryqKt8+Q==} + /@esbuild/android-arm64@0.17.14: + resolution: {integrity: sha512-eLOpPO1RvtsP71afiFTvS7tVFShJBCT0txiv/xjFBo5a7R7Gjw7X0IgIaFoLKhqXYAXhahoXm7qAmRXhY4guJg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + optional: true + + /@esbuild/android-arm64@0.17.19: + resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + optional: true + + /@esbuild/android-arm64@0.18.12: + resolution: {integrity: sha512-BMAlczRqC/LUt2P97E4apTBbkvS9JTJnp2DKFbCwpZ8vBvXVbNdqmvzW/OsdtI/+mGr+apkkpqGM8WecLkPgrA==} engines: {node: '>=12'} cpu: [arm64] os: [android] @@ -5483,8 +7269,24 @@ packages: dev: true optional: true - /@esbuild/android-arm@0.17.16: - resolution: {integrity: sha512-baLqRpLe4JnKrUXLJChoTN0iXZH7El/mu58GE3WIA6/H834k0XWvLRmGLG8y8arTRS9hJJibPnF0tiGhmWeZgw==} + /@esbuild/android-arm@0.17.14: + resolution: {integrity: sha512-0CnlwnjDU8cks0yJLXfkaU/uoLyRf9VZJs4p1PskBr2AlAHeEsFEwJEo0of/Z3g+ilw5mpyDwThlxzNEIxOE4g==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + optional: true + + /@esbuild/android-arm@0.17.19: + resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + optional: true + + /@esbuild/android-arm@0.18.12: + resolution: {integrity: sha512-LIxaNIQfkFZbTLb4+cX7dozHlAbAshhFE5PKdro0l+FnCpx1GDJaQ2WMcqm+ToXKMt8p8Uojk/MFRuGyz3V5Sw==} engines: {node: '>=12'} cpu: [arm] os: [android] @@ -5500,76 +7302,156 @@ packages: dev: true optional: true - /@esbuild/android-x64@0.17.16: - resolution: {integrity: sha512-G4wfHhrrz99XJgHnzFvB4UwwPxAWZaZBOFXh+JH1Duf1I4vIVfuYY9uVLpx4eiV2D/Jix8LJY+TAdZ3i40tDow==} + /@esbuild/android-x64@0.17.14: + resolution: {integrity: sha512-nrfQYWBfLGfSGLvRVlt6xi63B5IbfHm3tZCdu/82zuFPQ7zez4XjmRtF/wIRYbJQ/DsZrxJdEvYFE67avYXyng==} engines: {node: '>=12'} cpu: [x64] os: [android] requiresBuild: true optional: true - /@esbuild/darwin-arm64@0.16.17: - resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} + /@esbuild/android-x64@0.17.19: + resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] + cpu: [x64] + os: [android] requiresBuild: true - dev: true optional: true - /@esbuild/darwin-arm64@0.17.16: - resolution: {integrity: sha512-/Ofw8UXZxuzTLsNFmz1+lmarQI6ztMZ9XktvXedTbt3SNWDn0+ODTwxExLYQ/Hod91EZB4vZPQJLoqLF0jvEzA==} + /@esbuild/android-x64@0.18.12: + resolution: {integrity: sha512-zU5MyluNsykf5cOJ0LZZZjgAHbhPJ1cWfdH1ZXVMXxVMhEV0VZiZXQdwBBVvmvbF28EizeK7obG9fs+fpmS0eQ==} engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] + cpu: [x64] + os: [android] requiresBuild: true optional: true - /@esbuild/darwin-x64@0.16.17: - resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} + /@esbuild/darwin-arm64@0.16.17: + resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} engines: {node: '>=12'} - cpu: [x64] + cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /@esbuild/darwin-x64@0.17.16: - resolution: {integrity: sha512-SzBQtCV3Pdc9kyizh36Ol+dNVhkDyIrGb/JXZqFq8WL37LIyrXU0gUpADcNV311sCOhvY+f2ivMhb5Tuv8nMOQ==} + /@esbuild/darwin-arm64@0.17.14: + resolution: {integrity: sha512-eoSjEuDsU1ROwgBH/c+fZzuSyJUVXQTOIN9xuLs9dE/9HbV/A5IqdXHU1p2OfIMwBwOYJ9SFVGGldxeRCUJFyw==} engines: {node: '>=12'} - cpu: [x64] + cpu: [arm64] os: [darwin] requiresBuild: true optional: true - /@esbuild/freebsd-arm64@0.16.17: - resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} + /@esbuild/darwin-arm64@0.17.19: + resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} engines: {node: '>=12'} cpu: [arm64] - os: [freebsd] + os: [darwin] requiresBuild: true - dev: true optional: true - /@esbuild/freebsd-arm64@0.17.16: - resolution: {integrity: sha512-ZqftdfS1UlLiH1DnS2u3It7l4Bc3AskKeu+paJSfk7RNOMrOxmeFDhLTMQqMxycP1C3oj8vgkAT6xfAuq7ZPRA==} + /@esbuild/darwin-arm64@0.18.12: + resolution: {integrity: sha512-zUZMep7YONnp6954QOOwEBwFX9svlKd3ov6PkxKd53LGTHsp/gy7vHaPGhhjBmEpqXEXShi6dddjIkmd+NgMsA==} engines: {node: '>=12'} cpu: [arm64] - os: [freebsd] + os: [darwin] requiresBuild: true optional: true - /@esbuild/freebsd-x64@0.16.17: - resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} + /@esbuild/darwin-x64@0.16.17: + resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} engines: {node: '>=12'} cpu: [x64] - os: [freebsd] + os: [darwin] requiresBuild: true dev: true optional: true - /@esbuild/freebsd-x64@0.17.16: - resolution: {integrity: sha512-rHV6zNWW1tjgsu0dKQTX9L0ByiJHHLvQKrWtnz8r0YYJI27FU3Xu48gpK2IBj1uCSYhJ+pEk6Y0Um7U3rIvV8g==} + /@esbuild/darwin-x64@0.17.14: + resolution: {integrity: sha512-zN0U8RWfrDttdFNkHqFYZtOH8hdi22z0pFm0aIJPsNC4QQZv7je8DWCX5iA4Zx6tRhS0CCc0XC2m7wKsbWEo5g==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optional: true + + /@esbuild/darwin-x64@0.17.19: + resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optional: true + + /@esbuild/darwin-x64@0.18.12: + resolution: {integrity: sha512-ohqLPc7i67yunArPj1+/FeeJ7AgwAjHqKZ512ADk3WsE3FHU9l+m5aa7NdxXr0HmN1bjDlUslBjWNbFlD9y12Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optional: true + + /@esbuild/freebsd-arm64@0.16.17: + resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.17.14: + resolution: {integrity: sha512-z0VcD4ibeZWVQCW1O7szaLxGsx54gcCnajEJMdYoYjLiq4g1jrP2lMq6pk71dbS5+7op/L2Aod+erw+EUr28/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + optional: true + + /@esbuild/freebsd-arm64@0.17.19: + resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + optional: true + + /@esbuild/freebsd-arm64@0.18.12: + resolution: {integrity: sha512-GIIHtQXqgeOOqdG16a/A9N28GpkvjJnjYMhOnXVbn3EDJcoItdR58v/pGN31CHjyXDc8uCcRnFWmqaJt24AYJg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + optional: true + + /@esbuild/freebsd-x64@0.16.17: + resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.17.14: + resolution: {integrity: sha512-hd9mPcxfTgJlolrPlcXkQk9BMwNBvNBsVaUe5eNUqXut6weDQH8whcNaKNF2RO8NbpT6GY8rHOK2A9y++s+ehw==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + optional: true + + /@esbuild/freebsd-x64@0.17.19: + resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + optional: true + + /@esbuild/freebsd-x64@0.18.12: + resolution: {integrity: sha512-zK0b9a1/0wZY+6FdOS3BpZcPc1kcx2G5yxxfEJtEUzVxI6n/FrC2Phsxj/YblPuBchhBZ/1wwn7AyEBUyNSa6g==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] @@ -5585,8 +7467,24 @@ packages: dev: true optional: true - /@esbuild/linux-arm64@0.17.16: - resolution: {integrity: sha512-8yoZhGkU6aHu38WpaM4HrRLTFc7/VVD9Q2SvPcmIQIipQt2I/GMTZNdEHXoypbbGao5kggLcxg0iBKjo0SQYKA==} + /@esbuild/linux-arm64@0.17.14: + resolution: {integrity: sha512-FhAMNYOq3Iblcj9i+K0l1Fp/MHt+zBeRu/Qkf0LtrcFu3T45jcwB6A1iMsemQ42vR3GBhjNZJZTaCe3VFPbn9g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-arm64@0.17.19: + resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-arm64@0.18.12: + resolution: {integrity: sha512-JKgG8Q/LL/9sw/iHHxQyVMoQYu3rU3+a5Z87DxC+wAu3engz+EmctIrV+FGOgI6gWG1z1+5nDDbXiRMGQZXqiw==} engines: {node: '>=12'} cpu: [arm64] os: [linux] @@ -5602,8 +7500,24 @@ packages: dev: true optional: true - /@esbuild/linux-arm@0.17.16: - resolution: {integrity: sha512-n4O8oVxbn7nl4+m+ISb0a68/lcJClIbaGAoXwqeubj/D1/oMMuaAXmJVfFlRjJLu/ZvHkxoiFJnmbfp4n8cdSw==} + /@esbuild/linux-arm@0.17.14: + resolution: {integrity: sha512-BNTl+wSJ1omsH8s3TkQmIIIQHwvwJrU9u1ggb9XU2KTVM4TmthRIVyxSp2qxROJHhZuW/r8fht46/QE8hU8Qvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-arm@0.17.19: + resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-arm@0.18.12: + resolution: {integrity: sha512-y75OijvrBE/1XRrXq1jtrJfG26eHeMoqLJ2dwQNwviwTuTtHGCojsDO6BJNF8gU+3jTn1KzJEMETytwsFSvc+Q==} engines: {node: '>=12'} cpu: [arm] os: [linux] @@ -5619,8 +7533,24 @@ packages: dev: true optional: true - /@esbuild/linux-ia32@0.17.16: - resolution: {integrity: sha512-9ZBjlkdaVYxPNO8a7OmzDbOH9FMQ1a58j7Xb21UfRU29KcEEU3VTHk+Cvrft/BNv0gpWJMiiZ/f4w0TqSP0gLA==} + /@esbuild/linux-ia32@0.17.14: + resolution: {integrity: sha512-91OK/lQ5y2v7AsmnFT+0EyxdPTNhov3y2CWMdizyMfxSxRqHazXdzgBKtlmkU2KYIc+9ZK3Vwp2KyXogEATYxQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-ia32@0.17.19: + resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-ia32@0.18.12: + resolution: {integrity: sha512-yoRIAqc0B4lDIAAEFEIu9ttTRFV84iuAl0KNCN6MhKLxNPfzwCBvEMgwco2f71GxmpBcTtn7KdErueZaM2rEvw==} engines: {node: '>=12'} cpu: [ia32] os: [linux] @@ -5644,8 +7574,24 @@ packages: dev: true optional: true - /@esbuild/linux-loong64@0.17.16: - resolution: {integrity: sha512-TIZTRojVBBzdgChY3UOG7BlPhqJz08AL7jdgeeu+kiObWMFzGnQD7BgBBkWRwOtKR1i2TNlO7YK6m4zxVjjPRQ==} + /@esbuild/linux-loong64@0.17.14: + resolution: {integrity: sha512-vp15H+5NR6hubNgMluqqKza85HcGJgq7t6rMH7O3Y6ApiOWPkvW2AJfNojUQimfTp6OUrACUXfR4hmpcENXoMQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-loong64@0.17.19: + resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-loong64@0.18.12: + resolution: {integrity: sha512-qYgt3dHPVvf/MgbIBpJ4Sup/yb9DAopZ3a2JgMpNKIHUpOdnJ2eHBo/aQdnd8dJ21X/+sS58wxHtA9lEazYtXQ==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -5661,8 +7607,24 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el@0.17.16: - resolution: {integrity: sha512-UPeRuFKCCJYpBbIdczKyHLAIU31GEm0dZl1eMrdYeXDH+SJZh/i+2cAmD3A1Wip9pIc5Sc6Kc5cFUrPXtR0XHA==} + /@esbuild/linux-mips64el@0.17.14: + resolution: {integrity: sha512-90TOdFV7N+fgi6c2+GO9ochEkmm9kBAKnuD5e08GQMgMINOdOFHuYLPQ91RYVrnWwQ5683sJKuLi9l4SsbJ7Hg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-mips64el@0.17.19: + resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-mips64el@0.18.12: + resolution: {integrity: sha512-wHphlMLK4ufNOONqukELfVIbnGQJrHJ/mxZMMrP2jYrPgCRZhOtf0kC4yAXBwnfmULimV1qt5UJJOw4Kh13Yfg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] @@ -5678,8 +7640,24 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64@0.17.16: - resolution: {integrity: sha512-io6yShgIEgVUhExJejJ21xvO5QtrbiSeI7vYUnr7l+v/O9t6IowyhdiYnyivX2X5ysOVHAuyHW+Wyi7DNhdw6Q==} + /@esbuild/linux-ppc64@0.17.14: + resolution: {integrity: sha512-NnBGeoqKkTugpBOBZZoktQQ1Yqb7aHKmHxsw43NddPB2YWLAlpb7THZIzsRsTr0Xw3nqiPxbA1H31ZMOG+VVPQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-ppc64@0.17.19: + resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-ppc64@0.18.12: + resolution: {integrity: sha512-TeN//1Ft20ZZW41+zDSdOI/Os1bEq5dbvBvYkberB7PHABbRcsteeoNVZFlI0YLpGdlBqohEpjrn06kv8heCJg==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] @@ -5695,8 +7673,24 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64@0.17.16: - resolution: {integrity: sha512-WhlGeAHNbSdG/I2gqX2RK2gfgSNwyJuCiFHMc8s3GNEMMHUI109+VMBfhVqRb0ZGzEeRiibi8dItR3ws3Lk+cA==} + /@esbuild/linux-riscv64@0.17.14: + resolution: {integrity: sha512-0qdlKScLXA8MGVy21JUKvMzCYWovctuP8KKqhtE5A6IVPq4onxXhSuhwDd2g5sRCzNDlDjitc5sX31BzDoL5Fw==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-riscv64@0.17.19: + resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-riscv64@0.18.12: + resolution: {integrity: sha512-AgUebVS4DoAblBgiB2ACQ/8l4eGE5aWBb8ZXtkXHiET9mbj7GuWt3OnsIW/zX+XHJt2RYJZctbQ2S/mDjbp0UA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] @@ -5712,8 +7706,24 @@ packages: dev: true optional: true - /@esbuild/linux-s390x@0.17.16: - resolution: {integrity: sha512-gHRReYsJtViir63bXKoFaQ4pgTyah4ruiMRQ6im9YZuv+gp3UFJkNTY4sFA73YDynmXZA6hi45en4BGhNOJUsw==} + /@esbuild/linux-s390x@0.17.14: + resolution: {integrity: sha512-Hdm2Jo1yaaOro4v3+6/zJk6ygCqIZuSDJHdHaf8nVH/tfOuoEX5Riv03Ka15LmQBYJObUTNS1UdyoMk0WUn9Ww==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-s390x@0.17.19: + resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-s390x@0.18.12: + resolution: {integrity: sha512-dJ3Rb3Ei2u/ysSXd6pzleGtfDdc2MuzKt8qc6ls8vreP1G3B7HInX3i7gXS4BGeVd24pp0yqyS7bJ5NHaI9ing==} engines: {node: '>=12'} cpu: [s390x] os: [linux] @@ -5729,8 +7739,24 @@ packages: dev: true optional: true - /@esbuild/linux-x64@0.17.16: - resolution: {integrity: sha512-mfiiBkxEbUHvi+v0P+TS7UnA9TeGXR48aK4XHkTj0ZwOijxexgMF01UDFaBX7Q6CQsB0d+MFNv9IiXbIHTNd4g==} + /@esbuild/linux-x64@0.17.14: + resolution: {integrity: sha512-8KHF17OstlK4DuzeF/KmSgzrTWQrkWj5boluiiq7kvJCiQVzUrmSkaBvcLB2UgHpKENO2i6BthPkmUhNDaJsVw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-x64@0.17.19: + resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-x64@0.18.12: + resolution: {integrity: sha512-OrNJMGQbPaVyHHcDF8ybNSwu7TDOfX8NGpXCbetwOSP6txOJiWlgQnRymfC9ocR1S0Y5PW0Wb1mV6pUddqmvmQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] @@ -5746,8 +7772,24 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64@0.17.16: - resolution: {integrity: sha512-n8zK1YRDGLRZfVcswcDMDM0j2xKYLNXqei217a4GyBxHIuPMGrrVuJ+Ijfpr0Kufcm7C1k/qaIrGy6eG7wvgmA==} + /@esbuild/netbsd-x64@0.17.14: + resolution: {integrity: sha512-nVwpqvb3yyXztxIT2+VsxJhB5GCgzPdk1n0HHSnchRAcxqKO6ghXwHhJnr0j/B+5FSyEqSxF4q03rbA2fKXtUQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + optional: true + + /@esbuild/netbsd-x64@0.17.19: + resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + optional: true + + /@esbuild/netbsd-x64@0.18.12: + resolution: {integrity: sha512-55FzVCAiwE9FK8wWeCRuvjazNRJ1QqLCYGZVB6E8RuQuTeStSwotpSW4xoRGwp3a1wUsaVCdYcj5LGCASVJmMg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] @@ -5763,8 +7805,24 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64@0.17.16: - resolution: {integrity: sha512-lEEfkfsUbo0xC47eSTBqsItXDSzwzwhKUSsVaVjVji07t8+6KA5INp2rN890dHZeueXJAI8q0tEIfbwVRYf6Ew==} + /@esbuild/openbsd-x64@0.17.14: + resolution: {integrity: sha512-1RZ7uQQ9zcy/GSAJL1xPdN7NDdOOtNEGiJalg/MOzeakZeTrgH/DoCkbq7TaPDiPhWqnDF+4bnydxRqQD7il6g==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + optional: true + + /@esbuild/openbsd-x64@0.17.19: + resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + optional: true + + /@esbuild/openbsd-x64@0.18.12: + resolution: {integrity: sha512-qnluf8rfb6Y5Lw2tirfK2quZOBbVqmwxut7GPCIJsM8lc4AEUj9L8y0YPdLaPK0TECt4IdyBdBD/KRFKorlK3g==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] @@ -5780,8 +7838,24 @@ packages: dev: true optional: true - /@esbuild/sunos-x64@0.17.16: - resolution: {integrity: sha512-jlRjsuvG1fgGwnE8Afs7xYDnGz0dBgTNZfgCK6TlvPH3Z13/P5pi6I57vyLE8qZYLrGVtwcm9UbUx1/mZ8Ukag==} + /@esbuild/sunos-x64@0.17.14: + resolution: {integrity: sha512-nqMjDsFwv7vp7msrwWRysnM38Sd44PKmW8EzV01YzDBTcTWUpczQg6mGao9VLicXSgW/iookNK6AxeogNVNDZA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + optional: true + + /@esbuild/sunos-x64@0.17.19: + resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + optional: true + + /@esbuild/sunos-x64@0.18.12: + resolution: {integrity: sha512-+RkKpVQR7bICjTOPUpkTBTaJ4TFqQBX5Ywyd/HSdDkQGn65VPkTsR/pL4AMvuMWy+wnXgIl4EY6q4mVpJal8Kg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] @@ -5797,8 +7871,24 @@ packages: dev: true optional: true - /@esbuild/win32-arm64@0.17.16: - resolution: {integrity: sha512-TzoU2qwVe2boOHl/3KNBUv2PNUc38U0TNnzqOAcgPiD/EZxT2s736xfC2dYQbszAwo4MKzzwBV0iHjhfjxMimg==} + /@esbuild/win32-arm64@0.17.14: + resolution: {integrity: sha512-xrD0mccTKRBBIotrITV7WVQAwNJ5+1va6L0H9zN92v2yEdjfAN7864cUaZwJS7JPEs53bDTzKFbfqVlG2HhyKQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + optional: true + + /@esbuild/win32-arm64@0.17.19: + resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + optional: true + + /@esbuild/win32-arm64@0.18.12: + resolution: {integrity: sha512-GNHuciv0mFM7ouzsU0+AwY+7eV4Mgo5WnbhfDCQGtpvOtD1vbOiRjPYG6dhmMoFyBjj+pNqQu2X+7DKn0KQ/Gw==} engines: {node: '>=12'} cpu: [arm64] os: [win32] @@ -5814,8 +7904,24 @@ packages: dev: true optional: true - /@esbuild/win32-ia32@0.17.16: - resolution: {integrity: sha512-B8b7W+oo2yb/3xmwk9Vc99hC9bNolvqjaTZYEfMQhzdpBsjTvZBlXQ/teUE55Ww6sg//wlcDjOaqldOKyigWdA==} + /@esbuild/win32-ia32@0.17.14: + resolution: {integrity: sha512-nXpkz9bbJrLLyUTYtRotSS3t5b+FOuljg8LgLdINWFs3FfqZMtbnBCZFUmBzQPyxqU87F8Av+3Nco/M3hEcu1w==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + optional: true + + /@esbuild/win32-ia32@0.17.19: + resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + optional: true + + /@esbuild/win32-ia32@0.18.12: + resolution: {integrity: sha512-kR8cezhYipbbypGkaqCTWIeu4zID17gamC8YTPXYtcN3E5BhhtTnwKBn9I0PJur/T6UVwIEGYzkffNL0lFvxEw==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -5831,8 +7937,24 @@ packages: dev: true optional: true - /@esbuild/win32-x64@0.17.16: - resolution: {integrity: sha512-xJ7OH/nanouJO9pf03YsL9NAFQBHd8AqfrQd7Pf5laGyyTt/gToul6QYOA/i5i/q8y9iaM5DQFNTgpi995VkOg==} + /@esbuild/win32-x64@0.17.14: + resolution: {integrity: sha512-gPQmsi2DKTaEgG14hc3CHXHp62k8g6qr0Pas+I4lUxRMugGSATh/Bi8Dgusoz9IQ0IfdrvLpco6kujEIBoaogA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + optional: true + + /@esbuild/win32-x64@0.17.19: + resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + optional: true + + /@esbuild/win32-x64@0.18.12: + resolution: {integrity: sha512-O0UYQVkvfM/jO8a4OwoV0mAKSJw+mjWTAd1MJd/1FCX6uiMdLmMRPK/w6e9OQ0ob2WGxzIm9va/KG0Ja4zIOgg==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -5846,22 +7968,42 @@ packages: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: eslint: 8.22.0 - eslint-visitor-keys: 3.4.0 + eslint-visitor-keys: 3.4.1 dev: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.38.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.36.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.38.0 - eslint-visitor-keys: 3.4.0 + eslint: 8.36.0 + eslint-visitor-keys: 3.4.1 dev: true - /@eslint-community/regexpp@4.5.0: - resolution: {integrity: sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + /@eslint-community/eslint-utils@4.4.0(eslint@8.38.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.38.0 + eslint-visitor-keys: 3.4.1 + dev: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.44.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.44.0 + eslint-visitor-keys: 3.4.1 + dev: true + + /@eslint-community/regexpp@4.4.1: + resolution: {integrity: sha512-BISJ6ZE4xQsuL/FmsyRaiffpq977bMlsKfGHTQrOGFErfByxIe6iZTxPf/00Zon9b9a7iUykfQwejN3s2ZW/Bw==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true /@eslint/eslintrc@1.4.1: @@ -5869,8 +8011,41 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4 - espree: 9.5.1 + debug: 4.3.4(supports-color@8.1.1) + espree: 9.5.2 + globals: 13.20.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + /@eslint/eslintrc@2.0.1: + resolution: {integrity: sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4(supports-color@8.1.1) + espree: 9.5.2 + globals: 13.20.0 + ignore: 5.2.4 + 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/eslintrc@2.0.3: + resolution: {integrity: sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4(supports-color@8.1.1) + espree: 9.5.2 globals: 13.20.0 ignore: 5.2.4 import-fresh: 3.3.0 @@ -5879,14 +8054,15 @@ packages: strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color + dev: true - /@eslint/eslintrc@2.0.2: - resolution: {integrity: sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==} + /@eslint/eslintrc@2.1.0: + resolution: {integrity: sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 debug: 4.3.4(supports-color@8.1.1) - espree: 9.5.1 + espree: 9.6.0 globals: 13.20.0 ignore: 5.2.4 import-fresh: 3.3.0 @@ -5897,11 +8073,21 @@ packages: - supports-color dev: true + /@eslint/js@8.36.0: + resolution: {integrity: sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + /@eslint/js@8.38.0: resolution: {integrity: sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@eslint/js@8.44.0: + resolution: {integrity: sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + /@faker-js/faker@7.6.0: resolution: {integrity: sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==} engines: {node: '>=14.0.0', npm: '>=6.0.0'} @@ -5920,14 +8106,14 @@ packages: dependencies: '@felte/common': 1.1.4 - /@felte/reporter-svelte@1.1.5(svelte@3.58.0): + /@felte/reporter-svelte@1.1.5(svelte@3.57.0): resolution: {integrity: sha512-emV9eYpN6/JcUP5sDXC3zPFdTnEp4Tw3G7KyT/cSg9MSnhYz+6ZW7eBEGsFSS0dJHocIliZPk+T4ROKdQgFG+w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: svelte: ^3.31.0 dependencies: '@felte/common': 1.1.4 - svelte: 3.58.0 + svelte: 3.57.0 dev: false /@felte/validator-zod@1.0.13(zod@3.21.4): @@ -5944,13 +8130,37 @@ packages: resolution: {integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==} dev: false + /@floating-ui/core@1.3.1: + resolution: {integrity: sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g==} + dev: false + /@floating-ui/dom@0.5.4: resolution: {integrity: sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==} dependencies: '@floating-ui/core': 0.7.3 dev: false - /@floating-ui/react-dom@0.7.2(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0): + /@floating-ui/dom@1.4.1: + resolution: {integrity: sha512-loCXUOLzIC3jp50RFOKXZ/kQjjz26ryr/23M+FWG9jrmAv8lRf3DUfC2AiVZ3+K316GOhB08CR+Povwz8e9mDw==} + dependencies: + '@floating-ui/core': 1.3.1 + dev: false + + /@floating-ui/react-dom@0.7.2(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/dom': 0.5.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + use-isomorphic-layout-effect: 1.1.2(@types/react@18.0.29)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + + /@floating-ui/react-dom@0.7.2(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==} peerDependencies: react: '>=16.8.0' @@ -5959,11 +8169,22 @@ packages: '@floating-ui/dom': 0.5.4 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - use-isomorphic-layout-effect: 1.1.2(@types/react@18.0.35)(react@18.2.0) + use-isomorphic-layout-effect: 1.1.2(@types/react@18.0.37)(react@18.2.0) transitivePeerDependencies: - '@types/react' dev: false + /@floating-ui/react-dom@2.0.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/dom': 1.4.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@fontsource/inter@4.5.15: resolution: {integrity: sha512-FzleM9AxZQK2nqsTDtBiY0PMEVWvnKnuu2i09+p6DHvrHsuucoV2j0tmw+kAT3L4hvsLdAIDv6MdGehsPIdT+Q==} dev: false @@ -5989,7 +8210,7 @@ packages: dependencies: '@graphql-tools/utils': 9.2.1(graphql@16.6.0) graphql: 16.6.0 - tslib: 2.5.0 + tslib: 2.5.2 dev: false /@graphql-tools/schema@8.3.1(graphql@16.6.0): @@ -6020,7 +8241,7 @@ packages: dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.6.0) graphql: 16.6.0 - tslib: 2.5.0 + tslib: 2.5.2 dev: false /@graphql-typed-document-node/core@3.2.0(graphql@16.6.0): @@ -6054,10 +8275,21 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + /@humanwhocodes/config-array@0.11.10: + resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color + dev: true /@humanwhocodes/config-array@0.11.8: resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} @@ -6102,13 +8334,25 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 chalk: 4.1.2 jest-message-util: 27.5.1 jest-util: 27.5.1 slash: 3.0.0 dev: true + /@jest/console@29.5.0: + resolution: {integrity: sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@types/node': 18.15.10 + chalk: 4.1.2 + jest-message-util: 29.5.0 + jest-util: 29.5.0 + slash: 3.0.0 + dev: true + /@jest/core@27.5.1(ts-node@10.9.1): resolution: {integrity: sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -6123,7 +8367,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.8.1 @@ -6154,16 +8398,68 @@ packages: - utf-8-validate dev: true + /@jest/core@29.5.0(ts-node@10.9.1): + resolution: {integrity: sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.5.0 + '@jest/reporters': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 18.15.10 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.8.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.5.0 + jest-config: 29.5.0(@types/node@18.15.10)(ts-node@10.9.1) + jest-haste-map: 29.5.0 + jest-message-util: 29.5.0 + jest-regex-util: 29.4.3 + jest-resolve: 29.5.0 + jest-resolve-dependencies: 29.5.0 + jest-runner: 29.5.0 + jest-runtime: 29.5.0 + jest-snapshot: 29.5.0 + jest-util: 29.5.0 + jest-validate: 29.5.0 + jest-watcher: 29.5.0 + micromatch: 4.0.5 + pretty-format: 29.5.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + /@jest/environment@27.5.1: resolution: {integrity: sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 jest-mock: 27.5.1 dev: true + /@jest/environment@29.5.0: + resolution: {integrity: sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/fake-timers': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 18.15.10 + jest-mock: 29.5.0 + dev: true + /@jest/expect-utils@29.5.0: resolution: {integrity: sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -6171,18 +8467,40 @@ packages: jest-get-type: 29.4.3 dev: true + /@jest/expect@29.5.0: + resolution: {integrity: sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.5.0 + jest-snapshot: 29.5.0 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/fake-timers@27.5.1: resolution: {integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 '@sinonjs/fake-timers': 8.1.0 - '@types/node': 18.15.11 + '@types/node': 18.15.10 jest-message-util: 27.5.1 jest-mock: 27.5.1 jest-util: 27.5.1 dev: true + /@jest/fake-timers@29.5.0: + resolution: {integrity: sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@sinonjs/fake-timers': 10.1.0 + '@types/node': 18.15.10 + jest-message-util: 29.5.0 + jest-mock: 29.5.0 + jest-util: 29.5.0 + dev: true + /@jest/globals@27.5.1: resolution: {integrity: sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -6192,6 +8510,18 @@ packages: expect: 27.5.1 dev: true + /@jest/globals@29.5.0: + resolution: {integrity: sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.5.0 + '@jest/expect': 29.5.0 + '@jest/types': 29.5.0 + jest-mock: 29.5.0 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/reporters@27.5.1: resolution: {integrity: sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -6206,7 +8536,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -6230,6 +8560,43 @@ packages: - supports-color dev: true + /@jest/reporters@29.5.0: + resolution: {integrity: sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@jridgewell/trace-mapping': 0.3.17 + '@types/node': 18.15.10 + chalk: 4.1.2 + collect-v8-coverage: 1.0.1 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-instrument: 5.2.1 + istanbul-lib-report: 3.0.0 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.5 + jest-message-util: 29.5.0 + jest-util: 29.5.0 + jest-worker: 29.5.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.1.0 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/schemas@29.4.3: resolution: {integrity: sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -6246,6 +8613,15 @@ packages: source-map: 0.6.1 dev: true + /@jest/source-map@29.4.3: + resolution: {integrity: sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jridgewell/trace-mapping': 0.3.17 + callsites: 3.1.0 + graceful-fs: 4.2.11 + dev: true + /@jest/test-result@27.5.1: resolution: {integrity: sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -6256,6 +8632,16 @@ packages: collect-v8-coverage: 1.0.1 dev: true + /@jest/test-result@29.5.0: + resolution: {integrity: sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.5.0 + '@jest/types': 29.5.0 + '@types/istanbul-lib-coverage': 2.0.4 + collect-v8-coverage: 1.0.1 + dev: true + /@jest/test-sequencer@27.5.1: resolution: {integrity: sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -6268,11 +8654,21 @@ packages: - supports-color dev: true + /@jest/test-sequencer@29.5.0: + resolution: {integrity: sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.5.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.5.0 + slash: 3.0.0 + dev: true + /@jest/transform@27.5.1: resolution: {integrity: sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@jest/types': 27.5.1 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 @@ -6295,9 +8691,9 @@ packages: resolution: {integrity: sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@jest/types': 29.5.0 - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/trace-mapping': 0.3.17 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 convert-source-map: 2.0.0 @@ -6320,7 +8716,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 '@types/yargs': 15.0.15 chalk: 4.1.2 dev: true @@ -6331,7 +8727,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 '@types/yargs': 16.0.5 chalk: 4.1.2 dev: true @@ -6343,11 +8739,28 @@ packages: '@jest/schemas': 29.4.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.15.11 - '@types/yargs': 17.0.24 + '@types/node': 18.15.10 + '@types/yargs': 17.0.23 chalk: 4.1.2 dev: true + /@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@4.9.5)(vite@4.4.0): + resolution: {integrity: sha512-ou4ZJSXMMWHqGS4g8uNRbC5TiTWxAgQZiVucoUrOCWuPrTbkpJbmVyIi9jU72SBry7gQtuMEDp4YR8EEXAg7VQ==} + peerDependencies: + typescript: '>= 4.3.x' + vite: ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + dependencies: + glob: 7.2.3 + glob-promise: 4.2.2(glob@7.2.3) + magic-string: 0.27.0 + react-docgen-typescript: 2.2.2(typescript@4.9.5) + typescript: 4.9.5 + vite: 4.4.0(@types/node@20.4.1)(less@4.1.3) + dev: true + /@joshwooding/vite-plugin-react-docgen-typescript@0.2.2(typescript@4.9.5)(vite@4.2.1): resolution: {integrity: sha512-BlArZRyCNaQXo9jSW1crabSqdQXlgIB9bh3W7WpKTeopUFy2PqOkVFdOv3FvvcJOu0A3pC/ECyQMiXxXK547MQ==} peerDependencies: @@ -6360,34 +8773,37 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@4.9.5) typescript: 4.9.5 - vite: 4.2.1(@types/node@18.15.11) + vite: 4.2.1(@types/node@18.15.10) dev: true - /@jridgewell/gen-mapping@0.3.3: - resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + /@jridgewell/gen-mapping@0.1.1: + resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + + /@jridgewell/gen-mapping@0.3.2: + resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} engines: {node: '>=6.0.0'} dependencies: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/trace-mapping': 0.3.17 /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} - /@jridgewell/resolve-uri@3.1.1: - resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} - engines: {node: '>=6.0.0'} - /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} engines: {node: '>=6.0.0'} - /@jridgewell/source-map@0.3.3: - resolution: {integrity: sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==} + /@jridgewell/source-map@0.3.2: + resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==} dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/gen-mapping': 0.3.2 + '@jridgewell/trace-mapping': 0.3.17 dev: true /@jridgewell/sourcemap-codec@1.4.14: @@ -6396,8 +8812,8 @@ packages: /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - /@jridgewell/trace-mapping@0.3.18: - resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} + /@jridgewell/trace-mapping@0.3.17: + resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 @@ -6405,8 +8821,8 @@ packages: /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: - '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 /@jsdevtools/ono@7.1.3: resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} @@ -6415,21 +8831,17 @@ packages: resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} dev: true - /@ljharb/has-package-exports-patterns@0.0.2: - resolution: {integrity: sha512-4/RWEeXDO6bocPONheFe6gX/oQdP/bEpv0oL4HqjPP5DCenBSt0mHgahppY49N0CpsaqffdwPq+TlX9CYOq2Dw==} - dev: false - - /@lukeed/csprng@1.1.0: - resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + /@lukeed/csprng@1.0.1: + resolution: {integrity: sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g==} engines: {node: '>=8'} - /@lukemorales/query-key-factory@1.2.0(@tanstack/query-core@4.29.1): + /@lukemorales/query-key-factory@1.2.0(@tanstack/query-core@4.27.0): resolution: {integrity: sha512-ilUedLpJdpOCMKgxoncRkYEFF7C+K//IK/QWXHs0G6HjmZunT9XcrpWHNc5RjrBBqPhdxYNa7aUoEhjonwl4eQ==} engines: {node: '>=14'} peerDependencies: '@tanstack/query-core': ^4.0.0 dependencies: - '@tanstack/query-core': 4.29.1 + '@tanstack/query-core': 4.27.0 dev: false /@manypkg/find-root@1.1.0: @@ -6463,7 +8875,7 @@ packages: nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 - semver: 7.4.0 + semver: 7.5.3 tar: 6.1.13 transitivePeerDependencies: - encoding @@ -6474,7 +8886,7 @@ packages: resolution: {integrity: sha512-jLuwRlz8DQfQNiUCJR50Y09CGPq3fLtmtUQfVrj79E0JWu3dvsVcxVIcfhR5h0iXu+/z++zDrYeiJqifRynJkA==} dependencies: '@types/estree-jsx': 1.0.0 - '@types/mdx': 2.0.4 + '@types/mdx': 2.0.3 estree-util-build-jsx: 2.2.2 estree-util-is-identifier-name: 2.1.0 estree-util-to-js: 1.2.0 @@ -6499,43 +8911,59 @@ packages: peerDependencies: react: '>=16' dependencies: - '@types/mdx': 2.0.4 - '@types/react': 18.0.35 + '@types/mdx': 2.0.3 + '@types/react': 18.0.37 react: 18.2.0 dev: true - /@mdx-js/rollup@2.3.0(rollup@2.70.2): - resolution: {integrity: sha512-wLvRfJS/M4UmdqTd+WoaySEE7q4BIejYf1xAHXYvtT1du/1Tl/z2450Gg2+Hu7fh05KwRRiehiTP9Yc/Dtn0fA==} - peerDependencies: - rollup: '>=2' + /@microsoft/api-extractor-model@7.26.4(@types/node@18.15.10): + resolution: {integrity: sha512-PDCgCzXDo+SLY5bsfl4bS7hxaeEtnXj7XtuzEE+BtALp7B5mK/NrS2kHWU69pohgsRmEALycQdaQPXoyT2i5MQ==} dependencies: - '@mdx-js/mdx': 2.3.0 - '@rollup/pluginutils': 5.0.2(rollup@2.70.2) - rollup: 2.70.2 - source-map: 0.7.4 - vfile: 5.3.7 + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + '@rushstack/node-core-library': 3.55.2(@types/node@18.15.10) transitivePeerDependencies: - - supports-color - dev: false + - '@types/node' + dev: true - /@microsoft/api-extractor-model@7.26.4(@types/node@18.15.11): + /@microsoft/api-extractor-model@7.26.4(@types/node@20.4.1): resolution: {integrity: sha512-PDCgCzXDo+SLY5bsfl4bS7hxaeEtnXj7XtuzEE+BtALp7B5mK/NrS2kHWU69pohgsRmEALycQdaQPXoyT2i5MQ==} dependencies: '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.55.2(@types/node@18.15.11) + '@rushstack/node-core-library': 3.55.2(@types/node@20.4.1) + transitivePeerDependencies: + - '@types/node' + dev: true + + /@microsoft/api-extractor@7.34.4(@types/node@18.15.10): + resolution: {integrity: sha512-HOdcci2nT40ejhwPC3Xja9G+WSJmWhCUKKryRfQYsmE9cD+pxmBaKBKCbuS9jUcl6bLLb4Gz+h7xEN5r0QiXnQ==} + hasBin: true + dependencies: + '@microsoft/api-extractor-model': 7.26.4(@types/node@18.15.10) + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + '@rushstack/node-core-library': 3.55.2(@types/node@18.15.10) + '@rushstack/rig-package': 0.3.18 + '@rushstack/ts-command-line': 4.13.2 + colors: 1.2.5 + lodash: 4.17.21 + resolve: 1.22.2 + semver: 7.3.8 + source-map: 0.6.1 + typescript: 4.8.4 transitivePeerDependencies: - '@types/node' dev: true - /@microsoft/api-extractor@7.34.4(@types/node@18.15.11): + /@microsoft/api-extractor@7.34.4(@types/node@20.4.1): resolution: {integrity: sha512-HOdcci2nT40ejhwPC3Xja9G+WSJmWhCUKKryRfQYsmE9cD+pxmBaKBKCbuS9jUcl6bLLb4Gz+h7xEN5r0QiXnQ==} hasBin: true dependencies: - '@microsoft/api-extractor-model': 7.26.4(@types/node@18.15.11) + '@microsoft/api-extractor-model': 7.26.4(@types/node@20.4.1) '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.55.2(@types/node@18.15.11) + '@rushstack/node-core-library': 3.55.2(@types/node@20.4.1) '@rushstack/rig-package': 0.3.18 '@rushstack/ts-command-line': 4.13.2 colors: 1.2.5 @@ -6567,7 +8995,7 @@ packages: '@motionone/easing': 10.15.1 '@motionone/types': 10.15.1 '@motionone/utils': 10.15.1 - tslib: 2.5.0 + tslib: 2.5.2 dev: false /@motionone/dom@10.15.5: @@ -6578,14 +9006,14 @@ packages: '@motionone/types': 10.15.1 '@motionone/utils': 10.15.1 hey-listen: 1.0.8 - tslib: 2.5.0 + tslib: 2.5.2 dev: false /@motionone/easing@10.15.1: resolution: {integrity: sha512-6hIHBSV+ZVehf9dcKZLT7p5PEKHGhDwky2k8RKkmOvUoYP3S+dXsKupyZpqx5apjd9f+php4vXk4LuS+ADsrWw==} dependencies: '@motionone/utils': 10.15.1 - tslib: 2.5.0 + tslib: 2.5.2 dev: false /@motionone/generators@10.15.1: @@ -6593,7 +9021,7 @@ packages: dependencies: '@motionone/types': 10.15.1 '@motionone/utils': 10.15.1 - tslib: 2.5.0 + tslib: 2.5.2 dev: false /@motionone/types@10.15.1: @@ -6605,7 +9033,7 @@ packages: dependencies: '@motionone/types': 10.15.1 hey-listen: 1.0.8 - tslib: 2.5.0 + tslib: 2.5.2 dev: false /@mswjs/cookies@0.2.2: @@ -6621,8 +9049,8 @@ packages: dependencies: '@open-draft/until': 1.0.3 '@types/debug': 4.1.7 - '@xmldom/xmldom': 0.8.7 - debug: 4.3.4 + '@xmldom/xmldom': 0.8.6 + debug: 4.3.4(supports-color@8.1.1) headers-polyfill: 3.1.2 outvariant: 1.4.0 strict-event-emitter: 0.2.8 @@ -6638,7 +9066,7 @@ packages: tar-fs: 2.1.1 dev: true - /@nestjs/axios@2.0.0(@nestjs/common@9.4.0)(axios@1.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0): + /@nestjs/axios@2.0.0(@nestjs/common@9.3.12)(axios@1.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0): resolution: {integrity: sha512-F6oceoQLEn031uun8NiommeMkRIojQqVryxQy/mK7fx0CI0KbgkJL3SloCQcsOD+agoEnqKJKXZpEvL6FNswJg==} peerDependencies: '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 @@ -6646,7 +9074,7 @@ packages: reflect-metadata: ^0.1.12 rxjs: ^6.0.0 || ^7.0.0 dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) axios: 1.4.0 reflect-metadata: 0.1.13 rxjs: 7.8.0 @@ -6660,7 +9088,7 @@ packages: '@angular-devkit/core': 15.2.4(chokidar@3.5.3) '@angular-devkit/schematics': 15.2.4(chokidar@3.5.3) '@angular-devkit/schematics-cli': 15.2.4(chokidar@3.5.3) - '@nestjs/schematics': 9.1.0(chokidar@3.5.3)(typescript@4.9.5) + '@nestjs/schematics': 9.0.4(chokidar@3.5.3)(typescript@4.9.5) chalk: 4.1.2 chokidar: 3.5.3 cli-table3: 0.6.3 @@ -6686,8 +9114,8 @@ packages: - webpack-cli dev: true - /@nestjs/common@9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0): - resolution: {integrity: sha512-RUcVAQsEF4WPrmzFXEOUfZnPwrLTe1UVlzXTlSyfqfqbdWDPKDGlIPVelBLfc5/+RRUQ0I5iE4+CQvpCmkqldw==} + /@nestjs/common@9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0): + resolution: {integrity: sha512-NtrUG2VgCbhmZEO1yRt/Utq16uFRV+xeHAOtdYIsfHGG0ssAV2lVLlvFFAQYh0SQ+KuYY1Gsxd3GK2JFoJCNqQ==} peerDependencies: cache-manager: <=5 class-transformer: '*' @@ -6708,16 +9136,16 @@ packages: reflect-metadata: 0.1.13 rxjs: 7.8.0 tslib: 2.5.0 - uid: 2.0.2 + uid: 2.0.1 - /@nestjs/config@2.3.1(@nestjs/common@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0): + /@nestjs/config@2.3.1(@nestjs/common@9.3.12)(reflect-metadata@0.1.13)(rxjs@7.8.0): resolution: {integrity: sha512-Ckzel0NZ9CWhNsLfE1hxfDuxJuEbhQvGxSlmZ1/X8awjRmAA/g3kT6M1+MO1SHj1wMtPyUfd9WpwkiqFbiwQgA==} peerDependencies: '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 reflect-metadata: ^0.1.13 rxjs: ^6.0.0 || ^7.2.0 dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) dotenv: 16.0.3 dotenv-expand: 10.0.0 lodash: 4.17.21 @@ -6726,8 +9154,8 @@ packages: uuid: 9.0.0 dev: false - /@nestjs/core@9.4.0(@nestjs/common@9.4.0)(@nestjs/platform-express@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0): - resolution: {integrity: sha512-yTLryCgFD0462wPe4HIzhyTcDgibt8Stfwb5YzcX7Ma0NM4m8uBIpcPG109KBubp8ZmV85e5mw4rl20qLQQVsQ==} + /@nestjs/core@9.3.12(@nestjs/common@9.3.12)(@nestjs/platform-express@9.3.12)(@nestjs/websockets@9.4.2)(reflect-metadata@0.1.13)(rxjs@7.8.0): + resolution: {integrity: sha512-Qe0ZjJo7bOlfudn7KHLppYrt5i4k1nR1+9d5ppYat2bb5knCIT4kIqblj666n+22/2zvsHRiTo015cLyLKsLRQ==} requiresBuild: true peerDependencies: '@nestjs/common': ^9.0.0 @@ -6744,8 +9172,9 @@ packages: '@nestjs/websockets': optional: true dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/platform-express': 9.4.0(@nestjs/common@9.4.0)(@nestjs/core@9.4.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/platform-express': 9.3.12(@nestjs/common@9.3.12)(@nestjs/core@9.3.12) + '@nestjs/websockets': 9.4.2(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(reflect-metadata@0.1.13)(rxjs@7.8.0) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -6753,19 +9182,19 @@ packages: reflect-metadata: 0.1.13 rxjs: 7.8.0 tslib: 2.5.0 - uid: 2.0.2 + uid: 2.0.1 transitivePeerDependencies: - encoding - /@nestjs/devtools-integration@0.1.4(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(reflect-metadata@0.1.13): + /@nestjs/devtools-integration@0.1.4(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(reflect-metadata@0.1.13): resolution: {integrity: sha512-jAKTrpc+Ilrb7CFV1ZuG9b4OYVlFPHUXna2H8BrmxKResyqnzlzR/YIUr8zJE7p15MipAjiVaLHHyEL0C9OYGA==} peerDependencies: '@nestjs/common': ^9.3.7 '@nestjs/core': ^9.3.7 reflect-metadata: ^0.1.12 dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/core': 9.4.0(@nestjs/common@9.4.0)(@nestjs/platform-express@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/core': 9.3.12(@nestjs/common@9.3.12)(@nestjs/platform-express@9.3.12)(@nestjs/websockets@9.4.2)(reflect-metadata@0.1.13)(rxjs@7.8.0) chalk: 4.1.2 node-fetch: 2.6.9 reflect-metadata: 0.1.13 @@ -6773,20 +9202,20 @@ packages: - encoding dev: false - /@nestjs/event-emitter@1.4.1(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(reflect-metadata@0.1.13): + /@nestjs/event-emitter@1.4.1(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(reflect-metadata@0.1.13): resolution: {integrity: sha512-PmLpzMYgEKJNxOUrRjb6kNSm2PC6J+BeLTuF/bkYViGM/mVGvYOgU5jq8DQnXmiSmDmyWN+tO2cHSnR7odJJRA==} peerDependencies: '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 '@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 reflect-metadata: ^0.1.12 dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/core': 9.4.0(@nestjs/common@9.4.0)(@nestjs/platform-express@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/core': 9.3.12(@nestjs/common@9.3.12)(@nestjs/platform-express@9.3.12)(@nestjs/websockets@9.4.2)(reflect-metadata@0.1.13)(rxjs@7.8.0) eventemitter2: 6.4.9 reflect-metadata: 0.1.13 dev: false - /@nestjs/graphql@10.0.0(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(class-transformer@0.5.1)(class-validator@0.14.0)(graphql@16.6.0)(reflect-metadata@0.1.13): + /@nestjs/graphql@10.0.0(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(class-transformer@0.5.1)(class-validator@0.14.0)(graphql@16.6.0)(reflect-metadata@0.1.13): resolution: {integrity: sha512-fsOsoK2nMqmnBisrno+S+Yj/Qr/GLh2b3QCDEx4eooOEGM0+/NjCwmJ19iQ6nBLW6n4K8ZUvuCDMjg9ecaTZ+Q==} peerDependencies: '@apollo/subgraph': ^0.1.5 || ^0.3.0 @@ -6804,9 +9233,9 @@ packages: '@graphql-tools/merge': 8.2.2(graphql@16.6.0) '@graphql-tools/schema': 8.3.1(graphql@16.6.0) '@graphql-tools/utils': 8.6.1(graphql@16.6.0) - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/core': 9.4.0(@nestjs/common@9.4.0)(@nestjs/platform-express@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/mapped-types': 1.0.1(@nestjs/common@9.4.0)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/core': 9.3.12(@nestjs/common@9.3.12)(@nestjs/platform-express@9.3.12)(@nestjs/websockets@9.4.2)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/mapped-types': 1.0.1(@nestjs/common@9.3.12)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) chokidar: 3.5.3 fast-glob: 3.2.11 graphql: 16.6.0 @@ -6827,17 +9256,17 @@ packages: - utf-8-validate dev: false - /@nestjs/jwt@10.0.3(@nestjs/common@9.4.0): + /@nestjs/jwt@10.0.3(@nestjs/common@9.3.12): resolution: {integrity: sha512-WO8MI3uEMOFKpbO+SAg6l4aRCr+9KvaL+raFMZaXuEUDphXek6pqdox+4tex9242pNSJUA0trfAMaiy/yVrXQg==} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) '@types/jsonwebtoken': 9.0.1 jsonwebtoken: 9.0.0 dev: false - /@nestjs/mapped-types@1.0.1(@nestjs/common@9.4.0)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13): + /@nestjs/mapped-types@1.0.1(@nestjs/common@9.3.12)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13): resolution: {integrity: sha512-NFvofzSinp00j5rzUd4tf+xi9od6383iY0JP7o0Bnu1fuItAUkWBgc4EKuIQ3D+c2QI3i9pG1kDWAeY27EMGtg==} peerDependencies: '@nestjs/common': ^7.0.8 || ^8.0.0 @@ -6850,13 +9279,13 @@ packages: class-validator: optional: true dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) class-transformer: 0.5.1 class-validator: 0.14.0 reflect-metadata: 0.1.13 dev: false - /@nestjs/mapped-types@1.2.2(@nestjs/common@9.4.0)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13): + /@nestjs/mapped-types@1.2.2(@nestjs/common@9.3.12)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13): resolution: {integrity: sha512-3dHxLXs3M0GPiriAcCFFJQHoDFUuzTD5w6JDhE7TyfT89YKpe6tcCCIqOZWdXmt9AZjjK30RkHRSFF+QEnWFQg==} peerDependencies: '@nestjs/common': ^7.0.8 || ^8.0.0 || ^9.0.0 @@ -6869,30 +9298,30 @@ packages: class-validator: optional: true dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) class-transformer: 0.5.1 class-validator: 0.14.0 reflect-metadata: 0.1.13 dev: true - /@nestjs/passport@9.0.3(@nestjs/common@9.4.0)(passport@0.6.0): + /@nestjs/passport@9.0.3(@nestjs/common@9.3.12)(passport@0.6.0): resolution: {integrity: sha512-HplSJaimEAz1IOZEu+pdJHHJhQyBOPAYWXYHfAPQvRqWtw4FJF1VXl1Qtk9dcXQX1eKytDtH+qBzNQc19GWNEg==} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 passport: ^0.4.0 || ^0.5.0 || ^0.6.0 dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) passport: 0.6.0 dev: false - /@nestjs/platform-express@9.4.0(@nestjs/common@9.4.0)(@nestjs/core@9.4.0): - resolution: {integrity: sha512-PpnfghpNq7mwG43z3+pacHulsabUCBMla4nUikntXT525ORpZSDvh/nLi1HLfE4w5+FcINc8/RBOyYTeRVmiRQ==} + /@nestjs/platform-express@9.3.12(@nestjs/common@9.3.12)(@nestjs/core@9.3.12): + resolution: {integrity: sha512-iQToH9rnZHmm3a2YDKLEN7weU2qC/EVOBnuwTf1lIkqB48yLxlykSJu3KmgtlUUNDt2/HY527QIo+GZSZfCLyg==} peerDependencies: '@nestjs/common': ^9.0.0 '@nestjs/core': ^9.0.0 dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/core': 9.4.0(@nestjs/common@9.4.0)(@nestjs/platform-express@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/core': 9.3.12(@nestjs/common@9.3.12)(@nestjs/platform-express@9.3.12)(@nestjs/websockets@9.4.2)(reflect-metadata@0.1.13)(rxjs@7.8.0) body-parser: 1.20.2 cors: 2.8.5 express: 4.18.2 @@ -6901,13 +9330,31 @@ packages: transitivePeerDependencies: - supports-color - /@nestjs/schematics@9.1.0(chokidar@3.5.3)(typescript@4.9.5): - resolution: {integrity: sha512-/7CyMTnPJSK9/xD9CkCqwuHPOlHVlLC2RDnbdCJ7mIO07SdbBbY14msTqtYW9VRQtsjZPLh1GTChf7ryJUImwA==} + /@nestjs/platform-ws@9.4.2(@nestjs/common@9.3.12)(@nestjs/websockets@9.4.2)(rxjs@7.8.0): + resolution: {integrity: sha512-JURYiQ/0yoDD/Z14szKDMts6xUP7hI/z5CSBOSAEJk+GdhKB4VW6noTgZQu8KRSZxtLj5tm73JIaOMX9P97Buw==} peerDependencies: - typescript: '>=4.3.5' + '@nestjs/common': ^9.0.0 + '@nestjs/websockets': ^9.0.0 + rxjs: ^7.1.0 dependencies: - '@angular-devkit/core': 15.2.4(chokidar@3.5.3) - '@angular-devkit/schematics': 15.2.4(chokidar@3.5.3) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/websockets': 9.4.2(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(reflect-metadata@0.1.13)(rxjs@7.8.0) + rxjs: 7.8.0 + tslib: 2.5.2 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + + /@nestjs/schematics@9.0.4(chokidar@3.5.3)(typescript@4.9.5): + resolution: {integrity: sha512-egurCfAc4e5i1r2TmeAF0UrOKejFmT5oTdv4b7HcOVPupc3QGU7CbEfGleL3mkM5AjrixTQeMxU9bJ00ttAbGg==} + peerDependencies: + typescript: ^4.3.5 + dependencies: + '@angular-devkit/core': 15.0.4(chokidar@3.5.3) + '@angular-devkit/schematics': 15.0.4(chokidar@3.5.3) + fs-extra: 11.1.0 jsonc-parser: 3.2.0 pluralize: 8.0.0 typescript: 4.9.5 @@ -6915,7 +9362,7 @@ packages: - chokidar dev: true - /@nestjs/serve-static@3.0.1(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(express@4.18.2): + /@nestjs/serve-static@3.0.1(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(express@4.18.2): resolution: {integrity: sha512-i766UJPYOqvQ2BbRKh0/+Mmq5NkJnmKcShjWV1i5qpXyeM0KDZTn0n7g7ykWq/3LbQgjpMzrhYtGv35GX7GVQw==} peerDependencies: '@fastify/static': ^6.5.0 @@ -6931,13 +9378,13 @@ packages: fastify: optional: true dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/core': 9.4.0(@nestjs/common@9.4.0)(@nestjs/platform-express@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/core': 9.3.12(@nestjs/common@9.3.12)(@nestjs/platform-express@9.3.12)(@nestjs/websockets@9.4.2)(reflect-metadata@0.1.13)(rxjs@7.8.0) express: 4.18.2 path-to-regexp: 0.2.5 dev: false - /@nestjs/swagger@6.2.1(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13): + /@nestjs/swagger@6.2.1(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13): resolution: {integrity: sha512-9M2vkfJHIzLqDZwvM5TEZO0MxRCvIb0xVy0LsmWwxH1lrb0z/4MhU+r2CWDhBtTccVJrKxVPiU2s3T3b9uUJbg==} peerDependencies: '@fastify/static': ^6.0.0 @@ -6954,9 +9401,9 @@ packages: class-validator: optional: true dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/core': 9.4.0(@nestjs/common@9.4.0)(@nestjs/platform-express@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/mapped-types': 1.2.2(@nestjs/common@9.4.0)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/core': 9.3.12(@nestjs/common@9.3.12)(@nestjs/platform-express@9.3.12)(@nestjs/websockets@9.4.2)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/mapped-types': 1.2.2(@nestjs/common@9.3.12)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) class-transformer: 0.5.1 class-validator: 0.14.0 js-yaml: 4.1.0 @@ -6966,8 +9413,8 @@ packages: swagger-ui-dist: 4.15.5 dev: true - /@nestjs/testing@9.4.0(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(@nestjs/platform-express@9.4.0): - resolution: {integrity: sha512-xZWp363P4otcebg++gSjUcdCfTK0RorORzyFq3aLaSAQOlq8kxfFDRIKzEATR4aOUfqTMMsAA8lhnMJWf35N6A==} + /@nestjs/testing@9.3.12(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(@nestjs/platform-express@9.3.12): + resolution: {integrity: sha512-nH274IXEqU4hr4bcb71POe58hYLONt9RcfKKM5ZvOS7wYMnybMpKKR8DkC1WcfE1P2k2GQmQoHeSH5emPtYrBA==} peerDependencies: '@nestjs/common': ^9.0.0 '@nestjs/core': ^9.0.0 @@ -6979,11 +9426,30 @@ packages: '@nestjs/platform-express': optional: true dependencies: - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/core': 9.4.0(@nestjs/common@9.4.0)(@nestjs/platform-express@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) - '@nestjs/platform-express': 9.4.0(@nestjs/common@9.4.0)(@nestjs/core@9.4.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/core': 9.3.12(@nestjs/common@9.3.12)(@nestjs/platform-express@9.3.12)(@nestjs/websockets@9.4.2)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/platform-express': 9.3.12(@nestjs/common@9.3.12)(@nestjs/core@9.3.12) tslib: 2.5.0 - dev: false + + /@nestjs/websockets@9.4.2(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(reflect-metadata@0.1.13)(rxjs@7.8.0): + resolution: {integrity: sha512-u1Txsb+rHWOG0mHxTfNt/64eyYSCGg6t/k736P2bdYP1fP4ETo/Z/F4Othau8q0MsWvKi3ZgYRQbhcefaudQww==} + peerDependencies: + '@nestjs/common': ^9.0.0 + '@nestjs/core': ^9.0.0 + '@nestjs/platform-socket.io': ^9.0.0 + reflect-metadata: ^0.1.12 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/platform-socket.io': + optional: true + dependencies: + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/core': 9.3.12(@nestjs/common@9.3.12)(@nestjs/platform-express@9.3.12)(@nestjs/websockets@9.4.2)(reflect-metadata@0.1.13)(rxjs@7.8.0) + iterare: 1.2.1 + object-hash: 3.0.0 + reflect-metadata: 0.1.13 + rxjs: 7.8.0 + tslib: 2.5.2 /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -7060,15 +9526,26 @@ packages: open: 8.4.2 picocolors: 1.0.0 tiny-glob: 0.2.9 - tslib: 2.5.0 + tslib: 2.5.2 - /@playwright/test@1.32.3: - resolution: {integrity: sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==} + /@playwright/test@1.32.1: + resolution: {integrity: sha512-FTwjCuhlm1qHUGf4hWjfr64UMJD/z0hXYbk+O387Ioe6WdyZQ+0TBDAc6P+pHjx2xCv1VYNgrKbYrNixFWy4Dg==} engines: {node: '>=14'} hasBin: true dependencies: - '@types/node': 18.15.11 - playwright-core: 1.32.3 + '@types/node': 18.15.10 + playwright-core: 1.32.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /@playwright/test@1.35.1: + resolution: {integrity: sha512-b5YoFe6J9exsMYg0pQAobNDR85T1nLumUYgUTtKm4d21iX2L7WqKq9dW8NGJ+2vX0etZd+Y7UeuqsxDXm9+5ZA==} + engines: {node: '>=16'} + hasBin: true + dependencies: + '@types/node': 18.15.10 + playwright-core: 1.35.1 optionalDependencies: fsevents: 2.3.2 dev: true @@ -7099,22 +9576,6 @@ packages: resolution: {integrity: sha512-HrniowHRZXHuGT9XRgoXEaP2gJLXM5RMoItaY2PkjvuZ+iHc0Zjbm/302MB8YsPdWozAPHHn+jpFEcEn71OgPw==} requiresBuild: true - /@proload/core@0.3.3: - resolution: {integrity: sha512-7dAFWsIK84C90AMl24+N/ProHKm4iw0akcnoKjRvbfHifJZBLhaDsDus1QJmhG12lXj4e/uB/8mB/0aduCW+NQ==} - dependencies: - deepmerge: 4.3.1 - escalade: 3.1.1 - dev: false - - /@proload/plugin-tsm@0.2.1(@proload/core@0.3.3): - resolution: {integrity: sha512-Ex1sL2BxU+g8MHdAdq9SZKz+pU34o8Zcl9PHWo2WaG9hrnlZme607PU6gnpoAYsDBpHX327+eu60wWUk+d/b+A==} - peerDependencies: - '@proload/core': ^0.3.2 - dependencies: - '@proload/core': 0.3.3 - tsm: 2.3.0 - dev: false - /@radix-ui/number@1.0.0: resolution: {integrity: sha512-Ofwh/1HX69ZfJRiRBMTy7rgjAzHmwe4kW9C9Y99HTRUcYLUuVT0KESFj15rPjRgKJs20GPq8Bm5aEDJ8DuA3vA==} dependencies: @@ -7127,6 +9588,12 @@ packages: '@babel/runtime': 7.21.0 dev: false + /@radix-ui/primitive@1.0.1: + resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} + dependencies: + '@babel/runtime': 7.21.0 + dev: false + /@radix-ui/react-arrow@1.0.2(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==} peerDependencies: @@ -7139,6 +9606,72 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.0.29 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-avatar@1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-9ToF7YNex3Ste45LrAeTlKtONI9yVRt/zOS158iilIkW5K/Apeyb/TUQlcEFTEFvWr8Kzdi2ZYrm1/suiXPajQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-context': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-checkbox@1.0.3(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-55B8/vKzTuzxllH5sGJO4zaBf9gYpJuJRRzaOKm+0oAefRnMvbf+Kgww7IOANVN0w3z7agFJgtnXaZl8Uj95AA==} peerDependencies: @@ -7173,121 +9706,130 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-compose-refs@1.0.0(react@18.2.0): - resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==} + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true dependencies: '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 + '@types/react-dom': 18.0.11 react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-context@1.0.0(react@18.2.0): - resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==} + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true dependencies: '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-dialog@1.0.3(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-owNhq36kNPqC2/a+zJRioPg6HHnTn5B/sh/NjTY8r4W9g1L5VJlrzZIVcBr7R9Mg8iLjVmh6MGgMlfoVf/WO/A==} + /@radix-ui/react-compose-refs@1.0.0(react@18.2.0): + resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) - '@radix-ui/react-context': 1.0.0(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.0(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.0(react@18.2.0) - '@radix-ui/react-portal': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.0(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.1(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) - aria-hidden: 1.2.3 react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.0.35)(react@18.2.0) - transitivePeerDependencies: - - '@types/react' dev: false - /@radix-ui/react-direction@1.0.0(react@18.2.0): - resolution: {integrity: sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==} + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 + '@types/react': 18.0.29 react: 18.2.0 dev: false - /@radix-ui/react-dismissable-layer@1.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q==} + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) - '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) - '@radix-ui/react-use-escape-keydown': 1.0.2(react@18.2.0) + '@types/react': 18.0.37 react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-dropdown-menu@2.0.4(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-y6AT9+MydyXcByivdK1+QpjWoKaC7MLjkS/cH1Q3keEyMvDkiY85m8o2Bi6+Z1PPUlCsMULopxagQOSfN0wahg==} + /@radix-ui/react-context@1.0.0(react@18.2.0): + resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) - '@radix-ui/react-context': 1.0.0(react@18.2.0) - '@radix-ui/react-id': 1.0.0(react@18.2.0) - '@radix-ui/react-menu': 2.0.4(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - transitivePeerDependencies: - - '@types/react' dev: false - /@radix-ui/react-focus-guards@1.0.0(react@18.2.0): - resolution: {integrity: sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==} + /@radix-ui/react-context@1.0.1(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 + '@types/react': 18.0.29 react: 18.2.0 dev: false - /@radix-ui/react-focus-scope@1.0.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-spwXlNTfeIprt+kaEWE/qYuYT3ZAqJiAGjN/JgdvgVDTu8yc+HuX+WOWXrKliKnLnwck0F6JDkqIERncnih+4A==} + /@radix-ui/react-context@1.0.1(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) - '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + '@types/react': 18.0.37 react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-hover-card@1.0.5(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-jXRuZEkxSWdHZbVyL0J46cm7pQjmOMpwJEFKY+VqAJnV+FxS+zIZExI1OCeIiDwCBzUy6If1FfouOsfqBxr86g==} + /@radix-ui/react-dialog@1.0.0(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Yn9YU+QlHYLWwV1XfKiqnGVpWYWk6MeBVM6x/bcoyPvxgjQGoeT35482viLPctTMWoMw0PoHgqfSox7Ig+957Q==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 @@ -7296,229 +9838,1251 @@ packages: '@radix-ui/primitive': 1.0.0 '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) '@radix-ui/react-context': 1.0.0(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-popper': 1.1.1(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.0(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.0(react@18.2.0) + '@radix-ui/react-portal': 1.0.0(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-presence': 1.0.0(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.0(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) + aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.4(@types/react@18.0.37)(react@18.2.0) transitivePeerDependencies: - '@types/react' dev: false - /@radix-ui/react-id@1.0.0(react@18.2.0): - resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) - react: 18.2.0 - dev: false - - /@radix-ui/react-label@2.0.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-qcfbS3B8hTYmEO44RNcXB6pegkxRsJIbdxTMu0PEX0Luv5O2DvTIwwVYxQfUwLpM88EL84QRPLOLgwUSApMsLQ==} + /@radix-ui/react-dialog@1.0.3(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-owNhq36kNPqC2/a+zJRioPg6HHnTn5B/sh/NjTY8r4W9g1L5VJlrzZIVcBr7R9Mg8iLjVmh6MGgMlfoVf/WO/A==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 dependencies: '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-context': 1.0.0(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.3(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.0(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.0(react@18.2.0) + '@radix-ui/react-portal': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.0(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.1(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) + aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.0.29)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' dev: false - /@radix-ui/react-menu@2.0.4(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-mzKR47tZ1t193trEqlQoJvzY4u9vYfVH16ryBrVrCAGZzkgyWnMQYEZdUkM7y8ak9mrkKtJiqB47TlEnubeOFQ==} + /@radix-ui/react-dialog@1.0.3(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-owNhq36kNPqC2/a+zJRioPg6HHnTn5B/sh/NjTY8r4W9g1L5VJlrzZIVcBr7R9Mg8iLjVmh6MGgMlfoVf/WO/A==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 dependencies: '@babel/runtime': 7.21.0 '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-collection': 1.0.2(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) '@radix-ui/react-context': 1.0.0(react@18.2.0) - '@radix-ui/react-direction': 1.0.0(react@18.2.0) '@radix-ui/react-dismissable-layer': 1.0.3(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-focus-guards': 1.0.0(react@18.2.0) '@radix-ui/react-focus-scope': 1.0.2(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-id': 1.0.0(react@18.2.0) - '@radix-ui/react-popper': 1.1.1(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-portal': 1.0.2(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-presence': 1.0.0(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.3(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-slot': 1.0.1(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.0.35)(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.0.37)(react@18.2.0) transitivePeerDependencies: - '@types/react' dev: false - /@radix-ui/react-popper@1.1.1(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==} + /@radix-ui/react-direction@1.0.0(react@18.2.0): + resolution: {integrity: sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 dependencies: '@babel/runtime': 7.21.0 - '@floating-ui/react-dom': 0.7.2(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-arrow': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) - '@radix-ui/react-context': 1.0.0(react@18.2.0) - '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) - '@radix-ui/react-use-rect': 1.0.0(react@18.2.0) - '@radix-ui/react-use-size': 1.0.0(react@18.2.0) - '@radix-ui/rect': 1.0.0 react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - transitivePeerDependencies: - - '@types/react' dev: false - /@radix-ui/react-portal@1.0.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==} + /@radix-ui/react-direction@1.0.1(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@types/react': 18.0.29 + react: 18.2.0 + dev: false + + /@radix-ui/react-direction@1.0.1(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@types/react': 18.0.37 + react: 18.2.0 + dev: false + + /@radix-ui/react-dismissable-layer@1.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-n7kDRfx+LB1zLueRDvZ1Pd0bxdJWDUZNQ/GWoxDn2prnuJKRdxsjulejX/ePkOsLi2tTm6P24mDqlMSgQpsT6g==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.0.0(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-dismissable-layer@1.0.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.0.2(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-dropdown-menu@2.0.5(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-xdOrZzOTocqqkCkYo8yRPCib5OkTkqN7lqNCdxwPOdE466DOaNl4N8PkUIlsXthQvW5Wwkd+aEmWpfWlBoDPEw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-menu': 2.0.5(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-dropdown-menu@2.0.5(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-xdOrZzOTocqqkCkYo8yRPCib5OkTkqN7lqNCdxwPOdE466DOaNl4N8PkUIlsXthQvW5Wwkd+aEmWpfWlBoDPEw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-menu': 2.0.5(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-focus-guards@1.0.0(react@18.2.0): + resolution: {integrity: sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + react: 18.2.0 + dev: false + + /@radix-ui/react-focus-guards@1.0.1(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@types/react': 18.0.29 + react: 18.2.0 + dev: false + + /@radix-ui/react-focus-guards@1.0.1(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@types/react': 18.0.37 + react: 18.2.0 + dev: false + + /@radix-ui/react-focus-scope@1.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-C4SWtsULLGf/2L4oGeIHlvWQx7Rf+7cX/vKOAD2dXW0A1b5QXwi3wWeaEgW+wn+SEVrraMUk05vLU9fZZz5HbQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-focus-scope@1.0.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-spwXlNTfeIprt+kaEWE/qYuYT3ZAqJiAGjN/JgdvgVDTu8yc+HuX+WOWXrKliKnLnwck0F6JDkqIERncnih+4A==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-hover-card@1.0.5(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-jXRuZEkxSWdHZbVyL0J46cm7pQjmOMpwJEFKY+VqAJnV+FxS+zIZExI1OCeIiDwCBzUy6If1FfouOsfqBxr86g==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-context': 1.0.0(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.3(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-popper': 1.1.1(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-icons@1.3.0(react@18.2.0): + resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==} + peerDependencies: + react: ^16.x || ^17.x || ^18.x + dependencies: + react: 18.2.0 + dev: false + + /@radix-ui/react-id@1.0.0(react@18.2.0): + resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) + react: 18.2.0 + dev: false + + /@radix-ui/react-id@1.0.1(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 + react: 18.2.0 + dev: false + + /@radix-ui/react-id@1.0.1(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + react: 18.2.0 + dev: false + + /@radix-ui/react-label@2.0.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-qcfbS3B8hTYmEO44RNcXB6pegkxRsJIbdxTMu0PEX0Luv5O2DvTIwwVYxQfUwLpM88EL84QRPLOLgwUSApMsLQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-menu@2.0.5(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Gw4f9pwdH+w5w+49k0gLjN0PfRDHvxmAgG16AbyJZ7zhwZ6PBHKtWohvnSwfusfnK3L68dpBREHpVkj8wEM7ZA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-popper': 1.1.2(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 + '@types/react-dom': 18.0.11 + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.0.29)(react@18.2.0) + dev: false + + /@radix-ui/react-menu@2.0.5(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Gw4f9pwdH+w5w+49k0gLjN0PfRDHvxmAgG16AbyJZ7zhwZ6PBHKtWohvnSwfusfnK3L68dpBREHpVkj8wEM7ZA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-popper': 1.1.2(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.0.37)(react@18.2.0) + dev: false + + /@radix-ui/react-popover@1.0.6(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-cZ4defGpkZ0qTRtlIBzJLSzL6ht7ofhhW4i1+pkemjV1IKXm0wgCRnee154qlV6r9Ttunmh2TNZhMfV2bavUyA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-popper': 1.1.2(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.0.37)(react@18.2.0) + dev: false + + /@radix-ui/react-popper@1.1.1(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@floating-ui/react-dom': 0.7.2(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-arrow': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-context': 1.0.0(react@18.2.0) + '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) + '@radix-ui/react-use-rect': 1.0.0(react@18.2.0) + '@radix-ui/react-use-size': 1.0.0(react@18.2.0) + '@radix-ui/rect': 1.0.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-popper@1.1.1(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@floating-ui/react-dom': 0.7.2(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-arrow': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-context': 1.0.0(react@18.2.0) + '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) + '@radix-ui/react-use-rect': 1.0.0(react@18.2.0) + '@radix-ui/react-use-size': 1.0.0(react@18.2.0) + '@radix-ui/rect': 1.0.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-popper@1.1.2(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@floating-ui/react-dom': 2.0.1(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-use-rect': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/rect': 1.0.1 + '@types/react': 18.0.29 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-popper@1.1.2(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@floating-ui/react-dom': 2.0.1(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-use-rect': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/rect': 1.0.1 + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-portal@1.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-a8qyFO/Xb99d8wQdu4o7qnigNjTPG123uADNecz0eX4usnQEj7o+cG4ZX4zkqq98NYekT7UoEQIjxBNWIFuqTA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-portal@1.0.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-portal@1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.0.29 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-portal@1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-presence@1.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-presence@1.0.1(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-presence@1.0.1(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-primitive@1.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-EyXe6mnRlHZ8b6f4ilTDrXmkLShICIuOTTj0GX4w1rp+wSxf3+TD05u1UOITC8VsJ2a9nwHvdXtOXEOl0Cw/zQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-slot': 1.0.0(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-primitive@1.0.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-slot': 1.0.1(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-slot': 1.0.2(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-slot': 1.0.2(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-scroll-area@1.0.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-sBX9j8Q+0/jReNObEAveKIGXJtk3xUoSIx4cMKygGtO128QJyVDn01XNOFsyvihKDCTcu7SINzQ2jPAZEhIQtw==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/number': 1.0.0 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-context': 1.0.0(react@18.2.0) + '@radix-ui/react-direction': 1.0.0(react@18.2.0) + '@radix-ui/react-presence': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-select@1.2.1(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-GULRMITaOHNj79BZvQs3iZO0+f2IgI8g5HDhMi7Bnc13t7IlG86NFtOCfTLme4PNZdEtU+no+oGgcl6IFiphpQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/number': 1.0.0 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-collection': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-context': 1.0.0(react@18.2.0) + '@radix-ui/react-direction': 1.0.0(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.3(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.0(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.0(react@18.2.0) + '@radix-ui/react-popper': 1.1.1(@types/react@18.0.29)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.1(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) + '@radix-ui/react-use-previous': 1.0.0(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.0.2(react-dom@18.2.0)(react@18.2.0) + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.0.29)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-select@1.2.1(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-GULRMITaOHNj79BZvQs3iZO0+f2IgI8g5HDhMi7Bnc13t7IlG86NFtOCfTLme4PNZdEtU+no+oGgcl6IFiphpQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/number': 1.0.0 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-collection': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-context': 1.0.0(react@18.2.0) + '@radix-ui/react-direction': 1.0.0(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.3(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.0(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.0(react@18.2.0) + '@radix-ui/react-popper': 1.1.1(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.1(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) + '@radix-ui/react-use-previous': 1.0.0(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.0.2(react-dom@18.2.0)(react@18.2.0) + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.0.37)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-separator@1.0.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-lZoAG/rS2jzb/OSvyBrpN3dmikw20ewmWx1GkM1VldbDyD0DACCbH9LIXSrqyS/2mE1VYKOHmyq5W90Dx4ryqA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-slot@1.0.0(react@18.2.0): + resolution: {integrity: sha512-3mrKauI/tWXo1Ll+gN5dHcxDPdm/Df1ufcDLCecn+pnCIVcdWE7CujXo8QaXOWRJyZyQWWbpB8eFwHzWXlv5mQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + react: 18.2.0 + dev: false + + /@radix-ui/react-slot@1.0.1(react@18.2.0): + resolution: {integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + react: 18.2.0 + dev: false + + /@radix-ui/react-slot@1.0.2(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 + react: 18.2.0 + dev: false + + /@radix-ui/react-slot@1.0.2(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + react: 18.2.0 + dev: false + + /@radix-ui/react-tabs@1.0.4(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-context': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.0.11)(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-use-callback-ref@1.0.0(react@18.2.0): + resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@types/react': 18.0.29 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@types/react': 18.0.37 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-controllable-state@1.0.0(react@18.2.0): + resolution: {integrity: sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + react: 18.2.0 + dev: false + + /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-presence@1.0.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==} + /@radix-ui/react-use-escape-keydown@1.0.0(react@18.2.0): + resolution: {integrity: sha512-JwfBCUIfhXRxKExgIqGa4CQsiMemo1Xt0W/B4ei3fpzpvPENKpMKQ8mZSB6Acj3ebrAEgi2xiQvcI1PAAodvyg==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-primitive@1.0.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==} + /@radix-ui/react-use-escape-keydown@1.0.2(react@18.2.0): + resolution: {integrity: sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/react-slot': 1.0.1(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-roving-focus@1.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-stjCkIoMe6h+1fWtXlA6cRfikdBzCLp3SnVk7c48cv/uy3DTGoXhN76YaOYUJuy3aEDvDIKwKR5KSmvrtPvQPQ==} + /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-collection': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) - '@radix-ui/react-context': 1.0.0(react@18.2.0) - '@radix-ui/react-direction': 1.0.0(react@18.2.0) - '@radix-ui/react-id': 1.0.0(react@18.2.0) - '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-scroll-area@1.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-sBX9j8Q+0/jReNObEAveKIGXJtk3xUoSIx4cMKygGtO128QJyVDn01XNOFsyvihKDCTcu7SINzQ2jPAZEhIQtw==} + /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/number': 1.0.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) - '@radix-ui/react-context': 1.0.0(react@18.2.0) - '@radix-ui/react-direction': 1.0.0(react@18.2.0) - '@radix-ui/react-presence': 1.0.0(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-select@1.2.1(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-GULRMITaOHNj79BZvQs3iZO0+f2IgI8g5HDhMi7Bnc13t7IlG86NFtOCfTLme4PNZdEtU+no+oGgcl6IFiphpQ==} + /@radix-ui/react-use-layout-effect@1.0.0(react@18.2.0): + resolution: {integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/number': 1.0.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-collection': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) - '@radix-ui/react-context': 1.0.0(react@18.2.0) - '@radix-ui/react-direction': 1.0.0(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.0(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.0(react@18.2.0) - '@radix-ui/react-popper': 1.1.1(@types/react@18.0.35)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.1(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) - '@radix-ui/react-use-previous': 1.0.0(react@18.2.0) - '@radix-ui/react-visually-hidden': 1.0.2(react-dom@18.2.0)(react@18.2.0) - aria-hidden: 1.2.3 react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.0.35)(react@18.2.0) - transitivePeerDependencies: - - '@types/react' dev: false - /@radix-ui/react-separator@1.0.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-lZoAG/rS2jzb/OSvyBrpN3dmikw20ewmWx1GkM1VldbDyD0DACCbH9LIXSrqyS/2mE1VYKOHmyq5W90Dx4ryqA==} + /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.0.29 react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-slot@1.0.1(react@18.2.0): - resolution: {integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==} + /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@types/react': 18.0.37 react: 18.2.0 dev: false - /@radix-ui/react-use-callback-ref@1.0.0(react@18.2.0): - resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==} + /@radix-ui/react-use-previous@1.0.0(react@18.2.0): + resolution: {integrity: sha512-RG2K8z/K7InnOKpq6YLDmT49HGjNmrK+fr82UCVKT2sW0GYfVnYp4wZWBooT/EYfQ5faA9uIjvsuMMhH61rheg==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 dependencies: @@ -7526,61 +11090,83 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-use-controllable-state@1.0.0(react@18.2.0): - resolution: {integrity: sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==} + /@radix-ui/react-use-rect@1.0.0(react@18.2.0): + resolution: {integrity: sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + '@radix-ui/rect': 1.0.0 react: 18.2.0 dev: false - /@radix-ui/react-use-escape-keydown@1.0.2(react@18.2.0): - resolution: {integrity: sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==} + /@radix-ui/react-use-rect@1.0.1(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + '@radix-ui/rect': 1.0.1 + '@types/react': 18.0.29 react: 18.2.0 dev: false - /@radix-ui/react-use-layout-effect@1.0.0(react@18.2.0): - resolution: {integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==} + /@radix-ui/react-use-rect@1.0.1(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 + '@radix-ui/rect': 1.0.1 + '@types/react': 18.0.37 react: 18.2.0 dev: false - /@radix-ui/react-use-previous@1.0.0(react@18.2.0): - resolution: {integrity: sha512-RG2K8z/K7InnOKpq6YLDmT49HGjNmrK+fr82UCVKT2sW0GYfVnYp4wZWBooT/EYfQ5faA9uIjvsuMMhH61rheg==} + /@radix-ui/react-use-size@1.0.0(react@18.2.0): + resolution: {integrity: sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 dependencies: '@babel/runtime': 7.21.0 + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) react: 18.2.0 dev: false - /@radix-ui/react-use-rect@1.0.0(react@18.2.0): - resolution: {integrity: sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==} + /@radix-ui/react-use-size@1.0.1(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/rect': 1.0.0 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.0.29)(react@18.2.0) + '@types/react': 18.0.29 react: 18.2.0 dev: false - /@radix-ui/react-use-size@1.0.0(react@18.2.0): - resolution: {integrity: sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==} + /@radix-ui/react-use-size@1.0.1(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} peerDependencies: + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: '@babel/runtime': 7.21.0 - '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.0.37)(react@18.2.0) + '@types/react': 18.0.37 react: 18.2.0 dev: false @@ -7602,6 +11188,24 @@ packages: '@babel/runtime': 7.21.0 dev: false + /@radix-ui/rect@1.0.1: + resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} + dependencies: + '@babel/runtime': 7.21.0 + dev: false + + /@react-leaflet/core@2.1.0(leaflet@1.9.4)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==} + peerDependencies: + leaflet: ^1.9.0 + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + leaflet: 1.9.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@remix-run/router@1.6.2: resolution: {integrity: sha512-LzqpSrMK/3JBAVBI9u3NWtOhWNw5AMQfrUFYB0+bDHTSw17z++WJLsPsxAuK+oSddsxk4d7F/JcdDPM1M5YAhA==} engines: {node: '>=14'} @@ -7619,14 +11223,14 @@ packages: optional: true dependencies: '@babel/core': 7.17.9 - '@babel/helper-module-imports': 7.21.4 + '@babel/helper-module-imports': 7.22.5 '@rollup/pluginutils': 3.1.0(rollup@2.70.2) '@types/babel__core': 7.20.0 rollup: 2.70.2 dev: true - /@rollup/plugin-commonjs@24.1.0(rollup@2.70.2): - resolution: {integrity: sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ==} + /@rollup/plugin-commonjs@24.0.1(rollup@2.70.2): + resolution: {integrity: sha512-15LsiWRZk4eOGqvrJyu3z3DaBu5BhXIMeWnijSRvd8irrrg9SHpQ1pH+BUK4H6Z9wL9yOxZJMTLU+Au86XHxow==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^2.68.0||^3.0.0 @@ -7643,6 +11247,19 @@ packages: rollup: 2.70.2 dev: true + /@rollup/plugin-json@6.0.0(rollup@2.70.2): + resolution: {integrity: sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.0.2(rollup@2.70.2) + rollup: 2.70.2 + dev: true + /@rollup/plugin-node-resolve@13.2.1(rollup@2.70.2): resolution: {integrity: sha512-btX7kzGvp1JwShQI9V6IM841YKNPYjKCvUbNrQ2EcVYbULtUd/GH6wZ/qdqH13j9pOHBER+EZXNN2L8RSJhVRA==} engines: {node: '>= 10.0.0'} @@ -7654,7 +11271,7 @@ packages: builtin-modules: 3.3.0 deepmerge: 4.3.1 is-module: 1.0.0 - resolve: 1.22.2 + resolve: 1.22.1 rollup: 2.70.2 dev: true @@ -7677,13 +11294,13 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.2 + '@rollup/pluginutils': 5.0.2(rollup@2.70.2) estree-walker: 2.0.2 magic-string: 0.27.0 dev: false - /@rollup/plugin-terser@0.4.1(rollup@2.70.2): - resolution: {integrity: sha512-aKS32sw5a7hy+fEXVy+5T95aDIwjpGHCTv833HXVtyKMDoVS7pBr5K3L9hEQoNqbJFjfANPrNpIXlTQ7is00eA==} + /@rollup/plugin-terser@0.4.0(rollup@2.70.2): + resolution: {integrity: sha512-Ipcf3LPNerey1q9ZMjiaWHlNPEHNU/B5/uh9zXLltfEQ1lVSLLeZSgAtTPWGyw8Ip1guOeq+mDtdOlEj/wNxQw==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^2.x || ^3.x @@ -7694,7 +11311,7 @@ packages: rollup: 2.70.2 serialize-javascript: 6.0.1 smob: 0.0.6 - terser: 5.16.9 + terser: 5.16.8 dev: true /@rollup/pluginutils@3.1.0(rollup@2.70.2): @@ -7717,7 +11334,7 @@ packages: picomatch: 2.3.1 dev: true - /@rollup/pluginutils@5.0.2: + /@rollup/pluginutils@5.0.2(rollup@2.70.2): resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -7729,23 +11346,27 @@ packages: '@types/estree': 1.0.0 estree-walker: 2.0.2 picomatch: 2.3.1 - dev: false + rollup: 2.70.2 - /@rollup/pluginutils@5.0.2(rollup@2.70.2): - resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} - engines: {node: '>=14.0.0'} + /@rushstack/node-core-library@3.55.2(@types/node@18.15.10): + resolution: {integrity: sha512-SaLe/x/Q/uBVdNFK5V1xXvsVps0y7h1sN7aSJllQyFbugyOaxhNRF25bwEDnicARNEjJw0pk0lYnJQ9Kr6ev0A==} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 + '@types/node': '*' peerDependenciesMeta: - rollup: + '@types/node': optional: true dependencies: - '@types/estree': 1.0.0 - estree-walker: 2.0.2 - picomatch: 2.3.1 - rollup: 2.70.2 + '@types/node': 18.15.10 + colors: 1.2.5 + fs-extra: 7.0.1 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.2 + semver: 7.3.8 + z-schema: 5.0.5 + dev: true - /@rushstack/node-core-library@3.55.2(@types/node@18.15.11): + /@rushstack/node-core-library@3.55.2(@types/node@20.4.1): resolution: {integrity: sha512-SaLe/x/Q/uBVdNFK5V1xXvsVps0y7h1sN7aSJllQyFbugyOaxhNRF25bwEDnicARNEjJw0pk0lYnJQ9Kr6ev0A==} peerDependencies: '@types/node': '*' @@ -7753,7 +11374,7 @@ packages: '@types/node': optional: true dependencies: - '@types/node': 18.15.11 + '@types/node': 20.4.1 colors: 1.2.5 fs-extra: 7.0.1 import-lazy: 4.0.0 @@ -7888,6 +11509,18 @@ packages: type-detect: 4.0.8 dev: true + /@sinonjs/commons@3.0.0: + resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} + dependencies: + type-detect: 4.0.8 + dev: true + + /@sinonjs/fake-timers@10.1.0: + resolution: {integrity: sha512-w1qd368vtrwttm1PRJWPW1QHlbmHrVDGs1eBH/jZvRPUFS4MNXV9Q33EQdjOdeAxZ7O8+3wM7zxztm2nfUSyKw==} + dependencies: + '@sinonjs/commons': 3.0.0 + dev: true + /@sinonjs/fake-timers@8.1.0: resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==} dependencies: @@ -7914,7 +11547,7 @@ packages: '@storybook/csf': 0.0.2--canary.4566f4d.1 '@storybook/theming': 6.5.16(react-dom@18.2.0)(react@18.2.0) axe-core: 4.6.3 - core-js: 3.30.0 + core-js: 3.29.1 global: 4.4.0 lodash: 4.17.21 react: 18.2.0 @@ -7956,6 +11589,37 @@ packages: uuid-browser: 3.1.0 dev: true + /@storybook/addon-actions@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-vVoqE0Zw0g1PPnGfho8vRwjpXhQCpRNBQ/2U83/CSodHWL/MBYENG0XMby90TC72M26gNmEh0dn1YCUXvLdiew==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/components': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.0.26 + '@storybook/global': 5.0.0 + '@storybook/manager-api': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.0.26 + '@storybook/theming': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.26 + dequal: 2.0.3 + lodash: 4.17.21 + polished: 4.2.2 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-inspector: 6.0.1(react@18.2.0) + telejson: 7.1.0 + ts-dedent: 2.2.0 + uuid: 9.0.0 + dev: true + /@storybook/addon-backgrounds@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-7hW961WzTV29jbVM051IzN7pNYTRrk5njDMV6eMsKD7KyzGDiqBAX3QuXnv95s8MLWUKSee7UZa4DUgQYfjxRg==} peerDependencies: @@ -7969,20 +11633,73 @@ packages: dependencies: '@storybook/client-logger': 7.0.0-rc.10 '@storybook/components': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 7.0.0-rc.10 - '@storybook/global': 5.0.0 + '@storybook/core-events': 7.0.0-rc.10 + '@storybook/global': 5.0.0 + '@storybook/manager-api': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.0.0-rc.10 + '@storybook/theming': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.0-rc.10 + memoizerific: 1.11.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + ts-dedent: 2.2.0 + dev: true + + /@storybook/addon-backgrounds@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-sjTkOnSsVBBl1GruVVsNKWEuLCbKjkNun1mzIklfYAiHz9hTZIhe9MA2SGZoDozMUDIXQqSoMDEc3rnDtfqsnQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/components': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.0.26 + '@storybook/global': 5.0.0 + '@storybook/manager-api': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.0.26 + '@storybook/theming': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.26 + memoizerific: 1.11.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + ts-dedent: 2.2.0 + dev: true + + /@storybook/addon-controls@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-25lH3wRmCHtW4N9PN9v12XsCttpi8rU80ZL+qzlAvak/bmdT7xXidNi9MTZHDZxbqDeBcs8N49wbymqixvEfMQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/blocks': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) + '@storybook/client-logger': 7.0.0-rc.10 + '@storybook/components': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-common': 7.0.0-rc.10 '@storybook/manager-api': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) + '@storybook/node-logger': 7.0.0-rc.10 '@storybook/preview-api': 7.0.0-rc.10 '@storybook/theming': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) '@storybook/types': 7.0.0-rc.10 - memoizerific: 1.11.3 + lodash: 4.17.21 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) ts-dedent: 2.2.0 + transitivePeerDependencies: + - supports-color dev: true - /@storybook/addon-controls@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-25lH3wRmCHtW4N9PN9v12XsCttpi8rU80ZL+qzlAvak/bmdT7xXidNi9MTZHDZxbqDeBcs8N49wbymqixvEfMQ==} + /@storybook/addon-controls@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-mp1WuOYCPvR33orHn0XPABY5roF9Le8HnZwTpvfkrRMeMqLnYLnkCTZqY3JN/IOVlyQuYdqodP5CPDHNDLmvVg==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -7992,20 +11709,21 @@ packages: react-dom: optional: true dependencies: - '@storybook/blocks': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 7.0.0-rc.10 - '@storybook/components': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-common': 7.0.0-rc.10 - '@storybook/manager-api': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) - '@storybook/node-logger': 7.0.0-rc.10 - '@storybook/preview-api': 7.0.0-rc.10 - '@storybook/theming': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.0.0-rc.10 + '@storybook/blocks': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/client-logger': 7.0.26 + '@storybook/components': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-common': 7.0.26 + '@storybook/manager-api': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/node-logger': 7.0.26 + '@storybook/preview-api': 7.0.26 + '@storybook/theming': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.26 lodash: 4.17.21 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) ts-dedent: 2.2.0 transitivePeerDependencies: + - encoding - supports-color dev: true @@ -8019,8 +11737,8 @@ packages: '@storybook/mdx1-csf': optional: true dependencies: - '@babel/core': 7.21.4 - '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.21.4) + '@babel/core': 7.22.5 + '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.5) '@jest/transform': 29.5.0 '@mdx-js/react': 2.3.0(react@18.2.0) '@storybook/blocks': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) @@ -8046,6 +11764,40 @@ packages: - supports-color dev: true + /@storybook/addon-docs@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-C8DOwfmPBWDUS1IJbyJxykgVVHVzSSL+JFh3FwtF0hsqwjlNW4OvGDFbz0oAxyxs4V46xVcvh4E95e3GkW36BQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/core': 7.22.5 + '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.5) + '@jest/transform': 29.5.0 + '@mdx-js/react': 2.3.0(react@18.2.0) + '@storybook/blocks': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/client-logger': 7.0.26 + '@storybook/components': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/csf-plugin': 7.0.26 + '@storybook/csf-tools': 7.0.26 + '@storybook/global': 5.0.0 + '@storybook/mdx2-csf': 1.1.0 + '@storybook/node-logger': 7.0.26 + '@storybook/postinstall': 7.0.26 + '@storybook/preview-api': 7.0.26 + '@storybook/react-dom-shim': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/theming': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.26 + fs-extra: 11.1.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + remark-external-links: 8.0.0 + remark-slug: 6.1.0 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@storybook/addon-essentials@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-a4Tnfu+v1yneou2oYrDikAmNHsv4z7jAjyS4npuJTEW2a1WW+WPw8340BfZBI/y89nFYM6zyZGqy26Gie/QqVA==} peerDependencies: @@ -8073,6 +11825,33 @@ packages: - supports-color dev: true + /@storybook/addon-essentials@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-r+IOtxbIqlCKO8fDgLppubYm+GEW3ZDxjPwXMQdDGem9ENpz0QLKb49r89+UYqnnaYjuYKjDNUOqy0gX2HfUXQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/addon-actions': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-backgrounds': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-controls': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-docs': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-highlight': 7.0.26 + '@storybook/addon-measure': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-outline': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-toolbars': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-viewport': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-common': 7.0.26 + '@storybook/manager-api': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/node-logger': 7.0.26 + '@storybook/preview-api': 7.0.26 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + ts-dedent: 2.2.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@storybook/addon-highlight@7.0.0-rc.10: resolution: {integrity: sha512-AvCElbBqs2nS6W5wh79zkeRINEAL3LH3RFcT3oLFwSR5sI3NiWAuADN37XK+HN2RGf6E6Z7/ue3EcqELZAgCLw==} dependencies: @@ -8081,6 +11860,14 @@ packages: '@storybook/preview-api': 7.0.0-rc.10 dev: true + /@storybook/addon-highlight@7.0.26: + resolution: {integrity: sha512-+I+MoM7yXCA3YR2FwTSxSs6/IBpcc3Ey88WboGthR23ERmsgZOtum1S7KZ6cffNCOq4U0LzPkjKX2bICytFrIQ==} + dependencies: + '@storybook/core-events': 7.0.26 + '@storybook/global': 5.0.0 + '@storybook/preview-api': 7.0.26 + dev: true + /@storybook/addon-interactions@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-6MdBhlZ+vmS7tPoHPZn8hB5BJOw38ii670TvTy005UGj92+VeQRqlf9gVT5X7vnynmq6h/sj8MgSkSuqxL+3XA==} peerDependencies: @@ -8111,6 +11898,37 @@ packages: - supports-color dev: true + /@storybook/addon-interactions@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-trIbPFLdxF6XgGORhx8eSGmGZ/4/AekJyFluf2lgutGi4TPL5Xzrx3o1kTFPVdLAPplBuDIlVI4HSGHHH2zeTw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/components': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-common': 7.0.26 + '@storybook/core-events': 7.0.26 + '@storybook/global': 5.0.0 + '@storybook/instrumenter': 7.0.26 + '@storybook/manager-api': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.0.26 + '@storybook/theming': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.26 + jest-mock: 27.5.1 + polished: 4.2.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + ts-dedent: 2.2.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@storybook/addon-links@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Xgrm7o4RAqdnLwh/wGKyk7tL12eqQDLiyR2uahQrNUYG+MFz9hJCHzbBs/pVcOW2gdHRmdYMD6ZrZsk4/NehLA==} peerDependencies: @@ -8136,6 +11954,31 @@ packages: ts-dedent: 2.2.0 dev: true + /@storybook/addon-links@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-og+8AUAUpHsT+MVjhdQmRNJw9RUkHn5FFoou003b9V4UlPPNDYTo/tNEqOhUXn2l/ESAROJlR/q/8Qjdes24pA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/core-events': 7.0.26 + '@storybook/csf': 0.1.0 + '@storybook/global': 5.0.0 + '@storybook/manager-api': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.0.26 + '@storybook/router': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.26 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + ts-dedent: 2.2.0 + dev: true + /@storybook/addon-measure@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-GRjJPmjOcG0vLfJ5HvCoXq6GTjgR/Yxe8IXiC84aoUcwFTzMBHOb8J7doAY30SOqfuqBM3MTmTi7PBCHhFY6Cw==} peerDependencies: @@ -8158,6 +12001,28 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true + /@storybook/addon-measure@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-iAnI6q3GB8uSydK+S4m4ANpy0GpMpHhmU0oBtu6OmyyzHUH1RJ7/fGfBnzx6YT+rIOlqSFocxYGn74ylsp33Wg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/components': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.0.26 + '@storybook/global': 5.0.0 + '@storybook/manager-api': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.0.26 + '@storybook/types': 7.0.26 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + /@storybook/addon-outline@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Om+MFhkROJRtxHlVJ+DpLg+ROzdtFyNtWXgkQVH9YAo44SBkrBJbAAUj+0XmeWxr2gO3HEdLhRS4whoWBKAVbg==} peerDependencies: @@ -8181,6 +12046,72 @@ packages: ts-dedent: 2.2.0 dev: true + /@storybook/addon-outline@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-oL7D0IWO0M6hMw5cWEC6JdKXlGadlVIdhIrVN+0gdFxuxCHTGpebQ02DCvyfls29UssEOxPaO1XMdu9tDlctbg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/components': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.0.26 + '@storybook/global': 5.0.0 + '@storybook/manager-api': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.0.26 + '@storybook/types': 7.0.26 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + ts-dedent: 2.2.0 + dev: true + + /@storybook/addon-styling@1.3.2(less@4.1.3)(postcss@8.4.24)(react-dom@18.2.0)(react@18.2.0)(webpack@5.76.2): + resolution: {integrity: sha512-pxc2ncCH3jlOjsJFOmUECxIFvC4jmUxd0noeEC4shGfKvGhsssHpxcVtA36+s3JhDdx+Yhrk/0KeYoMe+35/qg==} + hasBin: true + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@babel/template': 7.22.5 + '@babel/types': 7.22.5 + '@storybook/api': 7.0.27(react-dom@18.2.0)(react@18.2.0) + '@storybook/components': 7.0.27(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-common': 7.0.27 + '@storybook/core-events': 7.0.27 + '@storybook/manager-api': 7.0.27(react-dom@18.2.0)(react@18.2.0) + '@storybook/node-logger': 7.0.27 + '@storybook/preview-api': 7.0.27 + '@storybook/theming': 7.0.27(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.27 + css-loader: 6.8.1(webpack@5.76.2) + less-loader: 11.1.3(less@4.1.3)(webpack@5.76.2) + postcss-loader: 7.3.3(postcss@8.4.24)(webpack@5.76.2) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + resolve-url-loader: 5.0.0 + sass-loader: 13.3.2(webpack@5.76.2) + style-loader: 3.3.3(webpack@5.76.2) + transitivePeerDependencies: + - encoding + - fibers + - less + - node-sass + - postcss + - sass + - sass-embedded + - supports-color + - webpack + dev: true + /@storybook/addon-toolbars@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-zpCFJKO5TOfEcVPRWj9oyftgsONJltFndLVtuWwVGaILcL0bGSahFtVhtwen3geCm+mi3AISgmsbCz5ABbsPyg==} peerDependencies: @@ -8201,6 +12132,26 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true + /@storybook/addon-toolbars@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-DrwqcWuCLjaTNFtAYUxO2VaLrr2ibhB3ZQwW7J6a4YFCJaV49wempGPq3BzTWvrPUtMxGp7J3ZusdH9jBgCzjA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/components': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/manager-api': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.0.26 + '@storybook/theming': 7.0.26(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + /@storybook/addon-viewport@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-aAEp2TEFSTjVmAfpWll3Kb0/tMFezWVEGI3WKlx2M5ladEC+jDygZrGovb4BpnknDQaHkrN0s9dsjUWTLmslww==} peerDependencies: @@ -8225,6 +12176,30 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true + /@storybook/addon-viewport@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-veAYxnR11sojXC7tlnBZ/USiafhWCsZNvjxmywl/XCh3MeDGFFDb2NN1s/7irAYXfNMOhgPGZED19BN9cQ8QRQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/components': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.0.26 + '@storybook/global': 5.0.0 + '@storybook/manager-api': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.0.26 + '@storybook/theming': 7.0.26(react-dom@18.2.0)(react@18.2.0) + memoizerific: 1.11.3 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + /@storybook/addons@6.5.16(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-p3DqQi+8QRL5k7jXhXmJZLsE/GqHqyY6PcoA1oNTJr0try48uhTGUOYkgzmqtDaa/qPFO5LP+xCPzZXckGtquQ==} peerDependencies: @@ -8239,7 +12214,7 @@ packages: '@storybook/router': 6.5.16(react-dom@18.2.0)(react@18.2.0) '@storybook/theming': 6.5.16(react-dom@18.2.0)(react@18.2.0) '@types/webpack-env': 1.18.0 - core-js: 3.30.0 + core-js: 3.29.1 global: 4.4.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -8259,7 +12234,7 @@ packages: '@storybook/router': 6.5.16(react-dom@18.2.0)(react@18.2.0) '@storybook/semver': 7.3.2 '@storybook/theming': 6.5.16(react-dom@18.2.0)(react@18.2.0) - core-js: 3.30.0 + core-js: 3.29.1 fast-deep-equal: 3.1.3 global: 4.4.0 lodash: 4.17.21 @@ -8273,6 +12248,23 @@ packages: util-deprecate: 1.0.2 dev: true + /@storybook/api@7.0.27(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-kvqtnahIdyp+c7qwG/IhY6e1ynet/G9k92J6n3UEpMqy0b+jKMpGE45uGdiMg5EDVGjvlDqN8Ed7v/ZDJFjlOw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/client-logger': 7.0.27 + '@storybook/manager-api': 7.0.27(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + /@storybook/blocks@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-E+v4EjHaSaHbFK2hiFcBW3X2Yz8+xANmioT94jr2AxvywAELMh5eOLByFwVdNK7e9umv7Qo2/icNvAzpaJdmrw==} peerDependencies: @@ -8290,7 +12282,41 @@ packages: '@storybook/preview-api': 7.0.0-rc.10 '@storybook/theming': 7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0) '@storybook/types': 7.0.0-rc.10 - '@types/lodash': 4.14.192 + '@types/lodash': 4.14.191 + color-convert: 2.0.1 + dequal: 2.0.3 + lodash: 4.17.21 + markdown-to-jsx: 7.2.0(react@18.2.0) + memoizerific: 1.11.3 + polished: 4.2.2 + react: 18.2.0 + react-colorful: 5.6.1(react-dom@18.2.0)(react@18.2.0) + react-dom: 18.2.0(react@18.2.0) + telejson: 7.1.0 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@storybook/blocks@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-VNYB6Y1Ocja8HVg4Bm1w7LvqRSEc9aLVD8BnI8BInHvekvxhaxTkfpA18qds7d8+RmerrJqAUhGx0jkIB/cvwA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/channels': 7.0.26 + '@storybook/client-logger': 7.0.26 + '@storybook/components': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.0.26 + '@storybook/csf': 0.1.0 + '@storybook/docs-tools': 7.0.26 + '@storybook/global': 5.0.0 + '@storybook/manager-api': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.0.26 + '@storybook/theming': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.26 + '@types/lodash': 4.14.191 color-convert: 2.0.1 dequal: 2.0.3 lodash: 4.17.21 @@ -8304,6 +12330,7 @@ packages: ts-dedent: 2.2.0 util-deprecate: 1.0.2 transitivePeerDependencies: + - encoding - supports-color dev: true @@ -8316,10 +12343,33 @@ packages: '@storybook/node-logger': 7.0.0-rc.10 '@types/ejs': 3.1.2 '@types/find-cache-dir': 3.2.1 - '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.17.16) + '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.17.19) + browser-assert: 1.2.1 + ejs: 3.1.9 + esbuild: 0.17.19 + esbuild-plugin-alias: 0.2.1 + express: 4.18.2 + find-cache-dir: 3.3.2 + fs-extra: 11.1.1 + process: 0.11.10 + util: 0.12.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@storybook/builder-manager@7.0.26: + resolution: {integrity: sha512-1Uk3dL3Yu5AuimfHAghBHs11wf7B+a+277astqLx7HSeh3L49zcDZS4NhGHKmtQjsEorbvmtty3s16q2k+fM8A==} + dependencies: + '@fal-works/esbuild-plugin-global-externals': 2.1.2 + '@storybook/core-common': 7.0.26 + '@storybook/manager': 7.0.26 + '@storybook/node-logger': 7.0.26 + '@types/ejs': 3.1.2 + '@types/find-cache-dir': 3.2.1 + '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.17.19) browser-assert: 1.2.1 ejs: 3.1.9 - esbuild: 0.17.16 + esbuild: 0.17.19 esbuild-plugin-alias: 0.2.1 express: 4.18.2 find-cache-dir: 3.3.2 @@ -8327,6 +12377,7 @@ packages: process: 0.11.10 util: 0.12.5 transitivePeerDependencies: + - encoding - supports-color dev: true @@ -8369,8 +12420,50 @@ packages: remark-slug: 7.0.1 rollup: 2.70.2 typescript: 4.9.5 - vite: 4.2.1(@types/node@18.15.11) + vite: 4.2.1(@types/node@18.15.10) + transitivePeerDependencies: + - supports-color + dev: true + + /@storybook/builder-vite@7.0.26(typescript@4.9.5)(vite@4.4.0): + resolution: {integrity: sha512-PRvySwvJEBLTZcUCKIULdxeFZeoDeK5odGFN0oIJhGZlOEI7jzbAcBT9SEZUh+Cv4Pk93XFr5+ZJCm/yrmF8RA==} + peerDependencies: + '@preact/preset-vite': '*' + typescript: '>= 4.3.x' + vite: ^3.0.0 || ^4.0.0 + vite-plugin-glimmerx: '*' + peerDependenciesMeta: + '@preact/preset-vite': + optional: true + typescript: + optional: true + vite-plugin-glimmerx: + optional: true + dependencies: + '@storybook/channel-postmessage': 7.0.26 + '@storybook/channel-websocket': 7.0.26 + '@storybook/client-logger': 7.0.26 + '@storybook/core-common': 7.0.26 + '@storybook/csf-plugin': 7.0.26 + '@storybook/mdx2-csf': 1.1.0 + '@storybook/node-logger': 7.0.26 + '@storybook/preview': 7.0.26 + '@storybook/preview-api': 7.0.26 + '@storybook/types': 7.0.26 + browser-assert: 1.2.1 + es-module-lexer: 0.9.3 + express: 4.18.2 + fs-extra: 11.1.1 + glob: 8.1.0 + glob-promise: 6.0.2(glob@8.1.0) + magic-string: 0.27.0 + remark-external-links: 8.0.0 + remark-slug: 6.1.0 + rollup: 2.70.2 + typescript: 4.9.5 + vite: 4.4.0(@types/node@20.4.1)(less@4.1.3) transitivePeerDependencies: + - encoding - supports-color dev: true @@ -8385,12 +12478,23 @@ packages: telejson: 7.1.0 dev: true - /@storybook/channel-postmessage@7.0.18: - resolution: {integrity: sha512-rpwBH5ANdPnugS6+7xG9qHSoS+aPSEnBxDKsONWFubfMTTXQuFkf/793rBbxGkoINdqh8kSdKOM2rIty6e9cmQ==} + /@storybook/channel-postmessage@7.0.26: + resolution: {integrity: sha512-ZvFLr/tUD9dWIjQtIn1JXHjqrbOP/uEEOqzwpKSVj0Cl4Vgc12s8hecbzBufkOF7fwLsFvfieSi7ENOmjoncdQ==} dependencies: - '@storybook/channels': 7.0.18 - '@storybook/client-logger': 7.0.18 - '@storybook/core-events': 7.0.18 + '@storybook/channels': 7.0.26 + '@storybook/client-logger': 7.0.26 + '@storybook/core-events': 7.0.26 + '@storybook/global': 5.0.0 + qs: 6.11.2 + telejson: 7.1.0 + dev: true + + /@storybook/channel-postmessage@7.0.27: + resolution: {integrity: sha512-ScpiStUHvtgy9RrCFNyzzH9l+zHF80lSwW/BZ1MRETJ9ZaOVPrm03U0Ju01wJC57DYPROwPU/wKMetNqKKEhdA==} + dependencies: + '@storybook/channels': 7.0.27 + '@storybook/client-logger': 7.0.27 + '@storybook/core-events': 7.0.27 '@storybook/global': 5.0.0 qs: 6.11.2 telejson: 7.1.0 @@ -8405,10 +12509,19 @@ packages: telejson: 7.1.0 dev: true + /@storybook/channel-websocket@7.0.26: + resolution: {integrity: sha512-c+0VcZf78RGnT/pWrH85yydt0azRKAHZF3SHWKM4+W8qOFr0Mk0+jqhPh1uoUoPDpBZDTKS/nzXY8cwUVwF/eA==} + dependencies: + '@storybook/channels': 7.0.26 + '@storybook/client-logger': 7.0.26 + '@storybook/global': 5.0.0 + telejson: 7.1.0 + dev: true + /@storybook/channels@6.5.16: resolution: {integrity: sha512-VylzaWQZaMozEwZPJdyJoz+0jpDa8GRyaqu9TGG6QGv+KU5POoZaGLDkRE7TzWkyyP0KQLo80K99MssZCpgSeg==} dependencies: - core-js: 3.30.0 + core-js: 3.29.1 ts-dedent: 2.2.0 util-deprecate: 1.0.2 dev: true @@ -8417,16 +12530,31 @@ packages: resolution: {integrity: sha512-LNjI2etxaK5hbBHziNbDzK5VajGU0BLcD04CO3LbGRC14hJvDfVnvymJeDbbgT1b7RPUwl/vv/azO1kVHDax/A==} dev: true - /@storybook/channels@7.0.18: - resolution: {integrity: sha512-rkA7ea0M3+dWS+71iHJdiZ5R2QuIdiVg0CgyLJHDagc1qej7pEVNhMWtppeq+X5Pwp9nkz8ZTQ7aCjTf6th0/A==} + /@storybook/channels@7.0.26: + resolution: {integrity: sha512-Br3XILhrtuL5Sdp91I04kKjJzSqU/N8gGL6B6nIfnuaHUvGMDuMCHAB+g7aoiyH5dnpDZ6yBVGNwtYAyJA+0Og==} + dev: true + + /@storybook/channels@7.0.27: + resolution: {integrity: sha512-YppvPa1qMyC+oCQJ3tf7Quzpf2NnBlvIRLPJiGAMssUwX5qE0iKe9lTtkNwMaNxEvzz6rDxewSlz+f/MWr4gPw==} + dev: true + + /@storybook/channels@7.2.0-rc.0: + resolution: {integrity: sha512-auUqrrYCTzjExtMCFLFVELZ6cNNRWVOUEiJ0I+JACjQFZn0i6cBZHCRkxz0HhcJw5usCeqWVuo0U3OQbR2scEQ==} + dependencies: + '@storybook/client-logger': 7.2.0-rc.0 + '@storybook/core-events': 7.2.0-rc.0 + '@storybook/global': 5.0.0 + qs: 6.11.2 + telejson: 7.1.0 + tiny-invariant: 1.3.1 dev: true /@storybook/cli@7.0.0-rc.10: resolution: {integrity: sha512-UTncMAUO6+WvXW0IrUDBlN94X5BDCre4qvlZLnPU6LDDZ53MOjESO4U+k/3B/ARRNFaFoiDh+hfkBLg3ulLqgg==} hasBin: true dependencies: - '@babel/core': 7.21.4 - '@babel/preset-env': 7.21.4(@babel/core@7.21.4) + '@babel/core': 7.22.5 + '@babel/preset-env': 7.20.2(@babel/core@7.22.5) '@ndelangen/get-tarball': 3.0.7 '@storybook/codemod': 7.0.0-rc.10 '@storybook/core-common': 7.0.0-rc.10 @@ -8450,13 +12578,62 @@ packages: get-port: 5.1.1 giget: 1.1.2 globby: 11.1.0 - jscodeshift: 0.14.0(@babel/preset-env@7.21.4) + jscodeshift: 0.14.0(@babel/preset-env@7.20.2) + leven: 3.1.0 + prettier: 2.8.8 + prompts: 2.4.2 + puppeteer-core: 2.1.1 + read-pkg-up: 7.0.1 + semver: 7.5.3 + shelljs: 0.8.5 + simple-update-notifier: 1.1.0 + strip-json-comments: 3.1.1 + tempy: 1.0.1 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: true + + /@storybook/cli@7.0.26: + resolution: {integrity: sha512-sZ136wRUYTdhhm/thegFoI47wOzl2X+K9eaiTTp0ARwnIUhXAPDQ0MKOD36hKbCX5T/pBE7r++7WoEReIbUDqQ==} + hasBin: true + dependencies: + '@babel/core': 7.22.5 + '@babel/preset-env': 7.20.2(@babel/core@7.22.5) + '@ndelangen/get-tarball': 3.0.7 + '@storybook/codemod': 7.0.26 + '@storybook/core-common': 7.0.26 + '@storybook/core-server': 7.0.26 + '@storybook/csf-tools': 7.0.26 + '@storybook/node-logger': 7.0.26 + '@storybook/telemetry': 7.0.26 + '@storybook/types': 7.0.26 + '@types/semver': 7.3.13 + chalk: 4.1.2 + commander: 6.2.1 + cross-spawn: 7.0.3 + detect-indent: 6.1.0 + envinfo: 7.8.1 + execa: 5.1.1 + express: 4.18.2 + find-up: 5.0.0 + fs-extra: 11.1.1 + get-npm-tarball-url: 2.0.3 + get-port: 5.1.1 + giget: 1.1.2 + globby: 11.1.0 + jscodeshift: 0.14.0(@babel/preset-env@7.20.2) leven: 3.1.0 + ora: 5.4.1 prettier: 2.8.8 prompts: 2.4.2 puppeteer-core: 2.1.1 read-pkg-up: 7.0.1 - semver: 7.4.0 + semver: 7.5.3 shelljs: 0.8.5 simple-update-notifier: 1.1.0 strip-json-comments: 3.1.1 @@ -8473,7 +12650,7 @@ packages: /@storybook/client-logger@6.5.16: resolution: {integrity: sha512-pxcNaCj3ItDdicPTXTtmYJE3YC1SjxFrBmHcyrN+nffeNyiMuViJdOOZzzzucTUG0wcOOX8jaSyak+nnHg5H1Q==} dependencies: - core-js: 3.30.0 + core-js: 3.29.1 global: 4.4.0 dev: true @@ -8483,8 +12660,20 @@ packages: '@storybook/global': 5.0.0 dev: true - /@storybook/client-logger@7.0.18: - resolution: {integrity: sha512-uKgFdVedYoRDZBVrE1IBdWNHDFln1IxWEeI+7ZiNSQwREG9swHpU5Fa8DceclM/oLjJRuzG1jFzv+XZY8894+Q==} + /@storybook/client-logger@7.0.26: + resolution: {integrity: sha512-OMVLbgceoeuM8sWOfTX/9a4zCrH78G32hg7x8yXLZnRJ9OLaHJHzUM0Onc4MLudqVUdaKH0c8ejpBXUyIr1rJQ==} + dependencies: + '@storybook/global': 5.0.0 + dev: true + + /@storybook/client-logger@7.0.27: + resolution: {integrity: sha512-t4F0ByHP4MNiyVI5sgqtxSccr4RmPAqTr/h6CeGLJKWzUYobBV5hwKUd/qlfwdjev2u9C7AdLFPBKVcHX5PteA==} + dependencies: + '@storybook/global': 5.0.0 + dev: true + + /@storybook/client-logger@7.2.0-rc.0: + resolution: {integrity: sha512-OjSqjuyOC8z/55ENBMrFSCmDLNPnuv5eFTahlBhLU7nYvQIPqXlJdgUQNa3+HzXqm47yhrkIDzXPHdiPzj/2pQ==} dependencies: '@storybook/global': 5.0.0 dev: true @@ -8492,9 +12681,9 @@ packages: /@storybook/codemod@7.0.0-rc.10: resolution: {integrity: sha512-BHAtI/G5/TyjV/714W06oMaEa3A7GGTGK4KGlEvC/g1i3bCeXMCGWCR1fp850OFX/AyQF5iETtAZx+vk7mvurQ==} dependencies: - '@babel/core': 7.21.4 - '@babel/preset-env': 7.20.2(@babel/core@7.21.4) - '@babel/types': 7.21.4 + '@babel/core': 7.21.3 + '@babel/preset-env': 7.20.2(@babel/core@7.21.3) + '@babel/types': 7.21.3 '@storybook/csf': 0.1.1-next.0 '@storybook/csf-tools': 7.0.0-rc.10 '@storybook/node-logger': 7.0.0-rc.10 @@ -8509,6 +12698,26 @@ packages: - supports-color dev: true + /@storybook/codemod@7.0.26: + resolution: {integrity: sha512-H9sV59FfGrGzGM+UZQclNglnc4cOkQvvF3EOWlR3BfDhx+STSB9VbCR308ygjUYw2TXZ2s5seCvHtVvA2yhILA==} + dependencies: + '@babel/core': 7.21.3 + '@babel/preset-env': 7.21.5(@babel/core@7.21.3) + '@babel/types': 7.21.3 + '@storybook/csf': 0.1.0 + '@storybook/csf-tools': 7.0.26 + '@storybook/node-logger': 7.0.26 + '@storybook/types': 7.0.26 + cross-spawn: 7.0.3 + globby: 11.1.0 + jscodeshift: 0.14.0(@babel/preset-env@7.21.5) + lodash: 4.17.21 + prettier: 2.8.8 + recast: 0.23.1 + transitivePeerDependencies: + - supports-color + dev: true + /@storybook/components@6.5.16(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-LzBOFJKITLtDcbW9jXl0/PaG+4xAz25PK8JxPZpIALbmOpYWOAPcO6V9C2heX6e6NgWFMUxjplkULEk9RCQMNA==} peerDependencies: @@ -8518,7 +12727,7 @@ packages: '@storybook/client-logger': 6.5.16 '@storybook/csf': 0.0.2--canary.4566f4d.1 '@storybook/theming': 6.5.16(react-dom@18.2.0)(react@18.2.0) - core-js: 3.30.0 + core-js: 3.29.1 memoizerific: 1.11.3 qs: 6.11.2 react: 18.2.0 @@ -8545,6 +12754,42 @@ packages: util-deprecate: 1.0.2 dev: true + /@storybook/components@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-n0TVWEF4Bc9JAyEIaN0PqwglbaYYRcPVG7ka+5wgGmBiuDlWI1SXd4EXxv2u0mVibHvtkHvOn6/GaZ1vG45p6g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/csf': 0.1.0 + '@storybook/global': 5.0.0 + '@storybook/theming': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.26 + memoizerific: 1.11.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + use-resize-observer: 9.1.0(react-dom@18.2.0)(react@18.2.0) + util-deprecate: 1.0.2 + dev: true + + /@storybook/components@7.0.27(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-utt4fA1td7QHpvuD/9dWm9UEoO5xTU3EsXk/U2fPUQzN9NEsbWKV/QubUYIpVy5iwwgUyMvqzWHM0veAriJW5A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/client-logger': 7.0.27 + '@storybook/csf': 0.1.0 + '@storybook/global': 5.0.0 + '@storybook/theming': 7.0.27(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.27 + memoizerific: 1.11.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + use-resize-observer: 9.1.0(react-dom@18.2.0)(react@18.2.0) + util-deprecate: 1.0.2 + dev: true + /@storybook/core-client@7.0.0-rc.10: resolution: {integrity: sha512-Z1zhznMt0NA9PNvBB/JplGAubZp3OszVseIYKj52WmPJHhD/VSlrCMGh9AVkSUsVunF4ciK92GvyMBPuYE/5LA==} dependencies: @@ -8552,6 +12797,13 @@ packages: '@storybook/preview-api': 7.0.0-rc.10 dev: true + /@storybook/core-client@7.0.26: + resolution: {integrity: sha512-1DA8mLnr0f6EuL74859IDK99a7CGNgMIN0/cAVNgYxq0WA4j+9ajsJ+/RIAgnS2NLVLR9kbezUtBEx4/H88IRA==} + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/preview-api': 7.0.26 + dev: true + /@storybook/core-common@7.0.0-rc.10: resolution: {integrity: sha512-csHdcVH7+YMj13WQ68OU6+6H9eYF9Sjhj84RDvlOuwJ0reXftf0RLFJwNrazwLPi7Wypxd0K/MhiM/mZOoY2+A==} dependencies: @@ -8560,36 +12812,102 @@ packages: '@types/node': 16.18.25 '@types/pretty-hrtime': 1.0.1 chalk: 4.1.2 - esbuild: 0.17.16 - esbuild-register: 3.4.2(esbuild@0.17.16) - file-system-cache: 2.1.1 + esbuild: 0.17.19 + esbuild-register: 3.4.2(esbuild@0.17.19) + file-system-cache: 2.3.0 + find-up: 5.0.0 + fs-extra: 11.1.1 + glob: 8.1.0 + glob-promise: 6.0.2(glob@8.1.0) + handlebars: 4.7.7 + lazy-universal-dotenv: 4.0.0 + picomatch: 2.3.1 + pkg-dir: 5.0.0 + pretty-hrtime: 1.0.3 + resolve-from: 5.0.0 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@storybook/core-common@7.0.26: + resolution: {integrity: sha512-rojZblzB0egNXX0bZ7R3TuPDiBSIhxpZCrorrDMHOZ8F+zuBxyTiZ0yMxEDn7i46T2n1vX+hUHhwZVxZrLn/ZQ==} + dependencies: + '@storybook/node-logger': 7.0.26 + '@storybook/types': 7.0.26 + '@types/node': 16.18.25 + '@types/node-fetch': 2.6.4 + '@types/pretty-hrtime': 1.0.1 + chalk: 4.1.2 + esbuild: 0.17.19 + esbuild-register: 3.4.2(esbuild@0.17.19) + file-system-cache: 2.3.0 + find-up: 5.0.0 + fs-extra: 11.1.1 + glob: 8.1.0 + glob-promise: 6.0.2(glob@8.1.0) + handlebars: 4.7.7 + lazy-universal-dotenv: 4.0.0 + node-fetch: 2.6.9 + picomatch: 2.3.1 + pkg-dir: 5.0.0 + pretty-hrtime: 1.0.3 + resolve-from: 5.0.0 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /@storybook/core-common@7.0.27: + resolution: {integrity: sha512-nlHXpn3CghCwkeIffZ7/PzcraCDXNZz+cnR4L8vtgJn1n6W7y92mxfF8gkRHuiYHWHbPWRVP9M5vAmVoiNMxjw==} + dependencies: + '@storybook/node-logger': 7.0.27 + '@storybook/types': 7.0.27 + '@types/node': 16.18.25 + '@types/node-fetch': 2.6.4 + '@types/pretty-hrtime': 1.0.1 + chalk: 4.1.2 + esbuild: 0.17.19 + esbuild-register: 3.4.2(esbuild@0.17.19) + file-system-cache: 2.3.0 find-up: 5.0.0 fs-extra: 11.1.1 glob: 8.1.0 glob-promise: 6.0.2(glob@8.1.0) handlebars: 4.7.7 lazy-universal-dotenv: 4.0.0 + node-fetch: 2.6.9 picomatch: 2.3.1 pkg-dir: 5.0.0 pretty-hrtime: 1.0.3 resolve-from: 5.0.0 ts-dedent: 2.2.0 transitivePeerDependencies: + - encoding - supports-color dev: true /@storybook/core-events@6.5.16: resolution: {integrity: sha512-qMZQwmvzpH5F2uwNUllTPg6eZXr2OaYZQRRN8VZJiuorZzDNdAFmiVWMWdkThwmyLEJuQKXxqCL8lMj/7PPM+g==} dependencies: - core-js: 3.30.0 + core-js: 3.29.1 dev: true /@storybook/core-events@7.0.0-rc.10: resolution: {integrity: sha512-Z4S6H1E5FuG7eiVozqcqNBSADt0kCDZeXlpR/gIOYLmTd/BDIQ2QhLt+G41BbEvck8nRnC7lZ9DXuref8V3pDA==} dev: true - /@storybook/core-events@7.0.18: - resolution: {integrity: sha512-7gxHBQDezdKOeq/u1LL80Bwjfcwsv7XOS3yWQElcgqp+gLaYB6OwwgtkCB2yV6a6l4nep9IdPWE8G3TxIzn9xw==} + /@storybook/core-events@7.0.26: + resolution: {integrity: sha512-ckZszphEAYs9wp8tPVhayEMzk8JxCiQfzbq0S45sbdqdTrl40PmsOjv5iPNaUYElI/Stfz+v4gDCEUfOsxyC+w==} + dev: true + + /@storybook/core-events@7.0.27: + resolution: {integrity: sha512-sNnqgO5i5DUIqeQfNbr987KWvAciMN9FmMBuYdKjVFMqWFyr44HTgnhfKwZZKl+VMDYkHA9Do7UGSYZIKy0P4g==} + dev: true + + /@storybook/core-events@7.2.0-rc.0: + resolution: {integrity: sha512-H6OTyP+QTH3nfr5EVkuOsfPMPcyGrRe+TKJsta1D493wh4VSMc8Z3Qz2p6KvisT8zQ0lTY9qzzzzgonNgWOhHQ==} dev: true /@storybook/core-server@7.0.0-rc.10: @@ -8602,7 +12920,7 @@ packages: '@storybook/core-events': 7.0.0-rc.10 '@storybook/csf': 0.1.1-next.0 '@storybook/csf-tools': 7.0.0-rc.10 - '@storybook/docs-mdx': 0.0.1-next.7 + '@storybook/docs-mdx': 0.1.1-next.0 '@storybook/global': 5.0.0 '@storybook/manager': 7.0.0-rc.10 '@storybook/node-logger': 7.0.0-rc.10 @@ -8630,7 +12948,58 @@ packages: pretty-hrtime: 1.0.3 prompts: 2.4.2 read-pkg-up: 7.0.1 - semver: 7.4.0 + semver: 7.5.3 + serve-favicon: 2.5.0 + telejson: 7.1.0 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + watchpack: 2.4.0 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: true + + /@storybook/core-server@7.0.26: + resolution: {integrity: sha512-QieqH19jBPZafxJVmCVK6GTYkRN/CJ8RQUvyRH2KNhqXP0tHYfL51FlU70ldo/vHX6Ax4Cje5hx/Nln9+DOMNg==} + dependencies: + '@aw-web-design/x-default-browser': 1.4.88 + '@discoveryjs/json-ext': 0.5.7 + '@storybook/builder-manager': 7.0.26 + '@storybook/core-common': 7.0.26 + '@storybook/core-events': 7.0.26 + '@storybook/csf': 0.1.0 + '@storybook/csf-tools': 7.0.26 + '@storybook/docs-mdx': 0.1.0 + '@storybook/global': 5.0.0 + '@storybook/manager': 7.0.26 + '@storybook/node-logger': 7.0.26 + '@storybook/preview-api': 7.0.26 + '@storybook/telemetry': 7.0.26 + '@storybook/types': 7.0.26 + '@types/detect-port': 1.3.2 + '@types/node': 16.18.25 + '@types/node-fetch': 2.6.4 + '@types/pretty-hrtime': 1.0.1 + '@types/semver': 7.3.13 + better-opn: 2.1.1 + chalk: 4.1.2 + cli-table3: 0.6.3 + compression: 1.7.4 + detect-port: 1.5.1 + express: 4.18.2 + fs-extra: 11.1.1 + globby: 11.1.0 + ip: 2.0.0 + lodash: 4.17.21 + node-fetch: 2.6.9 + open: 8.4.2 + pretty-hrtime: 1.0.3 + prompts: 2.4.2 + read-pkg-up: 7.0.1 + semver: 7.5.3 serve-favicon: 2.5.0 telejson: 7.1.0 ts-dedent: 2.2.0 @@ -8653,13 +13022,22 @@ packages: - supports-color dev: true + /@storybook/csf-plugin@7.0.26: + resolution: {integrity: sha512-D+wZvKlFxI/Vur8SRvkwKujOdV8ZL6xKiCX/07nFJXhhZoaeM+E78xPCL613Hj15GloujMkAnv7CT2rCiFJYow==} + dependencies: + '@storybook/csf-tools': 7.0.26 + unplugin: 0.10.2 + transitivePeerDependencies: + - supports-color + dev: true + /@storybook/csf-tools@7.0.0-rc.10: resolution: {integrity: sha512-gNn6Kkps/IaeNessIdxGmCciMyg7BWihoGCkq23yH1iAoslmc44coaVXAzLTFBork6AHYck6uiMI7LLaUNot1A==} dependencies: - '@babel/generator': 7.21.4 - '@babel/parser': 7.21.4 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/generator': 7.21.3 + '@babel/parser': 7.21.3 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 '@storybook/csf': 0.1.1-next.0 '@storybook/types': 7.0.0-rc.10 fs-extra: 11.1.1 @@ -8669,6 +13047,22 @@ packages: - supports-color dev: true + /@storybook/csf-tools@7.0.26: + resolution: {integrity: sha512-O8WJNOkvgrGV6gS/5ERkgqiXOxoXMuHtzdJpIM9DHPhzkSxB1Inl3WrX/dRRDNtmiHf87hBUuzhgo7YR7z4tuQ==} + dependencies: + '@babel/generator': 7.21.3 + '@babel/parser': 7.21.3 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 + '@storybook/csf': 0.1.0 + '@storybook/types': 7.0.26 + fs-extra: 11.1.1 + recast: 0.23.1 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - supports-color + dev: true + /@storybook/csf@0.0.1: resolution: {integrity: sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw==} dependencies: @@ -8693,14 +13087,18 @@ packages: type-fest: 2.19.0 dev: true - /@storybook/docs-mdx@0.0.1-next.7: - resolution: {integrity: sha512-JbgBf/EMBtx65iXtB3pOiX3818UeL9jZ+KAY241OAPqJVXjMQ5KaVOdg/57MSmd508HDIGx7CiImOMEmWwQ9/g==} + /@storybook/docs-mdx@0.1.0: + resolution: {integrity: sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg==} + dev: true + + /@storybook/docs-mdx@0.1.1-next.0: + resolution: {integrity: sha512-t5gehdVj1GxicE2W3mslaqaLGKg6u1wZRySVrWNM4Ue7PmuI0LvJDLcOcaIzizc4Duh+JljMvShK8NTaxhE1PA==} dev: true /@storybook/docs-tools@7.0.0-rc.10: resolution: {integrity: sha512-J8DbctJAiGjp4EpKnjyu1hvr99/H2V4KdWpLqHnIozLd4viELBaFQAuJBVE9Vub8B81vpw9s01JJEuCtEFB7rA==} dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@storybook/core-common': 7.0.0-rc.10 '@storybook/preview-api': 7.0.0-rc.10 '@storybook/types': 7.0.0-rc.10 @@ -8711,6 +13109,21 @@ packages: - supports-color dev: true + /@storybook/docs-tools@7.0.26: + resolution: {integrity: sha512-Ibpm/OTR2XmJgix5w+wMYbDwN0zp5e/pcqSHy36OvkBOG588IKSSzYdBjGdTLPHWBoehp2Kyndw/5dL/09ftXA==} + dependencies: + '@babel/core': 7.22.5 + '@storybook/core-common': 7.0.26 + '@storybook/preview-api': 7.0.26 + '@storybook/types': 7.0.26 + '@types/doctrine': 0.0.3 + doctrine: 3.0.0 + lodash: 4.17.21 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@storybook/global@5.0.0: resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} dev: true @@ -8725,14 +13138,24 @@ packages: '@storybook/preview-api': 7.0.0-rc.10 dev: true - /@storybook/instrumenter@7.0.18: - resolution: {integrity: sha512-fyQxeuVC0H+w3oyTuByE95xnAQ+l/WhUBVkHV2X+PWjg9vg9Y9JmrbNWynlvz5HLFlsY3qAWJh+ciVRVSvY5Jw==} + /@storybook/instrumenter@7.0.26: + resolution: {integrity: sha512-7Ty0LTslgkm5RyH6CqTAKhWz/cF6wq/sNdMYKwvVZHWNZ2LKMtXD0RWM2caCPruAGOQ9+52H+3s4TZGKaPSSWQ==} + dependencies: + '@storybook/channels': 7.0.26 + '@storybook/client-logger': 7.0.26 + '@storybook/core-events': 7.0.26 + '@storybook/global': 5.0.0 + '@storybook/preview-api': 7.0.26 + dev: true + + /@storybook/instrumenter@7.2.0-rc.0: + resolution: {integrity: sha512-DM2M23EcFEMyMIXzZ6PiKlDknPbqvQX2U/dtDoFwx6OITLTFV3xK4PghB/yjLMxPhl/bcZph15ps17/sb+YsJQ==} dependencies: - '@storybook/channels': 7.0.18 - '@storybook/client-logger': 7.0.18 - '@storybook/core-events': 7.0.18 + '@storybook/channels': 7.2.0-rc.0 + '@storybook/client-logger': 7.2.0-rc.0 + '@storybook/core-events': 7.2.0-rc.0 '@storybook/global': 5.0.0 - '@storybook/preview-api': 7.0.18 + '@storybook/preview-api': 7.2.0-rc.0 dev: true /@storybook/manager-api@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): @@ -8754,14 +13177,72 @@ packages: memoizerific: 1.11.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - semver: 7.4.0 + semver: 7.5.3 + store2: 2.14.2 + telejson: 7.1.0 + ts-dedent: 2.2.0 + dev: true + + /@storybook/manager-api@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-/2p6lU7r30qMXob/UnzRL9yq7XjoE+YQXv1KhrcePfMBARbelYw9RYhYT/AkXGtb9/Fa95uG3lNvoDLC1IQfMQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/channels': 7.0.26 + '@storybook/client-logger': 7.0.26 + '@storybook/core-events': 7.0.26 + '@storybook/csf': 0.1.0 + '@storybook/global': 5.0.0 + '@storybook/router': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/theming': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.26 + dequal: 2.0.3 + lodash: 4.17.21 + memoizerific: 1.11.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + semver: 7.5.3 + store2: 2.14.2 + telejson: 7.1.0 + ts-dedent: 2.2.0 + dev: true + + /@storybook/manager-api@7.0.27(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-CVgy4ti8h0Xc4nxiPujTzhMANl9wmfLGvSA9ZX6YUBbKFV4UOL4oj105iHPW7Ngse6Qoqj0rnhkOSmLczXT03w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/channels': 7.0.27 + '@storybook/client-logger': 7.0.27 + '@storybook/core-events': 7.0.27 + '@storybook/csf': 0.1.0 + '@storybook/global': 5.0.0 + '@storybook/router': 7.0.27(react-dom@18.2.0)(react@18.2.0) + '@storybook/theming': 7.0.27(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.27 + dequal: 2.0.3 + lodash: 4.17.21 + memoizerific: 1.11.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + semver: 7.5.3 store2: 2.14.2 telejson: 7.1.0 ts-dedent: 2.2.0 dev: true - /@storybook/manager@7.0.0-rc.10: - resolution: {integrity: sha512-WmGyBFPCaW7ee57nSaPCb5teeI3mUBL+cRY3wXi7n+tzCHuWlYM/AWmQgOtb2goMgt7iZn76+uL9JUfTbvfRCg==} + /@storybook/manager@7.0.0-rc.10: + resolution: {integrity: sha512-WmGyBFPCaW7ee57nSaPCb5teeI3mUBL+cRY3wXi7n+tzCHuWlYM/AWmQgOtb2goMgt7iZn76+uL9JUfTbvfRCg==} + dev: true + + /@storybook/manager@7.0.26: + resolution: {integrity: sha512-mxjU/pmHr8xL96HCipqazvZWQkxBPCbpZ2+YsJuJoLFN4m7RoOK21VK0euBW24NlSg7Vp57XGQcrJCv6xUTKMg==} + dev: true + + /@storybook/mdx2-csf@1.1.0: + resolution: {integrity: sha512-TXJJd5RAKakWx4BtpwvSNdgTDkKM6RkXU8GK34S/LhidQ5Pjz3wcnqb0TxEkfhK/ztbP8nKHqXFwLfa2CYkvQw==} dev: true /@storybook/mdx2-csf@1.1.0-next.1: @@ -8777,10 +13258,32 @@ packages: pretty-hrtime: 1.0.3 dev: true + /@storybook/node-logger@7.0.26: + resolution: {integrity: sha512-3Jqv3fRb8+Mn/aNl4IztgUAS/pvouVzpfHDc8+6KYAoFMeDXwHVlfF/+gRCpd/fbYaTHGrycIs5G48bC190Dgg==} + dependencies: + '@types/npmlog': 4.1.4 + chalk: 4.1.2 + npmlog: 5.0.1 + pretty-hrtime: 1.0.3 + dev: true + + /@storybook/node-logger@7.0.27: + resolution: {integrity: sha512-idoK+sDaTTPuxHcKhxn+l27Omhxvr1TQ0ALw1h8ehyMbW8TZBdWvYLYfmiWeI3+NQtmeudzxhKSVYTmAY4qDJw==} + dependencies: + '@types/npmlog': 4.1.4 + chalk: 4.1.2 + npmlog: 5.0.1 + pretty-hrtime: 1.0.3 + dev: true + /@storybook/postinstall@7.0.0-rc.10: resolution: {integrity: sha512-TLmwMcIuCGBTsFU2reyUTCofFyN9nCO6TXku8DzqD4UIj89RqVN+ngaOSl8uuqKhCYglocEWM4g88OG1Oaljjw==} dev: true + /@storybook/postinstall@7.0.26: + resolution: {integrity: sha512-NhJBpQ+49RWF63UkdwrEwBLJBjAZeTlruPWfXGUb343iaGNNTsD3jajbToFHncibewH83yk6MeGfiyUva60oJw==} + dev: true + /@storybook/preview-api@7.0.0-rc.10: resolution: {integrity: sha512-3oBm6Che7ctbOLS3TTbuySbLdxA1xqLDgn8AaOadCd4SmCfhxZNor35RO1TlN8S8pQPsGlT9UBWB4xKxhq0e2A==} dependencies: @@ -8801,16 +13304,55 @@ packages: util-deprecate: 1.0.2 dev: true - /@storybook/preview-api@7.0.18: - resolution: {integrity: sha512-xxtC0gPGMn/DbwvS4ZuJaBwfFNsjUCf0yLYHFrNe6fxncbvcLZ550RuyUwYuIRfsiKrlgfa3QmmCa4JM/JesHQ==} + /@storybook/preview-api@7.0.26: + resolution: {integrity: sha512-uJwA4errBOZOoDF2T7Z2oLqjAYvvjMr31sTsOoT0niJtWr29RQp8yS6VoSrsuh+y3FAVqBEl5pS+DX3IGLjvxw==} + dependencies: + '@storybook/channel-postmessage': 7.0.26 + '@storybook/channels': 7.0.26 + '@storybook/client-logger': 7.0.26 + '@storybook/core-events': 7.0.26 + '@storybook/csf': 0.1.0 + '@storybook/global': 5.0.0 + '@storybook/types': 7.0.26 + '@types/qs': 6.9.7 + dequal: 2.0.3 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.11.2 + synchronous-promise: 2.0.17 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/preview-api@7.0.27: + resolution: {integrity: sha512-FhauTuLzRsaIaEORQP5lxYrzwRgZPMnfYEPnzduyGgPiY6VZkS6wIiO6pKzat83V1L4J7m5aZhTB3HtvTwPhvg==} + dependencies: + '@storybook/channel-postmessage': 7.0.27 + '@storybook/channels': 7.0.27 + '@storybook/client-logger': 7.0.27 + '@storybook/core-events': 7.0.27 + '@storybook/csf': 0.1.0 + '@storybook/global': 5.0.0 + '@storybook/types': 7.0.27 + '@types/qs': 6.9.7 + dequal: 2.0.3 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.11.2 + synchronous-promise: 2.0.17 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/preview-api@7.2.0-rc.0: + resolution: {integrity: sha512-18oEkEps0k6n/RMAmThr/QpCawjrZDsUeFIKbRpZ0MYT2wuszLHYAGAMQceBaKoDSBFsO3rY6ijap+G92AT+TA==} dependencies: - '@storybook/channel-postmessage': 7.0.18 - '@storybook/channels': 7.0.18 - '@storybook/client-logger': 7.0.18 - '@storybook/core-events': 7.0.18 + '@storybook/channels': 7.2.0-rc.0 + '@storybook/client-logger': 7.2.0-rc.0 + '@storybook/core-events': 7.2.0-rc.0 '@storybook/csf': 0.1.0 '@storybook/global': 5.0.0 - '@storybook/types': 7.0.18 + '@storybook/types': 7.2.0-rc.0 '@types/qs': 6.9.7 dequal: 2.0.3 lodash: 4.17.21 @@ -8825,6 +13367,10 @@ packages: resolution: {integrity: sha512-885uU20XL54s31lAyIu5MaiD4t23gJ0X/JEMz2/LTvfluYDtQg66gXhMibKqEPgMHcPOND5ZrGoYPdaLDG20Dw==} dev: true + /@storybook/preview@7.0.26: + resolution: {integrity: sha512-9Uaxl/MEMYqjLlKAeAF2ATuaM0yQagXUfu2bEOpuor2ys9XoisDkvB7jfsCVqMZHeQ+mCdYyBICHhgqzxcO2Zg==} + dev: true + /@storybook/react-dom-shim@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-oRmjN4HLo/gumufM6xrCYMncggbsNQ2UC+0Wju2zs86v6mLmoG8CCVnTE2nyUBiy5IDZ464nBhdGYkkuUEYzrg==} peerDependencies: @@ -8835,6 +13381,16 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true + /@storybook/react-dom-shim@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-heobG4IovYAD9fo7qmUHylCSQjDd1eXDCOaTiy+XVKobHAJgkz1gKqbaFSP6KLkPE4cKyScku2K9mY0tcKIhMw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + /@storybook/react-vite@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5)(vite@4.2.1): resolution: {integrity: sha512-UqPAu/9FC7m3jcTcw6MgXzi9BTkAxKpr+t0lNgccyczvMRdWf5UNXPm9rdCEb7wmQx22DzwVmGPe+PFgVOIMsg==} engines: {node: '>=16'} @@ -8853,7 +13409,7 @@ packages: react: 18.2.0 react-docgen: 6.0.0-alpha.3 react-dom: 18.2.0(react@18.2.0) - vite: 4.2.1(@types/node@18.15.11) + vite: 4.2.1(@types/node@18.15.10) transitivePeerDependencies: - '@preact/preset-vite' - '@storybook/mdx1-csf' @@ -8862,6 +13418,33 @@ packages: - vite-plugin-glimmerx dev: true + /@storybook/react-vite@7.0.26(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5)(vite@4.4.0): + resolution: {integrity: sha512-yDkZAvlJ9RcXSuGZy8NdDhI394P7CRme7x6VtpgCi+iPaVW9A5laK7zOe1ewYnAcbKH6g7EJWQWDz2+PqAGiFw==} + engines: {node: '>=16'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + vite: ^3.0.0 || ^4.0.0 + dependencies: + '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@4.9.5)(vite@4.4.0) + '@rollup/pluginutils': 4.2.1 + '@storybook/builder-vite': 7.0.26(typescript@4.9.5)(vite@4.4.0) + '@storybook/react': 7.0.26(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5) + '@vitejs/plugin-react': 3.1.0(vite@4.4.0) + ast-types: 0.14.2 + magic-string: 0.27.0 + react: 18.2.0 + react-docgen: 6.0.0-alpha.3 + react-dom: 18.2.0(react@18.2.0) + vite: 4.4.0(@types/node@20.4.1)(less@4.1.3) + transitivePeerDependencies: + - '@preact/preset-vite' + - encoding + - supports-color + - typescript + - vite-plugin-glimmerx + dev: true + /@storybook/react@7.0.0-rc.10(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5): resolution: {integrity: sha512-AFN4Jzyp27jhe5qnbtG45yAHq/e3bkdL9p0di2t+AdHXyse8naud2pTzD/j3bbaU23R1iDVwVPQ+X7kAktu0EA==} engines: {node: '>=16.0.0'} @@ -8901,6 +13484,46 @@ packages: - supports-color dev: true + /@storybook/react@7.0.26(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5): + resolution: {integrity: sha512-+YK/1vF2Pd/PX7Ss5yPCIh9hee7iMVbu86gdjV9n9r6G244jQ7HLtdA01JKfq92/UgoysSWUjUECrxrUvcsh5w==} + engines: {node: '>=16.0.0'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/core-client': 7.0.26 + '@storybook/docs-tools': 7.0.26 + '@storybook/global': 5.0.0 + '@storybook/preview-api': 7.0.26 + '@storybook/react-dom-shim': 7.0.26(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.0.26 + '@types/escodegen': 0.0.6 + '@types/estree': 0.0.51 + '@types/node': 16.18.25 + acorn: 7.4.1 + acorn-jsx: 5.3.2(acorn@7.4.1) + acorn-walk: 7.2.0 + escodegen: 2.0.0 + html-tags: 3.3.1 + lodash: 4.17.21 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-element-to-jsx-string: 15.0.0(react-dom@18.2.0)(react@18.2.0) + ts-dedent: 2.2.0 + type-fest: 2.19.0 + typescript: 4.9.5 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@storybook/router@6.5.16(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-ZgeP8a5YV/iuKbv31V8DjPxlV4AzorRiR8OuSt/KqaiYXNXlOoQDz/qMmiNcrshrfLpmkzoq7fSo4T8lWo2UwQ==} peerDependencies: @@ -8908,7 +13531,7 @@ packages: react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: '@storybook/client-logger': 6.5.16 - core-js: 3.30.0 + core-js: 3.29.1 memoizerific: 1.11.3 qs: 6.11.2 react: 18.2.0 @@ -8929,12 +13552,38 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true + /@storybook/router@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-OfLittKxdahsgKsmQFoBX9q5tN/aqKMhhc/WbW88UPAQCUcEuazB0CwM+LI9YXY+n5L+vpLI4lGlgaqvPy4hHw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/client-logger': 7.0.26 + memoizerific: 1.11.3 + qs: 6.11.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + + /@storybook/router@7.0.27(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Onflm2mERipuYB3SR+0CFAZKPbDiLsJdgX09BP8bGrg7dVYwiGkL5dc9H/CP0KPxtC7kXT8x1Zc+yx0Y0kWiJw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/client-logger': 7.0.27 + memoizerific: 1.11.3 + qs: 6.11.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + /@storybook/semver@7.3.2: resolution: {integrity: sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==} engines: {node: '>=10'} hasBin: true dependencies: - core-js: 3.30.0 + core-js: 3.29.1 find-up: 4.1.0 dev: true @@ -8955,11 +13604,38 @@ packages: - supports-color dev: true + /@storybook/telemetry@7.0.26: + resolution: {integrity: sha512-TgvtARAiD+SNyWJJfQdPiWW5JQkbX1UdHKEqEhoJXsGDkEi2Zpb+1tdeP1qZ3Gfbd1K0/LDpXGcqLv6/deSEdg==} + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/core-common': 7.0.26 + chalk: 4.1.2 + detect-package-manager: 2.0.1 + fetch-retry: 5.0.4 + fs-extra: 11.1.1 + isomorphic-unfetch: 3.1.0 + nanoid: 3.3.6 + read-pkg-up: 7.0.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@storybook/testing-library@0.0.14-next.1: resolution: {integrity: sha512-1CAl40IKIhcPaCC4pYCG0b9IiYNymktfV/jTrX7ctquRY3akaN7f4A1SippVHosksft0M+rQTFE0ccfWW581fw==} dependencies: - '@storybook/client-logger': 7.0.18 - '@storybook/instrumenter': 7.0.18 + '@storybook/client-logger': 7.2.0-rc.0 + '@storybook/instrumenter': 7.2.0-rc.0 + '@testing-library/dom': 8.20.0 + '@testing-library/user-event': 13.5.0(@testing-library/dom@8.20.0) + ts-dedent: 2.2.0 + dev: true + + /@storybook/testing-library@0.0.14-next.2: + resolution: {integrity: sha512-i/SLSGm0o978ELok/SB4Qg1sZ3zr+KuuCkzyFqcCD0r/yf+bG35aQGkFqqxfSAdDxuQom0NO02FE+qys5Eapdg==} + dependencies: + '@storybook/client-logger': 7.0.26 + '@storybook/instrumenter': 7.0.26 '@testing-library/dom': 8.20.0 '@testing-library/user-event': 13.5.0(@testing-library/dom@8.20.0) ts-dedent: 2.2.0 @@ -8972,7 +13648,7 @@ packages: react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: '@storybook/client-logger': 6.5.16 - core-js: 3.30.0 + core-js: 3.29.1 memoizerific: 1.11.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -8993,25 +13669,71 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true + /@storybook/theming@7.0.26(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-7hxpT2yq+xZonSsEZHOF+HDHx6GE0qlys3EQ63K9XCJ8VeBnq9M5zHvMK9iXl90093ufxpvWsfDWgtja2zvmTw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0) + '@storybook/client-logger': 7.0.26 + '@storybook/global': 5.0.0 + memoizerific: 1.11.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + + /@storybook/theming@7.0.27(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-l2Lc8xX8QXQO8c9gpzdUUJ+0YqLoh8w74I7lzxiife0TzEQrhWD9aRJAVimm8Vzfq5x3CNeJNFHc5PcG8ypQig==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0) + '@storybook/client-logger': 7.0.27 + '@storybook/global': 5.0.0 + memoizerific: 1.11.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + /@storybook/types@7.0.0-rc.10: resolution: {integrity: sha512-aKyz4eJaWsywqX8kL2syTL8jGZFildIw/Z5o5S13KZwR+Vdijss2sNoDIAUK5XgSi9vxG/Jd13CS5zTEBaSpMA==} dependencies: '@storybook/channels': 7.0.0-rc.10 - '@types/babel__core': 7.20.0 + '@types/babel__core': 7.20.1 '@types/express': 4.17.9 - file-system-cache: 2.1.1 + file-system-cache: 2.3.0 dev: true - /@storybook/types@7.0.18: - resolution: {integrity: sha512-qPop2CbvmX42/BX29YT9jIzW2TlMcMjAE+KCpcKLBiD1oT5DJ1fhMzpe6RW9HkMegkBxjWx54iamN4oHM/pwcQ==} + /@storybook/types@7.0.26: + resolution: {integrity: sha512-5RBi6agtDglNXdffmw4+Fyv2dUdlIdeOdUj0O5+JRYajTxfHdurZd9r/42z4OstN+ORDkLA/svt8Q9JyRpIb6Q==} dependencies: - '@storybook/channels': 7.0.18 - '@types/babel__core': 7.20.0 + '@storybook/channels': 7.0.26 + '@types/babel__core': 7.20.1 + '@types/express': 4.17.9 + file-system-cache: 2.3.0 + dev: true + + /@storybook/types@7.0.27: + resolution: {integrity: sha512-pmJuIm+kGaZiDMyl2i5KFS9iGWrpW1jVcp9OMtHeK20LBzY5Hxq/JMc3E+fbVNkAX2hVlVGbbVUNPTvd9AjbrA==} + dependencies: + '@storybook/channels': 7.0.27 + '@types/babel__core': 7.20.1 + '@types/express': 4.17.9 + file-system-cache: 2.3.0 + dev: true + + /@storybook/types@7.2.0-rc.0: + resolution: {integrity: sha512-Vklvf0XWZkEZtvHI9+JkrC/nWBeQ06QtKmrAFfzQarxzU1oeR9n1sNOdYTFHlmwhnsDZ8Die//A5rUKVJFrJmQ==} + dependencies: + '@storybook/channels': 7.2.0-rc.0 + '@types/babel__core': 7.20.1 '@types/express': 4.17.9 - file-system-cache: 2.1.1 + file-system-cache: 2.3.0 dev: true - /@sveltejs/vite-plugin-svelte@1.0.8(svelte@3.58.0)(vite@4.0.3): + /@sveltejs/vite-plugin-svelte@1.0.8(svelte@3.57.0)(vite@4.0.3): resolution: {integrity: sha512-1xkVTB4pm6zuign858FzVYE9Fdw9MQBOlxrdd85STV0NvTDmcofcRpcrK+zcIyT8SZ2dseHLu8hvDwzssF6RfA==} engines: {node: ^14.18.0 || >= 16} peerDependencies: @@ -9027,15 +13749,15 @@ packages: deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.26.7 - svelte: 3.58.0 - svelte-hmr: 0.15.1(svelte@3.58.0) - vite: 4.0.3(@types/node@18.15.11) + svelte: 3.57.0 + svelte-hmr: 0.15.1(svelte@3.57.0) + vite: 4.0.3(@types/node@18.15.10) transitivePeerDependencies: - supports-color dev: true - /@sveltejs/vite-plugin-svelte@2.0.4(svelte@3.58.0)(vite@4.2.1): - resolution: {integrity: sha512-pjqhW00KwK2uzDGEr+yJBwut+D+4XfJO/+bHHdHzPRXn9+1Jeq5JcFHyrUiYaXgHtyhX0RsllCTm4ssAx4ZY7Q==} + /@sveltejs/vite-plugin-svelte@2.0.3(svelte@3.57.0)(vite@4.2.1): + resolution: {integrity: sha512-o+cguBFdwIGtRbNkYOyqTM7KvRUffxh5bfK4oJsWKG2obu+v/cbpT03tJrGl58C7tRXo/aEC0/axN5FVHBj0nA==} engines: {node: ^14.18.0 || >= 16} peerDependencies: svelte: ^3.54.0 @@ -9044,17 +13766,17 @@ packages: debug: 4.3.4(supports-color@8.1.1) deepmerge: 4.3.1 kleur: 4.1.5 - magic-string: 0.30.0 - svelte: 3.58.0 - svelte-hmr: 0.15.1(svelte@3.58.0) - vite: 4.2.1(@types/node@18.15.11) + magic-string: 0.29.0 + svelte: 3.57.0 + svelte-hmr: 0.15.1(svelte@3.57.0) + vite: 4.2.1(@types/node@20.3.2) vitefu: 0.2.4(vite@4.2.1) transitivePeerDependencies: - supports-color dev: true - /@swc/core-darwin-arm64@1.3.49: - resolution: {integrity: sha512-g7aIfXh6uPHmhLXdjXQq5t3HAyS/EdvujasW1DIS5k8UqOBaSoCcSGtLIjzcLv3KujqNfYcm118E+12H0nY6fQ==} + /@swc/core-darwin-arm64@1.3.42: + resolution: {integrity: sha512-hM6RrZFyoCM9mX3cj/zM5oXwhAqjUdOCLXJx7KTQps7NIkv/Qjvobgvyf2gAb89j3ARNo9NdIoLjTjJ6oALtiA==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] @@ -9062,8 +13784,8 @@ packages: dev: true optional: true - /@swc/core-darwin-x64@1.3.49: - resolution: {integrity: sha512-eSIxVX0YDw40Bre5sAx2BV3DzdIGzmQvCf2yiBvLqiiL6GC0mmuDeWbUCAzdUX6fJ6FUVEBMUVqNOc9oJ2/d5w==} + /@swc/core-darwin-x64@1.3.42: + resolution: {integrity: sha512-bjsWtHMb6wJK1+RGlBs2USvgZ0txlMk11y0qBLKo32gLKTqzUwRw0Fmfzuf6Ue2a/w//7eqMlPFEre4LvJajGw==} engines: {node: '>=10'} cpu: [x64] os: [darwin] @@ -9071,8 +13793,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm-gnueabihf@1.3.49: - resolution: {integrity: sha512-8mj3IcRVr/OJY0mVITz6Z5osNAMJK5GiKDaZ+3QejPLbl6aiu4sH4GmTHDRN14RnaVXOpecsGcUoQmNoNa3u3w==} + /@swc/core-linux-arm-gnueabihf@1.3.42: + resolution: {integrity: sha512-Oe0ggMz3MyqXNfeVmY+bBTL0hFSNY3bx8dhcqsh4vXk/ZVGse94QoC4dd92LuPHmKT0x6nsUzB86x2jU9QHW5g==} engines: {node: '>=10'} cpu: [arm] os: [linux] @@ -9080,8 +13802,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm64-gnu@1.3.49: - resolution: {integrity: sha512-Rmg9xw6tmpOpf6GKKjpHQGmjfHzqSths5ebI2ahrHlhekzZF2HYmPkVw4bHda8Bja6mbaw8FVBgBHjPU8mMeDA==} + /@swc/core-linux-arm64-gnu@1.3.42: + resolution: {integrity: sha512-ZJsa8NIW1RLmmHGTJCbM7OPSbBZ9rOMrLqDtUOGrT0uoJXZnnQqolflamB5wviW0X6h3Z3/PSTNGNDCJ3u3Lqg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -9089,8 +13811,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm64-musl@1.3.49: - resolution: {integrity: sha512-nlKPYMogAI3Aak6Mlkag8/2AlHAZ/DpH7RjhfMazsaGhD/sQOmYdyY9Al69ejpa419YJuREeeeLoojFlSsd30g==} + /@swc/core-linux-arm64-musl@1.3.42: + resolution: {integrity: sha512-YpZwlFAfOp5vkm/uVUJX1O7N3yJDO1fDQRWqsOPPNyIJkI2ydlRQtgN6ZylC159Qv+TimfXnGTlNr7o3iBAqjg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -9098,8 +13820,8 @@ packages: dev: true optional: true - /@swc/core-linux-x64-gnu@1.3.49: - resolution: {integrity: sha512-QOyeJQ6NVi73SJcizbwvIZTiGA/N+BxX9liRrvibumaQmRh8fWjJiLNsv3ODSHeuonak7E8Bf7a7NnSTyu48Mw==} + /@swc/core-linux-x64-gnu@1.3.42: + resolution: {integrity: sha512-0ccpKnsZbyHBzaQFdP8U9i29nvOfKitm6oJfdJzlqsY/jCqwvD8kv2CAKSK8WhJz//ExI2LqNrDI0yazx5j7+A==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -9107,8 +13829,8 @@ packages: dev: true optional: true - /@swc/core-linux-x64-musl@1.3.49: - resolution: {integrity: sha512-WlDMz+SOpYC9O/ZBUw1oiyWI7HyUCMlf/HS8Fy/kRI3eGoGCUxVTCJ1mP57GdQr4Wg32Y/ZpO2KSNQFWnT8mAw==} + /@swc/core-linux-x64-musl@1.3.42: + resolution: {integrity: sha512-7eckRRuTZ6+3K21uyfXXgc2ZCg0mSWRRNwNT3wap2bYkKPeqTgb8pm8xYSZNEiMuDonHEat6XCCV36lFY6kOdQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -9116,8 +13838,8 @@ packages: dev: true optional: true - /@swc/core-win32-arm64-msvc@1.3.49: - resolution: {integrity: sha512-41LZOeI94Za3twib8KOIjnHYAZ+nkBFmboaREsFR1760S7jiMVywqWX8nFZvn/CXj15Fjjgdgyuig+zMREwXwQ==} + /@swc/core-win32-arm64-msvc@1.3.42: + resolution: {integrity: sha512-t27dJkdw0GWANdN4TV0lY/V5vTYSx5SRjyzzZolep358ueCGuN1XFf1R0JcCbd1ojosnkQg2L7A7991UjXingg==} engines: {node: '>=10'} cpu: [arm64] os: [win32] @@ -9125,8 +13847,8 @@ packages: dev: true optional: true - /@swc/core-win32-ia32-msvc@1.3.49: - resolution: {integrity: sha512-IdqLPoMKssyAoOCZdNXmnAd6/uyx+Hb9KSfZUHepZaNfwMy6J5XXrOsbYs3v53FH8MtekUUdV+mMX4me9bcv9w==} + /@swc/core-win32-ia32-msvc@1.3.42: + resolution: {integrity: sha512-xfpc/Zt/aMILX4IX0e3loZaFyrae37u3MJCv1gJxgqrpeLi7efIQr3AmERkTK3mxTO6R5urSliWw2W3FyZ7D3Q==} engines: {node: '>=10'} cpu: [ia32] os: [win32] @@ -9134,8 +13856,8 @@ packages: dev: true optional: true - /@swc/core-win32-x64-msvc@1.3.49: - resolution: {integrity: sha512-7Fqjo5pS3uIohhSbYSaR0+e/bJdxmQb4oG97FIh5qvlCCGQaQ9UiaEeYy4uK0Ad+Menum1IXCAEiG7RHcl6Eyw==} + /@swc/core-win32-x64-msvc@1.3.42: + resolution: {integrity: sha512-ra2K4Tu++EJLPhzZ6L8hWUsk94TdK/2UKhL9dzCBhtzKUixsGCEqhtqH1zISXNvW8qaVLFIMUP37ULe80/IJaA==} engines: {node: '>=10'} cpu: [x64] os: [win32] @@ -9143,26 +13865,21 @@ packages: dev: true optional: true - /@swc/core@1.3.49: - resolution: {integrity: sha512-br44ZHOfE9YyRGcORSLkHFQHTvhwRcaithBJ1Q5y5iMGpLbH0Wai3GN49L60RvmGwxNJfWzT+E7+rNNR7ewKgA==} + /@swc/core@1.3.42: + resolution: {integrity: sha512-nVFUd5+7tGniM2cT3LXaqnu3735Cu4az8A9gAKK+8sdpASI52SWuqfDBmjFCK9xG90MiVDVp2PTZr0BWqCIzpw==} engines: {node: '>=10'} requiresBuild: true - peerDependencies: - '@swc/helpers': ^0.5.0 - peerDependenciesMeta: - '@swc/helpers': - optional: true optionalDependencies: - '@swc/core-darwin-arm64': 1.3.49 - '@swc/core-darwin-x64': 1.3.49 - '@swc/core-linux-arm-gnueabihf': 1.3.49 - '@swc/core-linux-arm64-gnu': 1.3.49 - '@swc/core-linux-arm64-musl': 1.3.49 - '@swc/core-linux-x64-gnu': 1.3.49 - '@swc/core-linux-x64-musl': 1.3.49 - '@swc/core-win32-arm64-msvc': 1.3.49 - '@swc/core-win32-ia32-msvc': 1.3.49 - '@swc/core-win32-x64-msvc': 1.3.49 + '@swc/core-darwin-arm64': 1.3.42 + '@swc/core-darwin-x64': 1.3.42 + '@swc/core-linux-arm-gnueabihf': 1.3.42 + '@swc/core-linux-arm64-gnu': 1.3.42 + '@swc/core-linux-arm64-musl': 1.3.42 + '@swc/core-linux-x64-gnu': 1.3.42 + '@swc/core-linux-x64-musl': 1.3.42 + '@swc/core-win32-arm64-msvc': 1.3.42 + '@swc/core-win32-ia32-msvc': 1.3.42 + '@swc/core-win32-x64-msvc': 1.3.42 dev: true /@t3-oss/env-core@0.3.1(typescript@4.9.5)(zod@3.21.4): @@ -9175,33 +13892,49 @@ packages: zod: 3.21.4 dev: false - /@tanstack/match-sorter-utils@8.8.4: - resolution: {integrity: sha512-rKH8LjZiszWEvmi01NR72QWZ8m4xmXre0OOwlRGnjU01Eqz/QnN+cqpty2PJ0efHblq09+KilvyR7lsbzmXVEw==} + /@tailwindcss/line-clamp@0.4.4(tailwindcss@3.2.7): + resolution: {integrity: sha512-5U6SY5z8N42VtrCrKlsTAA35gy2VSyYtHWCsg1H87NU1SXnEfekTVlrga9fzUDrrHcGi2Lb5KenUWb4lRQT5/g==} + peerDependencies: + tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1' + dependencies: + tailwindcss: 3.2.7(postcss@8.4.24)(ts-node@10.9.1) + dev: false + + /@tailwindcss/line-clamp@0.4.4(tailwindcss@3.3.2): + resolution: {integrity: sha512-5U6SY5z8N42VtrCrKlsTAA35gy2VSyYtHWCsg1H87NU1SXnEfekTVlrga9fzUDrrHcGi2Lb5KenUWb4lRQT5/g==} + peerDependencies: + tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1' + dependencies: + tailwindcss: 3.3.2(ts-node@10.9.1) + dev: true + + /@tanstack/match-sorter-utils@8.7.6: + resolution: {integrity: sha512-2AMpRiA6QivHOUiBpQAVxjiHAA68Ei23ZUMNaRJrN6omWiSFLoYrxGcT6BXtuzp0Jw4h6HZCmGGIM/gbwebO2A==} engines: {node: '>=12'} dependencies: remove-accents: 0.4.2 dev: true - /@tanstack/query-core@4.29.1: - resolution: {integrity: sha512-vkPewLEG8ua0efo3SsVT0BcBtkq5RZX8oPhDAyKL+k/rdOYSQTEocfGEXSaBwIwsXeOGBUpfKqI+UmHvNqdWXg==} + /@tanstack/query-core@4.27.0: + resolution: {integrity: sha512-sm+QncWaPmM73IPwFlmWSKPqjdTXZeFf/7aEmWh00z7yl2FjqophPt0dE1EHW9P1giMC5rMviv7OUbSDmWzXXA==} - /@tanstack/react-query-devtools@4.22.0(@tanstack/react-query@4.29.1)(react-dom@18.2.0)(react@18.2.0): + /@tanstack/react-query-devtools@4.22.0(@tanstack/react-query@4.28.0)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-YeYFBnfqvb+ZlA0IiJqiHNNSzepNhI1p2o9i8NlhQli9+Zrn230M47OBaBUs8qr3DD1dC2zGB1Dis50Ktz8gAA==} peerDependencies: '@tanstack/react-query': 4.22.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@tanstack/match-sorter-utils': 8.8.4 - '@tanstack/react-query': 4.29.1(react-dom@18.2.0)(react@18.2.0) + '@tanstack/match-sorter-utils': 8.7.6 + '@tanstack/react-query': 4.28.0(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) superjson: 1.12.2 use-sync-external-store: 1.2.0(react@18.2.0) dev: true - /@tanstack/react-query@4.29.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-/crv1v+OeuGG6EOvaQmyeo9GCKtH4jbmuhZkvk9ulufRiHcTr/A9+YP9GevEAZzUTdzXMwenpTbyxBGvG2xXvw==} + /@tanstack/react-query@4.28.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-8cGBV5300RHlvYdS4ea+G1JcZIt5CIuprXYFnsWggkmGoC0b5JaqG0fIX3qwDL9PTNkKvG76NGThIWbpXivMrQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -9212,18 +13945,35 @@ packages: react-native: optional: true dependencies: - '@tanstack/query-core': 4.29.1 + '@tanstack/query-core': 4.27.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) use-sync-external-store: 1.2.0(react@18.2.0) - /@tanstack/svelte-query@4.29.1(svelte@3.58.0): - resolution: {integrity: sha512-V+Ueq8fWHx6W9kBlVfrdFJbSUodJknOdgGKV90lNYM1WFeoOzqihXDOgeJHUaT+JKs96sRBQJAVN7NIbBR0iMw==} + /@tanstack/react-table@8.9.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Irvw4wqVF9hhuYzmNrlae4IKdlmgSyoRWnApSLebvYzqHoi5tEsYzBj6YPd0hX78aB/L+4w/jgK2eBQVpGfThQ==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16' + react-dom: '>=16' + dependencies: + '@tanstack/table-core': 8.9.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@tanstack/svelte-query@4.27.0(svelte@3.57.0): + resolution: {integrity: sha512-FmrviPCoWbgBOLgG4uOG4EHi80DDzvD4XK+0nQBTLb4c2cKyN7c98MLgG58yHbdFEPQyhhcJlqagXP18vWWS9w==} peerDependencies: svelte: ^3.54.0 dependencies: - '@tanstack/query-core': 4.29.1 - svelte: 3.58.0 + '@tanstack/query-core': 4.27.0 + svelte: 3.57.0 + dev: false + + /@tanstack/table-core@8.9.2: + resolution: {integrity: sha512-ajc0OF+karBAdaSz7OK09rCoAHB1XI1+wEhu+tDNMPc+XcO+dTlXXN/Vc0a8vym4kElvEjXEDd9c8Zfgt4bekA==} + engines: {node: '>=12'} dev: false /@tensorflow/tfjs-core@1.7.0: @@ -9242,7 +13992,7 @@ packages: resolution: {integrity: sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA==} engines: {node: '>=12'} dependencies: - '@babel/code-frame': 7.21.4 + '@babel/code-frame': 7.22.5 '@babel/runtime': 7.21.0 '@types/aria-query': 5.0.1 aria-query: 5.1.3 @@ -9281,14 +14031,14 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@testing-library/svelte@3.2.2(svelte@3.58.0): + /@testing-library/svelte@3.2.2(svelte@3.57.0): resolution: {integrity: sha512-IKwZgqbekC3LpoRhSwhd0JswRGxKdAGkf39UiDXTywK61YyLXbCYoR831e/UUC6EeNW4hiHPY+2WuovxOgI5sw==} engines: {node: '>= 10'} peerDependencies: svelte: 3.x dependencies: '@testing-library/dom': 8.20.0 - svelte: 3.58.0 + svelte: 3.57.0 dev: true /@testing-library/user-event@13.5.0(@testing-library/dom@8.20.0): @@ -9314,7 +14064,7 @@ packages: /@ts-morph/common@0.18.1: resolution: {integrity: sha512-RVE+zSRICWRsfrkAw5qCAK+4ZH9kwEFv5h0+/YeHTLieWP7F4wWq4JsKFuNWG+fYh/KF+8rAtgdj5zb2mm+DVA==} dependencies: - fast-glob: 3.2.12 + fast-glob: 3.3.0 minimatch: 5.1.6 mkdirp: 1.0.4 path-browserify: 1.0.1 @@ -9346,6 +14096,12 @@ packages: '@types/estree': 1.0.0 dev: false + /@types/archiver@5.3.2: + resolution: {integrity: sha512-IctHreBuWE5dvBDz/0WeKtyVKVRs4h75IblxOACL92wU66v+HGAfEYAOyXkOFphvRJMhuXdI9huDXpX0FC6lCw==} + dependencies: + '@types/readdir-glob': 1.1.1 + dev: true + /@types/argparse@1.0.38: resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} dev: true @@ -9354,11 +14110,30 @@ packages: resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==} dev: true + /@types/axios@0.14.0: + resolution: {integrity: sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ==} + deprecated: This is a stub types definition for axios (https://github.com/mzabriskie/axios). axios provides its own type definitions, so you don't need @types/axios installed! + dependencies: + axios: 1.4.0 + transitivePeerDependencies: + - debug + dev: true + /@types/babel__core@7.20.0: resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==} dependencies: - '@babel/parser': 7.21.4 - '@babel/types': 7.21.4 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 + '@types/babel__generator': 7.6.4 + '@types/babel__template': 7.4.1 + '@types/babel__traverse': 7.18.3 + dev: true + + /@types/babel__core@7.20.1: + resolution: {integrity: sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==} + dependencies: + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.18.3 @@ -9366,46 +14141,57 @@ packages: /@types/babel__generator@7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.21.3 /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.21.4 - '@babel/types': 7.21.4 + '@babel/parser': 7.21.3 + '@babel/types': 7.21.3 /@types/babel__traverse@7.18.3: resolution: {integrity: sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.21.3 /@types/bcrypt@5.0.0: resolution: {integrity: sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 dev: true /@types/body-parser@1.19.2: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 18.15.11 + '@types/node': 18.15.10 dev: true /@types/chai-subset@1.3.3: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} dependencies: - '@types/chai': 4.3.4 + '@types/chai': 4.3.5 dev: true /@types/chai@4.3.4: resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} dev: true + /@types/chai@4.3.5: + resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==} + dev: true + + /@types/classnames@2.3.1: + resolution: {integrity: sha512-zeOWb0JGBoVmlQoznvqXbE0tEC/HONsnoUNH19Hc96NFsTAwTXbTqb8FMYkru1F/iqp7a18Ws3nWJvtA1sHD1A==} + deprecated: This is a stub types definition. classnames provides its own type definitions, so you do not need this installed. + dependencies: + classnames: 2.3.2 + dev: true + /@types/connect@3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 dev: true /@types/cookie-session@2.0.44: @@ -9422,6 +14208,48 @@ packages: resolution: {integrity: sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==} dev: true + /@types/d3-array@3.0.5: + resolution: {integrity: sha512-Qk7fpJ6qFp+26VeQ47WY0mkwXaiq8+76RJcncDEfMc2ocRzXLO67bLFRNI4OX1aGBoPzsM5Y2T+/m1pldOgD+A==} + dev: false + + /@types/d3-color@3.1.0: + resolution: {integrity: sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==} + dev: false + + /@types/d3-ease@3.0.0: + resolution: {integrity: sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==} + dev: false + + /@types/d3-interpolate@3.0.1: + resolution: {integrity: sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==} + dependencies: + '@types/d3-color': 3.1.0 + dev: false + + /@types/d3-path@3.0.0: + resolution: {integrity: sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==} + dev: false + + /@types/d3-scale@4.0.3: + resolution: {integrity: sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==} + dependencies: + '@types/d3-time': 3.0.0 + dev: false + + /@types/d3-shape@3.1.1: + resolution: {integrity: sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==} + dependencies: + '@types/d3-path': 3.0.0 + dev: false + + /@types/d3-time@3.0.0: + resolution: {integrity: sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==} + dev: false + + /@types/d3-timer@3.0.0: + resolution: {integrity: sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==} + dev: false + /@types/debug@4.1.7: resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} dependencies: @@ -9431,6 +14259,20 @@ packages: resolution: {integrity: sha512-xxgAGA2SAU4111QefXPSp5eGbDm/hW6zhvYl9IeEPZEry9F4d66QAHm5qpUXjb6IsevZV/7emAEx5MhP6O192g==} dev: true + /@types/docker-modem@3.0.2: + resolution: {integrity: sha512-qC7prjoEYR2QEe6SmCVfB1x3rfcQtUr1n4x89+3e0wSTMQ/KYCyf+/RAA9n2tllkkNc6//JMUZePdFRiGIWfaQ==} + dependencies: + '@types/node': 18.15.10 + '@types/ssh2': 1.11.11 + dev: true + + /@types/dockerode@3.3.19: + resolution: {integrity: sha512-7CC5yIpQi+bHXwDK43b/deYXteP3Lem9gdocVVHJPSRJJLMfbiOchQV3rDmAPkMw+n3GIVj7m1six3JW+VcwwA==} + dependencies: + '@types/docker-modem': 3.0.2 + '@types/node': 18.15.10 + dev: true + /@types/doctrine@0.0.3: resolution: {integrity: sha512-w5jZ0ee+HaPOaX25X2/2oGR/7rgAQSYII7X7pp0m9KgBfMP7uKfMfTvcpl5Dj+eDBbpxKGiqE+flqDr6XTd2RA==} dev: true @@ -9446,22 +14288,16 @@ packages: /@types/eslint-scope@3.7.4: resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} dependencies: - '@types/eslint': 8.37.0 - '@types/estree': 1.0.0 - dev: true - - /@types/eslint@8.37.0: - resolution: {integrity: sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==} - dependencies: + '@types/eslint': 8.21.3 '@types/estree': 1.0.0 - '@types/json-schema': 7.0.11 dev: true - /@types/estree-jsx@0.0.1: - resolution: {integrity: sha512-gcLAYiMfQklDCPjQegGn0TBAn9it05ISEsEhlKQUddIk7o2XDokOcTN7HBO8tznM0D9dGezvHEfRZBfZf6me0A==} + /@types/eslint@8.21.3: + resolution: {integrity: sha512-fa7GkppZVEByMWGbTtE5MbmXWJTVbrjjaS8K6uQj+XtuuUv1fsuPAxhygfqLmsb/Ufb3CV8deFCpiMfAgi00Sw==} dependencies: '@types/estree': 1.0.0 - dev: false + '@types/json-schema': 7.0.11 + dev: true /@types/estree-jsx@1.0.0: resolution: {integrity: sha512-3qvGd0z8F2ENTGr/GG1yViqfiKmRfrXVx5sJyHGFu3z7m5g5utCQtGp/g29JnjflhtQJBv1WDQukHiT58xPcYQ==} @@ -9483,7 +14319,7 @@ packages: /@types/express-serve-static-core@4.17.33: resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 dev: true @@ -9505,27 +14341,31 @@ packages: resolution: {integrity: sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==} dependencies: '@types/jsonfile': 6.1.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 + dev: true + + /@types/geojson@7946.0.10: + resolution: {integrity: sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==} dev: true /@types/glob@7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 18.15.11 + '@types/node': 18.15.10 dev: true /@types/glob@8.1.0: resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 18.15.11 + '@types/node': 18.15.10 dev: true /@types/graceful-fs@4.1.6: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 dev: true /@types/hast@2.3.4: @@ -9535,6 +14375,7 @@ packages: /@types/html-escaper@3.0.0: resolution: {integrity: sha512-OcJcvP3Yk8mjYwf/IdXZtTE1tb/u0WF0qa29ER07ZHCYUBZXSN29Z1mBS+/96+kNMGTFUAbSz9X+pHmHpZrTCw==} + dev: true /@types/is-ci@3.0.0: resolution: {integrity: sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ==} @@ -9569,11 +14410,8 @@ packages: pretty-format: 26.6.2 dev: true - /@types/jest@29.5.0: - resolution: {integrity: sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==} - dependencies: - expect: 29.5.0 - pretty-format: 29.5.0 + /@types/jmespath@0.15.0: + resolution: {integrity: sha512-uaht4XcYSq5ZrPriQW8C+g5DhptewRd1E84ph7L167sCyzLObz+U3JTpmYq/CNkvjNsz2mtyQoHPNEYQYTzWmg==} dev: true /@types/js-levenshtein@1.1.1: @@ -9601,26 +14439,32 @@ packages: /@types/jsonfile@6.1.1: resolution: {integrity: sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 dev: true /@types/jsonwebtoken@9.0.1: resolution: {integrity: sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 /@types/keygrip@1.0.2: resolution: {integrity: sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==} dev: true + /@types/leaflet@1.9.3: + resolution: {integrity: sha512-Caa1lYOgKVqDkDZVWkto2Z5JtVo09spEaUt2S69LiugbBpoqQu92HYFMGUbYezZbnBkyOxMNPXHSgRrRY5UyIA==} + dependencies: + '@types/geojson': 7946.0.10 + dev: true + /@types/lodash.keyby@4.6.7: resolution: {integrity: sha512-3qSHbbxLfXlARIKsaijZPb/5ZHDajv1vRg7OlAOuBNwN29PESS3sEOPfG1s2kzSuZnKtxtXZd1lvGj5JasN91w==} dependencies: - '@types/lodash': 4.14.192 + '@types/lodash': 4.14.191 dev: true - /@types/lodash@4.14.192: - resolution: {integrity: sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==} + /@types/lodash@4.14.191: + resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==} dev: true /@types/mdast@3.0.11: @@ -9628,8 +14472,8 @@ packages: dependencies: '@types/unist': 2.0.6 - /@types/mdx@2.0.4: - resolution: {integrity: sha512-qCYrNdpKwN6YO6FVnx+ulfqifKlE3lQGsNhvDaW9Oxzyob/cRLBJWow8GHBBD4NxQ7BVvtsATgLsX0vZAWmtrg==} + /@types/mdx@2.0.3: + resolution: {integrity: sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ==} /@types/mime-types@2.1.1: resolution: {integrity: sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==} @@ -9647,6 +14491,13 @@ packages: resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} dev: true + /@types/moment@2.13.0: + resolution: {integrity: sha512-DyuyYGpV6r+4Z1bUznLi/Y7HpGn4iQ4IVcGn8zrr1P4KotKLdH0sbK1TFR6RGyX6B+G8u83wCzL+bpawKU/hdQ==} + deprecated: This is a stub types definition for Moment (https://github.com/moment/moment). Moment provides its own type definitions, so you don't need @types/moment installed! + dependencies: + moment: 2.29.4 + dev: true + /@types/ms@0.7.31: resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} @@ -9675,7 +14526,14 @@ packages: /@types/node-fetch@2.6.3: resolution: {integrity: sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 + form-data: 3.0.1 + dev: true + + /@types/node-fetch@2.6.4: + resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==} + dependencies: + '@types/node': 18.15.10 form-data: 3.0.1 dev: true @@ -9687,8 +14545,18 @@ packages: resolution: {integrity: sha512-rUDO6s9Q/El1R1I21HG4qw/LstTHCPO/oQNAwI/4b2f9EWvMnqt4d3HJwPMawfZ3UvodB8516Yg+VAq54YM+eA==} dev: true - /@types/node@18.15.11: - resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==} + /@types/node@18.15.10: + resolution: {integrity: sha512-9avDaQJczATcXgfmMAW3MIWArOO7A+m90vuCFLr8AotWf8igO/mRoYukrk2cqZVtv38tHs33retzHEilM7FpeQ==} + + /@types/node@20.3.1: + resolution: {integrity: sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==} + + /@types/node@20.3.2: + resolution: {integrity: sha512-vOBLVQeCQfIcF/2Y7eKFTqrMnizK5lRNQ7ykML/5RuwVXVWxYkgwS7xbt4B6fKCUPgbSL5FSsjHQpaGQP/dQmw==} + + /@types/node@20.4.1: + resolution: {integrity: sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==} + dev: true /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -9773,29 +14641,49 @@ packages: resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} dev: true + /@types/react-custom-scrollbars@4.0.10: + resolution: {integrity: sha512-1T430E+usndUjymkXB8k/zGpWehggircq/QaQMuFLMJceccAcD9vcmbUXF1LjeVP/+P4wI/bad6BF1E+7IGlqA==} + dependencies: + '@types/react': 18.0.37 + dev: true + /@types/react-dom@18.0.11: resolution: {integrity: sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==} dependencies: - '@types/react': 17.0.58 + '@types/react': 18.0.37 + + /@types/react@17.0.45: + resolution: {integrity: sha512-YfhQ22Lah2e3CHPsb93tRwIGNiSwkuz1/blk4e6QrWS0jQzCSNbGLtOEYhPg02W0yGTTmpajp7dCTbBAMN3qsg==} + dependencies: + '@types/prop-types': 15.7.5 + '@types/scheduler': 0.16.3 + csstype: 3.1.1 + dev: false - /@types/react@17.0.58: - resolution: {integrity: sha512-c1GzVY97P0fGxwGxhYq989j4XwlcHQoto6wQISOC2v6wm3h0PORRWJFHlkRjfGsiG3y1609WdQ+J+tKxvrEd6A==} + /@types/react@18.0.29: + resolution: {integrity: sha512-wXHktgUABxplw1+UnljseDq4+uztQyp2tlWZRIxHlpchsCFqiYkvaDS8JR7eKOQm8wziTH/el5qL7D6gYNkYcw==} dependencies: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.3 - csstype: 3.1.2 + csstype: 3.1.1 - /@types/react@18.0.35: - resolution: {integrity: sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag==} + /@types/react@18.0.37: + resolution: {integrity: sha512-4yaZZtkRN3ZIQD3KSEwkfcik8s0SWV+82dlJot1AbGYHCzJkWP3ENBY6wYeDRmKZ6HkrgoGAmR2HqdwYGp6OEw==} dependencies: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.3 - csstype: 3.1.2 + csstype: 3.1.1 + + /@types/readdir-glob@1.1.1: + resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==} + dependencies: + '@types/node': 18.15.10 + dev: true /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 dev: true /@types/resolve@1.20.2: @@ -9806,7 +14694,7 @@ packages: resolution: {integrity: sha512-jn7qwGFmJHwUSphV8zZneO3GmtlgLsmhs/LQyVvQbIIa+fzGMUiHI4HXJZL3FT8MJmgXWbLGiVVY7ElvHq6vDA==} deprecated: This is a stub types definition. sass provides its own type definitions, so you do not need this installed. dependencies: - sass: 1.62.0 + sass: 1.60.0 dev: true /@types/scheduler@0.16.3: @@ -9828,13 +14716,32 @@ packages: resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} dependencies: '@types/mime': 3.0.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 dev: true /@types/set-cookie-parser@2.4.2: resolution: {integrity: sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 + + /@types/ssh2-streams@0.1.9: + resolution: {integrity: sha512-I2J9jKqfmvXLR5GomDiCoHrEJ58hAOmFrekfFqmCFd+A6gaEStvWnPykoWUwld1PNg4G5ag1LwdA+Lz1doRJqg==} + dependencies: + '@types/node': 18.15.10 + dev: true + + /@types/ssh2@0.5.52: + resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==} + dependencies: + '@types/node': 18.15.10 + '@types/ssh2-streams': 0.1.9 + dev: true + + /@types/ssh2@1.11.11: + resolution: {integrity: sha512-LdnE7UBpvHCgUznvn2fwLt2hkaENcKPFqOyXGkvyTLfxCXBN6roc1RmECNYuzzbHePzD3PaAov5rri9hehzx9Q==} + dependencies: + '@types/node': 18.15.10 + dev: true /@types/stack-utils@2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} @@ -9844,7 +14751,7 @@ packages: resolution: {integrity: sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==} dependencies: '@types/cookiejar': 2.1.2 - '@types/node': 18.15.11 + '@types/node': 18.15.10 dev: true /@types/supertest@2.0.11: @@ -9856,13 +14763,17 @@ packages: /@types/testing-library__jest-dom@5.14.5: resolution: {integrity: sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==} dependencies: - '@types/jest': 29.5.0 + '@types/jest': 26.0.24 dev: true /@types/tmp@0.2.3: resolution: {integrity: sha512-dDZH/tXzwjutnuk4UacGgFRwV+JSLaXL1ikvidfJprkb7L9Nx1njcRHHmi3Dsvt7pgqqTEeucQuOrWHPFgzVHA==} dev: false + /@types/triple-beam@1.3.2: + resolution: {integrity: sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==} + dev: false + /@types/unist@2.0.6: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} @@ -9881,6 +14792,12 @@ packages: resolution: {integrity: sha512-56/MAlX5WMsPVbOg7tAxnYvNYMMWr/QJiIp6BxVSW3JJXUVzzOn64qW8TzQyMSqSUFM2+PVI4aUHcHOzIz/1tg==} dev: true + /@types/ws@8.5.4: + resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} + dependencies: + '@types/node': 18.15.10 + dev: false + /@types/yargs-parser@21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} @@ -9896,14 +14813,14 @@ packages: '@types/yargs-parser': 21.0.0 dev: true - /@types/yargs@17.0.24: - resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} + /@types/yargs@17.0.23: + resolution: {integrity: sha512-yuogunc04OnzGQCrfHx+Kk883Q4X0aSwmYZhKjI21m+SVYzjIbrWl8dOOwSv5hf2Um2pdCOXWo9isteZTNXUZQ==} dependencies: '@types/yargs-parser': 21.0.0 dev: true - /@typescript-eslint/eslint-plugin@5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.22.0)(typescript@4.9.5): - resolution: {integrity: sha512-vxHvLhH0qgBd3/tW6/VccptSfc8FxPQIkmNTVLWcCOVqSBvqpnKkBTYrhcGlXfSnd78azwe+PsjYFj0X34/njA==} + /@typescript-eslint/eslint-plugin@5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.22.0)(typescript@4.9.5): + resolution: {integrity: sha512-ZNW37Ccl3oMZkzxrYDUX4o7cnuPgU+YrcaYXzsRtLB16I1FR5SHMqga3zGsaSliZADCWo2v8qHWqAYIj8nWCCg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -9913,25 +14830,53 @@ packages: typescript: optional: true dependencies: - '@eslint-community/regexpp': 4.5.0 - '@typescript-eslint/parser': 5.58.0(eslint@8.22.0)(typescript@4.9.5) - '@typescript-eslint/scope-manager': 5.58.0 - '@typescript-eslint/type-utils': 5.58.0(eslint@8.22.0)(typescript@4.9.5) - '@typescript-eslint/utils': 5.58.0(eslint@8.22.0)(typescript@4.9.5) - debug: 4.3.4 + '@eslint-community/regexpp': 4.4.1 + '@typescript-eslint/parser': 5.56.0(eslint@8.22.0)(typescript@4.9.5) + '@typescript-eslint/scope-manager': 5.56.0 + '@typescript-eslint/type-utils': 5.56.0(eslint@8.22.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.56.0(eslint@8.22.0)(typescript@4.9.5) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.22.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 - semver: 7.4.0 + semver: 7.3.8 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/eslint-plugin@5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.36.0)(typescript@4.9.5): + resolution: {integrity: sha512-ZNW37Ccl3oMZkzxrYDUX4o7cnuPgU+YrcaYXzsRtLB16I1FR5SHMqga3zGsaSliZADCWo2v8qHWqAYIj8nWCCg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.4.1 + '@typescript-eslint/parser': 5.56.0(eslint@8.36.0)(typescript@4.9.5) + '@typescript-eslint/scope-manager': 5.56.0 + '@typescript-eslint/type-utils': 5.56.0(eslint@8.36.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.56.0(eslint@8.36.0)(typescript@4.9.5) + debug: 4.3.4(supports-color@8.1.1) + eslint: 8.36.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + natural-compare-lite: 1.4.0 + semver: 7.3.8 tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/eslint-plugin@5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.38.0)(typescript@4.9.5): - resolution: {integrity: sha512-vxHvLhH0qgBd3/tW6/VccptSfc8FxPQIkmNTVLWcCOVqSBvqpnKkBTYrhcGlXfSnd78azwe+PsjYFj0X34/njA==} + /@typescript-eslint/eslint-plugin@5.59.0(@typescript-eslint/parser@5.59.0)(eslint@8.38.0)(typescript@5.0.2): + resolution: {integrity: sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -9941,24 +14886,52 @@ packages: typescript: optional: true dependencies: - '@eslint-community/regexpp': 4.5.0 - '@typescript-eslint/parser': 5.58.0(eslint@8.38.0)(typescript@4.9.5) - '@typescript-eslint/scope-manager': 5.58.0 - '@typescript-eslint/type-utils': 5.58.0(eslint@8.38.0)(typescript@4.9.5) - '@typescript-eslint/utils': 5.58.0(eslint@8.38.0)(typescript@4.9.5) + '@eslint-community/regexpp': 4.4.1 + '@typescript-eslint/parser': 5.59.0(eslint@8.38.0)(typescript@5.0.2) + '@typescript-eslint/scope-manager': 5.59.0 + '@typescript-eslint/type-utils': 5.59.0(eslint@8.38.0)(typescript@5.0.2) + '@typescript-eslint/utils': 5.59.0(eslint@8.38.0)(typescript@5.0.2) debug: 4.3.4(supports-color@8.1.1) eslint: 8.38.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 - semver: 7.4.0 + semver: 7.5.3 + tsutils: 3.21.0(typescript@5.0.2) + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/eslint-plugin@5.61.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@4.9.5): + resolution: {integrity: sha512-A5l/eUAug103qtkwccSCxn8ZRwT+7RXWkFECdA4Cvl1dOlDUgTpAOfSEElZn2uSUxhdDpnCdetrf0jvU4qrL+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.4.1 + '@typescript-eslint/parser': 5.61.0(eslint@8.44.0)(typescript@4.9.5) + '@typescript-eslint/scope-manager': 5.61.0 + '@typescript-eslint/type-utils': 5.61.0(eslint@8.44.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.61.0(eslint@8.44.0)(typescript@4.9.5) + debug: 4.3.4(supports-color@8.1.1) + eslint: 8.44.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare-lite: 1.4.0 + semver: 7.5.3 tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/experimental-utils@4.33.0(eslint@8.38.0)(typescript@4.9.5): + /@typescript-eslint/experimental-utils@4.33.0(eslint@8.36.0)(typescript@4.9.5): resolution: {integrity: sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -9968,16 +14941,16 @@ packages: '@typescript-eslint/scope-manager': 4.33.0 '@typescript-eslint/types': 4.33.0 '@typescript-eslint/typescript-estree': 4.33.0(typescript@4.9.5) - eslint: 8.38.0 + eslint: 8.36.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0(eslint@8.38.0) + eslint-utils: 3.0.0(eslint@8.36.0) transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/parser@5.58.0(eslint@8.22.0)(typescript@4.9.5): - resolution: {integrity: sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==} + /@typescript-eslint/parser@5.56.0(eslint@8.22.0)(typescript@4.9.5): + resolution: {integrity: sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -9986,18 +14959,38 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.58.0 - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/typescript-estree': 5.58.0(typescript@4.9.5) - debug: 4.3.4 + '@typescript-eslint/scope-manager': 5.56.0 + '@typescript-eslint/types': 5.56.0 + '@typescript-eslint/typescript-estree': 5.56.0(typescript@4.9.5) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.22.0 typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@5.58.0(eslint@8.38.0)(typescript@4.9.5): - resolution: {integrity: sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==} + /@typescript-eslint/parser@5.56.0(eslint@8.36.0)(typescript@4.9.5): + resolution: {integrity: sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.56.0 + '@typescript-eslint/types': 5.56.0 + '@typescript-eslint/typescript-estree': 5.56.0(typescript@4.9.5) + debug: 4.3.4(supports-color@8.1.1) + eslint: 8.36.0 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@5.59.0(eslint@8.38.0)(typescript@5.0.2): + resolution: {integrity: sha512-qK9TZ70eJtjojSUMrrEwA9ZDQ4N0e/AuoOIgXuNBorXYcBDk397D2r5MIe1B3cok/oCtdNC5j+lUUpVB+Dpb+w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -10006,11 +14999,31 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.58.0 - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/typescript-estree': 5.58.0(typescript@4.9.5) + '@typescript-eslint/scope-manager': 5.59.0 + '@typescript-eslint/types': 5.59.0 + '@typescript-eslint/typescript-estree': 5.59.0(typescript@5.0.2) debug: 4.3.4(supports-color@8.1.1) eslint: 8.38.0 + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@5.61.0(eslint@8.44.0)(typescript@4.9.5): + resolution: {integrity: sha512-yGr4Sgyh8uO6fSi9hw3jAFXNBHbCtKKFMdX2IkT3ZqpKmtAq3lHS4ixB/COFuAIJpwl9/AqF7j72ZDWYKmIfvg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.61.0 + '@typescript-eslint/types': 5.61.0 + '@typescript-eslint/typescript-estree': 5.61.0(typescript@4.9.5) + debug: 4.3.4(supports-color@8.1.1) + eslint: 8.44.0 typescript: 4.9.5 transitivePeerDependencies: - supports-color @@ -10024,16 +15037,32 @@ packages: '@typescript-eslint/visitor-keys': 4.33.0 dev: true - /@typescript-eslint/scope-manager@5.58.0: - resolution: {integrity: sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA==} + /@typescript-eslint/scope-manager@5.56.0: + resolution: {integrity: sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.56.0 + '@typescript-eslint/visitor-keys': 5.56.0 + dev: true + + /@typescript-eslint/scope-manager@5.59.0: + resolution: {integrity: sha512-tsoldKaMh7izN6BvkK6zRMINj4Z2d6gGhO2UsI8zGZY3XhLq1DndP3Ycjhi1JwdwPRwtLMW4EFPgpuKhbCGOvQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.59.0 + '@typescript-eslint/visitor-keys': 5.59.0 + dev: true + + /@typescript-eslint/scope-manager@5.61.0: + resolution: {integrity: sha512-W8VoMjoSg7f7nqAROEmTt6LoBpn81AegP7uKhhW5KzYlehs8VV0ZW0fIDVbcZRcaP3aPSW+JZFua+ysQN+m/Nw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/visitor-keys': 5.58.0 + '@typescript-eslint/types': 5.61.0 + '@typescript-eslint/visitor-keys': 5.61.0 dev: true - /@typescript-eslint/type-utils@5.58.0(eslint@8.22.0)(typescript@4.9.5): - resolution: {integrity: sha512-FF5vP/SKAFJ+LmR9PENql7fQVVgGDOS+dq3j+cKl9iW/9VuZC/8CFmzIP0DLKXfWKpRHawJiG70rVH+xZZbp8w==} + /@typescript-eslint/type-utils@5.56.0(eslint@8.22.0)(typescript@4.9.5): + resolution: {integrity: sha512-8WxgOgJjWRy6m4xg9KoSHPzBNZeQbGlQOH7l2QEhQID/+YseaFxg5J/DLwWSsi9Axj4e/cCiKx7PVzOq38tY4A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -10042,9 +15071,9 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.58.0(typescript@4.9.5) - '@typescript-eslint/utils': 5.58.0(eslint@8.22.0)(typescript@4.9.5) - debug: 4.3.4 + '@typescript-eslint/typescript-estree': 5.56.0(typescript@4.9.5) + '@typescript-eslint/utils': 5.56.0(eslint@8.22.0)(typescript@4.9.5) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.22.0 tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 @@ -10052,8 +15081,28 @@ packages: - supports-color dev: true - /@typescript-eslint/type-utils@5.58.0(eslint@8.38.0)(typescript@4.9.5): - resolution: {integrity: sha512-FF5vP/SKAFJ+LmR9PENql7fQVVgGDOS+dq3j+cKl9iW/9VuZC/8CFmzIP0DLKXfWKpRHawJiG70rVH+xZZbp8w==} + /@typescript-eslint/type-utils@5.56.0(eslint@8.36.0)(typescript@4.9.5): + resolution: {integrity: sha512-8WxgOgJjWRy6m4xg9KoSHPzBNZeQbGlQOH7l2QEhQID/+YseaFxg5J/DLwWSsi9Axj4e/cCiKx7PVzOq38tY4A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.56.0(typescript@4.9.5) + '@typescript-eslint/utils': 5.56.0(eslint@8.36.0)(typescript@4.9.5) + debug: 4.3.4(supports-color@8.1.1) + eslint: 8.36.0 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/type-utils@5.59.0(eslint@8.38.0)(typescript@5.0.2): + resolution: {integrity: sha512-d/B6VSWnZwu70kcKQSCqjcXpVH+7ABKH8P1KNn4K7j5PXXuycZTPXF44Nui0TEm6rbWGi8kc78xRgOC4n7xFgA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -10062,10 +15111,30 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.58.0(typescript@4.9.5) - '@typescript-eslint/utils': 5.58.0(eslint@8.38.0)(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 5.59.0(typescript@5.0.2) + '@typescript-eslint/utils': 5.59.0(eslint@8.38.0)(typescript@5.0.2) debug: 4.3.4(supports-color@8.1.1) eslint: 8.38.0 + tsutils: 3.21.0(typescript@5.0.2) + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/type-utils@5.61.0(eslint@8.44.0)(typescript@4.9.5): + resolution: {integrity: sha512-kk8u//r+oVK2Aj3ph/26XdH0pbAkC2RiSjUYhKD+PExemG4XSjpGFeyZ/QM8lBOa7O8aGOU+/yEbMJgQv/DnCg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.61.0(typescript@4.9.5) + '@typescript-eslint/utils': 5.61.0(eslint@8.44.0)(typescript@4.9.5) + debug: 4.3.4(supports-color@8.1.1) + eslint: 8.44.0 tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: @@ -10077,8 +15146,18 @@ packages: engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} dev: true - /@typescript-eslint/types@5.58.0: - resolution: {integrity: sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g==} + /@typescript-eslint/types@5.56.0: + resolution: {integrity: sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/types@5.59.0: + resolution: {integrity: sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/types@5.61.0: + resolution: {integrity: sha512-ldyueo58KjngXpzloHUog/h9REmHl59G1b3a5Sng1GfBo14BkS3ZbMEb3693gnP1k//97lh7bKsp6/V/0v1veQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -10096,15 +15175,78 @@ packages: debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 - semver: 7.4.0 + semver: 7.5.3 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/typescript-estree@5.56.0(typescript@4.9.5): + resolution: {integrity: sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.56.0 + '@typescript-eslint/visitor-keys': 5.56.0 + debug: 4.3.4(supports-color@8.1.1) + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.3 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/typescript-estree@5.59.0(typescript@4.9.5): + resolution: {integrity: sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.59.0 + '@typescript-eslint/visitor-keys': 5.59.0 + debug: 4.3.4(supports-color@8.1.1) + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.3 tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree@5.58.0(typescript@4.9.5): - resolution: {integrity: sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q==} + /@typescript-eslint/typescript-estree@5.59.0(typescript@5.0.2): + resolution: {integrity: sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.59.0 + '@typescript-eslint/visitor-keys': 5.59.0 + debug: 4.3.4(supports-color@8.1.1) + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.3 + tsutils: 3.21.0(typescript@5.0.2) + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/typescript-estree@5.61.0(typescript@4.9.5): + resolution: {integrity: sha512-Fud90PxONnnLZ36oR5ClJBLTLfU4pIWBmnvGwTbEa2cXIqj70AEDEmOmpkFComjBZ/037ueKrOdHuYmSFVD7Rw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -10112,53 +15254,133 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/visitor-keys': 5.58.0 - debug: 4.3.4 + '@typescript-eslint/types': 5.61.0 + '@typescript-eslint/visitor-keys': 5.61.0 + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 - semver: 7.4.0 + semver: 7.5.3 tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@5.58.0(eslint@8.22.0)(typescript@4.9.5): - resolution: {integrity: sha512-gAmLOTFXMXOC+zP1fsqm3VceKSBQJNzV385Ok3+yzlavNHZoedajjS4UyS21gabJYcobuigQPs/z71A9MdJFqQ==} + /@typescript-eslint/utils@5.56.0(eslint@8.22.0)(typescript@4.9.5): + resolution: {integrity: sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.22.0) + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.56.0 + '@typescript-eslint/types': 5.56.0 + '@typescript-eslint/typescript-estree': 5.56.0(typescript@4.9.5) + eslint: 8.22.0 + eslint-scope: 5.1.1 + semver: 7.5.3 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/utils@5.56.0(eslint@8.36.0)(typescript@4.9.5): + resolution: {integrity: sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.36.0) + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.56.0 + '@typescript-eslint/types': 5.56.0 + '@typescript-eslint/typescript-estree': 5.56.0(typescript@4.9.5) + eslint: 8.36.0 + eslint-scope: 5.1.1 + semver: 7.5.3 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/utils@5.59.0(eslint@8.22.0)(typescript@4.9.5): + resolution: {integrity: sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.22.0) + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.59.0 + '@typescript-eslint/types': 5.59.0 + '@typescript-eslint/typescript-estree': 5.59.0(typescript@4.9.5) + eslint: 8.22.0 + eslint-scope: 5.1.1 + semver: 7.5.3 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/utils@5.59.0(eslint@8.38.0)(typescript@5.0.2): + resolution: {integrity: sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.38.0) + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.59.0 + '@typescript-eslint/types': 5.59.0 + '@typescript-eslint/typescript-estree': 5.59.0(typescript@5.0.2) + eslint: 8.38.0 + eslint-scope: 5.1.1 + semver: 7.5.3 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/utils@5.59.0(eslint@8.44.0)(typescript@4.9.5): + resolution: {integrity: sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.22.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) '@types/json-schema': 7.0.11 '@types/semver': 7.3.13 - '@typescript-eslint/scope-manager': 5.58.0 - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/typescript-estree': 5.58.0(typescript@4.9.5) - eslint: 8.22.0 + '@typescript-eslint/scope-manager': 5.59.0 + '@typescript-eslint/types': 5.59.0 + '@typescript-eslint/typescript-estree': 5.59.0(typescript@4.9.5) + eslint: 8.44.0 eslint-scope: 5.1.1 - semver: 7.4.0 + semver: 7.5.3 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/utils@5.58.0(eslint@8.38.0)(typescript@4.9.5): - resolution: {integrity: sha512-gAmLOTFXMXOC+zP1fsqm3VceKSBQJNzV385Ok3+yzlavNHZoedajjS4UyS21gabJYcobuigQPs/z71A9MdJFqQ==} + /@typescript-eslint/utils@5.61.0(eslint@8.44.0)(typescript@4.9.5): + resolution: {integrity: sha512-mV6O+6VgQmVE6+xzlA91xifndPW9ElFW8vbSF0xCT/czPXVhwDewKila1jOyRwa9AE19zKnrr7Cg5S3pJVrTWQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.38.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) '@types/json-schema': 7.0.11 '@types/semver': 7.3.13 - '@typescript-eslint/scope-manager': 5.58.0 - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/typescript-estree': 5.58.0(typescript@4.9.5) - eslint: 8.38.0 + '@typescript-eslint/scope-manager': 5.61.0 + '@typescript-eslint/types': 5.61.0 + '@typescript-eslint/typescript-estree': 5.61.0(typescript@4.9.5) + eslint: 8.44.0 eslint-scope: 5.1.1 - semver: 7.4.0 + semver: 7.5.3 transitivePeerDependencies: - supports-color - typescript @@ -10172,23 +15394,37 @@ packages: eslint-visitor-keys: 2.1.0 dev: true - /@typescript-eslint/visitor-keys@5.58.0: - resolution: {integrity: sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA==} + /@typescript-eslint/visitor-keys@5.56.0: + resolution: {integrity: sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.56.0 + eslint-visitor-keys: 3.4.1 + dev: true + + /@typescript-eslint/visitor-keys@5.59.0: + resolution: {integrity: sha512-qZ3iXxQhanchCeaExlKPV3gDQFxMUmU35xfd5eCXB6+kUw1TUAbIy2n7QIrwz9s98DQLzNWyHp61fY0da4ZcbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.59.0 + eslint-visitor-keys: 3.4.1 + dev: true + + /@typescript-eslint/visitor-keys@5.61.0: + resolution: {integrity: sha512-50XQ5VdbWrX06mQXhy93WywSFZZGsv3EOjq+lqp6WC2t+j3mb6A9xYVdrRxafvK88vg9k9u+CT4l6D8PEatjKg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.58.0 - eslint-visitor-keys: 3.4.0 + '@typescript-eslint/types': 5.61.0 + eslint-visitor-keys: 3.4.1 dev: true - /@vitejs/plugin-react-swc@3.3.0(vite@4.2.1): - resolution: {integrity: sha512-Ycg+n2eyCOTpn/wRy+evVo859+hw7qCj9iaX5CMny6x1fx1Uoq0xBG+a98lFtwLNGfGEnpI0F26YigRuxCRkwg==} + /@vitejs/plugin-react-swc@3.2.0(vite@4.2.1): + resolution: {integrity: sha512-IcBoXL/mcH7JdQr/nfDlDwTdIaH8Rg7LpfQDF4nAht+juHWIuv6WhpKPCSfY4+zztAaB07qdBoFz1XCZsgo3pQ==} peerDependencies: vite: ^4 dependencies: - '@swc/core': 1.3.49 - vite: 4.2.1(@types/node@18.15.11) - transitivePeerDependencies: - - '@swc/helpers' + '@swc/core': 1.3.42 + vite: 4.2.1(@types/node@18.15.10) dev: true /@vitejs/plugin-react@3.1.0(vite@4.2.1): @@ -10197,12 +15433,58 @@ packages: peerDependencies: vite: ^4.1.0-beta.0 dependencies: - '@babel/core': 7.21.4 - '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.4) + '@babel/core': 7.22.5 + '@babel/plugin-transform-react-jsx-self': 7.22.5(@babel/core@7.22.5) + '@babel/plugin-transform-react-jsx-source': 7.22.5(@babel/core@7.22.5) + magic-string: 0.27.0 + react-refresh: 0.14.0 + vite: 4.2.1(@types/node@18.15.10) + transitivePeerDependencies: + - supports-color + dev: true + + /@vitejs/plugin-react@3.1.0(vite@4.4.0): + resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.1.0-beta.0 + dependencies: + '@babel/core': 7.22.5 + '@babel/plugin-transform-react-jsx-self': 7.22.5(@babel/core@7.22.5) + '@babel/plugin-transform-react-jsx-source': 7.22.5(@babel/core@7.22.5) magic-string: 0.27.0 react-refresh: 0.14.0 - vite: 4.2.1(@types/node@18.15.11) + vite: 4.4.0(@types/node@20.4.1)(less@4.1.3) + transitivePeerDependencies: + - supports-color + dev: true + + /@vitejs/plugin-react@4.0.0(vite@4.3.9): + resolution: {integrity: sha512-HX0XzMjL3hhOYm+0s95pb0Z7F8O81G7joUHgfDd/9J/ZZf5k4xX6QAMFkKsHFxaHlf6X7GD7+XuaZ66ULiJuhQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 + dependencies: + '@babel/core': 7.22.5 + '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.22.5) + '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.22.5) + react-refresh: 0.14.0 + vite: 4.3.9(@types/node@20.3.1) + transitivePeerDependencies: + - supports-color + dev: true + + /@vitejs/plugin-react@4.0.1(vite@4.4.0): + resolution: {integrity: sha512-g25lL98essfeSj43HJ0o4DMp0325XK0ITkxpgChzJU/CyemgyChtlxfnRbjfwxDGCTRxTiXtQAsdebQXKMRSOA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 + dependencies: + '@babel/core': 7.22.5 + '@babel/plugin-transform-react-jsx-self': 7.22.5(@babel/core@7.22.5) + '@babel/plugin-transform-react-jsx-source': 7.22.5(@babel/core@7.22.5) + react-refresh: 0.14.0 + vite: 4.4.0(@types/node@20.4.1)(less@4.1.3) transitivePeerDependencies: - supports-color dev: true @@ -10224,6 +15506,7 @@ packages: - happy-dom - jsdom - less + - lightningcss - sass - stylus - sugarss @@ -10247,12 +15530,20 @@ packages: chai: 4.3.7 dev: true + /@vitest/expect@0.33.0: + resolution: {integrity: sha512-sVNf+Gla3mhTCxNJx+wJLDPp/WcstOe0Ksqz4Vec51MmgMth/ia0MGFEkIZmVGeTL5HtjYR4Wl/ZxBxBXZJTzQ==} + dependencies: + '@vitest/spy': 0.33.0 + '@vitest/utils': 0.33.0 + chai: 4.3.7 + dev: true + /@vitest/runner@0.28.5: resolution: {integrity: sha512-NKkHtLB+FGjpp5KmneQjTcPLWPTDfB7ie+MmF1PnUBf/tGe2OjGxWyB62ySYZ25EYp9krR5Bw0YPLS/VWh1QiA==} dependencies: '@vitest/utils': 0.28.5 p-limit: 4.0.0 - pathe: 1.1.0 + pathe: 1.1.1 dev: true /@vitest/runner@0.29.8: @@ -10260,7 +15551,23 @@ packages: dependencies: '@vitest/utils': 0.29.8 p-limit: 4.0.0 - pathe: 1.1.0 + pathe: 1.1.1 + dev: true + + /@vitest/runner@0.33.0: + resolution: {integrity: sha512-UPfACnmCB6HKRHTlcgCoBh6ppl6fDn+J/xR8dTufWiKt/74Y9bHci5CKB8tESSV82zKYtkBJo9whU3mNvfaisg==} + dependencies: + '@vitest/utils': 0.33.0 + p-limit: 4.0.0 + pathe: 1.1.1 + dev: true + + /@vitest/snapshot@0.33.0: + resolution: {integrity: sha512-tJjrl//qAHbyHajpFvr8Wsk8DIOODEebTu7pgBrP07iOepR5jYkLFiqLq2Ltxv+r0uptUb4izv1J8XBOwKkVYA==} + dependencies: + magic-string: 0.30.1 + pathe: 1.1.1 + pretty-format: 29.5.0 dev: true /@vitest/spy@0.28.5: @@ -10275,6 +15582,12 @@ packages: tinyspy: 1.1.1 dev: true + /@vitest/spy@0.33.0: + resolution: {integrity: sha512-Kv+yZ4hnH1WdiAkPUQTpRxW8kGtH8VRTnus7ZTGovFYM1ZezJpvGtb9nPIjPnptHbsyIAxYZsEpVPYgtpjGnrg==} + dependencies: + tinyspy: 2.1.1 + dev: true + /@vitest/utils@0.28.5: resolution: {integrity: sha512-UyZdYwdULlOa4LTUSwZ+Paz7nBHGTT72jKwdFSV4IjHF1xsokp+CabMdhjvVhYwkLfO88ylJT46YMilnkSARZA==} dependencies: @@ -10294,10 +15607,18 @@ packages: pretty-format: 27.5.1 dev: true + /@vitest/utils@0.33.0: + resolution: {integrity: sha512-pF1w22ic965sv+EN6uoePkAOTkAPWM03Ri/jXNyMIKBb/XHLDPfhLvf/Fa9g0YECevAIz56oVYXhodLvLQ/awA==} + dependencies: + diff-sequences: 29.4.3 + loupe: 2.3.6 + pretty-format: 29.5.0 + dev: true + /@vscode/emmet-helper@2.8.6: resolution: {integrity: sha512-IIB8jbiKy37zN8bAIHx59YmnIelY78CGHtThnibD/d3tQOKRY83bYVi9blwmZVUZh6l9nfkYH3tvReaiNxY9EQ==} dependencies: - emmet: 2.4.2 + emmet: 2.3.6 jsonc-parser: 2.3.1 vscode-languageserver-textdocument: 1.0.8 vscode-languageserver-types: 3.17.3 @@ -10414,8 +15735,8 @@ packages: '@xtuc/long': 4.2.2 dev: true - /@xmldom/xmldom@0.8.7: - resolution: {integrity: sha512-sI1Ly2cODlWStkINzqGrZ8K6n+MTSbAeQnAipGyL+KZCXuHaRlj2gyyy8B/9MvsFFqN7XHryQnB2QwhzvJXovg==} + /@xmldom/xmldom@0.8.6: + resolution: {integrity: sha512-uRjjusqpoqfmRkTaNuLJ2VohVr67Q5YwDATW3VU7PfzTj6IRaihGrYI7zckGZjxQPBIp63nfvJbM+Yu5ICh0Bg==} engines: {node: '>=10.0.0'} /@xstate/inspect@0.7.1(ws@8.13.0)(xstate@4.37.1): @@ -10433,7 +15754,42 @@ packages: xstate: 4.37.1 dev: true - /@xstate/svelte@2.0.1(svelte@3.58.0)(xstate@4.37.1): + /@xstate/inspect@0.7.1(ws@8.13.0)(xstate@4.38.0): + resolution: {integrity: sha512-lEIi6cSvzA9f+GzaJMRVe4xnNjPY/oKdU8rjb+qxqUYx2evLuqysFu0XbPmEjMCwpfdIvG4FFsZJ7Ng7+k9UHw==} + peerDependencies: + '@types/ws': ^8.0.0 + ws: ^8.0.0 + xstate: ^4.35.3 + peerDependenciesMeta: + '@types/ws': + optional: true + dependencies: + fast-safe-stringify: 2.1.1 + ws: 8.13.0 + xstate: 4.38.0 + dev: false + + /@xstate/react@3.2.2(@types/react@18.0.37)(react@18.2.0)(xstate@4.38.0): + resolution: {integrity: sha512-feghXWLedyq8JeL13yda3XnHPZKwYDN5HPBLykpLeuNpr9178tQd2/3d0NrH6gSd0sG5mLuLeuD+ck830fgzLQ==} + peerDependencies: + '@xstate/fsm': ^2.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + xstate: ^4.37.2 + peerDependenciesMeta: + '@xstate/fsm': + optional: true + xstate: + optional: true + dependencies: + react: 18.2.0 + use-isomorphic-layout-effect: 1.1.2(@types/react@18.0.37)(react@18.2.0) + use-sync-external-store: 1.2.0(react@18.2.0) + xstate: 4.38.0 + transitivePeerDependencies: + - '@types/react' + dev: false + + /@xstate/svelte@2.0.1(svelte@3.57.0)(xstate@4.37.1): resolution: {integrity: sha512-A4QSCt4EpbyzbPIPGCRLoRn50VUq6gXcmJPIwgBaCTsTRsPsp8KSvltDqS2msBKEDr4FHWXEXOC4QVGDWFL5dg==} peerDependencies: '@xstate/fsm': ^2.0.0 @@ -10445,7 +15801,7 @@ packages: xstate: optional: true dependencies: - svelte: 3.58.0 + svelte: 3.57.0 xstate: 4.37.1 dev: false @@ -10457,30 +15813,30 @@ packages: resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} dev: true - /@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.17.16): + /@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.17.19): resolution: {integrity: sha512-kYzDJO5CA9sy+on/s2aIW0411AklfCi8Ck/4QDivOqsMKpStZA2SsR+X27VTggGwpStWaLrjJcDcdDMowtG8MA==} engines: {node: '>=14.15.0'} peerDependencies: esbuild: '>=0.10.0' dependencies: - esbuild: 0.17.16 - tslib: 2.5.0 + esbuild: 0.17.19 + tslib: 2.5.2 dev: true /@yarnpkg/lockfile@1.1.0: resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} dev: true - /@yarnpkg/parsers@3.0.0-rc.42: - resolution: {integrity: sha512-eW9Mbegmb5bJjwawJM9ghjUjUqciNMhC6L7XrQPF/clXS5bbP66MstsgCT5hy9VlfUh/CfBT+0Wucf531dMjHA==} + /@yarnpkg/parsers@3.0.0-rc.40: + resolution: {integrity: sha512-sKbi5XhHKXCjzb5m0ftGuQuODM2iUXEsrCSl8MkKexNWHepCmU3IPaGTPC5gHZy4sOvsb9JqTLaZEez+kDzG+Q==} engines: {node: '>=14.15.0'} dependencies: js-yaml: 3.14.1 - tslib: 2.5.0 + tslib: 2.5.2 dev: true - /@zerodevx/svelte-toast@0.8.2: - resolution: {integrity: sha512-EDtZ/Hw37T/UWCQ5drhMss0J9vItYUSDivQ3+mET5My6No7YNiNQklj2bkE61UAzut2TjHJfOJNBZsj78ODFtw==} + /@zerodevx/svelte-toast@0.8.0: + resolution: {integrity: sha512-PAcQQGhGYkdZJqPY7obnGIdlBoeoMteogYoN/nJn87CI5LYgRz6X9ST4AA65jyGlybWnniYzlrTT/dFepvuC/g==} dev: false /@zkochan/js-yaml@0.0.6: @@ -10534,16 +15890,16 @@ packages: /acorn-globals@7.0.1: resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} dependencies: - acorn: 8.8.2 + acorn: 8.9.0 acorn-walk: 8.2.0 dev: true - /acorn-import-assertions@1.8.0(acorn@8.8.2): + /acorn-import-assertions@1.8.0(acorn@8.9.0): resolution: {integrity: sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==} peerDependencies: acorn: ^8 dependencies: - acorn: 8.8.2 + acorn: 8.9.0 dev: true /acorn-jsx@5.3.2(acorn@7.4.1): @@ -10561,10 +15917,23 @@ packages: dependencies: acorn: 8.8.2 + /acorn-jsx@5.3.2(acorn@8.9.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.9.0 + + /acorn-node@1.8.2: + resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==} + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + xtend: 4.0.2 + /acorn-walk@7.2.0: resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} engines: {node: '>=0.4.0'} - dev: true /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} @@ -10574,18 +15943,34 @@ packages: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} engines: {node: '>=0.4.0'} hasBin: true - dev: true /acorn@8.8.2: resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} engines: {node: '>=0.4.0'} hasBin: true + /acorn@8.9.0: + resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + /add-px-to-style@1.0.0: + resolution: {integrity: sha512-YMyxSlXpPjD8uWekCQGuN40lV4bnZagUwqa2m/uFv1z/tNImSk9fnXVMUI5qwME/zzI3MMQRvjZ+69zyfSSyew==} + dev: false + /address@1.2.2: resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} engines: {node: '>= 10.0.0'} dev: true + /adjust-sourcemap-loader@4.0.0: + resolution: {integrity: sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==} + engines: {node: '>=8.9'} + dependencies: + loader-utils: 2.0.4 + regex-parser: 2.2.11 + dev: true + /agent-base@5.1.1: resolution: {integrity: sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==} engines: {node: '>= 6.0.0'} @@ -10595,7 +15980,7 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -10607,6 +15992,17 @@ packages: indent-string: 4.0.0 dev: true + /ajv-formats@2.1.1(ajv@8.11.0): + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.11.0 + dev: true + /ajv-formats@2.1.1(ajv@8.12.0): resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -10636,6 +16032,15 @@ packages: ajv: 6.12.6 dev: true + /ajv-keywords@5.1.0(ajv@8.12.0): + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + dependencies: + ajv: 8.12.0 + fast-deep-equal: 3.1.3 + dev: false + /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: @@ -10644,6 +16049,15 @@ packages: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + /ajv@8.11.0: + resolution: {integrity: sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + /ajv@8.12.0: resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} dependencies: @@ -10661,23 +16075,23 @@ packages: uri-js: 4.4.1 dev: false - /algoliasearch@4.17.0: - resolution: {integrity: sha512-JMRh2Mw6sEnVMiz6+APsi7lx9a2jiDFF+WUtANaUVCv6uSU9UOLdo5h9K3pdP6frRRybaM2fX8b1u0nqICS9aA==} + /algoliasearch@4.18.0: + resolution: {integrity: sha512-pCuVxC1SVcpc08ENH32T4sLKSyzoU7TkRIDBMwSLfIiW+fq4znOmWDkAygHZ6pRcO9I1UJdqlfgnV7TRj+MXrA==} dependencies: - '@algolia/cache-browser-local-storage': 4.17.0 - '@algolia/cache-common': 4.17.0 - '@algolia/cache-in-memory': 4.17.0 - '@algolia/client-account': 4.17.0 - '@algolia/client-analytics': 4.17.0 - '@algolia/client-common': 4.17.0 - '@algolia/client-personalization': 4.17.0 - '@algolia/client-search': 4.17.0 - '@algolia/logger-common': 4.17.0 - '@algolia/logger-console': 4.17.0 - '@algolia/requester-browser-xhr': 4.17.0 - '@algolia/requester-common': 4.17.0 - '@algolia/requester-node-http': 4.17.0 - '@algolia/transporter': 4.17.0 + '@algolia/cache-browser-local-storage': 4.18.0 + '@algolia/cache-common': 4.18.0 + '@algolia/cache-in-memory': 4.18.0 + '@algolia/client-account': 4.18.0 + '@algolia/client-analytics': 4.18.0 + '@algolia/client-common': 4.18.0 + '@algolia/client-personalization': 4.18.0 + '@algolia/client-search': 4.18.0 + '@algolia/logger-common': 4.18.0 + '@algolia/logger-console': 4.18.0 + '@algolia/requester-browser-xhr': 4.18.0 + '@algolia/requester-common': 4.18.0 + '@algolia/requester-node-http': 4.18.0 + '@algolia/transporter': 4.18.0 dev: false /ansi-align@3.0.1: @@ -10706,7 +16120,6 @@ packages: /ansi-sequence-parser@1.1.0: resolution: {integrity: sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==} - dev: true /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} @@ -10749,6 +16162,35 @@ packages: /aproba@2.0.0: resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + /archiver-utils@2.1.0: + resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} + engines: {node: '>= 6'} + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 2.3.8 + dev: true + + /archiver@5.3.1: + resolution: {integrity: sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==} + engines: {node: '>= 10'} + dependencies: + archiver-utils: 2.1.0 + async: 3.2.4 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 2.2.0 + zip-stream: 4.1.0 + dev: true + /are-we-there-yet@2.0.0: resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} engines: {node: '>=10'} @@ -10774,7 +16216,7 @@ packages: resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} engines: {node: '>=10'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 dev: false /aria-query@5.1.3: @@ -10862,6 +16304,16 @@ packages: engines: {node: '>=0.10.0'} dev: true + /asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + dev: false + + /asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + dependencies: + safer-buffer: 2.1.2 + dev: true + /assert@2.0.0: resolution: {integrity: sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==} dependencies: @@ -10879,20 +16331,21 @@ packages: resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==} engines: {node: '>=4'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 + dev: true /ast-types@0.15.2: resolution: {integrity: sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==} engines: {node: '>=4'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 dev: true /ast-types@0.16.1: resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} engines: {node: '>=4'} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 dev: true /astral-regex@2.0.0: @@ -10910,93 +16363,90 @@ packages: engines: {node: ^14.18.0 || >=16.0.0} dependencies: '@astrojs/compiler': 0.32.0 - '@typescript-eslint/types': 5.58.0 + '@typescript-eslint/types': 5.61.0 astrojs-compiler-sync: 0.3.2(@astrojs/compiler@0.32.0) debug: 4.3.4(supports-color@8.1.1) - eslint-scope: 7.1.1 - eslint-visitor-keys: 3.4.0 - espree: 9.5.1 + eslint-scope: 7.2.0 + eslint-visitor-keys: 3.4.1 + espree: 9.6.0 transitivePeerDependencies: - supports-color dev: true - /astro@1.9.2(@types/node@18.15.11)(ts-node@10.9.1): - resolution: {integrity: sha512-L+Ma0eR0Aa6QZg7RF0lEs+106Ye1/zukvtq3KtsYIogAojltlwllwU9X5CwMBzFwA55NxpNp4gSRh5US/xb+8Q==} - engines: {node: ^14.18.0 || >=16.12.0, npm: '>=6.14.0'} + /astro@2.7.2(@types/node@18.15.10): + resolution: {integrity: sha512-2+vjXeVGU04aecs0mm93Qx9KdeVDw4OTeBIijs2Z+QLoe4RUYZnkqx5gR70VNfnoMdXoPp7+wB+ARcb0+ee/yg==} + engines: {node: '>=16.12.0', npm: '>=6.14.0'} hasBin: true + peerDependencies: + sharp: '>=0.31.0' + peerDependenciesMeta: + sharp: + optional: true dependencies: - '@astrojs/compiler': 0.31.4 - '@astrojs/language-server': 0.28.3 - '@astrojs/markdown-remark': 1.2.0 - '@astrojs/telemetry': 1.0.1 - '@astrojs/webapi': 1.1.1 - '@babel/core': 7.21.4 - '@babel/generator': 7.21.4 - '@babel/parser': 7.21.4 - '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.21.4) - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 - '@proload/core': 0.3.3 - '@proload/plugin-tsm': 0.2.1(@proload/core@0.3.3) - '@types/babel__core': 7.20.0 - '@types/html-escaper': 3.0.0 + '@astrojs/compiler': 1.5.1 + '@astrojs/internal-helpers': 0.1.1 + '@astrojs/language-server': 1.0.8 + '@astrojs/markdown-remark': 2.2.1(astro@2.7.2) + '@astrojs/telemetry': 2.1.1 + '@astrojs/webapi': 2.2.0 + '@babel/core': 7.22.5 + '@babel/generator': 7.22.5 + '@babel/parser': 7.22.5 + '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.5) + '@babel/traverse': 7.22.5 + '@babel/types': 7.22.5 + '@types/babel__core': 7.20.1 '@types/yargs-parser': 21.0.0 - acorn: 8.8.2 + acorn: 8.9.0 boxen: 6.2.1 + chokidar: 3.5.3 ci-info: 3.8.0 common-ancestor-path: 1.0.1 cookie: 0.5.0 debug: 4.3.4(supports-color@8.1.1) deepmerge-ts: 4.3.0 - devalue: 4.3.0 + devalue: 4.3.2 diff: 5.1.0 - es-module-lexer: 1.2.1 - estree-walker: 3.0.3 + es-module-lexer: 1.3.0 + esbuild: 0.17.19 + estree-walker: 3.0.0 execa: 6.1.0 - fast-glob: 3.2.12 + fast-glob: 3.3.0 github-slugger: 2.0.0 gray-matter: 4.0.3 - html-entities: 2.3.3 html-escaper: 3.0.3 - import-meta-resolve: 2.2.2 + js-yaml: 4.1.0 kleur: 4.1.5 magic-string: 0.27.0 mime: 3.0.0 - ora: 6.3.0 - path-browserify: 1.0.1 + ora: 6.3.1 + p-limit: 4.0.0 path-to-regexp: 6.2.1 - postcss: 8.4.21 - postcss-load-config: 3.1.4(postcss@8.4.21)(ts-node@10.9.1) preferred-pm: 3.0.3 prompts: 2.4.2 - recast: 0.20.5 rehype: 12.0.1 - resolve: 1.22.2 - rollup: 2.79.1 - semver: 7.4.0 - shiki: 0.11.1 - sirv: 2.0.2 - slash: 4.0.0 + semver: 7.5.3 + server-destroy: 1.0.1 + shiki: 0.14.3 string-width: 5.1.2 - strip-ansi: 7.0.1 - supports-esm: 1.0.0 + strip-ansi: 7.1.0 tsconfig-resolver: 3.0.1 typescript: 4.9.5 unist-util-visit: 4.1.2 vfile: 5.3.7 - vite: 3.2.5(@types/node@18.15.11) - vitefu: 0.2.4(vite@3.2.5) + vite: 4.4.0(@types/node@18.15.10)(less@4.1.3) + vitefu: 0.2.4(vite@4.4.0) yargs-parser: 21.1.1 zod: 3.21.4 transitivePeerDependencies: - '@types/node' - less + - lightningcss - sass - stylus - sugarss - supports-color - terser - - ts-node dev: false /astrojs-compiler-sync@0.3.2(@astrojs/compiler@0.32.0): @@ -11013,9 +16463,12 @@ packages: resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} dev: true + /async-lock@1.4.0: + resolution: {integrity: sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ==} + dev: true + /async@3.2.4: resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} - dev: true /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -11033,7 +16486,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.21.5 - caniuse-lite: 1.0.30001478 + caniuse-lite: 1.0.30001470 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -11041,6 +16494,21 @@ packages: postcss-value-parser: 4.2.0 dev: true + /autoprefixer@10.4.14(postcss@8.4.24): + resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.5 + caniuse-lite: 1.0.30001470 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.24 + postcss-value-parser: 4.2.0 + /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -11077,12 +16545,12 @@ packages: transitivePeerDependencies: - debug - /babel-core@7.0.0-bridge.0(@babel/core@7.21.4): + /babel-core@7.0.0-bridge.0(@babel/core@7.22.5): resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 dev: true /babel-eslint@10.1.0(eslint@8.22.0): @@ -11092,29 +16560,47 @@ packages: peerDependencies: eslint: '>= 4.12.1' dependencies: - '@babel/code-frame': 7.21.4 - '@babel/parser': 7.21.4 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/code-frame': 7.22.5 + '@babel/parser': 7.22.5 + '@babel/traverse': 7.22.5 + '@babel/types': 7.22.5 eslint: 8.22.0 eslint-visitor-keys: 1.3.0 - resolve: 1.22.2 + resolve: 1.22.1 transitivePeerDependencies: - supports-color dev: false - /babel-jest@27.5.1(@babel/core@7.21.4): + /babel-jest@27.5.1(@babel/core@7.22.5): resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: '@babel/core': ^7.8.0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/babel__core': 7.20.0 + '@types/babel__core': 7.20.1 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 27.5.1(@babel/core@7.21.4) + babel-preset-jest: 27.5.1(@babel/core@7.22.5) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-jest@29.5.0(@babel/core@7.22.5): + resolution: {integrity: sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.22.5 + '@jest/transform': 29.5.0 + '@types/babel__core': 7.20.1 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.5.0(@babel/core@7.22.5) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -11126,7 +16612,7 @@ packages: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} dependencies: - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.22.5 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 5.2.1 @@ -11139,23 +16625,33 @@ packages: resolution: {integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.4 - '@types/babel__core': 7.20.0 + '@babel/template': 7.22.5 + '@babel/types': 7.22.5 + '@types/babel__core': 7.20.1 '@types/babel__traverse': 7.18.3 dev: true - /babel-plugin-jsx-dom-expressions@0.36.9(@babel/core@7.21.4): - resolution: {integrity: sha512-4ACO10PoUvqRcBEErbhVGv5vAHXgkz7epvULHfqJXw5TPtDYwjhmhGxGNGSK6220ec/b85ElLrGHlqQiJxI0WQ==} + /babel-plugin-jest-hoist@29.5.0: + resolution: {integrity: sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/template': 7.22.5 + '@babel/types': 7.22.5 + '@types/babel__core': 7.20.1 + '@types/babel__traverse': 7.18.3 + dev: true + + /babel-plugin-jsx-dom-expressions@0.36.10(@babel/core@7.22.5): + resolution: {integrity: sha512-QA2k/14WGw+RgcGGnEuLWwnu4em6CGhjeXtjvgOYyFHYS2a+CzPeaVQHDOlfuiBcjq/3hWMspHMIMnPEOIzdBg==} peerDependencies: '@babel/core': ^7.20.12 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@babel/helper-module-imports': 7.18.6 - '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.4) - '@babel/types': 7.21.4 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.5) + '@babel/types': 7.22.5 html-entities: 2.3.3 - validate-html-nesting: 1.2.1 + validate-html-nesting: 1.2.2 dev: false /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.17.9): @@ -11163,7 +16659,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.21.4 + '@babel/compat-data': 7.22.5 '@babel/core': 7.17.9 '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.17.9) semver: 6.3.0 @@ -11171,14 +16667,27 @@ packages: - supports-color dev: true - /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.21.4): + /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.21.3): + resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.22.5 + '@babel/core': 7.21.3 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.3) + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.22.5): resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.21.4 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.4) + '@babel/compat-data': 7.22.5 + '@babel/core': 7.22.5 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.22.5) semver: 6.3.0 transitivePeerDependencies: - supports-color @@ -11191,83 +16700,117 @@ packages: dependencies: '@babel/core': 7.17.9 '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.17.9) - core-js-compat: 3.30.0 + core-js-compat: 3.29.1 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.21.3): + resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.3 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.3) + core-js-compat: 3.29.1 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.21.4): + /babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.22.5): resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.4) - core-js-compat: 3.30.0 + '@babel/core': 7.22.5 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.22.5) + core-js-compat: 3.29.1 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-regenerator@0.3.1(@babel/core@7.17.9): + resolution: {integrity: sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.9 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.17.9) transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator@0.3.1(@babel/core@7.17.9): - resolution: {integrity: sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==} + /babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.21.3): + resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.17.9) + '@babel/core': 7.21.3 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.3) transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.21.4): + /babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.22.5): resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.4 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.4) + '@babel/core': 7.22.5 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.22.5) transitivePeerDependencies: - supports-color dev: true - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.21.4): + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.22.5): resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.21.4 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.4) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.4) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.21.4) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.4) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.4) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.4) - dev: true - - /babel-preset-jest@27.5.1(@babel/core@7.21.4): + '@babel/core': 7.22.5 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.5) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.22.5) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.22.5) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.5) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.5) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.5) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.5) + dev: true + + /babel-preset-jest@27.5.1(@babel/core@7.22.5): resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 babel-plugin-jest-hoist: 27.5.1 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.5) + dev: true + + /babel-preset-jest@29.5.0(@babel/core@7.22.5): + resolution: {integrity: sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.22.5 + babel-plugin-jest-hoist: 29.5.0 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.5) dev: true - /babel-preset-solid@1.7.3(@babel/core@7.21.4): - resolution: {integrity: sha512-HOdyrij99zo+CBrmtDxSexBAl54vCBCfBoyueLBvcfVniaEXNd4ftKqSN6XQcLvFfCY28UFO+DHaigXzWKOfzg==} + /babel-preset-solid@1.7.7(@babel/core@7.22.5): + resolution: {integrity: sha512-tdxVzx3kgcIjNXAOmGRbzIhFBPeJjSakiN9yM+IYdL/+LtXNnbGqb0Va5tJb8Sjbk+QVEriovCyuzB5T7jeTvg==} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.21.4 - babel-plugin-jsx-dom-expressions: 0.36.9(@babel/core@7.21.4) + '@babel/core': 7.22.5 + babel-plugin-jsx-dom-expressions: 0.36.10(@babel/core@7.22.5) dev: false /backo2@1.0.2: @@ -11280,6 +16823,10 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + /base16@1.0.0: + resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==} + dev: false + /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -11287,6 +16834,12 @@ packages: resolution: {integrity: sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==} dev: true + /bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + dependencies: + tweetnacl: 0.14.5 + dev: true + /bcrypt@5.1.0: resolution: {integrity: sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==} engines: {node: '>= 10.0.0'} @@ -11318,6 +16871,10 @@ packages: engines: {node: '>=0.6'} dev: true + /big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + dev: true + /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -11405,10 +16962,6 @@ packages: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} dev: true - /boolean@3.2.0: - resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} - dev: false - /bowser@2.11.0: resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} @@ -11497,8 +17050,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001478 - electron-to-chromium: 1.4.361 + caniuse-lite: 1.0.30001470 + electron-to-chromium: 1.4.340 node-releases: 2.0.10 update-browserslist-db: 1.0.10(browserslist@4.21.5) @@ -11546,6 +17099,12 @@ packages: ieee754: 1.2.1 dev: false + /buildcheck@0.0.6: + resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} + engines: {node: '>=10.0.0'} + dev: true + optional: true + /builtin-modules@3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} @@ -11554,7 +17113,7 @@ packages: /builtins@5.0.1: resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} dependencies: - semver: 7.4.0 + semver: 7.5.3 dev: true /busboy@1.6.0: @@ -11563,6 +17122,11 @@ packages: dependencies: streamsearch: 1.1.0 + /byline@5.0.0: + resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} + engines: {node: '>=0.10.0'} + dev: true + /bytes@3.0.0: resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} engines: {node: '>= 0.8'} @@ -11623,7 +17187,7 @@ packages: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} dependencies: pascal-case: 3.1.2 - tslib: 2.5.0 + tslib: 2.5.2 dev: true /camelcase-css@2.0.1: @@ -11648,8 +17212,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - /caniuse-lite@1.0.30001478: - resolution: {integrity: sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw==} + /caniuse-lite@1.0.30001470: + resolution: {integrity: sha512-065uNwY6QtHCBOExzbV6m236DDhYCCtPmQUCoQtwkVqzud8v5QPidoMr6CoMkC2nfp6nksjttqWQRRh75LqUmA==} /ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -11785,7 +17349,7 @@ packages: resolution: {integrity: sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==} dependencies: '@types/validator': 13.7.14 - libphonenumber-js: 1.10.26 + libphonenumber-js: 1.10.24 validator: 13.9.0 /class-variance-authority@0.6.0(typescript@4.9.5): @@ -11800,6 +17364,21 @@ packages: typescript: 4.9.5 dev: false + /class-variance-authority@0.6.0(typescript@5.0.2): + resolution: {integrity: sha512-qdRDgfjx3GRb9fpwpSvn+YaidnT7IUJNe4wt5/SWwM+PmUwJUhQRk/8zAyNro0PmVfmen2635UboTjIBXXxy5A==} + peerDependencies: + typescript: '>= 4.5.5 < 6' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + clsx: 1.2.1 + typescript: 5.0.2 + dev: false + + /classnames@2.3.2: + resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==} + /clean-css@5.3.2: resolution: {integrity: sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==} engines: {node: '>= 10.0'} @@ -11851,8 +17430,8 @@ packages: engines: {node: '>=6'} dev: true - /cli-spinners@2.8.0: - resolution: {integrity: sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ==} + /cli-spinners@2.7.0: + resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==} engines: {node: '>=6'} /cli-table3@0.6.3: @@ -11926,6 +17505,20 @@ packages: engines: {node: '>=6'} dev: false + /cmdk@0.2.0(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-JQpKvEOb86SnvMZbYaFKYhvzFntWBeSZdyii0rZPhKJj9uwJBxu4DaVYDrRN7r3mPop56oPhRw+JYWTKs66TYw==} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + '@radix-ui/react-dialog': 1.0.0(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0) + command-score: 0.1.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -11961,12 +17554,18 @@ packages: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - dev: true /color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true + /color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + dev: false + /color@4.2.3: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} @@ -11988,6 +17587,13 @@ packages: engines: {node: '>=0.1.90'} dev: true + /colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + dependencies: + color: 3.2.1 + text-hex: 1.0.0 + dev: false + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -11998,6 +17604,10 @@ packages: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} dev: false + /command-score@0.1.2: + resolution: {integrity: sha512-VtDvQpIJBvBatnONUsPzXYFVKQQAhuf3XTNOAsdBxCNO/QCtUUd8LSgjn0GVarBkCad6aJCZfXgrjYbl/KRr7w==} + dev: false + /commander@10.0.0: resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==} engines: {node: '>=14'} @@ -12071,6 +17681,16 @@ packages: resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} dev: true + /compress-commons@4.1.1: + resolution: {integrity: sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==} + engines: {node: '>= 10'} + dependencies: + buffer-crc32: 0.2.13 + crc32-stream: 4.0.2 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + dev: true + /compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -12121,7 +17741,7 @@ packages: date-fns: 2.29.3 lodash: 4.17.21 rxjs: 7.8.0 - shell-quote: 1.8.1 + shell-quote: 1.8.0 spawn-command: 0.0.2-1 supports-color: 8.1.1 tree-kill: 1.2.2 @@ -12231,6 +17851,11 @@ packages: keygrip: 1.1.0 dev: false + /copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + dependencies: + is-what: 3.14.1 + /copy-anything@3.0.3: resolution: {integrity: sha512-fpW2W/BqEzqPp29QS+MwwfisHCQZtiduTe/m8idFo0xbti9fIZ2WVhAsCv4ggFVH3AgCkVdpoOCtQC6gBrdhjw==} engines: {node: '>=12.13'} @@ -12238,14 +17863,14 @@ packages: is-what: 4.1.8 dev: true - /core-js-compat@3.30.0: - resolution: {integrity: sha512-P5A2h/9mRYZFIAP+5Ab8ns6083IyVpSclU74UNvbGVQ8VM7n3n3/g2yF3AkKQ9NXz2O+ioxLbEWKnDtgsFamhg==} + /core-js-compat@3.29.1: + resolution: {integrity: sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==} dependencies: browserslist: 4.21.5 dev: true - /core-js@3.30.0: - resolution: {integrity: sha512-hQotSSARoNh1mYPi9O2YaWeiq/cEB95kOrFb4NCrO4RIFt1qqNpKsaE+vy/L3oiqvND5cThqXzUU3r9F7Efztg==} + /core-js@3.29.1: + resolution: {integrity: sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==} requiresBuild: true dev: true @@ -12259,7 +17884,7 @@ packages: object-assign: 4.1.1 vary: 1.1.2 - /cosmiconfig-typescript-loader@4.3.0(@types/node@18.15.11)(cosmiconfig@8.1.3)(ts-node@10.9.1)(typescript@4.9.5): + /cosmiconfig-typescript-loader@4.3.0(@types/node@18.15.10)(cosmiconfig@8.1.3)(ts-node@10.9.1)(typescript@4.9.5): resolution: {integrity: sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==} engines: {node: '>=12', npm: '>=6'} peerDependencies: @@ -12268,9 +17893,9 @@ packages: ts-node: '>=10' typescript: '>=3' dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 cosmiconfig: 8.1.3 - ts-node: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) + ts-node: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) typescript: 4.9.5 dev: true @@ -12295,6 +17920,40 @@ packages: path-type: 4.0.0 dev: true + /cosmiconfig@8.2.0: + resolution: {integrity: sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==} + engines: {node: '>=14'} + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + dev: true + + /cpu-features@0.0.8: + resolution: {integrity: sha512-BbHBvtYhUhksqTjr6bhNOjGgMnhwhGTQmOoZGD+K7BCaQDCuZl/Ve1ZxUSMRwVC4D/rkCPQ2MAIeYzrWyK7eEg==} + engines: {node: '>=10.0.0'} + requiresBuild: true + dependencies: + buildcheck: 0.0.6 + nan: 2.17.0 + dev: true + optional: true + + /crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + dev: true + + /crc32-stream@4.0.2: + resolution: {integrity: sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==} + engines: {node: '>= 10'} + dependencies: + crc-32: 1.2.2 + readable-stream: 3.6.2 + dev: true + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -12334,6 +17993,23 @@ packages: engines: {node: '>=8'} dev: true + /css-loader@6.8.1(webpack@5.76.2): + resolution: {integrity: sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + dependencies: + icss-utils: 5.1.0(postcss@8.4.24) + postcss: 8.4.24 + postcss-modules-extract-imports: 3.0.0(postcss@8.4.24) + postcss-modules-local-by-default: 4.0.3(postcss@8.4.24) + postcss-modules-scope: 3.0.0(postcss@8.4.24) + postcss-modules-values: 4.0.0(postcss@8.4.24) + postcss-value-parser: 4.2.0 + semver: 7.5.3 + webpack: 5.76.2(esbuild@0.17.19) + dev: true + /css-select@4.3.0: resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} dependencies: @@ -12351,6 +18027,10 @@ packages: fastparse: 1.1.2 dev: true + /css-unit-converter@1.1.2: + resolution: {integrity: sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==} + dev: false + /css-what@6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} @@ -12384,8 +18064,8 @@ packages: cssom: 0.3.8 dev: true - /csstype@3.1.2: - resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + /csstype@3.1.1: + resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} /csv-generate@3.4.3: resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} @@ -12426,6 +18106,77 @@ packages: - '@swc/wasm' dev: true + /d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + dependencies: + internmap: 2.0.3 + dev: false + + /d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + dev: false + + /d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + dev: false + + /d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + dev: false + + /d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + dependencies: + d3-color: 3.1.0 + dev: false + + /d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + dev: false + + /d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + dev: false + + /d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + dependencies: + d3-path: 3.1.0 + dev: false + + /d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + dependencies: + d3-time: 3.1.0 + dev: false + + /d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + dependencies: + d3-array: 3.2.4 + dev: false + + /d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + dev: false + /d@1.0.1: resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} dependencies: @@ -12433,7 +18184,7 @@ packages: type: 1.2.0 dev: true - /daisyui@2.51.5(autoprefixer@10.4.14)(postcss@8.4.21): + /daisyui@2.51.5(autoprefixer@10.4.14)(postcss@8.4.21)(ts-node@10.9.1): resolution: {integrity: sha512-L05dRw0tasmz2Ha+10LhftEGLq4kaA8vRR/T0wDaXfHwqcgsf81jfXDJ6NlZ63Z7Rl1k3rj7UHs0l0p7CM3aYA==} peerDependencies: autoprefixer: ^10.0.2 @@ -12444,7 +18195,7 @@ packages: css-selector-tokenizer: 0.8.0 postcss: 8.4.21 postcss-js: 4.0.1(postcss@8.4.21) - tailwindcss: 3.3.1(postcss@8.4.21) + tailwindcss: 3.3.2(ts-node@10.9.1) transitivePeerDependencies: - ts-node dev: true @@ -12457,7 +18208,7 @@ packages: /data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} - dev: false + dev: true /data-urls@2.0.0: resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==} @@ -12483,7 +18234,6 @@ packages: /dayjs@1.11.7: resolution: {integrity: sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==} - dev: false /debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} @@ -12516,17 +18266,6 @@ packages: dependencies: ms: 2.1.3 - /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 - /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -12552,6 +18291,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + dev: false + /decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} dev: true @@ -12630,6 +18373,10 @@ packages: dependencies: has-property-descriptors: 1.0.0 object-keys: 1.1.1 + dev: true + + /defined@1.0.1: + resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==} /defu@6.1.2: resolution: {integrity: sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==} @@ -12701,10 +18448,6 @@ packages: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} dev: false - /detect-node@2.1.0: - resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - dev: false - /detect-package-manager@2.0.1: resolution: {integrity: sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==} engines: {node: '>=12'} @@ -12717,13 +18460,26 @@ packages: hasBin: true dependencies: address: 1.2.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true - /devalue@4.3.0: - resolution: {integrity: sha512-n94yQo4LI3w7erwf84mhRUkUJfhLoCZiLyoOZ/QFsDbcWNZePrLwbQpvZBUG2TNxwV3VjCKPxkiiQA6pe3TrTA==} + /detective@5.2.1: + resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==} + engines: {node: '>=0.8.0'} + hasBin: true + dependencies: + acorn-node: 1.8.2 + defined: 1.0.1 + minimist: 1.2.8 + + /devalue@4.3.2: + resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==} + dev: false + + /diacritics@1.3.0: + resolution: {integrity: sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA==} dev: false /didyoumean@1.2.2: @@ -12761,6 +18517,36 @@ packages: /dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + /docker-compose@0.23.19: + resolution: {integrity: sha512-v5vNLIdUqwj4my80wxFDkNH+4S85zsRuH29SO7dCWVWPCMt/ohZBsGN6g6KXWifT0pzQ7uOxqEKCYCDPJ8Vz4g==} + engines: {node: '>= 6.0.0'} + dependencies: + yaml: 1.10.2 + dev: true + + /docker-modem@3.0.8: + resolution: {integrity: sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==} + engines: {node: '>= 8.0'} + dependencies: + debug: 4.3.4(supports-color@8.1.1) + readable-stream: 3.6.2 + split-ca: 1.0.1 + ssh2: 1.14.0 + transitivePeerDependencies: + - supports-color + dev: true + + /dockerode@3.3.5: + resolution: {integrity: sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==} + engines: {node: '>= 8.0'} + dependencies: + '@balena/dockerignore': 1.0.2 + docker-modem: 3.0.8 + tar-fs: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: true + /doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -12778,6 +18564,20 @@ packages: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} dev: true + /dom-css@2.1.0: + resolution: {integrity: sha512-w9kU7FAbaSh3QKijL6n59ofAhkkmMJ31GclJIz/vyQdjogfyxcB6Zf8CZyibOERI5o0Hxz30VmJS7+7r5fEj2Q==} + dependencies: + add-px-to-style: 1.0.0 + prefix-style: 2.0.1 + to-camel-case: 1.0.0 + dev: false + + /dom-helpers@3.4.0: + resolution: {integrity: sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==} + dependencies: + '@babel/runtime': 7.21.0 + dev: false + /dom-serializer@1.4.1: resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} dependencies: @@ -12827,7 +18627,7 @@ packages: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dependencies: no-case: 3.0.4 - tslib: 2.5.0 + tslib: 2.5.2 dev: true /dot-prop@5.3.0: @@ -12894,7 +18694,7 @@ packages: '@one-ini/wasm': 0.1.1 commander: 10.0.0 minimatch: 6.1.6 - semver: 7.4.0 + semver: 7.3.8 dev: true /ee-first@1.1.1: @@ -12908,8 +18708,8 @@ packages: jake: 10.8.5 dev: true - /electron-to-chromium@1.4.361: - resolution: {integrity: sha512-VocVwjPp05HUXzf3xmL0boRn5b0iyqC7amtDww84Jb1QJNPBc7F69gJyEeXRoriLBC4a5pSyckdllrXAg4mmRA==} + /electron-to-chromium@1.4.340: + resolution: {integrity: sha512-zx8hqumOqltKsv/MF50yvdAlPF9S/4PXbyfzJS6ZGhbddGkRegdwImmfSVqCkEziYzrIGZ/TlrzBND4FysfkDg==} /element-resize-detector@1.2.4: resolution: {integrity: sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==} @@ -12917,16 +18717,21 @@ packages: batch-processor: 1.0.0 dev: true + /emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + dev: true + /emittery@0.8.1: resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==} engines: {node: '>=10'} dev: true - /emmet@2.4.2: - resolution: {integrity: sha512-YgmsMkhUgzhJMgH5noGudfxqrQn1bapvF0y7C1e7A0jWFImsRrrvVslzyZz0919NED/cjFOpVWx7c973V+2S/w==} + /emmet@2.3.6: + resolution: {integrity: sha512-pLS4PBPDdxuUAmw7Me7+TcHbykTsBKN/S9XJbUOMFQrNv9MoshzyMFK/R57JBm94/6HSL4vHnDeEmxlC82NQ4A==} dependencies: - '@emmetio/abbreviation': 2.3.1 - '@emmetio/css-abbreviation': 2.1.6 + '@emmetio/abbreviation': 2.2.3 + '@emmetio/css-abbreviation': 2.1.4 dev: false /emoji-regex@8.0.0: @@ -12935,6 +18740,15 @@ packages: /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + /emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + dev: true + + /enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + dev: false + /encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} @@ -12975,6 +18789,14 @@ packages: hasBin: true dev: true + /errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + requiresBuild: true + dependencies: + prr: 1.0.1 + optional: true + /error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -13043,8 +18865,8 @@ packages: resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==} dev: true - /es-module-lexer@1.2.1: - resolution: {integrity: sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==} + /es-module-lexer@1.3.0: + resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==} dev: false /es-set-tostringtag@2.0.1: @@ -13081,10 +18903,6 @@ packages: next-tick: 1.1.0 dev: true - /es6-error@4.1.1: - resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} - dev: false - /es6-iterator@2.0.3: resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} dependencies: @@ -13249,13 +19067,13 @@ packages: resolution: {integrity: sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==} dev: true - /esbuild-register@3.4.2(esbuild@0.17.16): + /esbuild-register@3.4.2(esbuild@0.17.19): resolution: {integrity: sha512-kG/XyTDyz6+YDuyfB9ZoSIOOmgyFCH+xPRtsCa8W85HLRV5Csp+o3jWVbOSHgSLfyLc5DmP+KFDNwty4mEjC+Q==} peerDependencies: esbuild: '>=0.12 <1' dependencies: - debug: 4.3.4 - esbuild: 0.17.16 + debug: 4.3.4(supports-color@8.1.1) + esbuild: 0.17.19 transitivePeerDependencies: - supports-color dev: true @@ -13351,34 +19169,92 @@ packages: '@esbuild/win32-x64': 0.16.17 dev: true - /esbuild@0.17.16: - resolution: {integrity: sha512-aeSuUKr9aFVY9Dc8ETVELGgkj4urg5isYx8pLf4wlGgB0vTFjxJQdHnNH6Shmx4vYYrOTLCHtRI5i1XZ9l2Zcg==} + /esbuild@0.17.14: + resolution: {integrity: sha512-vOO5XhmVj/1XQR9NQ1UPq6qvMYL7QFJU57J5fKBKBKxp17uDt5PgxFDb4A2nEiXhr1qQs4x0F5+66hVVw4ruNw==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.17.16 - '@esbuild/android-arm64': 0.17.16 - '@esbuild/android-x64': 0.17.16 - '@esbuild/darwin-arm64': 0.17.16 - '@esbuild/darwin-x64': 0.17.16 - '@esbuild/freebsd-arm64': 0.17.16 - '@esbuild/freebsd-x64': 0.17.16 - '@esbuild/linux-arm': 0.17.16 - '@esbuild/linux-arm64': 0.17.16 - '@esbuild/linux-ia32': 0.17.16 - '@esbuild/linux-loong64': 0.17.16 - '@esbuild/linux-mips64el': 0.17.16 - '@esbuild/linux-ppc64': 0.17.16 - '@esbuild/linux-riscv64': 0.17.16 - '@esbuild/linux-s390x': 0.17.16 - '@esbuild/linux-x64': 0.17.16 - '@esbuild/netbsd-x64': 0.17.16 - '@esbuild/openbsd-x64': 0.17.16 - '@esbuild/sunos-x64': 0.17.16 - '@esbuild/win32-arm64': 0.17.16 - '@esbuild/win32-ia32': 0.17.16 - '@esbuild/win32-x64': 0.17.16 + '@esbuild/android-arm': 0.17.14 + '@esbuild/android-arm64': 0.17.14 + '@esbuild/android-x64': 0.17.14 + '@esbuild/darwin-arm64': 0.17.14 + '@esbuild/darwin-x64': 0.17.14 + '@esbuild/freebsd-arm64': 0.17.14 + '@esbuild/freebsd-x64': 0.17.14 + '@esbuild/linux-arm': 0.17.14 + '@esbuild/linux-arm64': 0.17.14 + '@esbuild/linux-ia32': 0.17.14 + '@esbuild/linux-loong64': 0.17.14 + '@esbuild/linux-mips64el': 0.17.14 + '@esbuild/linux-ppc64': 0.17.14 + '@esbuild/linux-riscv64': 0.17.14 + '@esbuild/linux-s390x': 0.17.14 + '@esbuild/linux-x64': 0.17.14 + '@esbuild/netbsd-x64': 0.17.14 + '@esbuild/openbsd-x64': 0.17.14 + '@esbuild/sunos-x64': 0.17.14 + '@esbuild/win32-arm64': 0.17.14 + '@esbuild/win32-ia32': 0.17.14 + '@esbuild/win32-x64': 0.17.14 + + /esbuild@0.17.19: + resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.17.19 + '@esbuild/android-arm64': 0.17.19 + '@esbuild/android-x64': 0.17.19 + '@esbuild/darwin-arm64': 0.17.19 + '@esbuild/darwin-x64': 0.17.19 + '@esbuild/freebsd-arm64': 0.17.19 + '@esbuild/freebsd-x64': 0.17.19 + '@esbuild/linux-arm': 0.17.19 + '@esbuild/linux-arm64': 0.17.19 + '@esbuild/linux-ia32': 0.17.19 + '@esbuild/linux-loong64': 0.17.19 + '@esbuild/linux-mips64el': 0.17.19 + '@esbuild/linux-ppc64': 0.17.19 + '@esbuild/linux-riscv64': 0.17.19 + '@esbuild/linux-s390x': 0.17.19 + '@esbuild/linux-x64': 0.17.19 + '@esbuild/netbsd-x64': 0.17.19 + '@esbuild/openbsd-x64': 0.17.19 + '@esbuild/sunos-x64': 0.17.19 + '@esbuild/win32-arm64': 0.17.19 + '@esbuild/win32-ia32': 0.17.19 + '@esbuild/win32-x64': 0.17.19 + + /esbuild@0.18.12: + resolution: {integrity: sha512-XuOVLDdtsDslXStStduT41op21Ytmf4/BDS46aa3xPJ7X5h2eMWBF1oAe3QjUH3bDksocNXgzGUZ7XHIBya6Tg==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.12 + '@esbuild/android-arm64': 0.18.12 + '@esbuild/android-x64': 0.18.12 + '@esbuild/darwin-arm64': 0.18.12 + '@esbuild/darwin-x64': 0.18.12 + '@esbuild/freebsd-arm64': 0.18.12 + '@esbuild/freebsd-x64': 0.18.12 + '@esbuild/linux-arm': 0.18.12 + '@esbuild/linux-arm64': 0.18.12 + '@esbuild/linux-ia32': 0.18.12 + '@esbuild/linux-loong64': 0.18.12 + '@esbuild/linux-mips64el': 0.18.12 + '@esbuild/linux-ppc64': 0.18.12 + '@esbuild/linux-riscv64': 0.18.12 + '@esbuild/linux-s390x': 0.18.12 + '@esbuild/linux-x64': 0.18.12 + '@esbuild/netbsd-x64': 0.18.12 + '@esbuild/openbsd-x64': 0.18.12 + '@esbuild/sunos-x64': 0.18.12 + '@esbuild/win32-arm64': 0.18.12 + '@esbuild/win32-ia32': 0.18.12 + '@esbuild/win32-x64': 0.18.12 /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} @@ -13418,13 +19294,13 @@ packages: source-map: 0.6.1 dev: true - /eslint-config-prettier@6.15.0(eslint@8.38.0): + /eslint-config-prettier@6.15.0(eslint@8.36.0): resolution: {integrity: sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==} hasBin: true peerDependencies: eslint: '>=3.14.1' dependencies: - eslint: 8.38.0 + eslint: 8.36.0 get-stdin: 6.0.0 dev: true @@ -13437,16 +19313,25 @@ packages: eslint: 8.22.0 dev: true - /eslint-config-prettier@8.8.0(eslint@8.38.0): + /eslint-config-prettier@8.8.0(eslint@8.36.0): resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.38.0 + eslint: 8.36.0 + dev: true + + /eslint-config-prettier@8.8.0(eslint@8.44.0): + resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.44.0 dev: true - /eslint-config-standard-with-typescript@23.0.0(@typescript-eslint/eslint-plugin@5.58.0)(eslint-plugin-import@2.27.5)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.38.0)(typescript@4.9.5): + /eslint-config-standard-with-typescript@23.0.0(@typescript-eslint/eslint-plugin@5.61.0)(eslint-plugin-import@2.27.5)(eslint-plugin-n@15.6.1)(eslint-plugin-promise@6.1.1)(eslint@8.44.0)(typescript@4.9.5): resolution: {integrity: sha512-iaaWifImn37Z1OXbNW1es7KI+S7D408F9ys0bpaQf2temeBWlvb0Nc5qHkOgYaRb5QxTZT32GGeN1gtswASOXA==} peerDependencies: '@typescript-eslint/eslint-plugin': ^5.0.0 @@ -13456,19 +19341,19 @@ packages: eslint-plugin-promise: ^6.0.0 typescript: '*' dependencies: - '@typescript-eslint/eslint-plugin': 5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.38.0)(typescript@4.9.5) - '@typescript-eslint/parser': 5.58.0(eslint@8.38.0)(typescript@4.9.5) - eslint: 8.38.0 - eslint-config-standard: 17.0.0(eslint-plugin-import@2.27.5)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.38.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0) - eslint-plugin-n: 15.7.0(eslint@8.38.0) - eslint-plugin-promise: 6.1.1(eslint@8.38.0) + '@typescript-eslint/eslint-plugin': 5.61.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.61.0(eslint@8.44.0)(typescript@4.9.5) + eslint: 8.44.0 + eslint-config-standard: 17.0.0(eslint-plugin-import@2.27.5)(eslint-plugin-n@15.6.1)(eslint-plugin-promise@6.1.1)(eslint@8.44.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.61.0)(eslint@8.44.0) + eslint-plugin-n: 15.6.1(eslint@8.44.0) + eslint-plugin-promise: 6.1.1(eslint@8.44.0) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /eslint-config-standard@17.0.0(eslint-plugin-import@2.27.5)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.38.0): + /eslint-config-standard@17.0.0(eslint-plugin-import@2.27.5)(eslint-plugin-n@15.6.1)(eslint-plugin-promise@6.1.1)(eslint@8.44.0): resolution: {integrity: sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==} peerDependencies: eslint: ^8.0.1 @@ -13476,24 +19361,24 @@ packages: eslint-plugin-n: ^15.0.0 eslint-plugin-promise: ^6.0.0 dependencies: - eslint: 8.38.0 - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0) - eslint-plugin-n: 15.7.0(eslint@8.38.0) - eslint-plugin-promise: 6.1.1(eslint@8.38.0) + eslint: 8.44.0 + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.61.0)(eslint@8.44.0) + eslint-plugin-n: 15.6.1(eslint@8.44.0) + eslint-plugin-promise: 6.1.1(eslint@8.44.0) dev: true /eslint-import-resolver-node@0.3.7: resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} dependencies: debug: 3.2.7 - is-core-module: 2.12.0 - resolve: 1.22.2 + is-core-module: 2.11.0 + resolve: 1.22.1 transitivePeerDependencies: - supports-color dev: true - /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.58.0)(eslint-plugin-import@2.27.5)(eslint@8.38.0): - resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} + /eslint-import-resolver-typescript@3.5.3(eslint-plugin-import@2.27.5)(eslint@8.36.0): + resolution: {integrity: sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -13501,22 +19386,48 @@ packages: dependencies: debug: 4.3.4(supports-color@8.1.1) enhanced-resolve: 5.12.0 - eslint: 8.38.0 - eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0) - get-tsconfig: 4.5.0 - globby: 13.1.4 - is-core-module: 2.12.0 + eslint: 8.36.0 + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-typescript@3.5.3)(eslint@8.36.0) + get-tsconfig: 4.4.0 + globby: 13.1.3 + is-core-module: 2.11.0 is-glob: 4.0.3 synckit: 0.8.5 transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - supports-color dev: true - /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0): + /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.3)(eslint@8.36.0): + resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} + 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': 5.56.0(eslint@8.36.0)(typescript@4.9.5) + debug: 3.2.7 + eslint: 8.36.0 + eslint-import-resolver-node: 0.3.7 + eslint-import-resolver-typescript: 3.5.3(eslint-plugin-import@2.27.5)(eslint@8.36.0) + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-node@0.3.7)(eslint@8.22.0): resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -13537,16 +19448,15 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.58.0(eslint@8.38.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.56.0(eslint@8.22.0)(typescript@4.9.5) debug: 3.2.7 - eslint: 8.38.0 + eslint: 8.22.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.58.0)(eslint-plugin-import@2.27.5)(eslint@8.38.0) transitivePeerDependencies: - supports-color dev: true - /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint@8.22.0): + /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-node@0.3.7)(eslint@8.44.0): resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -13567,54 +19477,54 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.58.0(eslint@8.22.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.61.0(eslint@8.44.0)(typescript@4.9.5) debug: 3.2.7 - eslint: 8.22.0 + eslint: 8.44.0 eslint-import-resolver-node: 0.3.7 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-astro@0.21.1(eslint@8.38.0): - resolution: {integrity: sha512-gzT9R0b/Hl5sks8/WSMTlzu2VC7vdd99MnBYkq61Mk1zBWQ+C9MAuHeTcU72sFmR5XJX56NNURY6H+cN2StRcA==} + /eslint-plugin-astro@0.21.0(eslint@8.44.0): + resolution: {integrity: sha512-7pEhTfYT+tlOMOSmQV77TNgCeuFZgqSAqJPTHh6LYlwLqYyBQLURc5RRtlQqCJkufSh4Fan4nsV3LXCT/vjpCA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '>=7.0.0' dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - '@typescript-eslint/types': 5.58.0 + '@typescript-eslint/types': 5.61.0 astro-eslint-parser: 0.9.5 - eslint: 8.38.0 - eslint-utils: 3.0.0(eslint@8.38.0) - postcss: 8.4.21 + eslint: 8.44.0 + eslint-utils: 3.0.0(eslint@8.44.0) + postcss: 8.4.24 postcss-selector-parser: 6.0.11 + sourcemap-codec: 1.4.8 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-es@4.1.0(eslint@8.38.0): + /eslint-plugin-es@4.1.0(eslint@8.44.0): resolution: {integrity: sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==} engines: {node: '>=8.10.0'} peerDependencies: eslint: '>=4.19.1' dependencies: - eslint: 8.38.0 + eslint: 8.44.0 eslint-utils: 2.1.0 regexpp: 3.2.0 dev: true - /eslint-plugin-eslint-comments@3.2.0(eslint@8.38.0): + /eslint-plugin-eslint-comments@3.2.0(eslint@8.36.0): resolution: {integrity: sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==} engines: {node: '>=6.5.0'} peerDependencies: eslint: '>=4.19.1' dependencies: escape-string-regexp: 1.0.5 - eslint: 8.38.0 + eslint: 8.36.0 ignore: 5.2.4 dev: true - /eslint-plugin-functional@3.7.2(eslint@8.38.0)(typescript@4.9.5): + /eslint-plugin-functional@3.7.2(eslint@8.36.0)(typescript@4.9.5): resolution: {integrity: sha512-BuWPOeE0nuXYlZjObYOHnYf7G3iG+sysxw84I579MsrH+hy5XdXb2sdabmXQ5z7eFGCg2/DWNbZ/yz5GAgtcUg==} engines: {node: '>=10.18.0'} peerDependencies: @@ -13627,18 +19537,18 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/experimental-utils': 4.33.0(eslint@8.38.0)(typescript@4.9.5) + '@typescript-eslint/experimental-utils': 4.33.0(eslint@8.36.0)(typescript@4.9.5) array.prototype.flatmap: 1.3.1 deepmerge: 4.3.1 escape-string-regexp: 4.0.0 - eslint: 8.38.0 + eslint: 8.36.0 object.fromentries: 2.0.6 typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0): + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-typescript@3.5.3)(eslint@8.36.0): resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: @@ -13648,21 +19558,21 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.58.0(eslint@8.38.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.56.0(eslint@8.36.0)(typescript@4.9.5) array-includes: 3.1.6 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.38.0 + eslint: 8.36.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.38.0) + eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.3)(eslint@8.36.0) has: 1.0.3 - is-core-module: 2.12.0 + is-core-module: 2.11.0 is-glob: 4.0.3 minimatch: 3.1.2 object.values: 1.1.6 - resolve: 1.22.2 + resolve: 1.22.1 semver: 6.3.0 tsconfig-paths: 3.14.2 transitivePeerDependencies: @@ -13671,7 +19581,7 @@ packages: - supports-color dev: true - /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.58.0)(eslint@8.22.0): + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.56.0)(eslint@8.22.0): resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: @@ -13681,7 +19591,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.58.0(eslint@8.22.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.56.0(eslint@8.22.0)(typescript@4.9.5) array-includes: 3.1.6 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 @@ -13689,13 +19599,46 @@ packages: doctrine: 2.1.0 eslint: 8.22.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint@8.22.0) + eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.56.0)(eslint-import-resolver-node@0.3.7)(eslint@8.22.0) has: 1.0.3 - is-core-module: 2.12.0 + is-core-module: 2.11.0 is-glob: 4.0.3 minimatch: 3.1.2 object.values: 1.1.6 - resolve: 1.22.2 + resolve: 1.22.1 + semver: 6.3.0 + tsconfig-paths: 3.14.2 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.61.0)(eslint@8.44.0): + resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} + 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': 5.61.0(eslint@8.44.0)(typescript@4.9.5) + array-includes: 3.1.6 + array.prototype.flat: 1.3.1 + array.prototype.flatmap: 1.3.1 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.44.0 + eslint-import-resolver-node: 0.3.7 + eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-node@0.3.7)(eslint@8.44.0) + has: 1.0.3 + is-core-module: 2.11.0 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.values: 1.1.6 + resolve: 1.22.1 semver: 6.3.0 tsconfig-paths: 3.14.2 transitivePeerDependencies: @@ -13704,30 +19647,30 @@ packages: - supports-color dev: true - /eslint-plugin-n@15.7.0(eslint@8.38.0): - resolution: {integrity: sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==} + /eslint-plugin-n@15.6.1(eslint@8.44.0): + resolution: {integrity: sha512-R9xw9OtCRxxaxaszTQmQAlPgM+RdGjaL1akWuY/Fv9fRAi8Wj4CUKc6iYVG8QNRjRuo8/BqVYIpfqberJUEacA==} engines: {node: '>=12.22.0'} peerDependencies: eslint: '>=7.0.0' dependencies: builtins: 5.0.1 - eslint: 8.38.0 - eslint-plugin-es: 4.1.0(eslint@8.38.0) - eslint-utils: 3.0.0(eslint@8.38.0) + eslint: 8.44.0 + eslint-plugin-es: 4.1.0(eslint@8.44.0) + eslint-utils: 3.0.0(eslint@8.44.0) ignore: 5.2.4 - is-core-module: 2.12.0 + is-core-module: 2.11.0 minimatch: 3.1.2 resolve: 1.22.2 - semver: 7.4.0 + semver: 7.5.3 dev: true - /eslint-plugin-promise@6.1.1(eslint@8.38.0): + /eslint-plugin-promise@6.1.1(eslint@8.44.0): resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - eslint: 8.38.0 + eslint: 8.44.0 dev: true /eslint-plugin-react-hooks@4.6.0(eslint@8.22.0): @@ -13739,6 +19682,40 @@ packages: eslint: 8.22.0 dev: true + /eslint-plugin-react-hooks@4.6.0(eslint@8.38.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.38.0 + dev: true + + /eslint-plugin-react-hooks@4.6.0(eslint@8.44.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.44.0 + dev: true + + /eslint-plugin-react-refresh@0.3.4(eslint@8.38.0): + resolution: {integrity: sha512-E0ViBglxSQAERBp6eTj5fPgtCRtDonnbCFiVQBhf4Dto2blJRxg1dFUMdMh7N6ljTI4UwPhHwYDQ3Dyo4m6bwA==} + peerDependencies: + eslint: '>=7' + dependencies: + eslint: 8.38.0 + dev: true + + /eslint-plugin-react-refresh@0.4.1(eslint@8.44.0): + resolution: {integrity: sha512-QgrvtRJkmV+m4w953LS146+6RwEe5waouubFVNLBfOjXJf6MLczjymO8fOcKj9jMS8aKkTCMJqiPu2WEeFI99A==} + peerDependencies: + eslint: '>=7' + dependencies: + eslint: 8.44.0 + dev: true + /eslint-plugin-react@7.32.2(eslint@8.22.0): resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} engines: {node: '>=4'} @@ -13770,7 +19747,7 @@ packages: eslint: '>=6' dependencies: '@storybook/csf': 0.0.1 - '@typescript-eslint/utils': 5.58.0(eslint@8.22.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.59.0(eslint@8.22.0)(typescript@4.9.5) eslint: 8.22.0 requireindex: 1.2.0 ts-dedent: 2.2.0 @@ -13779,27 +19756,93 @@ packages: - typescript dev: true - /eslint-plugin-svelte3@4.0.0(eslint@8.22.0)(svelte@3.58.0): + /eslint-plugin-storybook@0.6.11(eslint@8.44.0)(typescript@4.9.5): + resolution: {integrity: sha512-lIVmCqQgA0bhcuS1yWYBFrnPHBKPEQI+LHPDtlN81UE1/17onCqgwUW7Nyt7gS2OHjCAiOR4npjTGEoe0hssKw==} + engines: {node: 12.x || 14.x || >= 16} + peerDependencies: + eslint: '>=6' + dependencies: + '@storybook/csf': 0.0.1 + '@typescript-eslint/utils': 5.59.0(eslint@8.44.0)(typescript@4.9.5) + eslint: 8.44.0 + requireindex: 1.2.0 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /eslint-plugin-svelte3@4.0.0(eslint@8.22.0)(svelte@3.57.0): resolution: {integrity: sha512-OIx9lgaNzD02+MDFNLw0GEUbuovNcglg+wnd/UY0fbZmlQSz7GlQiQ1f+yX0XvC07XPcDOnFcichqI3xCwp71g==} peerDependencies: eslint: '>=8.0.0' svelte: ^3.2.0 dependencies: eslint: 8.22.0 - svelte: 3.58.0 + svelte: 3.57.0 dev: true - /eslint-plugin-tailwindcss@3.11.0(tailwindcss@3.3.1): - resolution: {integrity: sha512-RaraOG4D6VXutKnoNvFQ4+frTWGJDKtezy1yCrGFS7Um1to/npDNdh2GL19IRoGB/eanbtwhxFXy+xyEw0grAg==} + /eslint-plugin-tailwindcss@3.10.1(tailwindcss@3.2.7): + resolution: {integrity: sha512-NLPZ6b6nd/8CgGNMQ6NDiPUfBLQpSGu/u9RyX3MCZOwzNs2dFt1OamNAiRuo3Ixh7Gv4t5UcAcdNt8z74UDJkA==} engines: {node: '>=12.13.0'} peerDependencies: tailwindcss: ^3.2.2 dependencies: - fast-glob: 3.2.12 - postcss: 8.4.21 - tailwindcss: 3.3.1(postcss@8.4.21) + fast-glob: 3.3.0 + postcss: 8.4.24 + tailwindcss: 3.2.7(postcss@8.4.21)(ts-node@10.9.1) dev: false + /eslint-plugin-unused-imports@2.0.0(@typescript-eslint/eslint-plugin@5.56.0)(eslint@8.22.0): + resolution: {integrity: sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.0.0 + eslint: ^8.0.0 + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.22.0)(typescript@4.9.5) + eslint: 8.22.0 + eslint-rule-composer: 0.3.0 + dev: true + + /eslint-plugin-unused-imports@2.0.0(@typescript-eslint/eslint-plugin@5.56.0)(eslint@8.36.0): + resolution: {integrity: sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.0.0 + eslint: ^8.0.0 + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.36.0)(typescript@4.9.5) + eslint: 8.36.0 + eslint-rule-composer: 0.3.0 + dev: true + + /eslint-plugin-unused-imports@2.0.0(@typescript-eslint/eslint-plugin@5.61.0)(eslint@8.44.0): + resolution: {integrity: sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.0.0 + eslint: ^8.0.0 + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 5.61.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@4.9.5) + eslint: 8.44.0 + eslint-rule-composer: 0.3.0 + dev: true + + /eslint-rule-composer@0.3.0: + resolution: {integrity: sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==} + engines: {node: '>=4.0.0'} + dev: true + /eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -13815,6 +19858,14 @@ packages: esrecurse: 4.3.0 estraverse: 5.3.0 + /eslint-scope@7.2.0: + resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + /eslint-utils@2.1.0: resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} engines: {node: '>=6'} @@ -13831,13 +19882,23 @@ packages: eslint: 8.22.0 eslint-visitor-keys: 2.1.0 - /eslint-utils@3.0.0(eslint@8.38.0): + /eslint-utils@3.0.0(eslint@8.36.0): resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: eslint: '>=5' dependencies: - eslint: 8.38.0 + eslint: 8.36.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-utils@3.0.0(eslint@8.44.0): + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.44.0 eslint-visitor-keys: 2.1.0 dev: true @@ -13849,8 +19910,12 @@ packages: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} engines: {node: '>=10'} - /eslint-visitor-keys@3.4.0: - resolution: {integrity: sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==} + /eslint-visitor-keys@3.3.0: + resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + /eslint-visitor-keys@3.4.1: + resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} /eslint@8.22.0: @@ -13864,13 +19929,13 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 eslint-utils: 3.0.0(eslint@8.22.0) - eslint-visitor-keys: 3.4.0 - espree: 9.5.1 + eslint-visitor-keys: 3.3.0 + espree: 9.5.0 esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -13900,16 +19965,65 @@ packages: transitivePeerDependencies: - supports-color + /eslint@8.36.0: + resolution: {integrity: sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.36.0) + '@eslint-community/regexpp': 4.4.1 + '@eslint/eslintrc': 2.0.1 + '@eslint/js': 8.36.0 + '@humanwhocodes/config-array': 0.11.8 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4(supports-color@8.1.1) + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.1.1 + eslint-visitor-keys: 3.3.0 + espree: 9.5.0 + 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.20.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-sdsl: 4.4.0 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + /eslint@8.38.0: resolution: {integrity: sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.38.0) - '@eslint-community/regexpp': 4.5.0 - '@eslint/eslintrc': 2.0.2 + '@eslint-community/regexpp': 4.4.1 + '@eslint/eslintrc': 2.0.3 '@eslint/js': 8.38.0 - '@humanwhocodes/config-array': 0.11.8 + '@humanwhocodes/config-array': 0.11.10 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 ajv: 6.12.6 @@ -13918,9 +20032,9 @@ packages: debug: 4.3.4(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.1.1 - eslint-visitor-keys: 3.4.0 - espree: 9.5.1 + eslint-scope: 7.2.0 + eslint-visitor-keys: 3.4.1 + espree: 9.5.2 esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -13949,13 +20063,78 @@ packages: - supports-color dev: true - /espree@9.5.1: - resolution: {integrity: sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==} + /eslint@8.44.0: + resolution: {integrity: sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) + '@eslint-community/regexpp': 4.4.1 + '@eslint/eslintrc': 2.1.0 + '@eslint/js': 8.44.0 + '@humanwhocodes/config-array': 0.11.10 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4(supports-color@8.1.1) + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.0 + eslint-visitor-keys: 3.4.1 + espree: 9.6.0 + 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.20.0 + graphemer: 1.4.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + 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 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.5.0: + resolution: {integrity: sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: acorn: 8.8.2 acorn-jsx: 5.3.2(acorn@8.8.2) - eslint-visitor-keys: 3.4.0 + eslint-visitor-keys: 3.4.1 + + /espree@9.5.2: + resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.9.0 + acorn-jsx: 5.3.2(acorn@8.9.0) + eslint-visitor-keys: 3.4.1 + + /espree@9.6.0: + resolution: {integrity: sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.9.0 + acorn-jsx: 5.3.2(acorn@8.9.0) + eslint-visitor-keys: 3.4.1 + dev: true /esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} @@ -13987,8 +20166,8 @@ packages: resolution: {integrity: sha512-YNF+mZ/Wu2FU/gvmzuWtYc8rloubL7wfXCTgouFrnjGVXPA/EeYYA7pupXWrb3Iv1cTBeSSxxJIbK23l4MRNqg==} engines: {node: '>=8.3.0'} dependencies: - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/traverse': 7.22.5 + '@babel/types': 7.22.5 c8: 7.13.0 transitivePeerDependencies: - supports-color @@ -14034,6 +20213,10 @@ packages: /estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + /estree-walker@3.0.0: + resolution: {integrity: sha512-s6ceX0NFiU/vKPiKvFdR83U1Zffu7upwZsGwpoqfg5rbbq1l50WQ5hCeIvM6E6oD4shUHCYMsiFPns4Jk0YfMQ==} + dev: false + /estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} dependencies: @@ -14063,6 +20246,10 @@ packages: resolution: {integrity: sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==} dev: false + /eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + dev: false + /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -14270,6 +20457,11 @@ packages: /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + /fast-equals@5.0.1: + resolution: {integrity: sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==} + engines: {node: '>=6.0.0'} + dev: false + /fast-glob@3.2.11: resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} engines: {node: '>=8.6.0'} @@ -14302,6 +20494,16 @@ packages: micromatch: 4.0.5 dev: true + /fast-glob@3.3.0: + resolution: {integrity: sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -14338,20 +20540,50 @@ packages: bser: 2.1.1 dev: true + /fbemitter@3.0.0: + resolution: {integrity: sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==} + dependencies: + fbjs: 3.0.5 + transitivePeerDependencies: + - encoding + dev: false + + /fbjs-css-vars@1.0.2: + resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} + dev: false + + /fbjs@3.0.5: + resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} + dependencies: + cross-fetch: 3.1.5 + fbjs-css-vars: 1.0.2 + loose-envify: 1.4.0 + object-assign: 4.1.1 + promise: 7.3.1 + setimmediate: 1.0.5 + ua-parser-js: 1.0.35 + transitivePeerDependencies: + - encoding + dev: false + /fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} dependencies: pend: 1.2.0 dev: true - /felte@1.2.7(svelte@3.58.0): + /fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + dev: false + + /felte@1.2.7(svelte@3.57.0): resolution: {integrity: sha512-VfCkYBODReCUrYeRMmJ9lRs7O/pC4PYKMTT7E2K6m9UzmTGpm3Ql3C518J3gUVVG5ZeEeSEifUaqmrAcaWB89w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: svelte: ^3.31.0 dependencies: '@felte/core': 1.3.7 - svelte: 3.58.0 + svelte: 3.57.0 dev: false /fetch-blob@3.2.0: @@ -14360,7 +20592,7 @@ packages: dependencies: node-domexception: 1.0.0 web-streams-polyfill: 3.2.1 - dev: false + dev: true /fetch-retry@5.0.4: resolution: {integrity: sha512-LXcdgpdcVedccGg0AZqg+S8lX/FCdwXD92WNZ5k5qsb0irRhSFsBOpcJt7oevyqT2/C2nEE0zSFNdBEpj3YOSw==} @@ -14378,11 +20610,11 @@ packages: dependencies: flat-cache: 3.0.4 - /file-system-cache@2.1.1: - resolution: {integrity: sha512-vgZ1uDsK29DM4pptUOv47zdJO2tYM5M/ERyAE9Jk0QBN6e64Md+a+xJSOp68dCCDH4niFMVD8nC8n8A5ic0bmg==} + /file-system-cache@2.3.0: + resolution: {integrity: sha512-l4DMNdsIPsVnKrgEXbJwDJsA5mB8rGwHYERMgqQx/xAUtChPJMre1bXBzDEqqVbWv9AIbFezXMxeEkZDSrXUOQ==} dependencies: fs-extra: 11.1.1 - ramda: 0.28.0 + ramda: 0.29.0 dev: true /file-type@12.4.2: @@ -14517,10 +20749,26 @@ packages: /flatted@3.2.7: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} - /flow-parser@0.203.1: - resolution: {integrity: sha512-Nw2M8MPP/Zb+yhvmPDEjzkCXLtgyWGKXZjAYOVftm+wIf3xd4FKa7nRI9v67rODs0WzxMbPc8IPs/7o/dyxo/Q==} - engines: {node: '>=0.4.0'} - dev: true + /flow-parser@0.202.1: + resolution: {integrity: sha512-IA8mhyNEUtzAKh+lj1yNDLFiUr1NSwPC+exQgghQNARFU/DeWGpoNmuYYzMDFIYsOdVdDoTJTxRc+/cS9CVvNg==} + engines: {node: '>=0.4.0'} + dev: true + + /flux@4.0.4(react@18.2.0): + resolution: {integrity: sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==} + peerDependencies: + react: ^15.0.2 || ^16.0.0 || ^17.0.0 + dependencies: + fbemitter: 3.0.0 + fbjs: 3.0.5 + react: 18.2.0 + transitivePeerDependencies: + - encoding + dev: false + + /fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + dev: false /follow-redirects@1.15.2: resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} @@ -14560,17 +20808,17 @@ packages: typescript: '>3.6.0' webpack: ^5.11.0 dependencies: - '@babel/code-frame': 7.21.4 + '@babel/code-frame': 7.22.5 chalk: 4.1.2 chokidar: 3.5.3 cosmiconfig: 7.1.0 deepmerge: 4.3.1 fs-extra: 10.1.0 - memfs: 3.5.0 + memfs: 3.4.13 minimatch: 3.1.2 node-abort-controller: 3.1.1 schema-utils: 3.1.1 - semver: 7.4.0 + semver: 7.5.3 tapable: 2.2.1 typescript: 4.9.5 webpack: 5.76.2 @@ -14612,7 +20860,7 @@ packages: engines: {node: '>=12.20.0'} dependencies: fetch-blob: 3.2.0 - dev: false + dev: true /formidable@1.2.6: resolution: {integrity: sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==} @@ -14625,7 +20873,6 @@ packages: /fraction.js@4.2.0: resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} - dev: true /framer-motion@8.5.5(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-5IDx5bxkjWHWUF3CVJoSyUVOtrbAxtzYBBowRE2uYI/6VYhkEBD+rbTHEGuUmbGHRj6YqqSfoG7Aa1cLyWCrBA==} @@ -14659,6 +20906,15 @@ packages: universalify: 2.0.0 dev: true + /fs-extra@11.1.0: + resolution: {integrity: sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + /fs-extra@11.1.1: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} engines: {node: '>=14.14'} @@ -14822,8 +21078,8 @@ packages: get-intrinsic: 1.2.0 dev: true - /get-tsconfig@4.5.0: - resolution: {integrity: sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ==} + /get-tsconfig@4.4.0: + resolution: {integrity: sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==} dev: true /giget@1.1.2: @@ -14835,7 +21091,7 @@ packages: https-proxy-agent: 5.0.1 mri: 1.2.0 node-fetch-native: 1.1.0 - pathe: 1.1.0 + pathe: 1.1.1 tar: 6.1.13 transitivePeerDependencies: - supports-color @@ -14938,28 +21194,16 @@ packages: once: 1.4.0 dev: true - /glob@9.3.5: - resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + /glob@9.3.2: + resolution: {integrity: sha512-BTv/JhKXFEHsErMte/AnfiSv8yYOLLiyH2lTg8vn02O21zWFgHPTfxtgn1QRe7NRgggUhC8hacR2Re94svHqeA==} engines: {node: '>=16 || 14 >=14.17'} dependencies: fs.realpath: 1.0.0 - minimatch: 8.0.4 - minipass: 4.2.8 - path-scurry: 1.6.4 + minimatch: 7.4.3 + minipass: 4.2.5 + path-scurry: 1.6.3 dev: true - /global-agent@3.0.0: - resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} - engines: {node: '>=10.0'} - dependencies: - boolean: 3.2.0 - es6-error: 4.1.1 - matcher: 3.0.0 - roarr: 2.15.4 - semver: 7.4.0 - serialize-error: 7.0.1 - dev: false - /global-dirs@0.1.1: resolution: {integrity: sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==} engines: {node: '>=4'} @@ -15009,6 +21253,7 @@ packages: engines: {node: '>= 0.4'} dependencies: define-properties: 1.2.0 + dev: true /globalyzer@0.1.0: resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} @@ -15019,17 +21264,17 @@ packages: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.2.12 + fast-glob: 3.3.0 ignore: 5.2.4 merge2: 1.4.1 slash: 3.0.0 - /globby@13.1.4: - resolution: {integrity: sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==} + /globby@13.1.3: + resolution: {integrity: sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: dir-glob: 3.0.1 - fast-glob: 3.2.12 + fast-glob: 3.3.0 ignore: 5.2.4 merge2: 1.4.1 slash: 4.0.0 @@ -15038,12 +21283,12 @@ packages: /globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - /goober@2.1.12(csstype@3.1.2): + /goober@2.1.12(csstype@3.1.1): resolution: {integrity: sha512-yXHAvO08FU1JgTXX6Zn6sYCUFfB/OJSX8HHjDSgerZHZmFKAb08cykp5LBw5QnmyMcZyPRMqkdyHUSSzge788Q==} peerDependencies: csstype: ^3.0.10 dependencies: - csstype: 3.1.2 + csstype: 3.1.1 dev: false /gopd@1.0.1: @@ -15057,6 +21302,10 @@ packages: /grapheme-splitter@1.0.4: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + /graphql-tag@2.12.6(graphql@16.6.0): resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} engines: {node: '>=10'} @@ -15064,7 +21313,7 @@ packages: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: graphql: 16.6.0 - tslib: 2.5.0 + tslib: 2.5.2 dev: false /graphql-ws@5.5.5(graphql@16.6.0): @@ -15140,16 +21389,11 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - /has-package-exports@1.3.0: - resolution: {integrity: sha512-e9OeXPQnmPhYoJ63lXC4wWe34TxEGZDZ3OQX9XRqp2VwsfLl3bQBy7VehLnd34g3ef8CmYlBLGqEMKXuz8YazQ==} - dependencies: - '@ljharb/has-package-exports-patterns': 0.0.2 - dev: false - /has-property-descriptors@1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} dependencies: get-intrinsic: 1.2.0 + dev: true /has-proto@1.0.1: resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} @@ -15280,8 +21524,8 @@ packages: /headers-polyfill@3.1.2: resolution: {integrity: sha512-tWCK4biJ6hcLqTviLXVR9DTRfYGQMXEIUj3gwJ2rZ5wO/at3XtkI4g8mCvFdUF9l1KMBNCfmNAdnahm1cgavQA==} - /helmet@6.1.5: - resolution: {integrity: sha512-UgAvdoG0BhF9vcCh/j0bWtElo2ZHHk6OzC98NLCM6zK03DEVSM0vUAtT7iR+oTo2Mi6sGelAH3tL6B/uUWxV4g==} + /helmet@6.0.1: + resolution: {integrity: sha512-8wo+VdQhTMVBMCITYZaGTbE4lvlthelPYSvoyNvk4RECTmrVjMerp9RfUOQXZWLvCcAn1pKj7ZRxK4lI9Alrcw==} engines: {node: '>=14.0.0'} dev: false @@ -15347,7 +21591,7 @@ packages: he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.16.9 + terser: 5.16.8 dev: true /html-parse-stringify@3.0.1: @@ -15413,7 +21657,7 @@ packages: engines: {node: '>= 6.0.0'} dependencies: agent-base: 5.1.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -15423,7 +21667,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -15452,6 +21696,13 @@ packages: hasBin: true dev: true + /i18n-iso-countries@7.6.0: + resolution: {integrity: sha512-HPKjOUKS0BkjiY4ayrsuFbu7Ock++pXLs+FAOYl4WfTL5L0ploEH68fiRAP6Zev5g/jvMFt54KcPGJcb942wbg==} + engines: {node: '>= 12'} + dependencies: + diacritics: 1.3.0 + dev: false + /i18next-browser-languagedetector@7.0.1: resolution: {integrity: sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==} dependencies: @@ -15466,8 +21717,8 @@ packages: - encoding dev: false - /i18next@22.4.14: - resolution: {integrity: sha512-VtLPtbdwGn0+DAeE00YkiKKXadkwg+rBUV+0v8v0ikEjwdiJ0gmYChVE4GIa9HXymY6wKapkL93vGT7xpq6aTw==} + /i18next@22.4.13: + resolution: {integrity: sha512-GX7flMHRRqQA0I1yGLmaZ4Hwt1JfLqagk8QPDPZsqekbKtXsuIngSVWM/s3SLgNkrEXjA+0sMGNuOEkkmyqmWg==} dependencies: '@babel/runtime': 7.21.0 dev: false @@ -15483,6 +21734,14 @@ packages: engines: {node: '>=0.10.0'} dependencies: safer-buffer: 2.1.2 + + /icss-utils@5.1.0(postcss@8.4.24): + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.24 dev: true /idb-keyval@3.2.0: @@ -15496,6 +21755,13 @@ packages: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} + /image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + requiresBuild: true + optional: true + /immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} dev: false @@ -15619,6 +21885,11 @@ packages: through: 2.3.8 wrap-ansi: 7.0.0 + /install@0.13.0: + resolution: {integrity: sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==} + engines: {node: '>= 0.10'} + dev: false + /internal-slot@1.0.5: resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} engines: {node: '>= 0.4'} @@ -15628,6 +21899,11 @@ packages: side-channel: 1.0.4 dev: true + /internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + dev: false + /interpret@1.4.0: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} @@ -15688,7 +21964,6 @@ packages: /is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: true /is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} @@ -15730,8 +22005,8 @@ packages: ci-info: 3.8.0 dev: true - /is-core-module@2.12.0: - resolution: {integrity: sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==} + /is-core-module@2.11.0: + resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: has: 1.0.3 @@ -15944,7 +22219,6 @@ packages: /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - dev: true /is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} @@ -16027,6 +22301,9 @@ packages: get-intrinsic: 1.2.0 dev: true + /is-what@3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + /is-what@4.1.8: resolution: {integrity: sha512-yq8gMao5upkPoGEU9LsB2P+K3Kt8Q3fQFCGyNCWOAnJAMzEXVV9drYb0TXr42TTliLLhKIBvulgAXgtLLnwzGA==} engines: {node: '>=12.13'} @@ -16081,8 +22358,8 @@ packages: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} dependencies: - '@babel/core': 7.17.9 - '@babel/parser': 7.21.4 + '@babel/core': 7.22.5 + '@babel/parser': 7.22.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.0 @@ -16146,6 +22423,14 @@ packages: throat: 6.0.2 dev: true + /jest-changed-files@29.5.0: + resolution: {integrity: sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + execa: 5.1.1 + p-limit: 3.1.0 + dev: true + /jest-circus@27.5.1: resolution: {integrity: sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -16153,7 +22438,7 @@ packages: '@jest/environment': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -16173,6 +22458,34 @@ packages: - supports-color dev: true + /jest-circus@29.5.0: + resolution: {integrity: sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.5.0 + '@jest/expect': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 18.15.10 + chalk: 4.1.2 + co: 4.6.0 + dedent: 0.7.0 + is-generator-fn: 2.1.0 + jest-each: 29.5.0 + jest-matcher-utils: 29.5.0 + jest-message-util: 29.5.0 + jest-runtime: 29.5.0 + jest-snapshot: 29.5.0 + jest-util: 29.5.0 + p-limit: 3.1.0 + pretty-format: 29.5.0 + pure-rand: 6.0.2 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - supports-color + dev: true + /jest-cli@27.5.1(ts-node@10.9.1): resolution: {integrity: sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -16203,6 +22516,62 @@ packages: - utf-8-validate dev: true + /jest-cli@29.5.0(@types/node@18.15.10)(ts-node@10.9.1): + resolution: {integrity: sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.5.0(ts-node@10.9.1) + '@jest/test-result': 29.5.0 + '@jest/types': 29.5.0 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + import-local: 3.1.0 + jest-config: 29.5.0(@types/node@18.15.10)(ts-node@10.9.1) + jest-util: 29.5.0 + jest-validate: 29.5.0 + prompts: 2.4.2 + yargs: 17.7.1 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + + /jest-cli@29.5.0(@types/node@20.3.1)(ts-node@10.9.1): + resolution: {integrity: sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.5.0(ts-node@10.9.1) + '@jest/test-result': 29.5.0 + '@jest/types': 29.5.0 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + import-local: 3.1.0 + jest-config: 29.5.0(@types/node@20.3.1)(ts-node@10.9.1) + jest-util: 29.5.0 + jest-validate: 29.5.0 + prompts: 2.4.2 + yargs: 17.7.1 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + /jest-config@27.5.1(ts-node@10.9.1): resolution: {integrity: sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -16212,10 +22581,10 @@ packages: ts-node: optional: true dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.22.5 '@jest/test-sequencer': 27.5.1 '@jest/types': 27.5.1 - babel-jest: 27.5.1(@babel/core@7.21.4) + babel-jest: 27.5.1(@babel/core@7.22.5) chalk: 4.1.2 ci-info: 3.8.0 deepmerge: 4.3.1 @@ -16236,7 +22605,7 @@ packages: pretty-format: 27.5.1 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) + ts-node: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) transitivePeerDependencies: - bufferutil - canvas @@ -16244,6 +22613,86 @@ packages: - utf-8-validate dev: true + /jest-config@29.5.0(@types/node@18.15.10)(ts-node@10.9.1): + resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.22.5 + '@jest/test-sequencer': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 18.15.10 + babel-jest: 29.5.0(@babel/core@7.22.5) + chalk: 4.1.2 + ci-info: 3.8.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.5.0 + jest-environment-node: 29.5.0 + jest-get-type: 29.4.3 + jest-regex-util: 29.4.3 + jest-resolve: 29.5.0 + jest-runner: 29.5.0 + jest-util: 29.5.0 + jest-validate: 29.5.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.5.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + ts-node: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) + transitivePeerDependencies: + - supports-color + dev: true + + /jest-config@29.5.0(@types/node@20.3.1)(ts-node@10.9.1): + resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.22.5 + '@jest/test-sequencer': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 20.3.1 + babel-jest: 29.5.0(@babel/core@7.22.5) + chalk: 4.1.2 + ci-info: 3.8.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.5.0 + jest-environment-node: 29.5.0 + jest-get-type: 29.4.3 + jest-regex-util: 29.4.3 + jest-resolve: 29.5.0 + jest-runner: 29.5.0 + jest-util: 29.5.0 + jest-validate: 29.5.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.5.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + ts-node: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) + transitivePeerDependencies: + - supports-color + dev: true + /jest-diff@26.6.2: resolution: {integrity: sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==} engines: {node: '>= 10.14.2'} @@ -16281,6 +22730,13 @@ packages: detect-newline: 3.1.0 dev: true + /jest-docblock@29.4.3: + resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + /jest-each@27.5.1: resolution: {integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -16292,6 +22748,17 @@ packages: pretty-format: 27.5.1 dev: true + /jest-each@29.5.0: + resolution: {integrity: sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + chalk: 4.1.2 + jest-get-type: 29.4.3 + jest-util: 29.5.0 + pretty-format: 29.5.0 + dev: true + /jest-environment-jsdom@27.5.1: resolution: {integrity: sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -16299,7 +22766,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 jest-mock: 27.5.1 jest-util: 27.5.1 jsdom: 16.7.0 @@ -16317,11 +22784,23 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 jest-mock: 27.5.1 jest-util: 27.5.1 dev: true + /jest-environment-node@29.5.0: + resolution: {integrity: sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.5.0 + '@jest/fake-timers': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 18.15.10 + jest-mock: 29.5.0 + jest-util: 29.5.0 + dev: true + /jest-get-type@26.3.0: resolution: {integrity: sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==} engines: {node: '>= 10.14.2'} @@ -16343,7 +22822,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@types/graceful-fs': 4.1.6 - '@types/node': 18.15.11 + '@types/node': 18.15.10 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -16363,7 +22842,7 @@ packages: dependencies: '@jest/types': 29.5.0 '@types/graceful-fs': 4.1.6 - '@types/node': 18.15.11 + '@types/node': 18.15.10 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -16384,7 +22863,7 @@ packages: '@jest/source-map': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 chalk: 4.1.2 co: 4.6.0 expect: 27.5.1 @@ -16409,6 +22888,14 @@ packages: pretty-format: 27.5.1 dev: true + /jest-leak-detector@29.5.0: + resolution: {integrity: sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.4.3 + pretty-format: 29.5.0 + dev: true + /jest-matcher-utils@27.5.1: resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -16433,7 +22920,7 @@ packages: resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@babel/code-frame': 7.21.4 + '@babel/code-frame': 7.22.5 '@jest/types': 27.5.1 '@types/stack-utils': 2.0.1 chalk: 4.1.2 @@ -16448,7 +22935,7 @@ packages: resolution: {integrity: sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/code-frame': 7.21.4 + '@babel/code-frame': 7.22.5 '@jest/types': 29.5.0 '@types/stack-utils': 2.0.1 chalk: 4.1.2 @@ -16475,7 +22962,16 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 + dev: true + + /jest-mock@29.5.0: + resolution: {integrity: sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@types/node': 18.15.10 + jest-util: 29.5.0 dev: true /jest-pnp-resolver@1.2.3(jest-resolve@27.5.1): @@ -16490,6 +22986,18 @@ packages: jest-resolve: 27.5.1 dev: true + /jest-pnp-resolver@1.2.3(jest-resolve@29.5.0): + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 29.5.0 + dev: true + /jest-regex-util@27.5.1: resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -16511,6 +23019,16 @@ packages: - supports-color dev: true + /jest-resolve-dependencies@29.5.0: + resolution: {integrity: sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-regex-util: 29.4.3 + jest-snapshot: 29.5.0 + transitivePeerDependencies: + - supports-color + dev: true + /jest-resolve@27.5.1: resolution: {integrity: sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -16527,6 +23045,21 @@ packages: slash: 3.0.0 dev: true + /jest-resolve@29.5.0: + resolution: {integrity: sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.5.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.5.0) + jest-util: 29.5.0 + jest-validate: 29.5.0 + resolve: 1.22.2 + resolve.exports: 2.0.2 + slash: 3.0.0 + dev: true + /jest-runner@27.5.1: resolution: {integrity: sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -16536,7 +23069,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 chalk: 4.1.2 emittery: 0.8.1 graceful-fs: 4.2.11 @@ -16553,10 +23086,39 @@ packages: source-map-support: 0.5.21 throat: 6.0.2 transitivePeerDependencies: - - bufferutil - - canvas + - bufferutil + - canvas + - supports-color + - utf-8-validate + dev: true + + /jest-runner@29.5.0: + resolution: {integrity: sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.5.0 + '@jest/environment': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 18.15.10 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.4.3 + jest-environment-node: 29.5.0 + jest-haste-map: 29.5.0 + jest-leak-detector: 29.5.0 + jest-message-util: 29.5.0 + jest-resolve: 29.5.0 + jest-runtime: 29.5.0 + jest-util: 29.5.0 + jest-watcher: 29.5.0 + jest-worker: 29.5.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: - supports-color - - utf-8-validate dev: true /jest-runtime@27.5.1: @@ -16589,11 +23151,41 @@ packages: - supports-color dev: true + /jest-runtime@29.5.0: + resolution: {integrity: sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.5.0 + '@jest/fake-timers': 29.5.0 + '@jest/globals': 29.5.0 + '@jest/source-map': 29.4.3 + '@jest/test-result': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 18.15.10 + chalk: 4.1.2 + cjs-module-lexer: 1.2.2 + collect-v8-coverage: 1.0.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.5.0 + jest-message-util: 29.5.0 + jest-mock: 29.5.0 + jest-regex-util: 29.4.3 + jest-resolve: 29.5.0 + jest-snapshot: 29.5.0 + jest-util: 29.5.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /jest-serializer@27.5.1: resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 graceful-fs: 4.2.11 dev: true @@ -16601,16 +23193,16 @@ packages: resolution: {integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@babel/core': 7.21.4 - '@babel/generator': 7.21.4 - '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.21.4) - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/core': 7.22.5 + '@babel/generator': 7.22.5 + '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.22.5) + '@babel/traverse': 7.22.5 + '@babel/types': 7.22.5 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 '@types/babel__traverse': 7.18.3 '@types/prettier': 2.7.2 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.5) chalk: 4.1.2 expect: 27.5.1 graceful-fs: 4.2.11 @@ -16622,7 +23214,38 @@ packages: jest-util: 27.5.1 natural-compare: 1.4.0 pretty-format: 27.5.1 - semver: 7.4.0 + semver: 7.5.3 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-snapshot@29.5.0: + resolution: {integrity: sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.22.5 + '@babel/generator': 7.22.5 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.5) + '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.22.5) + '@babel/traverse': 7.22.5 + '@babel/types': 7.22.5 + '@jest/expect-utils': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@types/babel__traverse': 7.18.3 + '@types/prettier': 2.7.2 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.5) + chalk: 4.1.2 + expect: 29.5.0 + graceful-fs: 4.2.11 + jest-diff: 29.5.0 + jest-get-type: 29.4.3 + jest-matcher-utils: 29.5.0 + jest-message-util: 29.5.0 + jest-util: 29.5.0 + natural-compare: 1.4.0 + pretty-format: 29.5.0 + semver: 7.5.3 transitivePeerDependencies: - supports-color dev: true @@ -16632,7 +23255,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 chalk: 4.1.2 ci-info: 3.8.0 graceful-fs: 4.2.11 @@ -16644,7 +23267,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.5.0 - '@types/node': 18.15.11 + '@types/node': 18.15.10 chalk: 4.1.2 ci-info: 3.8.0 graceful-fs: 4.2.11 @@ -16663,24 +23286,50 @@ packages: pretty-format: 27.5.1 dev: true + /jest-validate@29.5.0: + resolution: {integrity: sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.4.3 + leven: 3.1.0 + pretty-format: 29.5.0 + dev: true + /jest-watcher@27.5.1: resolution: {integrity: sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.15.11 + '@types/node': 18.15.10 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 27.5.1 string-length: 4.0.2 dev: true + /jest-watcher@29.5.0: + resolution: {integrity: sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 18.15.10 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.5.0 + string-length: 4.0.2 + dev: true + /jest-worker@26.6.2: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -16689,7 +23338,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -16698,7 +23347,7 @@ packages: resolution: {integrity: sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 jest-util: 29.5.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -16725,14 +23374,59 @@ packages: - utf-8-validate dev: true - /jiti@1.18.2: - resolution: {integrity: sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==} + /jest@29.5.0(@types/node@18.15.10)(ts-node@10.9.1): + resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.5.0(ts-node@10.9.1) + '@jest/types': 29.5.0 + import-local: 3.1.0 + jest-cli: 29.5.0(@types/node@18.15.10)(ts-node@10.9.1) + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + + /jest@29.5.0(@types/node@20.3.1)(ts-node@10.9.1): + resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.5.0(ts-node@10.9.1) + '@jest/types': 29.5.0 + import-local: 3.1.0 + jest-cli: 29.5.0(@types/node@20.3.1)(ts-node@10.9.1) + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + + /jiti@1.19.1: + resolution: {integrity: sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==} hasBin: true /jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} dev: true + /jmespath@0.16.0: + resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==} + engines: {node: '>= 0.6.0'} + dev: false + /joi@17.9.2: resolution: {integrity: sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==} dependencies: @@ -16773,19 +23467,19 @@ packages: peerDependencies: '@babel/preset-env': ^7.1.6 dependencies: - '@babel/core': 7.21.4 - '@babel/parser': 7.21.4 - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.21.4) - '@babel/preset-env': 7.20.2(@babel/core@7.21.4) - '@babel/preset-flow': 7.21.4(@babel/core@7.21.4) - '@babel/preset-typescript': 7.16.7(@babel/core@7.21.4) - '@babel/register': 7.21.0(@babel/core@7.21.4) - babel-core: 7.0.0-bridge.0(@babel/core@7.21.4) + '@babel/core': 7.22.5 + '@babel/parser': 7.22.5 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.22.5) + '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.22.5) + '@babel/preset-env': 7.20.2(@babel/core@7.22.5) + '@babel/preset-flow': 7.18.6(@babel/core@7.22.5) + '@babel/preset-typescript': 7.16.7(@babel/core@7.22.5) + '@babel/register': 7.21.0(@babel/core@7.22.5) + babel-core: 7.0.0-bridge.0(@babel/core@7.22.5) chalk: 4.1.2 - flow-parser: 0.203.1 + flow-parser: 0.202.1 graceful-fs: 4.2.11 micromatch: 4.0.5 neo-async: 2.6.2 @@ -16797,25 +23491,25 @@ packages: - supports-color dev: true - /jscodeshift@0.14.0(@babel/preset-env@7.21.4): + /jscodeshift@0.14.0(@babel/preset-env@7.21.5): resolution: {integrity: sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA==} hasBin: true peerDependencies: '@babel/preset-env': ^7.1.6 dependencies: - '@babel/core': 7.21.4 - '@babel/parser': 7.21.4 - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.21.4) - '@babel/preset-env': 7.21.4(@babel/core@7.21.4) - '@babel/preset-flow': 7.21.4(@babel/core@7.21.4) - '@babel/preset-typescript': 7.16.7(@babel/core@7.21.4) - '@babel/register': 7.21.0(@babel/core@7.21.4) - babel-core: 7.0.0-bridge.0(@babel/core@7.21.4) + '@babel/core': 7.22.5 + '@babel/parser': 7.22.5 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.22.5) + '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.22.5) + '@babel/preset-env': 7.21.5(@babel/core@7.21.3) + '@babel/preset-flow': 7.18.6(@babel/core@7.22.5) + '@babel/preset-typescript': 7.16.7(@babel/core@7.22.5) + '@babel/register': 7.21.0(@babel/core@7.22.5) + babel-core: 7.0.0-bridge.0(@babel/core@7.22.5) chalk: 4.1.2 - flow-parser: 0.203.1 + flow-parser: 0.202.1 graceful-fs: 4.2.11 micromatch: 4.0.5 neo-async: 2.6.2 @@ -16837,7 +23531,7 @@ packages: optional: true dependencies: abab: 2.0.6 - acorn: 8.8.2 + acorn: 8.9.0 acorn-globals: 6.0.0 cssom: 0.4.4 cssstyle: 2.3.0 @@ -16850,7 +23544,7 @@ packages: http-proxy-agent: 4.0.1 https-proxy-agent: 5.0.1 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.4 + nwsapi: 2.2.2 parse5: 6.0.1 saxes: 5.0.1 symbol-tree: 3.2.4 @@ -16892,7 +23586,7 @@ packages: http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.4 + nwsapi: 2.2.2 parse5: 7.1.2 saxes: 6.0.0 symbol-tree: 3.2.4 @@ -16939,7 +23633,7 @@ packages: dependencies: '@bcherny/json-schema-ref-parser': 10.0.5-fork '@types/json-schema': 7.0.11 - '@types/lodash': 4.14.192 + '@types/lodash': 4.14.191 '@types/prettier': 2.7.2 cli-color: 2.0.3 get-stdin: 8.0.0 @@ -16971,10 +23665,6 @@ packages: /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - /json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - dev: false - /json5@1.0.2: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true @@ -17024,7 +23714,7 @@ packages: jws: 3.2.2 lodash: 4.17.21 ms: 2.1.3 - semver: 7.4.0 + semver: 7.5.3 dev: false /jsx-ast-utils@3.3.3: @@ -17072,6 +23762,10 @@ packages: /kolorist@1.7.0: resolution: {integrity: sha512-ymToLHqL02udwVdbkowNpzjFd6UzozMtshPQKVi5k1EjKRqKqBrOnE9QbLEb0/pV76SAiIT13hdL8R6suc+f3g==} + /kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + dev: false + /lazy-universal-dotenv@4.0.0: resolution: {integrity: sha512-aXpZJRnTkpK6gQ/z4nk+ZBLd/Qdp118cvPruLSIQzQNRhKwEcdXCOzXuF55VDqIiuAaY3UGZ10DJtvZzDcvsxg==} engines: {node: '>=14.0.0'} @@ -17081,6 +23775,47 @@ packages: dotenv-expand: 10.0.0 dev: true + /lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + dependencies: + readable-stream: 2.3.8 + dev: true + + /leaflet@1.9.4: + resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==} + dev: false + + /less-loader@11.1.3(less@4.1.3)(webpack@5.76.2): + resolution: {integrity: sha512-A5b7O8dH9xpxvkosNrP0dFp2i/dISOJa9WwGF3WJflfqIERE2ybxh1BFDj5CovC2+jCE4M354mk90hN6ziXlVw==} + engines: {node: '>= 14.15.0'} + peerDependencies: + less: ^3.5.0 || ^4.0.0 + webpack: ^5.0.0 + dependencies: + less: 4.1.3 + webpack: 5.76.2(esbuild@0.17.19) + dev: true + + /less@4.1.3: + resolution: {integrity: sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==} + engines: {node: '>=6'} + hasBin: true + dependencies: + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.5.2 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.2.0 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + /leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -17101,8 +23836,8 @@ packages: prelude-ls: 1.2.1 type-check: 0.4.0 - /libphonenumber-js@1.10.26: - resolution: {integrity: sha512-oB3l4J5gEhMV+ymmlIjWedsbCpsNRqbEZ/E/MpN2QVyinKNra6DcuXywxSk/72M3DZDoH/6kzurOq1erznBMwQ==} + /libphonenumber-js@1.10.24: + resolution: {integrity: sha512-3Dk8f5AmrcWqg+oHhmm9hwSTqpWHBdSqsHmjCJGroULFubi0+x7JEIGmRZCuL3TI8Tx39xaKqfnhsDQ4ALa/Nw==} /lie@3.1.1: resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==} @@ -17171,6 +23906,15 @@ packages: engines: {node: '>=6.11.5'} dev: true + /loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 + dev: true + /local-pkg@0.4.3: resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} engines: {node: '>=14'} @@ -17206,10 +23950,30 @@ packages: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} dev: true + /lodash.curry@4.1.1: + resolution: {integrity: sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==} + dev: false + /lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: true + /lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + dev: true + + /lodash.difference@4.5.0: + resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} + dev: true + + /lodash.flatten@4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + dev: true + + /lodash.flow@3.5.0: + resolution: {integrity: sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==} + dev: false + /lodash.get@4.4.2: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} dev: true @@ -17234,6 +23998,10 @@ packages: resolution: {integrity: sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==} dev: true + /lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: true + /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -17241,6 +24009,10 @@ packages: resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} dev: true + /lodash.pick@4.4.0: + resolution: {integrity: sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==} + dev: true + /lodash.snakecase@4.1.1: resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} dev: true @@ -17249,6 +24021,10 @@ packages: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} dev: true + /lodash.union@4.6.0: + resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} + dev: true + /lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} dev: true @@ -17285,6 +24061,17 @@ packages: wrap-ansi: 6.2.0 dev: true + /logform@2.5.1: + resolution: {integrity: sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==} + dependencies: + '@colors/colors': 1.5.0 + '@types/triple-beam': 1.3.2 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.4.3 + triple-beam: 1.3.0 + dev: false + /longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} dev: false @@ -17309,7 +24096,7 @@ packages: /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 dev: true /lru-cache@4.1.5: @@ -17330,9 +24117,9 @@ packages: dependencies: yallist: 4.0.0 - /lru-cache@9.0.1: - resolution: {integrity: sha512-C8QsKIN1UIXeOs3iWmiZ1lQY+EnKDojWd37fXy1aSbJvH4iSma1uy2OWuoB3m4SYRli5+CUjDv3Dij5DVoetmg==} - engines: {node: 14 || >=16.14} + /lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} dev: true /lru-queue@0.1.0: @@ -17390,17 +24177,17 @@ packages: resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} engines: {node: '>=12'} dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.4.14 /magic-string@0.29.0: resolution: {integrity: sha512-WcfidHrDjMY+eLjlU+8OvwREqHwpgCeKVBUpQ3OhYYuvfaYCUgcbuBzappNzZvg/v8onU3oQj+BYpkOJe9Iw4Q==} engines: {node: '>=12'} dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.4.14 dev: true - /magic-string@0.30.0: - resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==} + /magic-string@0.30.1: + resolution: {integrity: sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==} engines: {node: '>=12'} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -17412,7 +24199,6 @@ packages: dependencies: pify: 4.0.1 semver: 5.7.1 - dev: true /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} @@ -17474,13 +24260,6 @@ packages: remove-accents: 0.4.2 dev: false - /matcher@3.0.0: - resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} - engines: {node: '>=10'} - dependencies: - escape-string-regexp: 4.0.0 - dev: false - /mdast-util-definitions@4.0.0: resolution: {integrity: sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==} dependencies: @@ -17509,7 +24288,7 @@ packages: '@types/mdast': 3.0.11 '@types/unist': 2.0.6 decode-named-character-reference: 1.0.2 - mdast-util-to-string: 3.2.0 + mdast-util-to-string: 3.1.1 micromark: 3.1.0 micromark-util-decode-numeric-character-reference: 1.0.0 micromark-util-decode-string: 1.0.2 @@ -17527,7 +24306,7 @@ packages: dependencies: '@types/mdast': 3.0.11 mdast-util-to-markdown: 1.5.0 - micromark-extension-frontmatter: 1.1.0 + micromark-extension-frontmatter: 1.0.1 dev: false /mdast-util-gfm-autolink-literal@1.0.3: @@ -17598,19 +24377,6 @@ packages: - supports-color dev: false - /mdast-util-mdx-jsx@1.2.0: - resolution: {integrity: sha512-5+ot/kfxYd3ChgEMwsMUO71oAfYjyRI3pADEK4I7xTmWLGQ8Y7ghm1CG36zUoUvDPxMlIYwQV/9DYHAUWdG4dA==} - dependencies: - '@types/estree-jsx': 0.0.1 - '@types/mdast': 3.0.11 - mdast-util-to-markdown: 1.5.0 - parse-entities: 4.0.1 - stringify-entities: 4.0.3 - unist-util-remove-position: 4.0.2 - unist-util-stringify-position: 3.0.3 - vfile-message: 3.1.4 - dev: false - /mdast-util-mdx-jsx@2.1.2: resolution: {integrity: sha512-o9vBCYQK5ZLGEj3tCGISJGjvafyHRVJlZmfJzSE7xjiogSzIeph/Z4zMY65q4WGRMezQBeAwPlrdymDYYYx0tA==} dependencies: @@ -17681,7 +24447,7 @@ packages: '@types/unist': 2.0.6 longest-streak: 3.1.0 mdast-util-phrasing: 3.0.1 - mdast-util-to-string: 3.2.0 + mdast-util-to-string: 3.1.1 micromark-util-decode-string: 1.0.2 unist-util-visit: 4.1.2 zwitch: 2.0.4 @@ -17691,8 +24457,8 @@ packages: resolution: {integrity: sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==} dev: true - /mdast-util-to-string@3.2.0: - resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} + /mdast-util-to-string@3.1.1: + resolution: {integrity: sha512-tGvhT94e+cVnQt8JWE9/b3cUQZWS732TJxXHktvP+BYo62PpYD53Ls/6cC60rW21dW+txxiM4zMdc6abASvZKA==} dependencies: '@types/mdast': 3.0.11 @@ -17700,8 +24466,8 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} - /memfs@3.5.0: - resolution: {integrity: sha512-yK6o8xVJlQerz57kvPROwTMgx5WtGwC2ZxDtOUsnGl49rHjYkfQoPNZPCKH73VdLE1BwBu/+Fx/NL8NYMUw2aA==} + /memfs@3.4.13: + resolution: {integrity: sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==} engines: {node: '>= 4.0.0'} dependencies: fs-monkey: 1.0.3 @@ -17799,8 +24565,8 @@ packages: uvu: 0.5.6 dev: false - /micromark-extension-frontmatter@1.1.0: - resolution: {integrity: sha512-0nLelmvXR5aZ+F2IL6/Ed4cDnHLpL/VD/EELKuclsTWHrLI8UgxGHEmeoumeX2FXiM6z2WrBIOEcbKUZR8RYNg==} + /micromark-extension-frontmatter@1.0.1: + resolution: {integrity: sha512-9OJhCXkrpj8qIXW5AAgRZGvS8Q4GTMdH5+Ljt98kV4XQVflRGeEhNRYp6O/zCvf8c8lZ+wc4uwmbly27pS/s4Q==} dependencies: fault: 2.0.1 micromark-util-character: 1.1.0 @@ -17818,8 +24584,8 @@ packages: uvu: 0.5.6 dev: false - /micromark-extension-gfm-footnote@1.1.0: - resolution: {integrity: sha512-RWYce7j8+c0n7Djzv5NzGEGitNNYO3uj+h/XYMdS/JinH1Go+/Qkomg/rfxExFzYTiydaV6GLeffGO5qcJbMPA==} + /micromark-extension-gfm-footnote@1.0.4: + resolution: {integrity: sha512-E/fmPmDqLiMUP8mLJ8NbJWJ4bTw6tS+FEQS8CcuDtZpILuOb2kjLqPEeAePF1djXROHXChM/wPJw0iS4kHCcIg==} dependencies: micromark-core-commonmark: 1.0.6 micromark-factory-space: 1.0.0 @@ -17831,8 +24597,8 @@ packages: uvu: 0.5.6 dev: false - /micromark-extension-gfm-strikethrough@1.0.5: - resolution: {integrity: sha512-X0oI5eYYQVARhiNfbETy7BfLSmSilzN1eOuoRnrf9oUNsPRrWOAe9UqSizgw1vNxQBfOwL+n2610S3bYjVNi7w==} + /micromark-extension-gfm-strikethrough@1.0.4: + resolution: {integrity: sha512-/vjHU/lalmjZCT5xt7CcHVJGq8sYRm80z24qAKXzaHzem/xsDYb2yLL+NNVbYvmpLx3O7SYPuGL5pzusL9CLIQ==} dependencies: micromark-util-chunked: 1.0.0 micromark-util-classify-character: 1.0.0 @@ -17852,14 +24618,14 @@ packages: uvu: 0.5.6 dev: false - /micromark-extension-gfm-tagfilter@1.0.2: - resolution: {integrity: sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==} + /micromark-extension-gfm-tagfilter@1.0.1: + resolution: {integrity: sha512-Ty6psLAcAjboRa/UKUbbUcwjVAv5plxmpUTy2XC/3nJFL37eHej8jrHrRzkqcpipJliuBH30DTs7+3wqNcQUVA==} dependencies: micromark-util-types: 1.0.2 dev: false - /micromark-extension-gfm-task-list-item@1.0.4: - resolution: {integrity: sha512-9XlIUUVnYXHsFF2HZ9jby4h3npfX10S1coXTnV035QGPgrtNYQq3J6IfIvcCIUAJrrqBVi5BqA/LmaOMJqPwMQ==} + /micromark-extension-gfm-task-list-item@1.0.3: + resolution: {integrity: sha512-PpysK2S1Q/5VXi72IIapbi/jliaiOFzv7THH4amwXeYXLq3l1uo8/2Be0Ac1rEwK20MQEsGH2ltAZLNY2KI/0Q==} dependencies: micromark-factory-space: 1.0.0 micromark-util-character: 1.1.0 @@ -17872,11 +24638,11 @@ packages: resolution: {integrity: sha512-p2sGjajLa0iYiGQdT0oelahRYtMWvLjy8J9LOCxzIQsllMCGLbsLW+Nc+N4vi02jcRJvedVJ68cjelKIO6bpDA==} dependencies: micromark-extension-gfm-autolink-literal: 1.0.3 - micromark-extension-gfm-footnote: 1.1.0 - micromark-extension-gfm-strikethrough: 1.0.5 + micromark-extension-gfm-footnote: 1.0.4 + micromark-extension-gfm-strikethrough: 1.0.4 micromark-extension-gfm-table: 1.0.5 - micromark-extension-gfm-tagfilter: 1.0.2 - micromark-extension-gfm-task-list-item: 1.0.4 + micromark-extension-gfm-tagfilter: 1.0.1 + micromark-extension-gfm-task-list-item: 1.0.3 micromark-util-combine-extensions: 1.0.0 micromark-util-types: 1.0.2 dev: false @@ -17929,8 +24695,8 @@ packages: /micromark-extension-mdxjs@1.0.0: resolution: {integrity: sha512-TZZRZgeHvtgm+IhtgC2+uDMR7h8eTKF0QUX9YsgoL9+bADBpBY6SiLvWqnBlLbCEevITmTqmEuY3FoxMKVs1rQ==} dependencies: - acorn: 8.8.2 - acorn-jsx: 5.3.2(acorn@8.8.2) + acorn: 8.9.0 + acorn-jsx: 5.3.2(acorn@8.9.0) micromark-extension-mdx-expression: 1.0.4 micromark-extension-mdx-jsx: 1.0.3 micromark-extension-mdx-md: 1.0.0 @@ -18198,20 +24964,13 @@ packages: brace-expansion: 2.0.1 dev: true - /minimatch@7.4.6: - resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} + /minimatch@7.4.3: + resolution: {integrity: sha512-5UB4yYusDtkRPbRiy1cqZ1IpGNcJCGlEMG17RKzPddpyiPKoCdwohbED8g4QXT0ewCt8LTkQXuljsUfQ3FKM4A==} engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 dev: true - /minimatch@8.0.4: - resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} - engines: {node: '>=16 || 14 >=14.17'} - dependencies: - brace-expansion: 2.0.1 - dev: true - /minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} @@ -18234,14 +24993,9 @@ packages: dependencies: yallist: 4.0.0 - /minipass@4.2.8: - resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} - engines: {node: '>=8'} - - /minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + /minipass@4.2.5: + resolution: {integrity: sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==} engines: {node: '>=8'} - dev: true /minizlib@2.1.2: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} @@ -18273,12 +25027,25 @@ packages: /mlly@1.2.0: resolution: {integrity: sha512-+c7A3CV0KGdKcylsI6khWyts/CYrGTrRVo4R/I7u/cUsy0Conxa6LUhiEzVKIw14lc2L5aiO4+SeVe4TeGRKww==} dependencies: - acorn: 8.8.2 - pathe: 1.1.0 + acorn: 8.9.0 + pathe: 1.1.1 pkg-types: 1.0.2 ufo: 1.1.1 dev: true + /mlly@1.4.0: + resolution: {integrity: sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==} + dependencies: + acorn: 8.9.0 + pathe: 1.1.1 + pkg-types: 1.0.3 + ufo: 1.1.2 + dev: true + + /moment@2.29.4: + resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} + dev: true + /mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -18335,14 +25102,50 @@ packages: - encoding - supports-color - /multer-s3@3.0.1(@aws-sdk/abort-controller@3.329.0)(@aws-sdk/client-s3@3.325.0): + /msw@1.2.2(typescript@4.9.5): + resolution: {integrity: sha512-GsW3PE/Es/a1tYThXcM8YHOZ1S1MtivcS3He/LQbbTCx3rbWJYCtWD5XXyJ53KlNPT7O1VI9sCW3xMtgFe8XpQ==} + engines: {node: '>=14'} + hasBin: true + requiresBuild: true + peerDependencies: + typescript: '>= 4.4.x <= 5.1.x' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@mswjs/cookies': 0.2.2 + '@mswjs/interceptors': 0.17.9 + '@open-draft/until': 1.0.3 + '@types/cookie': 0.4.1 + '@types/js-levenshtein': 1.1.1 + chalk: 4.1.1 + chokidar: 3.5.3 + cookie: 0.4.2 + graphql: 16.6.0 + headers-polyfill: 3.1.2 + inquirer: 8.2.5 + is-node-process: 1.2.0 + js-levenshtein: 1.1.6 + node-fetch: 2.6.9 + outvariant: 1.4.0 + path-to-regexp: 6.2.1 + strict-event-emitter: 0.4.6 + type-fest: 2.19.0 + typescript: 4.9.5 + yargs: 17.7.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /multer-s3@3.0.1(@aws-sdk/abort-controller@3.347.0)(@aws-sdk/client-s3@3.325.0): resolution: {integrity: sha512-BFwSO80a5EW4GJRBdUuSHblz2jhVSAze33ZbnGpcfEicoT0iRolx4kWR+AJV07THFRCQ78g+kelKFdjkCCaXeQ==} engines: {node: '>= 12.0.0'} peerDependencies: '@aws-sdk/client-s3': ^3.0.0 dependencies: '@aws-sdk/client-s3': 3.325.0 - '@aws-sdk/lib-storage': 3.325.0(@aws-sdk/abort-controller@3.329.0)(@aws-sdk/client-s3@3.325.0) + '@aws-sdk/lib-storage': 3.325.0(@aws-sdk/abort-controller@3.347.0)(@aws-sdk/client-s3@3.325.0) file-type: 3.9.0 html-comment-regex: 1.1.2 run-parallel: 1.2.0 @@ -18372,6 +25175,11 @@ packages: object-assign: 4.1.1 thenify-all: 1.6.0 + /nan@2.17.0: + resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==} + dev: true + optional: true + /nanoid@3.3.6: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -18384,6 +25192,19 @@ packages: /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + /needle@3.2.0: + resolution: {integrity: sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==} + engines: {node: '>= 4.4.x'} + hasBin: true + requiresBuild: true + dependencies: + debug: 3.2.7 + iconv-lite: 0.6.3 + sax: 1.2.4 + transitivePeerDependencies: + - supports-color + optional: true + /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -18400,11 +25221,22 @@ packages: '@nestjs/graphql': optional: true dependencies: - '@nestjs/graphql': 10.0.0(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)(class-transformer@0.5.1)(class-validator@0.14.0)(graphql@16.6.0)(reflect-metadata@0.1.13) + '@nestjs/graphql': 10.0.0(@nestjs/common@9.3.12)(@nestjs/core@9.3.12)(class-transformer@0.5.1)(class-validator@0.14.0)(graphql@16.6.0)(reflect-metadata@0.1.13) accesscontrol: 2.2.1 dev: false - /nestjs-prisma@0.20.0(@nestjs/common@9.4.0)(@prisma/client@4.13.0)(prisma@4.13.0): + /nestjs-cls@3.5.0(@nestjs/common@9.3.12)(@nestjs/core@9.3.12): + resolution: {integrity: sha512-C7JX4Q8scpmCl4fZsNjx2JS50rL1fhdx2mckkZo300dUviVLiYMmy5Q0bXnrwW2cquk78mi9Vd3WlGuYCrExDQ==} + engines: {node: '>=12.17.0'} + peerDependencies: + '@nestjs/common': '> 7.0.0 < 11' + '@nestjs/core': '> 7.0.0 < 11' + dependencies: + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/core': 9.3.12(@nestjs/common@9.3.12)(@nestjs/platform-express@9.3.12)(@nestjs/websockets@9.4.2)(reflect-metadata@0.1.13)(rxjs@7.8.0) + dev: false + + /nestjs-prisma@0.20.0(@nestjs/common@9.3.12)(@prisma/client@4.13.0)(prisma@4.13.0): resolution: {integrity: sha512-tqOcoJQh4U8Y7akyCwluo1VFqhqjqYy8JAYYfrKRhvZ7N4trt3wYXXllRGH3LeLaom09QchHP9WEo/BAV9JMjg==} peerDependencies: '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 @@ -18413,7 +25245,7 @@ packages: dependencies: '@angular-devkit/core': 13.3.11 '@angular-devkit/schematics': 13.3.11 - '@nestjs/common': 9.4.0(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) + '@nestjs/common': 9.3.12(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.0) '@prisma/client': 4.13.0(prisma@4.13.0) '@schematics/angular': 13.3.11 prisma: 4.13.0 @@ -18435,7 +25267,7 @@ packages: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: lower-case: 2.0.2 - tslib: 2.5.0 + tslib: 2.5.2 dev: true /node-abort-controller@3.1.1: @@ -18460,7 +25292,7 @@ packages: /node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} - dev: false + dev: true /node-emoji@1.11.0: resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} @@ -18506,7 +25338,7 @@ packages: data-uri-to-buffer: 4.0.1 fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - dev: false + dev: true /node-gyp-build@4.6.0: resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} @@ -18549,8 +25381,8 @@ packages: engines: {node: '>=10'} dependencies: hosted-git-info: 4.1.0 - is-core-module: 2.12.0 - semver: 7.4.0 + is-core-module: 2.11.0 + semver: 7.5.3 validate-npm-package-license: 3.0.4 dev: true @@ -18561,7 +25393,6 @@ packages: /normalize-range@0.1.2: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} - dev: true /notation@1.3.6: resolution: {integrity: sha512-DIuJmrP/Gg1DcXKaApsqcjsJD6jEccqKSfmU3BUx/f1GHsMiTJh70cERwYc64tOmTRTARCeMwkqNNzjh3AHhiw==} @@ -18595,8 +25426,8 @@ packages: boolbase: 1.0.0 dev: true - /nwsapi@2.2.4: - resolution: {integrity: sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g==} + /nwsapi@2.2.2: + resolution: {integrity: sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==} dev: true /nx@15.0.2: @@ -18616,7 +25447,7 @@ packages: '@nrwl/tao': 15.0.2 '@parcel/watcher': 2.0.4 '@yarnpkg/lockfile': 1.1.0 - '@yarnpkg/parsers': 3.0.0-rc.42 + '@yarnpkg/parsers': 3.0.0-rc.40 '@zkochan/js-yaml': 0.0.6 axios: 1.4.0 chalk: 4.1.0 @@ -18673,6 +25504,7 @@ packages: /object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} + dev: true /object.assign@4.1.4: resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} @@ -18750,6 +25582,12 @@ packages: dependencies: wrappy: 1.0.2 + /one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + dependencies: + fn.name: 1.1.0 + dev: false + /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -18807,6 +25645,18 @@ packages: type-check: 0.4.0 word-wrap: 1.2.3 + /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 + /ora@5.4.1: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} @@ -18814,25 +25664,25 @@ packages: bl: 4.1.0 chalk: 4.1.2 cli-cursor: 3.1.0 - cli-spinners: 2.8.0 + cli-spinners: 2.7.0 is-interactive: 1.0.0 is-unicode-supported: 0.1.0 log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 - /ora@6.3.0: - resolution: {integrity: sha512-1/D8uRFY0ay2kgBpmAwmSA404w4OoPVhHMqRqtjvrcK/dnzcEZxMJ+V4DUbyICu8IIVRclHcOf5wlD1tMY4GUQ==} + /ora@6.3.1: + resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: chalk: 5.2.0 cli-cursor: 4.0.0 - cli-spinners: 2.8.0 + cli-spinners: 2.7.0 is-interactive: 2.0.0 is-unicode-supported: 1.3.0 log-symbols: 5.1.0 stdin-discarder: 0.1.0 - strip-ansi: 7.0.1 + strip-ansi: 7.1.0 wcwidth: 1.0.1 dev: false @@ -18879,7 +25729,6 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: yocto-queue: 1.0.0 - dev: true /p-locate@3.0.0: resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} @@ -18924,7 +25773,7 @@ packages: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} dependencies: dot-case: 3.0.4 - tslib: 2.5.0 + tslib: 2.5.2 dev: true /parent-module@1.0.1: @@ -18950,7 +25799,7 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} dependencies: - '@babel/code-frame': 7.21.4 + '@babel/code-frame': 7.22.5 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -18964,6 +25813,10 @@ packages: unist-util-visit-children: 2.0.2 dev: false + /parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + /parse-passwd@1.0.0: resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} engines: {node: '>=0.10.0'} @@ -18986,7 +25839,7 @@ packages: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} dependencies: no-case: 3.0.4 - tslib: 2.5.0 + tslib: 2.5.2 dev: true /passport-http@0.3.0: @@ -19026,6 +25879,7 @@ packages: /path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + dev: true /path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} @@ -19052,12 +25906,12 @@ packages: /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - /path-scurry@1.6.4: - resolution: {integrity: sha512-Qp/9IHkdNiXJ3/Kon++At2nVpnhRiPq/aSvQN+H3U1WZbvNRK0RIQK/o4HMqPoXjpuGJUEWpHSs6Mnjxqh3TQg==} + /path-scurry@1.6.3: + resolution: {integrity: sha512-RAmB+n30SlN+HnNx6EbcpoDy9nwdpcGPnEKrJnu6GZoDWBdIjo1UQMVtW2ybtC7LC2oKLcMq8y5g8WnKLiod9g==} engines: {node: '>=16 || 14 >=14.17'} dependencies: - lru-cache: 9.0.1 - minipass: 5.0.0 + lru-cache: 7.18.3 + minipass: 4.2.5 dev: true /path-to-regexp@0.1.7: @@ -19085,6 +25939,10 @@ packages: resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==} dev: true + /pathe@1.1.1: + resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + dev: true + /pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true @@ -19105,6 +25963,10 @@ packages: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} dev: true + /performance-now@2.1.0: + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} + dev: false + /periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} dependencies: @@ -19156,16 +26018,30 @@ packages: resolution: {integrity: sha512-hM58GKXOcj8WTqUXnsQyJYXdeAPbythQgEF3nTcEo+nkD49chjQ9IKm/QJy9xf6JakXptz86h7ecP2024rrLaQ==} dependencies: jsonc-parser: 3.2.0 - mlly: 1.2.0 - pathe: 1.1.0 + mlly: 1.4.0 + pathe: 1.1.1 + dev: true + + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.4.0 + pathe: 1.1.1 dev: true - /playwright-core@1.32.3: - resolution: {integrity: sha512-SB+cdrnu74ZIn5Ogh/8278ngEh9NEEV0vR4sJFmK04h2iZpybfbqBY0bX6+BLYWVdV12JLLI+JEFtSnYgR+mWg==} + /playwright-core@1.32.1: + resolution: {integrity: sha512-KZYUQC10mXD2Am1rGlidaalNGYk3LU1vZqqNk0gT4XPty1jOqgup8KDP8l2CUlqoNKhXM5IfGjWgW37xvGllBA==} engines: {node: '>=14'} hasBin: true dev: true + /playwright-core@1.35.1: + resolution: {integrity: sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==} + engines: {node: '>=16'} + hasBin: true + dev: true + /please-upgrade-node@3.2.0: resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==} dependencies: @@ -19195,6 +26071,28 @@ packages: read-cache: 1.0.0 resolve: 1.22.2 + /postcss-import@14.1.0(postcss@8.4.24): + resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.24 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.1 + + /postcss-import@15.1.0(postcss@8.4.24): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.24 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.2 + /postcss-js@4.0.1(postcss@8.4.21): resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} engines: {node: ^12 || ^14 || >= 16} @@ -19204,7 +26102,16 @@ packages: camelcase-css: 2.0.1 postcss: 8.4.21 - /postcss-load-config@3.1.4(postcss@8.4.21): + /postcss-js@4.0.1(postcss@8.4.24): + 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.24 + + /postcss-load-config@3.1.4(postcss@8.4.21)(ts-node@10.9.1): resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} engines: {node: '>= 10'} peerDependencies: @@ -19218,9 +26125,10 @@ packages: dependencies: lilconfig: 2.1.0 postcss: 8.4.21 + ts-node: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) yaml: 1.10.2 - /postcss-load-config@3.1.4(postcss@8.4.21)(ts-node@10.9.1): + /postcss-load-config@3.1.4(postcss@8.4.24)(ts-node@10.9.1): resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} engines: {node: '>= 10'} peerDependencies: @@ -19233,10 +26141,82 @@ packages: optional: true dependencies: lilconfig: 2.1.0 - postcss: 8.4.21 - ts-node: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) + postcss: 8.4.24 + ts-node: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) yaml: 1.10.2 + /postcss-load-config@4.0.1(postcss@8.4.24)(ts-node@10.9.1): + resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + postcss: 8.4.24 + ts-node: 10.9.1(@types/node@18.15.10)(typescript@4.9.5) + yaml: 2.3.1 + + /postcss-loader@7.3.3(postcss@8.4.24)(webpack@5.76.2): + resolution: {integrity: sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==} + engines: {node: '>= 14.15.0'} + peerDependencies: + postcss: ^7.0.0 || ^8.0.1 + webpack: ^5.0.0 + dependencies: + cosmiconfig: 8.2.0 + jiti: 1.19.1 + postcss: 8.4.24 + semver: 7.5.3 + webpack: 5.76.2(esbuild@0.17.19) + dev: true + + /postcss-modules-extract-imports@3.0.0(postcss@8.4.24): + resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.24 + dev: true + + /postcss-modules-local-by-default@4.0.3(postcss@8.4.24): + resolution: {integrity: sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + icss-utils: 5.1.0(postcss@8.4.24) + postcss: 8.4.24 + postcss-selector-parser: 6.0.11 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-modules-scope@3.0.0(postcss@8.4.24): + resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.24 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-modules-values@4.0.0(postcss@8.4.24): + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + icss-utils: 5.1.0(postcss@8.4.24) + postcss: 8.4.24 + dev: true + /postcss-nested@6.0.0(postcss@8.4.21): resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==} engines: {node: '>=12.0'} @@ -19246,6 +26226,24 @@ packages: postcss: 8.4.21 postcss-selector-parser: 6.0.11 + /postcss-nested@6.0.0(postcss@8.4.24): + resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.24 + postcss-selector-parser: 6.0.11 + + /postcss-nested@6.0.1(postcss@8.4.24): + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.24 + postcss-selector-parser: 6.0.11 + /postcss-selector-parser@6.0.11: resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==} engines: {node: '>=4'} @@ -19253,6 +26251,10 @@ packages: cssesc: 3.0.0 util-deprecate: 1.0.2 + /postcss-value-parser@3.3.1: + resolution: {integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==} + dev: false + /postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -19264,6 +26266,14 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /postcss@8.4.24: + resolution: {integrity: sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + /preferred-pm@3.0.3: resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} engines: {node: '>=10'} @@ -19273,6 +26283,10 @@ packages: path-exists: 4.0.0 which-pm: 2.0.0 + /prefix-style@2.0.1: + resolution: {integrity: sha512-gdr1MBNVT0drzTq95CbSNdsrBDoHGlb2aDJP/FoY+1e+jSDPOb1Cv554gH2MGiSr2WTcXi/zu+NaFzfcHQkfBQ==} + dev: false + /prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} @@ -19282,37 +26296,47 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - /prettier-plugin-astro@0.7.2: - resolution: {integrity: sha512-mmifnkG160BtC727gqoimoxnZT/dwr8ASxpoGGl6EHevhfblSOeu+pwH1LAm5Qu1MynizktztFujHHaijLCkww==} + /prettier-plugin-astro@0.10.0: + resolution: {integrity: sha512-dPzop0gKZyVGpTDQmfy+e7FKXC9JT3mlpfYA2diOVz+Ui+QR1U4G/s+OesKl2Hib2JJOtAYJs/l+ovgT0ljlFA==} + engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'} + dependencies: + '@astrojs/compiler': 1.5.1 + prettier: 2.8.8 + sass-formatter: 0.7.6 + dev: true + + /prettier-plugin-astro@0.9.1: + resolution: {integrity: sha512-pYZXSbdq0eElvzoIMArzv1SBn1NUXzopjlcnt6Ql8VW32PjC12NovwBjXJ6rh8qQLi7vF8jNqAbraKW03UPfag==} engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'} dependencies: - '@astrojs/compiler': 0.31.4 + '@astrojs/compiler': 1.5.1 prettier: 2.8.8 sass-formatter: 0.7.6 synckit: 0.8.5 + dev: false - /prettier-plugin-svelte@2.10.0(prettier@2.8.7)(svelte@3.58.0): + /prettier-plugin-svelte@2.10.0(prettier@2.8.7)(svelte@3.57.0): resolution: {integrity: sha512-GXMY6t86thctyCvQq+jqElO+MKdB09BkL3hexyGP3Oi8XLKRFaJP1ud/xlWCZ9ZIa2BxHka32zhHfcuU+XsRQg==} peerDependencies: prettier: ^1.16.4 || ^2.0.0 svelte: ^3.2.0 dependencies: prettier: 2.8.7 - svelte: 3.58.0 + svelte: 3.57.0 dev: true - /prettier-plugin-svelte@2.10.0(prettier@2.8.8)(svelte@3.58.0): + /prettier-plugin-svelte@2.10.0(prettier@2.8.8)(svelte@3.57.0): resolution: {integrity: sha512-GXMY6t86thctyCvQq+jqElO+MKdB09BkL3hexyGP3Oi8XLKRFaJP1ud/xlWCZ9ZIa2BxHka32zhHfcuU+XsRQg==} peerDependencies: prettier: ^1.16.4 || ^2.0.0 svelte: ^3.2.0 dependencies: prettier: 2.8.8 - svelte: 3.58.0 + svelte: 3.57.0 dev: true - /prettier-plugin-tailwindcss@0.2.7(prettier@2.8.7): - resolution: {integrity: sha512-jQopIOgjLpX+y8HeD56XZw7onupRTC0cw7eKKUimI7vhjkPF5/1ltW5LyqaPtSyc8HvEpvNZsvvsGFa2qpa59w==} + /prettier-plugin-tailwindcss@0.2.5(prettier@2.8.7): + resolution: {integrity: sha512-vZ/iKieyCx0WTxHbkf5E1rBlv/ybFk8WTT4hL5W2jlVxum2Zbe0jMUpuQdDrpa4z2vnPiJ5KIWCqL/kd16fKYg==} engines: {node: '>=12.17.0'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' @@ -19440,6 +26464,12 @@ packages: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} + /promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + dependencies: + asap: 2.0.6 + dev: false + /prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -19453,6 +26483,12 @@ packages: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 + + /properties-reader@2.2.0: + resolution: {integrity: sha512-CgVcr8MwGoBKK24r9TwHfZkLLaNFHQ6y4wgT9w/XzdpacOOi5ciH4hcuLechSDAwXsfrGQtI2JTutY2djOx2Ow==} + engines: {node: '>=10'} + dependencies: + mkdirp: 1.0.4 dev: true /property-information@6.2.0: @@ -19469,6 +26505,10 @@ packages: /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + /prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + optional: true + /pseudomap@1.0.2: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} dev: true @@ -19508,7 +26548,7 @@ packages: engines: {node: '>=8.16.0'} dependencies: '@types/mime-types': 2.1.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) extract-zip: 1.7.0 https-proxy-agent: 4.0.0 mime: 2.6.0 @@ -19523,6 +26563,14 @@ packages: - utf-8-validate dev: true + /pure-color@1.3.0: + resolution: {integrity: sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==} + dev: false + + /pure-rand@6.0.2: + resolution: {integrity: sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==} + dev: true + /q@1.5.1: resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} engines: {node: '>=0.6.0', teleport: '>=0.2.0'} @@ -19561,8 +26609,14 @@ packages: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} - /ramda@0.28.0: - resolution: {integrity: sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==} + /raf@3.4.1: + resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} + dependencies: + performance-now: 2.1.0 + dev: false + + /ramda@0.29.0: + resolution: {integrity: sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==} dev: true /randombytes@2.1.0: @@ -19603,6 +26657,15 @@ packages: iconv-lite: 0.4.24 unpipe: 1.0.0 + /react-base16-styling@0.6.0: + resolution: {integrity: sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==} + dependencies: + base16: 1.0.0 + lodash.curry: 4.1.1 + lodash.flow: 3.5.0 + pure-color: 1.3.0 + dev: false + /react-colorful@5.6.1(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} peerDependencies: @@ -19613,6 +26676,19 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true + /react-custom-scrollbars@4.2.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-VtJTUvZ7kPh/auZWIbBRceGPkE30XBYe+HktFxuMWBR2eVQQ+Ur6yFJMoaYcNpyGq22uYJ9Wx4UAEcC0K+LNPQ==} + peerDependencies: + react: ^0.14.0 || ^15.0.0 || ^16.0.0 + react-dom: ^0.14.0 || ^15.0.0 || ^16.0.0 + dependencies: + dom-css: 2.1.0 + prop-types: 15.8.1 + raf: 3.4.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /react-docgen-typescript@2.2.2(typescript@4.9.5): resolution: {integrity: sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==} peerDependencies: @@ -19626,8 +26702,8 @@ packages: engines: {node: '>=12.0.0'} hasBin: true dependencies: - '@babel/core': 7.21.4 - '@babel/generator': 7.21.4 + '@babel/core': 7.22.5 + '@babel/generator': 7.22.5 ast-types: 0.14.2 commander: 2.20.3 doctrine: 3.0.0 @@ -19671,21 +26747,21 @@ packages: react: 18.2.0 dev: false - /react-hot-toast@2.4.0(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0): + /react-hot-toast@2.4.0(csstype@3.1.1)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-qnnVbXropKuwUpriVVosgo8QrB+IaPJCpL8oBI6Ov84uvHZ5QQcTp2qg6ku2wNfgJl6rlQXJIQU5q+5lmPOutA==} engines: {node: '>=10'} peerDependencies: react: '>=16' react-dom: '>=16' dependencies: - goober: 2.1.12(csstype@3.1.2) + goober: 2.1.12(csstype@3.1.1) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) transitivePeerDependencies: - csstype dev: false - /react-i18next@12.2.0(i18next@22.4.14)(react-dom@18.2.0)(react@18.2.0): + /react-i18next@12.2.0(i18next@22.4.13)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-5XeVgSygaGfyFmDd2WcXvINRw2WEC1XviW1LXY/xLOEMzsCFRwKqfnHN+hUjla8ZipbVJR27GCMSuTr0BhBBBQ==} peerDependencies: i18next: '>= 19.0.0' @@ -19700,7 +26776,7 @@ packages: dependencies: '@babel/runtime': 7.21.0 html-parse-stringify: 3.0.1 - i18next: 22.4.14 + i18next: 22.4.13 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false @@ -19724,7 +26800,6 @@ packages: /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: true /react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} @@ -19738,12 +26813,46 @@ packages: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true + /react-json-view@1.21.3(@types/react@18.0.37)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==} + peerDependencies: + react: ^17.0.0 || ^16.3.0 || ^15.5.4 + react-dom: ^17.0.0 || ^16.3.0 || ^15.5.4 + dependencies: + flux: 4.0.4(react@18.2.0) + react: 18.2.0 + react-base16-styling: 0.6.0 + react-dom: 18.2.0(react@18.2.0) + react-lifecycles-compat: 3.0.4 + react-textarea-autosize: 8.5.2(@types/react@18.0.37)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - encoding + dev: false + + /react-leaflet@4.2.1(leaflet@1.9.4)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==} + peerDependencies: + leaflet: ^1.9.0 + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + '@react-leaflet/core': 2.1.0(leaflet@1.9.4)(react-dom@18.2.0)(react@18.2.0) + leaflet: 1.9.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react-lifecycles-compat@3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + dev: false + /react-refresh@0.14.0: resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} engines: {node: '>=0.10.0'} dev: true - /react-remove-scroll-bar@2.3.4(@types/react@18.0.35)(react@18.2.0): + /react-remove-scroll-bar@2.3.4(@types/react@18.0.29)(react@18.2.0): resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} engines: {node: '>=10'} peerDependencies: @@ -19753,13 +26862,48 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.0.35 + '@types/react': 18.0.29 react: 18.2.0 - react-style-singleton: 2.2.1(@types/react@18.0.35)(react@18.2.0) - tslib: 2.5.0 + react-style-singleton: 2.2.1(@types/react@18.0.29)(react@18.2.0) + tslib: 2.5.2 + dev: false + + /react-remove-scroll-bar@2.3.4(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.37 + react: 18.2.0 + react-style-singleton: 2.2.1(@types/react@18.0.37)(react@18.2.0) + tslib: 2.5.2 + dev: false + + /react-remove-scroll@2.5.4(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-xGVKJJr0SJGQVirVFAUZ2k1QLyO6m+2fy0l8Qawbp5Jgrv3DeLalrfMNBFSlmz5kriGGzsVBtGVnf4pTKIhhWA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.37 + react: 18.2.0 + react-remove-scroll-bar: 2.3.4(@types/react@18.0.37)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.0.37)(react@18.2.0) + tslib: 2.5.2 + use-callback-ref: 1.3.0(@types/react@18.0.37)(react@18.2.0) + use-sidecar: 1.1.2(@types/react@18.0.37)(react@18.2.0) dev: false - /react-remove-scroll@2.5.5(@types/react@18.0.35)(react@18.2.0): + /react-remove-scroll@2.5.5(@types/react@18.0.29)(react@18.2.0): resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} engines: {node: '>=10'} peerDependencies: @@ -19769,13 +26913,43 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.0.35 + '@types/react': 18.0.29 react: 18.2.0 - react-remove-scroll-bar: 2.3.4(@types/react@18.0.35)(react@18.2.0) - react-style-singleton: 2.2.1(@types/react@18.0.35)(react@18.2.0) - tslib: 2.5.0 - use-callback-ref: 1.3.0(@types/react@18.0.35)(react@18.2.0) - use-sidecar: 1.1.2(@types/react@18.0.35)(react@18.2.0) + react-remove-scroll-bar: 2.3.4(@types/react@18.0.29)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.0.29)(react@18.2.0) + tslib: 2.5.2 + use-callback-ref: 1.3.0(@types/react@18.0.29)(react@18.2.0) + use-sidecar: 1.1.2(@types/react@18.0.29)(react@18.2.0) + dev: false + + /react-remove-scroll@2.5.5(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.37 + react: 18.2.0 + react-remove-scroll-bar: 2.3.4(@types/react@18.0.37)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.0.37)(react@18.2.0) + tslib: 2.5.2 + use-callback-ref: 1.3.0(@types/react@18.0.37)(react@18.2.0) + use-sidecar: 1.1.2(@types/react@18.0.37)(react@18.2.0) + dev: false + + /react-resize-detector@8.1.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-S7szxlaIuiy5UqLhLL1KY3aoyGHbZzsTpYal9eYMwCyKqoqoVLCmIgAgNyIM1FhnP2KyBygASJxdhejrzjMb+w==} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + lodash: 4.17.21 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) dev: false /react-router-dom@6.11.2(react-dom@18.2.0)(react@18.2.0): @@ -19810,21 +26984,91 @@ packages: throttle-debounce: 3.0.1 dev: true - /react-style-singleton@2.2.1(@types/react@18.0.35)(react@18.2.0): - resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} - engines: {node: '>=10'} + /react-smooth@2.0.3(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-yl4y3XiMorss7ayF5QnBiSprig0+qFHui8uh7Hgg46QX5O+aRMRKlfGGNGLHno35JkQSvSYY8eCWkBfHfrSHfg==} + peerDependencies: + prop-types: ^15.6.0 + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + fast-equals: 5.0.1 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-transition-group: 2.9.0(react-dom@18.2.0)(react@18.2.0) + dev: false + + /react-style-singleton@2.2.1(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.29 + get-nonce: 1.0.1 + invariant: 2.2.4 + react: 18.2.0 + tslib: 2.5.2 + dev: false + + /react-style-singleton@2.2.1(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.37 + get-nonce: 1.0.1 + invariant: 2.2.4 + react: 18.2.0 + tslib: 2.5.2 + dev: false + + /react-textarea-autosize@8.5.2(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-uOkyjkEl0ByEK21eCJMHDGBAAd/BoFQBawYK5XItjAmCTeSbjxghd8qnt7nzsLYzidjnoObu6M26xts0YGKsGg==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.21.0 + react: 18.2.0 + use-composed-ref: 1.3.0(react@18.2.0) + use-latest: 1.2.1(@types/react@18.0.37)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + + /react-transition-group@2.9.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==} + peerDependencies: + react: '>=15.0.0' + react-dom: '>=15.0.0' + dependencies: + dom-helpers: 3.4.0 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-lifecycles-compat: 3.0.4 + dev: false + + /react-zoom-pan-pinch@3.0.8(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-z6O5SV5X+XBo/LLO59PgzNE2WT+tp8lw1w3M0y138jCXViwHWKK1MqorICbmSVSOOD5Fa2o6pcg1ppJj9vzqJA==} + engines: {node: '>=8', npm: '>=5'} peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + react: '*' + react-dom: '*' dependencies: - '@types/react': 18.0.35 - get-nonce: 1.0.1 - invariant: 2.2.4 react: 18.2.0 - tslib: 2.5.0 + react-dom: 18.2.0(react@18.2.0) dev: false /react@18.2.0: @@ -19886,22 +27130,18 @@ packages: string_decoder: 1.3.0 util-deprecate: 1.0.2 + /readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + dependencies: + minimatch: 5.1.6 + dev: true + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} dependencies: picomatch: 2.3.1 - /recast@0.20.5: - resolution: {integrity: sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ==} - engines: {node: '>= 4'} - dependencies: - ast-types: 0.14.2 - esprima: 4.0.1 - source-map: 0.6.1 - tslib: 2.5.0 - dev: false - /recast@0.21.5: resolution: {integrity: sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg==} engines: {node: '>= 4'} @@ -19909,7 +27149,7 @@ packages: ast-types: 0.15.2 esprima: 4.0.1 source-map: 0.6.1 - tslib: 2.5.0 + tslib: 2.5.2 dev: true /recast@0.23.1: @@ -19920,9 +27160,37 @@ packages: ast-types: 0.16.1 esprima: 4.0.1 source-map: 0.6.1 - tslib: 2.5.0 + tslib: 2.5.2 dev: true + /recharts-scale@0.4.5: + resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + dependencies: + decimal.js-light: 2.5.1 + dev: false + + /recharts@2.7.2(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-HMKRBkGoOXHW+7JcRa6+MukPSifNtJlqbc+JreGVNA407VLE/vOP+8n3YYjprDVVIF9E2ZgwWnL3D7K/LUFzBg==} + engines: {node: '>=12'} + peerDependencies: + prop-types: ^15.6.0 + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + classnames: 2.3.2 + eventemitter3: 4.0.7 + lodash: 4.17.21 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-is: 16.13.1 + react-resize-detector: 8.1.0(react-dom@18.2.0)(react@18.2.0) + react-smooth: 2.0.3(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0) + recharts-scale: 0.4.5 + reduce-css-calc: 2.1.8 + victory-vendor: 36.6.11 + dev: false + /rechoir@0.6.2: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} @@ -19938,6 +27206,13 @@ packages: strip-indent: 3.0.0 dev: true + /reduce-css-calc@2.1.8: + resolution: {integrity: sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==} + dependencies: + css-unit-converter: 1.1.2 + postcss-value-parser: 3.3.1 + dev: false + /reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} @@ -19961,6 +27236,10 @@ packages: '@babel/runtime': 7.21.0 dev: true + /regex-parser@2.2.11: + resolution: {integrity: sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==} + dev: true + /regexp.prototype.flags@1.4.3: resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} engines: {node: '>= 0.4'} @@ -20060,7 +27339,7 @@ packages: dependencies: '@types/mdast': 3.0.11 mdast-util-frontmatter: 1.0.1 - micromark-extension-frontmatter: 1.1.0 + micromark-extension-frontmatter: 1.0.1 unified: 10.1.2 dev: false @@ -20117,7 +27396,7 @@ packages: '@types/hast': 2.3.4 '@types/mdast': 3.0.11 github-slugger: 1.5.0 - mdast-util-to-string: 3.2.0 + mdast-util-to-string: 3.1.1 unified: 10.1.2 unist-util-visit: 4.1.2 dev: true @@ -20186,6 +27465,17 @@ packages: global-dirs: 0.1.1 dev: true + /resolve-url-loader@5.0.0: + resolution: {integrity: sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==} + engines: {node: '>=12'} + dependencies: + adjust-sourcemap-loader: 4.0.0 + convert-source-map: 1.9.0 + loader-utils: 2.0.4 + postcss: 8.4.24 + source-map: 0.6.1 + dev: true + /resolve-url@0.2.1: resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} deprecated: https://github.com/lydell/resolve-url#deprecated @@ -20196,18 +27486,31 @@ packages: engines: {node: '>=10'} dev: true + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: true + /resolve@1.19.0: resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} dependencies: - is-core-module: 2.12.0 + is-core-module: 2.11.0 path-parse: 1.0.7 dev: true + /resolve@1.22.1: + resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + hasBin: true + dependencies: + is-core-module: 2.11.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + /resolve@1.22.2: resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} hasBin: true dependencies: - is-core-module: 2.12.0 + is-core-module: 2.11.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -20215,7 +27518,7 @@ packages: resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} hasBin: true dependencies: - is-core-module: 2.12.0 + is-core-module: 2.11.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 dev: true @@ -20303,7 +27606,7 @@ packages: engines: {node: '>=14'} hasBin: true dependencies: - glob: 9.3.5 + glob: 9.3.2 dev: true /rimraf@4.4.1: @@ -20311,21 +27614,9 @@ packages: engines: {node: '>=14'} hasBin: true dependencies: - glob: 9.3.5 + glob: 9.3.2 dev: true - /roarr@2.15.4: - resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} - engines: {node: '>=8.0'} - dependencies: - boolean: 3.2.0 - detect-node: 2.1.0 - globalthis: 1.0.3 - json-stringify-safe: 5.0.1 - semver-compare: 1.0.0 - sprintf-js: 1.1.2 - dev: false - /rollup-plugin-dts@4.2.2(rollup@2.70.2)(typescript@4.9.5): resolution: {integrity: sha512-A3g6Rogyko/PXeKoUlkjxkP++8UDVpgA7C+Tdl77Xj4fgEaIjPSnxRmR53EzvoYy97VMVwLAOcWJudaVAuxneQ==} engines: {node: '>=v12.22.11'} @@ -20337,7 +27628,7 @@ packages: rollup: 2.70.2 typescript: 4.9.5 optionalDependencies: - '@babel/code-frame': 7.21.4 + '@babel/code-frame': 7.22.5 dev: true /rollup-plugin-size@0.2.2: @@ -20355,11 +27646,11 @@ packages: peerDependencies: rollup: ^2.0.0 dependencies: - '@babel/code-frame': 7.21.4 + '@babel/code-frame': 7.22.5 jest-worker: 26.6.2 rollup: 2.70.2 serialize-javascript: 4.0.0 - terser: 5.16.9 + terser: 5.16.8 dev: true /rollup-plugin-typescript-paths@1.4.0(typescript@4.9.5): @@ -20414,8 +27705,15 @@ packages: optionalDependencies: fsevents: 2.3.2 - /rollup@3.20.2: - resolution: {integrity: sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==} + /rollup@3.25.1: + resolution: {integrity: sha512-tywOR+rwIt5m2ZAWSe5AIJcTat8vGlnPFAv15ycCrw33t6iFsXZ6mzHVFh2psSjxQPmI+xgzMZZizUAukBI4aQ==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + + /rollup@3.26.2: + resolution: {integrity: sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -20439,7 +27737,7 @@ packages: /rxjs@7.8.0: resolution: {integrity: sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==} dependencies: - tslib: 2.5.0 + tslib: 2.5.2 /s.color@0.0.15: resolution: {integrity: sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==} @@ -20468,6 +27766,11 @@ packages: is-regex: 1.1.4 dev: true + /safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + dev: false + /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -20485,9 +27788,32 @@ packages: dependencies: suf-log: 2.5.3 - /sass@1.62.0: - resolution: {integrity: sha512-Q4USplo4pLYgCi+XlipZCWUQz5pkg/ruSSgJ0WRDSb/+3z9tXUOkQ7QPYn4XrhZKYAK4HlpaQecRwKLJX6+DBg==} - engines: {node: '>=14.0.0'} + /sass-loader@13.3.2(webpack@5.76.2): + resolution: {integrity: sha512-CQbKl57kdEv+KDLquhC+gE3pXt74LEAzm+tzywcA0/aHZuub8wTErbjAoNI57rPUWRYRNC5WUnNl8eGJNbDdwg==} + engines: {node: '>= 14.15.0'} + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + sass: ^1.3.0 + sass-embedded: '*' + webpack: ^5.0.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + sass-embedded: + optional: true + dependencies: + neo-async: 2.6.2 + webpack: 5.76.2(esbuild@0.17.19) + dev: true + + /sass@1.60.0: + resolution: {integrity: sha512-updbwW6fNb5gGm8qMXzVO7V4sWf7LMXnMly/JEyfbfERbVH46Fn6q02BX7/eHTdKpE7d+oTkMMQpFWNUMfFbgQ==} + engines: {node: '>=12.0.0'} hasBin: true dependencies: chokidar: 3.5.3 @@ -20495,6 +27821,10 @@ packages: source-map-js: 1.0.2 dev: true + /sax@1.2.4: + resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + optional: true + /saxes@5.0.1: resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} engines: {node: '>=10'} @@ -20537,11 +27867,11 @@ packages: /semver-compare@1.0.0: resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} + dev: true /semver@5.7.1: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} hasBin: true - dev: true /semver@6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} @@ -20568,8 +27898,8 @@ packages: lru-cache: 6.0.0 dev: true - /semver@7.4.0: - resolution: {integrity: sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==} + /semver@7.5.3: + resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==} engines: {node: '>=10'} hasBin: true dependencies: @@ -20616,13 +27946,6 @@ packages: transitivePeerDependencies: - supports-color - /serialize-error@7.0.1: - resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} - engines: {node: '>=10'} - dependencies: - type-fest: 0.13.1 - dev: false - /serialize-javascript@4.0.0: resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} dependencies: @@ -20635,9 +27958,8 @@ packages: randombytes: 2.1.0 dev: true - /seroval@0.5.1: - resolution: {integrity: sha512-ZfhQVB59hmIauJG5Ydynupy8KHyr5imGNtdDhbZG68Ufh1Ynkv9KOYOAABf71oVbQxJ8VkWnMHAjEHE7fWkH5g==} - engines: {node: '>=10'} + /serialize-query-params@2.0.2: + resolution: {integrity: sha512-1chMo1dST4pFA9RDXAtF0Rbjaut4is7bzFbI1Z26IuMub68pNCILku85aYmeFhvnY//BXUPUhoRMjYcsT93J/Q==} dev: false /serve-favicon@2.5.0: @@ -20674,12 +27996,20 @@ packages: transitivePeerDependencies: - supports-color + /server-destroy@1.0.1: + resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} + dev: false + /set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} /set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + /setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + dev: false + /setprototypeof@1.1.1: resolution: {integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==} dev: false @@ -20720,8 +28050,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - /shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + /shell-quote@1.8.0: + resolution: {integrity: sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==} /shelljs@0.8.5: resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} @@ -20733,22 +28063,22 @@ packages: rechoir: 0.6.2 dev: true - /shiki@0.11.1: - resolution: {integrity: sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==} + /shiki@0.14.2: + resolution: {integrity: sha512-ltSZlSLOuSY0M0Y75KA+ieRaZ0Trf5Wl3gutE7jzLuIcWxLp5i/uEnLoQWNvgKXQ5OMpGkJnVMRLAuzjc0LJ2A==} dependencies: + ansi-sequence-parser: 1.1.0 jsonc-parser: 3.2.0 vscode-oniguruma: 1.7.0 - vscode-textmate: 6.0.0 - dev: false + vscode-textmate: 8.0.0 - /shiki@0.14.1: - resolution: {integrity: sha512-+Jz4nBkCBe0mEDqo1eKRcCdjRtrCjozmcbTUjbPTX7OOJfEbTZzlUWlZtGe3Gb5oV1/jnojhG//YZc3rs9zSEw==} + /shiki@0.14.3: + resolution: {integrity: sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g==} dependencies: ansi-sequence-parser: 1.1.0 jsonc-parser: 3.2.0 vscode-oniguruma: 1.7.0 vscode-textmate: 8.0.0 - dev: true + dev: false /side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} @@ -20768,7 +28098,6 @@ packages: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} dependencies: is-arrayish: 0.3.2 - dev: true /simple-update-notifier@1.1.0: resolution: {integrity: sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==} @@ -20783,7 +28112,7 @@ packages: dependencies: '@polka/url': 1.0.0-next.21 mrmime: 1.0.1 - totalist: 3.0.1 + totalist: 3.0.0 dev: false /sisteransi@1.0.5: @@ -20821,6 +28150,7 @@ packages: /slash@4.0.0: resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} engines: {node: '>=12'} + dev: true /slice-ansi@3.0.0: resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} @@ -20865,11 +28195,8 @@ packages: resolution: {integrity: sha512-V21+XeNni+tTyiST1MHsa84AQhT1aFZipzPpOFAVB8DkHzwJyjjAmt9bgwnuZiZWnIbMo2duE29wybxv/7HWUw==} dev: true - /solid-js@1.7.3: - resolution: {integrity: sha512-4hwaF/zV/xbNeBBIYDyu3dcReOZBECbO//mrra6GqOrKy4Soyo+fnKjpZSa0nODm6j1aL0iQRh/7ofYowH+jzw==} - dependencies: - csstype: 3.1.2 - seroval: 0.5.1 + /solid-js@1.4.3: + resolution: {integrity: sha512-3uh2cbT4ICronIasLAxycF6SVgvqcfwFCDCzlEA9CEahn1qQg8Rw8aRGiI4O51PrHcN5aPRO9knYYRCs0PgzcQ==} dev: false /sorcery@0.10.0: @@ -20886,6 +28213,13 @@ packages: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} + /source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: @@ -20949,6 +28283,10 @@ packages: resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} dev: true + /split-ca@1.0.1: + resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} + dev: true + /split2@3.2.2: resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} dependencies: @@ -20958,8 +28296,27 @@ packages: /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - /sprintf-js@1.1.2: - resolution: {integrity: sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==} + /ssh-remote-port-forward@1.0.4: + resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + dependencies: + '@types/ssh2': 0.5.52 + ssh2: 1.14.0 + dev: true + + /ssh2@1.14.0: + resolution: {integrity: sha512-AqzD1UCqit8tbOKoj6ztDDi1ffJZ2rV2SwlgrVVrHPkV5vWqGJOVp5pmtj18PunkPJAuKQsnInyKV+/Nb2bUnA==} + engines: {node: '>=10.16.0'} + requiresBuild: true + dependencies: + asn1: 0.2.6 + bcrypt-pbkdf: 1.0.2 + optionalDependencies: + cpu-features: 0.0.8 + nan: 2.17.0 + dev: true + + /stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} dev: false /stack-utils@2.0.6: @@ -20986,6 +28343,10 @@ packages: resolution: {integrity: sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA==} dev: true + /std-env@3.3.3: + resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==} + dev: true + /stdin-discarder@0.1.0: resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -21016,6 +28377,18 @@ packages: - utf-8-validate dev: true + /storybook@7.0.26: + resolution: {integrity: sha512-N6+/QBIahTnOJ3mQFNh+PIimjw+yUUoBlnMq8kE1Rg6QFi8ErEK8xte6uppiTh+7ShpqeLhp9ipuDV6DwJ9Aqg==} + hasBin: true + dependencies: + '@storybook/cli': 7.0.26 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: true + /stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} dependencies: @@ -21072,7 +28445,7 @@ packages: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.0.1 + strip-ansi: 7.1.0 /string.prototype.matchall@4.0.8: resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} @@ -21144,8 +28517,8 @@ packages: dependencies: ansi-regex: 5.0.1 - /strip-ansi@7.0.1: - resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} dependencies: ansi-regex: 6.0.1 @@ -21187,13 +28560,13 @@ packages: /strip-literal@0.4.2: resolution: {integrity: sha512-pv48ybn4iE1O9RLgCAN0iU4Xv7RlBTiit6DKmMiErbs9x1wH6vXBs45tWc0H5wUIF6TLTrKweqkmYF/iraQKNw==} dependencies: - acorn: 8.8.2 + acorn: 8.9.0 dev: true /strip-literal@1.0.1: resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==} dependencies: - acorn: 8.8.2 + acorn: 8.9.0 dev: true /strnum@1.0.5: @@ -21209,6 +28582,15 @@ packages: through: 2.3.8 dev: true + /style-loader@3.3.3(webpack@5.76.2): + resolution: {integrity: sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + dependencies: + webpack: 5.76.2(esbuild@0.17.19) + dev: true + /style-to-object@0.4.1: resolution: {integrity: sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==} dependencies: @@ -21237,7 +28619,7 @@ packages: engines: {node: '>=8'} hasBin: true dependencies: - '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/gen-mapping': 0.3.2 commander: 4.1.1 glob: 7.1.6 lines-and-columns: 1.2.4 @@ -21269,6 +28651,26 @@ packages: - supports-color dev: true + /superagent@6.1.0: + resolution: {integrity: sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==} + engines: {node: '>= 7.0.0'} + deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at <https://github.com/visionmedia/superagent/releases>. + dependencies: + component-emitter: 1.3.0 + cookiejar: 2.1.4 + debug: 4.3.4(supports-color@8.1.1) + fast-safe-stringify: 2.1.1 + form-data: 3.0.1 + formidable: 1.2.6 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.11.2 + readable-stream: 3.6.2 + semver: 7.5.3 + transitivePeerDependencies: + - supports-color + dev: true + /superjson@1.12.2: resolution: {integrity: sha512-ugvUo9/WmvWOjstornQhsN/sR9mnGtWGYeTxFuqLb4AiT4QdUavjGFRALCPKWWnAiUJ4HTpytj5e0t5HoMRkXg==} engines: {node: '>=10'} @@ -21286,6 +28688,16 @@ packages: - supports-color dev: true + /supertest@6.1.3: + resolution: {integrity: sha512-v2NVRyP73XDewKb65adz+yug1XMtmvij63qIWHZzSX8tp6wiq6xBLUy4SUAd2NII6wIipOmHT/FD9eicpJwdgQ==} + engines: {node: '>=6.0.0'} + dependencies: + methods: 1.1.2 + superagent: 6.1.0 + transitivePeerDependencies: + - supports-color + dev: true + /supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -21304,12 +28716,6 @@ packages: dependencies: has-flag: 4.0.0 - /supports-esm@1.0.0: - resolution: {integrity: sha512-96Am8CDqUaC0I2+C/swJ0yEvM8ZnGn4unoers/LSdE4umhX7mELzqyLzx3HnZAluq5PXIsGMKqa7NkqaeHMPcg==} - dependencies: - has-package-exports: 1.3.0 - dev: false - /supports-hyperlinks@2.3.0: resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} engines: {node: '>=8'} @@ -21322,20 +28728,20 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - /svelte-check@2.10.3(@babel/core@7.21.4)(postcss@8.4.21)(svelte@3.58.0): + /svelte-check@2.10.3(@babel/core@7.21.3)(postcss@8.4.21)(svelte@3.57.0): resolution: {integrity: sha512-Nt1aWHTOKFReBpmJ1vPug0aGysqPwJh2seM1OvICfM2oeyaA62mOiy5EvkXhltGfhCcIQcq2LoE0l1CwcWPjlw==} hasBin: true peerDependencies: svelte: ^3.24.0 dependencies: - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/trace-mapping': 0.3.9 chokidar: 3.5.3 fast-glob: 3.2.12 import-fresh: 3.3.0 picocolors: 1.0.0 sade: 1.8.1 - svelte: 3.58.0 - svelte-preprocess: 4.10.7(@babel/core@7.21.4)(postcss@8.4.21)(svelte@3.58.0)(typescript@4.9.5) + svelte: 3.57.0 + svelte-preprocess: 4.10.7(@babel/core@7.21.3)(postcss@8.4.21)(svelte@3.57.0)(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - '@babel/core' @@ -21350,16 +28756,16 @@ packages: - sugarss dev: true - /svelte-hmr@0.15.1(svelte@3.58.0): + /svelte-hmr@0.15.1(svelte@3.57.0): resolution: {integrity: sha512-BiKB4RZ8YSwRKCNVdNxK/GfY+r4Kjgp9jCLEy0DuqAKfmQtpL38cQK3afdpjw4sqSs4PLi3jIPJIFp259NkZtA==} engines: {node: ^12.20 || ^14.13.1 || >= 16} peerDependencies: svelte: '>=3.19.0' dependencies: - svelte: 3.58.0 + svelte: 3.57.0 dev: true - /svelte-preprocess@4.10.7(@babel/core@7.21.4)(postcss@8.4.21)(svelte@3.58.0)(typescript@4.9.5): + /svelte-preprocess@4.10.7(@babel/core@7.21.3)(postcss@8.4.21)(svelte@3.57.0)(typescript@4.9.5): resolution: {integrity: sha512-sNPBnqYD6FnmdBrUmBCaqS00RyCsCpj2BG58A1JBswNF7b0OKviwxqVrOL/CKyJrLSClrSeqQv5BXNg2RUbPOw==} engines: {node: '>= 9.11.2'} requiresBuild: true @@ -21400,7 +28806,7 @@ packages: typescript: optional: true dependencies: - '@babel/core': 7.21.4 + '@babel/core': 7.21.3 '@types/pug': 2.0.6 '@types/sass': 1.45.0 detect-indent: 6.1.0 @@ -21408,20 +28814,20 @@ packages: postcss: 8.4.21 sorcery: 0.10.0 strip-indent: 3.0.0 - svelte: 3.58.0 + svelte: 3.57.0 typescript: 4.9.5 dev: true - /svelte@3.58.0: - resolution: {integrity: sha512-brIBNNB76mXFmU/Kerm4wFnkskBbluBDCjx/8TcpYRb298Yh2dztS2kQ6bhtjMcvUhd5ynClfwpz5h2gnzdQ1A==} + /svelte@3.57.0: + resolution: {integrity: sha512-WMXEvF+RtAaclw0t3bPDTUe19pplMlfyKDsixbHQYgCWi9+O9VN0kXU1OppzrB9gPAvz4NALuoca2LfW2bOjTQ==} engines: {node: '>= 8'} /swagger-ui-dist@4.15.5: resolution: {integrity: sha512-V3eIa28lwB6gg7/wfNvAbjwJYmDXy1Jo1POjyTzlB6wPcHiGlRxq39TSjYGVjQrUSAzpv+a7nzp7mDxgNy57xA==} dev: true - /swagger-ui-dist@4.18.2: - resolution: {integrity: sha512-oVBoBl9Dg+VJw8uRWDxlyUyHoNEDC0c1ysT6+Boy6CTgr2rUcLcfPon4RvxgS2/taNW6O0+US+Z/dlAsWFjOAQ==} + /swagger-ui-dist@4.18.1: + resolution: {integrity: sha512-n7AT4wzKIPpHy/BGflJOepGMrbY/7Cd5yVd9ptVczaJGAKScbVJrZxFbAE2ZSZa8KmqdQ0+pOs3/5mWY5tSMZQ==} dev: false /swagger-ui-express@4.6.2(express@4.18.2): @@ -21431,7 +28837,7 @@ packages: express: '>=4.0.0' dependencies: express: 4.18.2 - swagger-ui-dist: 4.18.2 + swagger-ui-dist: 4.18.1 dev: false /symbol-observable@1.2.0: @@ -21457,22 +28863,34 @@ packages: engines: {node: ^14.18.0 || >=16.0.0} dependencies: '@pkgr/utils': 2.3.1 - tslib: 2.5.0 + tslib: 2.5.2 + + /tailwind-merge@1.10.0: + resolution: {integrity: sha512-WFnDXSS4kFTZwjKg5/oZSGzBRU/l+qcbv5NVTzLUQvJ9yovDAP05h0F2+ZFW0Lw9EcgRoc2AfURUdZvnEFrXKg==} + dev: false - /tailwind-merge@1.12.0: - resolution: {integrity: sha512-Y17eDp7FtN1+JJ4OY0Bqv9OA41O+MS8c1Iyr3T6JFLnOgLg3EvcyMKZAnQ8AGyvB5Nxm3t9Xb5Mhe139m8QT/g==} + /tailwind-merge@1.13.2: + resolution: {integrity: sha512-R2/nULkdg1VR/EL4RXg4dEohdoxNUJGLMnWIQnPKL+O9Twu7Cn3Rxi4dlXkDzZrEGtR+G+psSXFouWlpTyLhCQ==} dev: false - /tailwindcss-animate@1.0.5(tailwindcss@3.3.1): + /tailwindcss-animate@1.0.5(tailwindcss@3.2.7): resolution: {integrity: sha512-UU3qrOJ4lFQABY+MVADmBm+0KW3xZyhMdRvejwtXqYOL7YjHYxmuREFAZdmVG5LPe5E9CAst846SLC4j5I3dcw==} peerDependencies: tailwindcss: '>=3.0.0 || insiders' dependencies: - tailwindcss: 3.3.1(postcss@8.4.21) + tailwindcss: 3.2.7(postcss@8.4.24)(ts-node@10.9.1) dev: false - /tailwindcss@3.3.1(postcss@8.4.21): - resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==} + /tailwindcss-animate@1.0.5(tailwindcss@3.3.2): + resolution: {integrity: sha512-UU3qrOJ4lFQABY+MVADmBm+0KW3xZyhMdRvejwtXqYOL7YjHYxmuREFAZdmVG5LPe5E9CAst846SLC4j5I3dcw==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + dependencies: + tailwindcss: 3.3.2(ts-node@10.9.1) + dev: true + + /tailwindcss@3.2.7(postcss@8.4.21)(ts-node@10.9.1): + resolution: {integrity: sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==} engines: {node: '>=12.13.0'} hasBin: true peerDependencies: @@ -21481,12 +28899,12 @@ packages: arg: 5.0.2 chokidar: 3.5.3 color-name: 1.1.4 + detective: 5.2.1 didyoumean: 1.2.2 dlv: 1.1.3 - fast-glob: 3.2.12 + fast-glob: 3.3.0 glob-parent: 6.0.2 is-glob: 4.0.3 - jiti: 1.18.2 lilconfig: 2.1.0 micromatch: 4.0.5 normalize-path: 3.0.0 @@ -21495,18 +28913,17 @@ packages: postcss: 8.4.21 postcss-import: 14.1.0(postcss@8.4.21) postcss-js: 4.0.1(postcss@8.4.21) - postcss-load-config: 3.1.4(postcss@8.4.21) + postcss-load-config: 3.1.4(postcss@8.4.21)(ts-node@10.9.1) postcss-nested: 6.0.0(postcss@8.4.21) postcss-selector-parser: 6.0.11 postcss-value-parser: 4.2.0 quick-lru: 5.1.1 - resolve: 1.22.2 - sucrase: 3.32.0 + resolve: 1.22.1 transitivePeerDependencies: - ts-node - /tailwindcss@3.3.1(postcss@8.4.21)(ts-node@10.9.1): - resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==} + /tailwindcss@3.2.7(postcss@8.4.24)(ts-node@10.9.1): + resolution: {integrity: sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==} engines: {node: '>=12.13.0'} hasBin: true peerDependencies: @@ -21515,36 +28932,74 @@ packages: arg: 5.0.2 chokidar: 3.5.3 color-name: 1.1.4 + detective: 5.2.1 didyoumean: 1.2.2 dlv: 1.1.3 fast-glob: 3.2.12 glob-parent: 6.0.2 is-glob: 4.0.3 - jiti: 1.18.2 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.21 - postcss-import: 14.1.0(postcss@8.4.21) - postcss-js: 4.0.1(postcss@8.4.21) - postcss-load-config: 3.1.4(postcss@8.4.21)(ts-node@10.9.1) - postcss-nested: 6.0.0(postcss@8.4.21) + postcss: 8.4.24 + postcss-import: 14.1.0(postcss@8.4.24) + postcss-js: 4.0.1(postcss@8.4.24) + postcss-load-config: 3.1.4(postcss@8.4.24)(ts-node@10.9.1) + postcss-nested: 6.0.0(postcss@8.4.24) postcss-selector-parser: 6.0.11 postcss-value-parser: 4.2.0 quick-lru: 5.1.1 + resolve: 1.22.1 + transitivePeerDependencies: + - ts-node + + /tailwindcss@3.3.2(ts-node@10.9.1): + resolution: {integrity: sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.5.3 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.0 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.19.1 + 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.24 + postcss-import: 15.1.0(postcss@8.4.24) + postcss-js: 4.0.1(postcss@8.4.24) + postcss-load-config: 4.0.1(postcss@8.4.24)(ts-node@10.9.1) + postcss-nested: 6.0.1(postcss@8.4.24) + postcss-selector-parser: 6.0.11 + postcss-value-parser: 4.2.0 resolve: 1.22.2 sucrase: 3.32.0 transitivePeerDependencies: - ts-node - dev: true /tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} dev: true + /tar-fs@2.0.1: + resolution: {integrity: sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==} + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + dev: true + /tar-fs@2.1.1: resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} dependencies: @@ -21571,7 +29026,7 @@ packages: dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 - minipass: 4.2.8 + minipass: 4.2.5 minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 @@ -21631,6 +29086,31 @@ packages: supports-hyperlinks: 2.3.0 dev: true + /terser-webpack-plugin@5.3.7(esbuild@0.17.19)(webpack@5.76.2): + resolution: {integrity: sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + dependencies: + '@jridgewell/trace-mapping': 0.3.17 + esbuild: 0.17.19 + jest-worker: 27.5.1 + schema-utils: 3.1.1 + serialize-javascript: 6.0.1 + terser: 5.16.8 + webpack: 5.76.2(esbuild@0.17.19) + dev: true + /terser-webpack-plugin@5.3.7(webpack@5.76.2): resolution: {integrity: sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==} engines: {node: '>= 10.13.0'} @@ -21647,31 +29127,31 @@ packages: uglify-js: optional: true dependencies: - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/trace-mapping': 0.3.17 jest-worker: 27.5.1 schema-utils: 3.1.1 serialize-javascript: 6.0.1 - terser: 5.16.9 + terser: 5.16.8 webpack: 5.76.2 dev: true - /terser@5.16.9: - resolution: {integrity: sha512-HPa/FdTB9XGI2H1/keLFZHxl6WNvAI4YalHGtDQTlMnJcoqSab1UwL4l1hGEhs6/GmLHBZIg/YgB++jcbzoOEg==} + /terser@5.16.8: + resolution: {integrity: sha512-QI5g1E/ef7d+PsDifb+a6nnVgC4F22Bg6T0xrBrz6iloVB4PUkkunp6V8nzoOOZJIzjWVdAGqCdlKlhLq/TbIA==} engines: {node: '>=10'} hasBin: true dependencies: - '@jridgewell/source-map': 0.3.3 - acorn: 8.8.2 + '@jridgewell/source-map': 0.3.2 + acorn: 8.9.0 commander: 2.20.3 source-map-support: 0.5.21 dev: true - /tesseract.js-core@4.0.3: - resolution: {integrity: sha512-NqSqnq0dNhlQYw9JYWUh0rymN0gG/GaLmP2gdRnxUIivzYkPa0aYQW4WSRO6lGKIabyqxa2j2sELBujyOFYVgQ==} + /tesseract.js-core@4.0.2: + resolution: {integrity: sha512-ZVyYPN+ZAos31ZErqzcFme6XaOA8xrZxisNyk3nTicHVPSqtQH7UgZ36XoZZY0Exeugr5A+Uxv5ll9aMd1c0jg==} dev: false - /tesseract.js@4.0.3(eslint@8.22.0): - resolution: {integrity: sha512-hgFhIx1pqtZD95/xG6mhETpy5lbIzgBoBGPUntb2uIXVk7KcfHo3+ObgPxNZtLOI7LfXXfBvgfraQVmQSo7m9g==} + /tesseract.js@4.0.2(eslint@8.22.0): + resolution: {integrity: sha512-OKc6N68czBa8QnoBwx+kAUG4OyqskXa1O7wSrOXOEYZ0TQcGa3daRVIsRhtVAmNMTrvZnOsg49kfNrw0BZaFEA==} requiresBuild: true dependencies: babel-eslint: 10.1.0(eslint@8.22.0) @@ -21684,7 +29164,7 @@ packages: opencollective-postinstall: 2.0.3 regenerator-runtime: 0.13.11 resolve-url: 0.2.1 - tesseract.js-core: 4.0.3 + tesseract.js-core: 4.0.2 wasm-feature-detect: 1.5.1 zlibjs: 0.3.1 transitivePeerDependencies: @@ -21702,11 +29182,38 @@ packages: minimatch: 3.1.2 dev: true + /testcontainers@9.8.0: + resolution: {integrity: sha512-61IlJeVrUbS5JlAgM/N0koFnRxsID+vDap7CUmgaHXSGxmFofCiokB7kD96c1BtDWGOznrd7lTAPGSkd3RVkPA==} + engines: {node: '>= 10.16'} + dependencies: + '@balena/dockerignore': 1.0.2 + '@types/archiver': 5.3.2 + '@types/dockerode': 3.3.19 + archiver: 5.3.1 + async-lock: 1.4.0 + byline: 5.0.0 + debug: 4.3.4(supports-color@8.1.1) + docker-compose: 0.23.19 + dockerode: 3.3.5 + get-port: 5.1.1 + node-fetch: 2.6.9 + properties-reader: 2.2.0 + ssh-remote-port-forward: 1.0.4 + tar-fs: 2.1.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /text-extensions@1.9.0: resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} engines: {node: '>=0.10'} dev: true + /text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + dev: false + /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -21759,10 +29266,18 @@ packages: globalyzer: 0.1.0 globrex: 0.1.2 + /tiny-invariant@1.3.1: + resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} + dev: true + /tinybench@2.4.0: resolution: {integrity: sha512-iyziEiyFxX4kyxSp+MtY1oCH/lvjH3PxFN8PGCDeqcZWAJ/i+9y+nL85w99PxVzrIvew/GSkSbDYtiGVa85Afg==} dev: true + /tinybench@2.5.0: + resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==} + dev: true + /tinypool@0.3.1: resolution: {integrity: sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ==} engines: {node: '>=14.0.0'} @@ -21773,11 +29288,21 @@ packages: engines: {node: '>=14.0.0'} dev: true + /tinypool@0.6.0: + resolution: {integrity: sha512-FdswUUo5SxRizcBc6b1GSuLpLjisa8N8qMyYoP3rl+bym+QauhtJP5bvZY1ytt8krKGmMLYIRl36HBZfeAoqhQ==} + engines: {node: '>=14.0.0'} + dev: true + /tinyspy@1.1.1: resolution: {integrity: sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==} engines: {node: '>=14.0.0'} dev: true + /tinyspy@2.1.1: + resolution: {integrity: sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==} + engines: {node: '>=14.0.0'} + dev: true + /tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -21794,16 +29319,32 @@ packages: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} dev: true + /to-camel-case@1.0.0: + resolution: {integrity: sha512-nD8pQi5H34kyu1QDMFjzEIYqk0xa9Alt6ZfrdEMuHCFOfTLhDG5pgTu/aAM9Wt9lXILwlXmWP43b8sav0GNE8Q==} + dependencies: + to-space-case: 1.0.0 + dev: false + /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} + /to-no-case@1.0.2: + resolution: {integrity: sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==} + dev: false + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 + /to-space-case@1.0.0: + resolution: {integrity: sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==} + dependencies: + to-no-case: 1.0.2 + dev: false + /toidentifier@1.0.0: resolution: {integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==} engines: {node: '>=0.6'} @@ -21813,8 +29354,8 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - /totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + /totalist@3.0.0: + resolution: {integrity: sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==} engines: {node: '>=6'} dev: false @@ -21858,6 +29399,10 @@ packages: engines: {node: '>=8'} dev: true + /triple-beam@1.3.0: + resolution: {integrity: sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==} + dev: false + /trough@2.1.0: resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==} @@ -21894,11 +29439,94 @@ packages: lodash: 4.17.21 make-error: 1.3.6 mkdirp: 1.0.4 - semver: 7.4.0 + semver: 7.3.8 typescript: 4.9.5 yargs-parser: 20.2.9 dev: true + /ts-jest@29.1.0(@babel/core@7.22.5)(jest@29.5.0)(typescript@4.9.5): + resolution: {integrity: sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.22.5 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.5.0(@types/node@18.15.10)(ts-node@10.9.1) + jest-util: 29.5.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.3.8 + typescript: 4.9.5 + yargs-parser: 21.1.1 + dev: true + + /ts-jest@29.1.0(@babel/core@7.22.5)(jest@29.5.0)(typescript@5.0.2): + resolution: {integrity: sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.22.5 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.5.0(@types/node@20.3.1)(ts-node@10.9.1) + jest-util: 29.5.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.3.8 + typescript: 5.0.2 + yargs-parser: 21.1.1 + dev: true + + /ts-loader@9.2.3(typescript@4.9.5)(webpack@5.76.2): + resolution: {integrity: sha512-sEyWiU3JMHBL55CIeC4iqJQadI0U70A5af0kvgbNLHVNz2ACztQg0j/9x10bjjIht8WfFYLKfn4L6tkZ+pu+8Q==} + engines: {node: '>=12.0.0'} + peerDependencies: + typescript: '*' + webpack: ^5.0.0 + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.12.0 + micromatch: 4.0.5 + semver: 7.3.8 + typescript: 4.9.5 + webpack: 5.76.2 + dev: true + /ts-morph@17.0.1: resolution: {integrity: sha512-10PkHyXmrtsTvZSL+cqtJLTgFXkU43Gd0JCc0Rw6GchWbqKe0Rwgt1v3ouobTZwQzF1mGhDeAlWYBMGRV7y+3g==} dependencies: @@ -21906,7 +29534,7 @@ packages: code-block-writer: 11.0.3 dev: true - /ts-node@10.9.1(@types/node@18.15.11)(typescript@4.9.5): + /ts-node@10.9.1(@types/node@18.15.10)(typescript@4.9.5): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -21925,8 +29553,8 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 - '@types/node': 18.15.11 - acorn: 8.8.2 + '@types/node': 18.15.10 + acorn: 8.9.0 acorn-walk: 8.2.0 arg: 4.1.3 create-require: 1.1.1 @@ -21955,7 +29583,7 @@ packages: dependencies: chalk: 4.1.2 enhanced-resolve: 5.12.0 - tsconfig-paths: 4.1.2 + tsconfig-paths: 4.2.0 dev: true /tsconfig-paths@3.14.2: @@ -21976,6 +29604,15 @@ packages: strip-bom: 3.0.0 dev: true + /tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + /tsconfig-resolver@3.0.1: resolution: {integrity: sha512-ZHqlstlQF449v8glscGRXzL6l2dZvASPCdXJRWG4gHEZlUVx2Jtmr+a2zeVG4LCsKhDXKRj5R3h0C/98UcVAQg==} dependencies: @@ -21997,13 +29634,8 @@ packages: /tslib@2.5.0: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} - /tsm@2.3.0: - resolution: {integrity: sha512-++0HFnmmR+gMpDtKTnW3XJ4yv9kVGi20n+NfyQWB9qwJvTaIWY9kBmzek2YUQK5APTQ/1DTrXmm4QtFPmW9Rzw==} - engines: {node: '>=12'} - hasBin: true - dependencies: - esbuild: 0.15.18 - dev: false + /tslib@2.5.2: + resolution: {integrity: sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==} /tsscmp@1.0.6: resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} @@ -22020,6 +29652,16 @@ packages: typescript: 4.9.5 dev: true + /tsutils@3.21.0(typescript@5.0.2): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.0.2 + dev: true + /tty-table@4.2.1: resolution: {integrity: sha512-xz0uKo+KakCQ+Dxj1D/tKn2FSyreSYWzdkL/BYhgN6oMW808g8QRMuh1atAV9fjTPbWBjfbkKQpI/5rEcnAc7g==} engines: {node: '>=8.0.0'} @@ -22034,6 +29676,10 @@ packages: yargs: 17.7.1 dev: true + /tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + dev: true + /type-check@0.3.2: resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} engines: {node: '>= 0.8.0'} @@ -22125,10 +29771,10 @@ packages: /typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - /typedoc-plugin-markdown@3.15.1(typedoc@0.23.28): - resolution: {integrity: sha512-TaXE8gc8s5YepU1Ogyqfkh+khPE1/n4rV5vaoZCNyXvSLv62jWmHf443lHiQh7r07qAimUOKAndaaufAeIUSiQ==} + /typedoc-plugin-markdown@3.14.0(typedoc@0.23.28): + resolution: {integrity: sha512-UyQLkLRkfTFhLdhSf3RRpA3nNInGn+k6sll2vRXjflaMNwQAAiB61SYbisNZTg16t4K1dt1bPQMMGLrxS0GZ0Q==} peerDependencies: - typedoc: '>=0.24.0' + typedoc: '>=0.23.0' dependencies: handlebars: 4.7.7 typedoc: 0.23.28(typescript@4.9.5) @@ -22143,8 +29789,8 @@ packages: dependencies: lunr: 2.3.9 marked: 4.3.0 - minimatch: 7.4.6 - shiki: 0.14.1 + minimatch: 7.4.3 + shiki: 0.14.2 typescript: 4.9.5 dev: true @@ -22159,9 +29805,22 @@ packages: engines: {node: '>=4.2.0'} hasBin: true + /typescript@5.0.2: + resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==} + engines: {node: '>=12.20'} + hasBin: true + + /ua-parser-js@1.0.35: + resolution: {integrity: sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==} + dev: false + /ufo@1.1.1: resolution: {integrity: sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg==} + /ufo@1.1.2: + resolution: {integrity: sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==} + dev: true + /uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} engines: {node: '>=0.8.0'} @@ -22170,11 +29829,11 @@ packages: dev: true optional: true - /uid@2.0.2: - resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + /uid@2.0.1: + resolution: {integrity: sha512-PF+1AnZgycpAIEmNtjxGBVmKbZAQguaa4pBUq6KNaGEcpzZ2klCNZLM34tsjp76maN00TttiiUf6zkIBpJQm2A==} engines: {node: '>=8'} dependencies: - '@lukeed/csprng': 1.1.0 + '@lukeed/csprng': 1.0.1 /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -22185,6 +29844,13 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /undici@5.22.1: + resolution: {integrity: sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==} + engines: {node: '>=14.0'} + dependencies: + busboy: 1.6.0 + dev: false + /unfetch@4.2.0: resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} dev: true @@ -22247,12 +29913,6 @@ packages: dependencies: '@types/unist': 2.0.6 - /unist-util-map@3.1.3: - resolution: {integrity: sha512-4/mDauoxqZ6geK97lJ6n2kDk6JK88Vh+hWMSJqyaaP/7eqN1dDhjcjnNxKNm3YU6Sw7PVJtcFMUbnmHvYzb6Vg==} - dependencies: - '@types/unist': 2.0.6 - dev: false - /unist-util-modify-children@3.1.1: resolution: {integrity: sha512-yXi4Lm+TG5VG+qvokP6tpnk+r1EPwyYL04JWDxLvgvPV40jANh7nm3udk65OOWquvbMDe+PL9+LmkxDpTv/7BA==} dependencies: @@ -22340,7 +30000,7 @@ packages: /unplugin@0.10.2: resolution: {integrity: sha512-6rk7GUa4ICYjae5PrAllvcDeuT8pA9+j5J5EkxbMFaV+SalHhxZ7X2dohMzu6C3XzsMT+6jwR/+pwPNR3uK9MA==} dependencies: - acorn: 8.8.2 + acorn: 8.9.0 chokidar: 3.5.3 webpack-sources: 3.2.3 webpack-virtual-modules: 0.4.6 @@ -22373,7 +30033,7 @@ packages: requires-port: 1.0.0 dev: true - /use-callback-ref@1.3.0(@types/react@18.0.35)(react@18.2.0): + /use-callback-ref@1.3.0(@types/react@18.0.29)(react@18.2.0): resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==} engines: {node: '>=10'} peerDependencies: @@ -22383,12 +30043,48 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.0.35 + '@types/react': 18.0.29 + react: 18.2.0 + tslib: 2.5.2 + dev: false + + /use-callback-ref@1.3.0(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.37 + react: 18.2.0 + tslib: 2.5.2 + dev: false + + /use-composed-ref@1.3.0(react@18.2.0): + resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /use-isomorphic-layout-effect@1.1.2(@types/react@18.0.29)(react@18.2.0): + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.29 react: 18.2.0 - tslib: 2.5.0 dev: false - /use-isomorphic-layout-effect@1.1.2(@types/react@18.0.35)(react@18.2.0): + /use-isomorphic-layout-effect@1.1.2(@types/react@18.0.37)(react@18.2.0): resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} peerDependencies: '@types/react': '*' @@ -22397,8 +30093,41 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.0.35 + '@types/react': 18.0.37 + react: 18.2.0 + dev: false + + /use-latest@1.2.1(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.37 + react: 18.2.0 + use-isomorphic-layout-effect: 1.1.2(@types/react@18.0.37)(react@18.2.0) + dev: false + + /use-query-params@2.2.1(react-dom@18.2.0)(react-router-dom@6.11.2)(react@18.2.0): + resolution: {integrity: sha512-i6alcyLB8w9i3ZK3caNftdb+UnbfBRNPDnc89CNQWkGRmDrm/gfydHvMBfVsQJRq3NoHOM2dt/ceBWG2397v1Q==} + peerDependencies: + '@reach/router': ^1.2.1 + react: '>=16.8.0' + react-dom: '>=16.8.0' + react-router-dom: '>=5' + peerDependenciesMeta: + '@reach/router': + optional: true + react-router-dom: + optional: true + dependencies: react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-router-dom: 6.11.2(react-dom@18.2.0)(react@18.2.0) + serialize-query-params: 2.0.2 dev: false /use-resize-observer@9.1.0(react-dom@18.2.0)(react@18.2.0): @@ -22412,7 +30141,7 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /use-sidecar@1.1.2(@types/react@18.0.35)(react@18.2.0): + /use-sidecar@1.1.2(@types/react@18.0.29)(react@18.2.0): resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} peerDependencies: @@ -22422,10 +30151,26 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.0.35 + '@types/react': 18.0.29 detect-node-es: 1.1.0 react: 18.2.0 - tslib: 2.5.0 + tslib: 2.5.2 + dev: false + + /use-sidecar@1.1.2(@types/react@18.0.37)(react@18.2.0): + resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.37 + detect-node-es: 1.1.0 + react: 18.2.0 + tslib: 2.5.2 dev: false /use-sync-external-store@1.2.0(react@18.2.0): @@ -22472,7 +30217,6 @@ packages: /uuid@9.0.0: resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} hasBin: true - dev: false /uvu@0.5.6: resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} @@ -22504,13 +30248,13 @@ packages: resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} engines: {node: '>=10.12.0'} dependencies: - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/trace-mapping': 0.3.17 '@types/istanbul-lib-coverage': 2.0.4 convert-source-map: 1.9.0 dev: true - /validate-html-nesting@1.2.1: - resolution: {integrity: sha512-T1ab131NkP3BfXB7KUSgV7Rhu81R2id+L6NaJ7NypAAG5iV6gXnPpQE5RK1fvb+3JYsPTL+ihWna5sr5RN9gaQ==} + /validate-html-nesting@1.2.2: + resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==} dev: false /validate-npm-package-license@3.0.4: @@ -22554,7 +30298,26 @@ packages: unist-util-stringify-position: 3.0.3 vfile-message: 3.1.4 - /vite-node@0.28.5(@types/node@18.15.11): + /victory-vendor@36.6.11: + resolution: {integrity: sha512-nT8kCiJp8dQh8g991J/R5w5eE2KnO8EAIP0xocWlh9l2okngMWglOPoMZzJvek8Q1KUc4XE/mJxTZnvOB1sTYg==} + dependencies: + '@types/d3-array': 3.0.5 + '@types/d3-ease': 3.0.0 + '@types/d3-interpolate': 3.0.1 + '@types/d3-scale': 4.0.3 + '@types/d3-shape': 3.1.1 + '@types/d3-time': 3.0.0 + '@types/d3-timer': 3.0.0 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + dev: false + + /vite-node@0.28.5(@types/node@18.15.10): resolution: {integrity: sha512-LmXb9saMGlrMZbXTvOveJKwMTBTNUH66c8rJnQ0ZPNX+myPEol64+szRzXtV5ORb0Hb/91yq+/D3oERoyAt6LA==} engines: {node: '>=v14.16.0'} hasBin: true @@ -22562,14 +30325,15 @@ packages: cac: 6.7.14 debug: 4.3.4(supports-color@8.1.1) mlly: 1.2.0 - pathe: 1.1.0 + pathe: 1.1.1 picocolors: 1.0.0 source-map: 0.6.1 source-map-support: 0.5.21 - vite: 4.2.1(@types/node@18.15.11) + vite: 4.4.0(@types/node@18.15.10)(less@4.1.3) transitivePeerDependencies: - '@types/node' - less + - lightningcss - sass - stylus - sugarss @@ -22577,20 +30341,43 @@ packages: - terser dev: true - /vite-node@0.29.8(@types/node@18.15.11): + /vite-node@0.29.8(@types/node@18.15.10): resolution: {integrity: sha512-b6OtCXfk65L6SElVM20q5G546yu10/kNrhg08afEoWlFRJXFq9/6glsvSVY+aI6YeC1tu2TtAqI2jHEQmOmsFw==} engines: {node: '>=v14.16.0'} hasBin: true dependencies: cac: 6.7.14 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) mlly: 1.2.0 - pathe: 1.1.0 + pathe: 1.1.1 + picocolors: 1.0.0 + vite: 4.4.0(@types/node@18.15.10)(less@4.1.3) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite-node@0.33.0(@types/node@18.15.10)(less@4.1.3): + resolution: {integrity: sha512-19FpHYbwWWxDr73ruNahC+vtEdza52kA90Qb3La98yZ0xULqV8A5JLNPUff0f5zID4984tW7l3DH2przTJUZSw==} + engines: {node: '>=v14.18.0'} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4(supports-color@8.1.1) + mlly: 1.4.0 + pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.2.1(@types/node@18.15.11) + vite: 4.4.0(@types/node@18.15.10)(less@4.1.3) transitivePeerDependencies: - '@types/node' - less + - lightningcss - sass - stylus - sugarss @@ -22598,21 +30385,95 @@ packages: - terser dev: true - /vite-plugin-dts@1.7.3(@types/node@18.15.11)(vite@4.0.3): + /vite-plugin-checker@0.6.1(eslint@8.38.0)(typescript@5.0.2)(vite@4.3.9): + resolution: {integrity: sha512-4fAiu3W/IwRJuJkkUZlWbLunSzsvijDf0eDN6g/MGh6BUK4SMclOTGbLJCPvdAcMOQvVmm8JyJeYLYd4//8CkA==} + engines: {node: '>=14.16'} + peerDependencies: + eslint: '>=7' + meow: ^9.0.0 + optionator: ^0.9.1 + stylelint: '>=13' + typescript: '*' + vite: '>=2.0.0' + vls: '*' + vti: '*' + vue-tsc: '>=1.3.9' + peerDependenciesMeta: + eslint: + optional: true + meow: + optional: true + optionator: + optional: true + stylelint: + optional: true + typescript: + optional: true + vls: + optional: true + vti: + optional: true + vue-tsc: + optional: true + dependencies: + '@babel/code-frame': 7.22.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + chokidar: 3.5.3 + commander: 8.3.0 + eslint: 8.38.0 + fast-glob: 3.2.12 + fs-extra: 11.1.1 + lodash.debounce: 4.0.8 + lodash.pick: 4.4.0 + npm-run-path: 4.0.1 + semver: 7.5.3 + strip-ansi: 6.0.1 + tiny-invariant: 1.3.1 + typescript: 5.0.2 + vite: 4.3.9(@types/node@20.3.1) + vscode-languageclient: 7.0.0 + vscode-languageserver: 7.0.0 + vscode-languageserver-textdocument: 1.0.8 + vscode-uri: 3.0.7 + dev: true + + /vite-plugin-dts@1.7.3(@types/node@18.15.10)(vite@4.0.3): resolution: {integrity: sha512-u3t45p6fTbzUPMkwYe0ESwuUeiRMlwdPfD3dRyDKUwLe2WmEYcFyVp2o9/ke2EMrM51lQcmNWdV9eLcgjD1/ng==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: '>=2.9.0' dependencies: - '@microsoft/api-extractor': 7.34.4(@types/node@18.15.11) + '@microsoft/api-extractor': 7.34.4(@types/node@18.15.10) '@rollup/pluginutils': 5.0.2(rollup@2.70.2) - '@rushstack/node-core-library': 3.55.2(@types/node@18.15.11) + '@rushstack/node-core-library': 3.55.2(@types/node@18.15.10) debug: 4.3.4(supports-color@8.1.1) - fast-glob: 3.2.12 + fast-glob: 3.3.0 + fs-extra: 10.1.0 + kolorist: 1.7.0 + ts-morph: 17.0.1 + vite: 4.0.3(@types/node@18.15.10) + transitivePeerDependencies: + - '@types/node' + - rollup + - supports-color + dev: true + + /vite-plugin-dts@1.7.3(@types/node@20.4.1)(vite@4.4.0): + resolution: {integrity: sha512-u3t45p6fTbzUPMkwYe0ESwuUeiRMlwdPfD3dRyDKUwLe2WmEYcFyVp2o9/ke2EMrM51lQcmNWdV9eLcgjD1/ng==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: '>=2.9.0' + dependencies: + '@microsoft/api-extractor': 7.34.4(@types/node@20.4.1) + '@rollup/pluginutils': 5.0.2(rollup@2.70.2) + '@rushstack/node-core-library': 3.55.2(@types/node@20.4.1) + debug: 4.3.4(supports-color@8.1.1) + fast-glob: 3.3.0 fs-extra: 10.1.0 kolorist: 1.7.0 ts-morph: 17.0.1 - vite: 4.0.3(@types/node@18.15.11) + vite: 4.4.0(@types/node@20.4.1)(less@4.1.3) transitivePeerDependencies: - '@types/node' - rollup @@ -22631,33 +30492,50 @@ packages: dotenv: 16.0.3 dotenv-expand: 8.0.3 ejs: 3.1.9 - fast-glob: 3.2.12 + fast-glob: 3.3.0 fs-extra: 10.1.0 html-minifier-terser: 6.1.0 node-html-parser: 5.4.2 pathe: 0.2.0 - vite: 4.0.3(@types/node@18.15.11) + vite: 4.0.3(@types/node@18.15.10) dev: true - /vite-plugin-terminal@1.1.0(vite@4.2.1): + /vite-plugin-terminal@1.1.0(vite@4.2.1): + resolution: {integrity: sha512-W550yBGApBSp67LgqCSSA9u3aMEFFHqTleYMxcVQFf5XCY973bGTSjHW7ZjUsJT3VkiW9mfmc+azhVHvsEMpcg==} + engines: {node: '>=14'} + peerDependencies: + vite: ^2.0.0||^3.0.0||^4.0.0 + dependencies: + '@rollup/plugin-strip': 3.0.2 + debug: 4.3.4(supports-color@8.1.1) + kolorist: 1.7.0 + sirv: 2.0.2 + ufo: 1.1.1 + vite: 4.2.1(@types/node@18.15.10) + transitivePeerDependencies: + - rollup + - supports-color + dev: false + + /vite-plugin-terminal@1.1.0(vite@4.3.9): resolution: {integrity: sha512-W550yBGApBSp67LgqCSSA9u3aMEFFHqTleYMxcVQFf5XCY973bGTSjHW7ZjUsJT3VkiW9mfmc+azhVHvsEMpcg==} engines: {node: '>=14'} peerDependencies: vite: ^2.0.0||^3.0.0||^4.0.0 dependencies: '@rollup/plugin-strip': 3.0.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) kolorist: 1.7.0 sirv: 2.0.2 ufo: 1.1.1 - vite: 4.2.1(@types/node@18.15.11) + vite: 4.3.9(@types/node@20.3.1) transitivePeerDependencies: - rollup - supports-color dev: false - /vite-tsconfig-paths@4.2.0(typescript@4.9.5)(vite@4.2.1): - resolution: {integrity: sha512-jGpus0eUy5qbbMVGiTxCL1iB9ZGN6Bd37VGLJU39kTDD6ZfULTTb1bcc5IeTWqWJKiWV5YihCaibeASPiGi8kw==} + /vite-tsconfig-paths@4.0.7(typescript@4.9.5)(vite@4.2.1): + resolution: {integrity: sha512-MwIYaby6kcbQGZqMH+gAK6h0UYQGOkjsuAgw4q6bP/5vWkn8VKvnmLuCQHA2+IzHAJHnE8OFTO4lnJLFMf9+7Q==} peerDependencies: vite: '*' peerDependenciesMeta: @@ -22667,13 +30545,13 @@ packages: debug: 4.3.4(supports-color@8.1.1) globrex: 0.1.2 tsconfck: 2.1.1(typescript@4.9.5) - vite: 4.2.1(@types/node@18.15.11) + vite: 4.2.1(@types/node@20.3.2) transitivePeerDependencies: - supports-color - typescript dev: false - /vite@3.2.5(@types/node@18.15.11): + /vite@3.2.5(@types/node@18.15.10): resolution: {integrity: sha512-4mVEpXpSOgrssFZAOmGIr85wPHKvaDAcXqxVxVRZhljkJOMZi1ibLibzjLHzJvcok8BMguLc7g1W6W/GqZbLdQ==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -22698,15 +30576,15 @@ packages: terser: optional: true dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 esbuild: 0.15.18 - postcss: 8.4.21 - resolve: 1.22.2 + postcss: 8.4.24 + resolve: 1.22.1 rollup: 2.79.1 optionalDependencies: fsevents: 2.3.2 - /vite@4.0.3(@types/node@18.15.11): + /vite@4.0.3(@types/node@18.15.10): resolution: {integrity: sha512-HvuNv1RdE7deIfQb8mPk51UKjqptO/4RXZ5yXSAvurd5xOckwS/gg8h9Tky3uSbnjYTgUm0hVCet1cyhKd73ZA==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -22731,16 +30609,16 @@ packages: terser: optional: true dependencies: - '@types/node': 18.15.11 + '@types/node': 18.15.10 esbuild: 0.16.17 - postcss: 8.4.21 - resolve: 1.22.2 - rollup: 3.20.2 + postcss: 8.4.24 + resolve: 1.22.1 + rollup: 3.26.2 optionalDependencies: fsevents: 2.3.2 dev: true - /vite@4.2.1(@types/node@18.15.11): + /vite@4.2.1(@types/node@18.15.10): resolution: {integrity: sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -22765,13 +30643,151 @@ packages: terser: optional: true dependencies: - '@types/node': 18.15.11 - esbuild: 0.17.16 - postcss: 8.4.21 - resolve: 1.22.2 - rollup: 3.20.2 + '@types/node': 18.15.10 + esbuild: 0.17.14 + postcss: 8.4.24 + resolve: 1.22.1 + rollup: 3.26.2 + optionalDependencies: + fsevents: 2.3.2 + + /vite@4.2.1(@types/node@20.3.2): + resolution: {integrity: sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.3.2 + esbuild: 0.17.14 + postcss: 8.4.24 + resolve: 1.22.1 + rollup: 3.26.2 + optionalDependencies: + fsevents: 2.3.2 + + /vite@4.3.9(@types/node@20.3.1): + resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.3.1 + esbuild: 0.17.19 + postcss: 8.4.24 + rollup: 3.25.1 + optionalDependencies: + fsevents: 2.3.2 + + /vite@4.4.0(@types/node@18.15.10)(less@4.1.3): + resolution: {integrity: sha512-Wf+DCEjuM8aGavEYiF77hnbxEZ+0+/jC9nABR46sh5Xi+GYeSvkeEFRiVuI3x+tPjxgZeS91h1jTAQTPFgePpA==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.15.10 + esbuild: 0.18.12 + less: 4.1.3 + postcss: 8.4.24 + rollup: 3.26.2 + optionalDependencies: + fsevents: 2.3.2 + + /vite@4.4.0(@types/node@20.4.1)(less@4.1.3): + resolution: {integrity: sha512-Wf+DCEjuM8aGavEYiF77hnbxEZ+0+/jC9nABR46sh5Xi+GYeSvkeEFRiVuI3x+tPjxgZeS91h1jTAQTPFgePpA==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.4.1 + esbuild: 0.18.12 + less: 4.1.3 + postcss: 8.4.24 + rollup: 3.26.2 optionalDependencies: fsevents: 2.3.2 + dev: true /vitefu@0.2.4(vite@3.2.5): resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} @@ -22781,7 +30797,7 @@ packages: vite: optional: true dependencies: - vite: 3.2.5(@types/node@18.15.11) + vite: 3.2.5(@types/node@18.15.10) dev: false /vitefu@0.2.4(vite@4.2.1): @@ -22792,9 +30808,20 @@ packages: vite: optional: true dependencies: - vite: 4.2.1(@types/node@18.15.11) + vite: 4.2.1(@types/node@20.3.2) dev: true + /vitefu@0.2.4(vite@4.4.0): + resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + vite: + optional: true + dependencies: + vite: 4.4.0(@types/node@18.15.10)(less@4.1.3) + dev: false + /vitest@0.24.5(jsdom@20.0.3): resolution: {integrity: sha512-zw6JhPUHtLILQDe5Q39b/SzoITkG+R7hcFjuthp4xsi6zpmfQPOZcHodZ+3bqoWl4EdGK/p1fuMiEwdxgbGLOA==} engines: {node: '>=v14.16.0'} @@ -22819,7 +30846,7 @@ packages: dependencies: '@types/chai': 4.3.4 '@types/chai-subset': 1.3.3 - '@types/node': 18.15.11 + '@types/node': 18.15.10 chai: 4.3.7 debug: 4.3.4(supports-color@8.1.1) jsdom: 20.0.3 @@ -22828,7 +30855,7 @@ packages: tinybench: 2.4.0 tinypool: 0.3.1 tinyspy: 1.1.1 - vite: 3.2.5(@types/node@18.15.11) + vite: 3.2.5(@types/node@18.15.10) transitivePeerDependencies: - less - sass @@ -22862,12 +30889,12 @@ packages: dependencies: '@types/chai': 4.3.4 '@types/chai-subset': 1.3.3 - '@types/node': 18.15.11 + '@types/node': 18.15.10 '@vitest/expect': 0.28.5 '@vitest/runner': 0.28.5 '@vitest/spy': 0.28.5 '@vitest/utils': 0.28.5 - acorn: 8.8.2 + acorn: 8.9.0 acorn-walk: 8.2.0 cac: 6.7.14 chai: 4.3.7 @@ -22882,11 +30909,12 @@ packages: tinybench: 2.4.0 tinypool: 0.3.1 tinyspy: 1.1.1 - vite: 4.2.1(@types/node@18.15.11) - vite-node: 0.28.5(@types/node@18.15.11) + vite: 4.4.0(@types/node@18.15.10)(less@4.1.3) + vite-node: 0.28.5(@types/node@18.15.10) why-is-node-running: 2.2.2 transitivePeerDependencies: - less + - lightningcss - sass - stylus - sugarss @@ -22927,7 +30955,7 @@ packages: dependencies: '@types/chai': 4.3.4 '@types/chai-subset': 1.3.3 - '@types/node': 18.15.11 + '@types/node': 18.15.10 '@vitest/expect': 0.29.8 '@vitest/runner': 0.29.8 '@vitest/spy': 0.29.8 @@ -22936,7 +30964,7 @@ packages: acorn-walk: 8.2.0 cac: 6.7.14 chai: 4.3.7 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) local-pkg: 0.4.3 pathe: 1.1.0 picocolors: 1.0.0 @@ -22946,11 +30974,77 @@ packages: tinybench: 2.4.0 tinypool: 0.4.0 tinyspy: 1.1.1 - vite: 4.2.1(@types/node@18.15.11) - vite-node: 0.29.8(@types/node@18.15.11) + vite: 4.4.0(@types/node@18.15.10)(less@4.1.3) + vite-node: 0.29.8(@types/node@18.15.10) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vitest@0.33.0(less@4.1.3): + resolution: {integrity: sha512-1CxaugJ50xskkQ0e969R/hW47za4YXDUfWJDxip1hwbnhUjYolpfUn2AMOulqG/Dtd9WYAtkHmM/m3yKVrEejQ==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + dependencies: + '@types/chai': 4.3.5 + '@types/chai-subset': 1.3.3 + '@types/node': 18.15.10 + '@vitest/expect': 0.33.0 + '@vitest/runner': 0.33.0 + '@vitest/snapshot': 0.33.0 + '@vitest/spy': 0.33.0 + '@vitest/utils': 0.33.0 + acorn: 8.9.0 + acorn-walk: 8.2.0 + cac: 6.7.14 + chai: 4.3.7 + debug: 4.3.4(supports-color@8.1.1) + local-pkg: 0.4.3 + magic-string: 0.30.1 + pathe: 1.1.1 + picocolors: 1.0.0 + std-env: 3.3.3 + strip-literal: 1.0.1 + tinybench: 2.5.0 + tinypool: 0.6.0 + vite: 4.4.0(@types/node@18.15.10)(less@4.1.3) + vite-node: 0.33.0(@types/node@18.15.10)(less@4.1.3) why-is-node-running: 2.2.2 transitivePeerDependencies: - less + - lightningcss - sass - stylus - sugarss @@ -22981,11 +31075,32 @@ packages: vscode-uri: 3.0.7 dev: false + /vscode-jsonrpc@6.0.0: + resolution: {integrity: sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==} + engines: {node: '>=8.0.0 || >=10.0.0'} + dev: true + /vscode-jsonrpc@8.1.0: resolution: {integrity: sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==} engines: {node: '>=14.0.0'} dev: false + /vscode-languageclient@7.0.0: + resolution: {integrity: sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==} + engines: {vscode: ^1.52.0} + dependencies: + minimatch: 3.1.2 + semver: 7.5.3 + vscode-languageserver-protocol: 3.16.0 + dev: true + + /vscode-languageserver-protocol@3.16.0: + resolution: {integrity: sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==} + dependencies: + vscode-jsonrpc: 6.0.0 + vscode-languageserver-types: 3.16.0 + dev: true + /vscode-languageserver-protocol@3.17.3: resolution: {integrity: sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==} dependencies: @@ -22995,12 +31110,22 @@ packages: /vscode-languageserver-textdocument@1.0.8: resolution: {integrity: sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q==} - dev: false + + /vscode-languageserver-types@3.16.0: + resolution: {integrity: sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==} + dev: true /vscode-languageserver-types@3.17.3: resolution: {integrity: sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==} dev: false + /vscode-languageserver@7.0.0: + resolution: {integrity: sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==} + hasBin: true + dependencies: + vscode-languageserver-protocol: 3.16.0 + dev: true + /vscode-languageserver@8.1.0: resolution: {integrity: sha512-eUt8f1z2N2IEUDBsKaNapkz7jl5QpskN2Y0G01T/ItMxBxw1fJwvtySGB9QMecatne8jFIWJGWI61dWjyTLQsw==} hasBin: true @@ -23011,13 +31136,8 @@ packages: /vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} - /vscode-textmate@6.0.0: - resolution: {integrity: sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==} - dev: false - /vscode-textmate@8.0.0: resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} - dev: true /vscode-uri@2.1.2: resolution: {integrity: sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==} @@ -23025,7 +31145,6 @@ packages: /vscode-uri@3.0.7: resolution: {integrity: sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA==} - dev: false /w3c-hr-time@1.0.2: resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} @@ -23099,7 +31218,7 @@ packages: /web-streams-polyfill@3.2.1: resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} engines: {node: '>= 8'} - dev: false + dev: true /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -23148,8 +31267,8 @@ packages: '@webassemblyjs/ast': 1.11.1 '@webassemblyjs/wasm-edit': 1.11.1 '@webassemblyjs/wasm-parser': 1.11.1 - acorn: 8.8.2 - acorn-import-assertions: 1.8.0(acorn@8.8.2) + acorn: 8.9.0 + acorn-import-assertions: 1.8.0(acorn@8.9.0) browserslist: 4.21.5 chrome-trace-event: 1.0.3 enhanced-resolve: 5.12.0 @@ -23173,6 +31292,46 @@ packages: - uglify-js dev: true + /webpack@5.76.2(esbuild@0.17.19): + resolution: {integrity: sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + '@types/eslint-scope': 3.7.4 + '@types/estree': 0.0.51 + '@webassemblyjs/ast': 1.11.1 + '@webassemblyjs/wasm-edit': 1.11.1 + '@webassemblyjs/wasm-parser': 1.11.1 + acorn: 8.9.0 + acorn-import-assertions: 1.8.0(acorn@8.9.0) + browserslist: 4.21.5 + chrome-trace-event: 1.0.3 + enhanced-resolve: 5.12.0 + es-module-lexer: 0.9.3 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.1.1 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.7(esbuild@0.17.19)(webpack@5.76.2) + watchpack: 2.4.0 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + dev: true + /whatwg-encoding@1.0.5: resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} dependencies: @@ -23313,6 +31472,32 @@ packages: execa: 4.1.0 dev: true + /winston-transport@4.5.0: + resolution: {integrity: sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==} + engines: {node: '>= 6.4.0'} + dependencies: + logform: 2.5.1 + readable-stream: 3.6.2 + triple-beam: 1.3.0 + dev: false + + /winston@3.9.0: + resolution: {integrity: sha512-jW51iW/X95BCW6MMtZWr2jKQBP4hV5bIDq9QrIjfDk6Q9QuxvTKEAlpUNAzP+HYHFFCeENhph16s0zEunu4uuQ==} + engines: {node: '>= 12.0.0'} + dependencies: + '@colors/colors': 1.5.0 + '@dabh/diagnostics': 2.0.3 + async: 3.2.4 + is-stream: 2.0.1 + logform: 2.5.1 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.4.3 + stack-trace: 0.0.10 + triple-beam: 1.3.0 + winston-transport: 4.5.0 + dev: false + /word-wrap@1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} @@ -23344,7 +31529,7 @@ packages: dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 - strip-ansi: 7.0.1 + strip-ansi: 7.1.0 dev: false /wrappy@1.0.2: @@ -23412,7 +31597,6 @@ packages: optional: true utf-8-validate: optional: true - dev: true /ws@8.5.0: resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==} @@ -23443,6 +31627,10 @@ packages: /xstate@4.37.1: resolution: {integrity: sha512-MuB7s01nV5vG2CzaBg2msXLGz7JuS+x/NBkQuZAwgEYCnWA8iQMiRz2VGxD3pcFjZAOih3fOgDD3kDaFInEx+g==} + /xstate@4.38.0: + resolution: {integrity: sha512-oFjw2YZPyu6HeO0JWCSqfhAALsjFPURsrD2FUFN3u213dWwYU68RFuLtSHco+cEUhpQFW+hRG3PNYgq8HatudQ==} + dev: false + /xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -23469,6 +31657,10 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} + /yaml@2.3.1: + resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} + engines: {node: '>= 14'} + /yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -23551,7 +31743,6 @@ packages: /yocto-queue@1.0.0: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} - dev: true /z-schema@5.0.5: resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} @@ -23565,6 +31756,15 @@ packages: commander: 9.5.0 dev: true + /zip-stream@4.1.0: + resolution: {integrity: sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==} + engines: {node: '>= 10'} + dependencies: + archiver-utils: 2.1.0 + compress-commons: 4.1.1 + readable-stream: 3.6.2 + dev: true + /zlibjs@0.3.1: resolution: {integrity: sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==} dev: false diff --git a/scripts/init.js b/scripts/init.js index e7d0ec5c8d..170c271170 100755 --- a/scripts/init.js +++ b/scripts/init.js @@ -5,6 +5,7 @@ const fs = require('fs'); const rootDir = path.join(__dirname, '..'); const workflowServiceRoot = path.join(rootDir, 'services/workflows-service'); +const workflowWebsocketServiceRoot = path.join(rootDir, 'services/websocket-service'); const backofficeRoot = path.join(rootDir, 'apps/backoffice-v2'); const ensureEnvFileIsPresent = projectPath => { @@ -27,5 +28,6 @@ run('pnpm run build'); console.log('π preparing environment'); ensureEnvFileIsPresent(backofficeRoot); ensureEnvFileIsPresent(workflowServiceRoot); +ensureEnvFileIsPresent(workflowWebsocketServiceRoot); console.log('β All done!'); diff --git a/sdks/web-ui-sdk/CHANGELOG.md b/sdks/web-ui-sdk/CHANGELOG.md index d19d4353a0..7fc7392895 100644 --- a/sdks/web-ui-sdk/CHANGELOG.md +++ b/sdks/web-ui-sdk/CHANGELOG.md @@ -1,5 +1,40 @@ # web-ui-sdk +## 1.3.7 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.6 + +## 1.3.6 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.5 + +## 1.3.5 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.4 + +## 1.3.4 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.3 + +## 1.3.3 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.2 + ## 1.3.2 ### Patch Changes diff --git a/sdks/web-ui-sdk/package.json b/sdks/web-ui-sdk/package.json index 76a5f5a154..03972731c6 100644 --- a/sdks/web-ui-sdk/package.json +++ b/sdks/web-ui-sdk/package.json @@ -21,7 +21,7 @@ "types": "dist/index.d.ts", "name": "@ballerine/web-ui-sdk", "private": false, - "version": "1.3.2", + "version": "1.3.7", "type": "module", "files": [ "dist" @@ -40,6 +40,7 @@ "deprecated:build:sdk": "rollup --config", "start": "vite preview", "test": "vitest run", + "test:unit": "vitest run", "test:watch": "vitest", "test:e2e": "playwright test", "playwright:install": "playwright install --with-deps", @@ -74,6 +75,7 @@ "eslint-config-prettier": "^8.5.0", "eslint-plugin-storybook": "^0.6.6", "eslint-plugin-svelte3": "^4.0.0", + "eslint-plugin-unused-imports": "^2.0.0", "jsdom": "^20.0.2", "postcss": "^8.4.18", "prettier": "^2.7.1", @@ -91,7 +93,7 @@ "vitest": "^0.24.5" }, "dependencies": { - "@ballerine/common": "0.4.3", + "@ballerine/common": "0.5.6", "@zerodevx/svelte-toast": "^0.8.0", "compressorjs": "^1.1.1", "deepmerge": "^4.3.0", diff --git a/sdks/workflow-browser-sdk/CHANGELOG.md b/sdks/workflow-browser-sdk/CHANGELOG.md index cda9566adf..8def59c74d 100644 --- a/sdks/workflow-browser-sdk/CHANGELOG.md +++ b/sdks/workflow-browser-sdk/CHANGELOG.md @@ -1,5 +1,53 @@ # @ballerine/workflow-browser-sdk +## 0.4.9 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.6 + - @ballerine/workflow-core@0.4.16 + +## 0.4.8 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.5 + - @ballerine/workflow-core@0.4.15 + +## 0.4.7 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.4 + - @ballerine/workflow-core@0.4.14 + +## 0.4.6 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.3 + - @ballerine/workflow-core@0.4.13 + +## 0.4.5 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.2 + - @ballerine/workflow-core@0.4.12 + +## 0.4.4 + +### Patch Changes + +- Updated dependencies +- Updated dependencies [be5c9bc4] + - @ballerine/common@0.5.0 + ## 0.4.3 ### Patch Changes diff --git a/sdks/workflow-browser-sdk/package.json b/sdks/workflow-browser-sdk/package.json index c751234254..f7d8bfea86 100644 --- a/sdks/workflow-browser-sdk/package.json +++ b/sdks/workflow-browser-sdk/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/workflow-browser-sdk", "author": "Ballerine <dev@ballerine.com>", - "version": "0.4.3", + "version": "0.4.9", "description": "workflow-browser-sdk", "module": "./dist/esm/index.js", "main": "./dist/cjs/index.js", @@ -23,14 +23,15 @@ "lint": "eslint . --ext .ts", "dev": "concurrently --kill-others \"pnpm build -w\" \"pnpm watch\"", "test": "vitest run", + "test:unit": "vitest run", "test:watch": "vitest" }, "engines": { "node": ">=12" }, "dependencies": { - "@ballerine/common": "^0.4.3", - "@ballerine/workflow-core": "^0.4.9", + "@ballerine/common": "0.5.6", + "@ballerine/workflow-core": "^0.4.16", "xstate": "^4.37.0" }, "devDependencies": { @@ -39,6 +40,7 @@ "@babel/preset-typescript": "7.16.7", "@rollup/plugin-babel": "5.3.1", "@rollup/plugin-commonjs": "^24.0.1", + "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "13.2.1", "@rollup/plugin-replace": "4.0.0", "@rollup/plugin-terser": "^0.4.0", @@ -56,6 +58,7 @@ "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-functional": "^3.0.2", "eslint-plugin-import": "^2.22.0", + "eslint-plugin-unused-imports": "^2.0.0", "fs-extra": "^11.1.0", "jsdom": "^20.0.2", "msw": "^1.1.0", diff --git a/sdks/workflow-browser-sdk/rollup.config.ts b/sdks/workflow-browser-sdk/rollup.config.ts index d6644262cf..31b604e6f4 100644 --- a/sdks/workflow-browser-sdk/rollup.config.ts +++ b/sdks/workflow-browser-sdk/rollup.config.ts @@ -11,6 +11,7 @@ import { readJsonSync } from 'fs-extra'; import path from 'path'; import { RollupOptions } from 'rollup'; import dts from 'rollup-plugin-dts'; +import json from '@rollup/plugin-json'; import visualizer from 'rollup-plugin-visualizer'; // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -104,7 +105,7 @@ function esm({ input, packageDir, external, banner }: Options): RollupOptions { banner, preserveModules: true, }, - plugins: [babelPlugin, nodeResolve({ extensions: ['.ts'] })], + plugins: [babelPlugin, json(), nodeResolve({ extensions: ['.ts'] })], }; } @@ -138,6 +139,7 @@ function umdDev({ input, umdExternal, packageDir, banner, jsName }: Options): Ro banner, }, plugins: [ + json(), babelPlugin, commonjs(), nodeResolve({ extensions: ['.ts'] }), @@ -159,6 +161,7 @@ function umdProd({ input, umdExternal, packageDir, banner, jsName }: Options): R banner, }, plugins: [ + json(), babelPlugin, commonjs(), nodeResolve({ extensions: ['.ts'] }), diff --git a/sdks/workflow-browser-sdk/src/lib/workflow-browser-sdk.ts b/sdks/workflow-browser-sdk/src/lib/workflow-browser-sdk.ts index ba18c5876d..8808fe1071 100644 --- a/sdks/workflow-browser-sdk/src/lib/workflow-browser-sdk.ts +++ b/sdks/workflow-browser-sdk/src/lib/workflow-browser-sdk.ts @@ -262,6 +262,9 @@ export class WorkflowBrowserSDK { { method: this.#__backendOptions.endpoints.uploadFile.method, body: formData, + headers: { + Authorization: this.#__backendOptions.headers?.Authorization ?? '', + }, }, ); diff --git a/sdks/workflow-node-sdk/package.json b/sdks/workflow-node-sdk/package.json index d30f34ccdd..41be2e3309 100644 --- a/sdks/workflow-node-sdk/package.json +++ b/sdks/workflow-node-sdk/package.json @@ -16,13 +16,14 @@ "scripts": { "build": "rollup --config rollup.config.js", "watch": "concurrently --kill-others \"rollup --config rollup.config.js -w\" \"tsc -b --watch\"", - "test": "vitest run" + "test": "vitest run", + "test:unit": "vitest run" }, "engines": { "node": ">=12" }, "dependencies": { - "@ballerine/workflow-core": "^0.4.9", + "@ballerine/workflow-core": "0.4.9", "json-logic-js": "^2.0.2", "xstate": "^4.36.0" }, @@ -32,6 +33,7 @@ "@babel/preset-typescript": "7.16.7", "@rollup/plugin-babel": "5.3.1", "@rollup/plugin-commonjs": "^24.0.1", + "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "13.2.1", "@rollup/plugin-replace": "4.0.0", "@types/babel__core": "^7.20.0", @@ -47,6 +49,7 @@ "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-functional": "^3.0.2", "eslint-plugin-import": "^2.22.0", + "eslint-plugin-unused-imports": "^2.0.0", "fs-extra": "^11.1.0", "prettier": "^2.1.1", "rollup": "2.70.2", diff --git a/sdks/workflow-node-sdk/rollup.config.ts b/sdks/workflow-node-sdk/rollup.config.ts index c3fdac43ba..cf608f2ae5 100644 --- a/sdks/workflow-node-sdk/rollup.config.ts +++ b/sdks/workflow-node-sdk/rollup.config.ts @@ -1,17 +1,18 @@ -import { RollupOptions } from 'rollup' -import babel from '@rollup/plugin-babel' -import { terser } from 'rollup-plugin-terser' +import { RollupOptions } from 'rollup'; +import babel from '@rollup/plugin-babel'; +import { terser } from 'rollup-plugin-terser'; // rollup-plugin-size doesn't have a types package. // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import size from 'rollup-plugin-size' -import visualizer from 'rollup-plugin-visualizer' -import replace from '@rollup/plugin-replace' -import nodeResolve from '@rollup/plugin-node-resolve' -import commonjs from '@rollup/plugin-commonjs' -import path from 'path' -import dts from 'rollup-plugin-dts' -import { readJsonSync } from 'fs-extra' +import size from 'rollup-plugin-size'; +import visualizer from 'rollup-plugin-visualizer'; +import replace from '@rollup/plugin-replace'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import path from 'path'; +import dts from 'rollup-plugin-dts'; +import { readJsonSync } from 'fs-extra'; +import json from '@rollup/plugin-json'; type Options = { input: string; @@ -33,7 +34,7 @@ const umdDevPlugin = (type: 'development' | 'production') => const babelPlugin = babel({ babelHelpers: 'bundled', exclude: /node_modules/, - extensions: ['.ts', ], + extensions: ['.ts'], }); export default function rollup(options: RollupOptions): RollupOptions[] { @@ -63,10 +64,7 @@ function buildConfigs(opts: { }): RollupOptions[] { const input = path.resolve('./', opts.entryFile); - const packageJson = - readJsonSync( - path.resolve(process.cwd(), 'package.json') - ) ?? {}; + const packageJson = readJsonSync(path.resolve(process.cwd(), 'package.json')) ?? {}; const banner = createBanner(opts.name); @@ -102,12 +100,9 @@ function esm({ input, packageDir, external, banner }: Options): RollupOptions { sourcemap: true, dir: `${packageDir}/dist/esm`, banner, - preserveModules: true + preserveModules: true, }, - plugins: [ - babelPlugin, - nodeResolve({ extensions: ['.ts', ] }), - ], + plugins: [json(), babelPlugin, nodeResolve({ extensions: ['.ts'] })], }; } @@ -124,21 +119,11 @@ function cjs({ input, external, packageDir, banner }: Options): RollupOptions { exports: 'named', banner, }, - plugins: [ - babelPlugin, - commonjs(), - nodeResolve({ extensions: ['.ts', ] }), - ], + plugins: [json(), babelPlugin, commonjs(), nodeResolve({ extensions: ['.ts'] })], }; } -function umdDev({ - input, - umdExternal, - packageDir, - banner, - jsName, -}: Options): RollupOptions { +function umdDev({ input, umdExternal, packageDir, banner, jsName }: Options): RollupOptions { return { // UMD (Dev) external: umdExternal, @@ -151,21 +136,16 @@ function umdDev({ banner, }, plugins: [ + json(), babelPlugin, commonjs(), - nodeResolve({ extensions: ['.ts', ] }), + nodeResolve({ extensions: ['.ts'] }), umdDevPlugin('development'), ], }; } -function umdProd({ - input, - umdExternal, - packageDir, - banner, - jsName, -}: Options): RollupOptions { +function umdProd({ input, umdExternal, packageDir, banner, jsName }: Options): RollupOptions { return { // UMD (Prod) external: umdExternal, @@ -178,9 +158,10 @@ function umdProd({ banner, }, plugins: [ + json(), babelPlugin, commonjs(), - nodeResolve({ extensions: ['.ts', ] }), + nodeResolve({ extensions: ['.ts'] }), umdDevPlugin('production'), terser(), size({}), @@ -192,12 +173,7 @@ function umdProd({ }; } -function types({ - input, - packageDir, - external, - banner, -}: Options): RollupOptions { +function types({ input, packageDir, external, banner }: Options): RollupOptions { return { // TYPES external, diff --git a/sdks/workflow-node-sdk/src/lib/workflow-node-sdk.ts b/sdks/workflow-node-sdk/src/lib/workflow-node-sdk.ts index b857b4818f..dc90f124a6 100644 --- a/sdks/workflow-node-sdk/src/lib/workflow-node-sdk.ts +++ b/sdks/workflow-node-sdk/src/lib/workflow-node-sdk.ts @@ -13,7 +13,7 @@ export class WorkflowNodeSDK { } async sendEvent(event: Parameters<TCreateWorkflowCoreReturn['sendEvent']>[0]) { - this.#__service.sendEvent(event); + return await this.#__service.sendEvent(event); } getSnapshot() { diff --git a/services/websocket-service/.env.example b/services/websocket-service/.env.example new file mode 100644 index 0000000000..6ce0ea705f --- /dev/null +++ b/services/websocket-service/.env.example @@ -0,0 +1,3 @@ +PORT=3500 +NODE_ENV=development +COMPOSE_PROJECT_NAME=ballerine-ws diff --git a/services/websocket-service/.eslintignore b/services/websocket-service/.eslintignore new file mode 100644 index 0000000000..320c107b3e --- /dev/null +++ b/services/websocket-service/.eslintignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +package-lock.json diff --git a/services/websocket-service/.eslintrc.cjs b/services/websocket-service/.eslintrc.cjs new file mode 100644 index 0000000000..e729fd568c --- /dev/null +++ b/services/websocket-service/.eslintrc.cjs @@ -0,0 +1,38 @@ +const config = require('../../packages/config/eslintrc.base.cjs'); + +module.exports = { + ...config, + extends: ['plugin:import/recommended', 'plugin:import/typescript', ...config.extends], + parserOptions: { + ...config.parserOptions, + tsconfigRootDir: __dirname, + project: './tsconfig.json', + }, + env: { + ...config.env, + node: true, + }, + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts'], + }, + 'import/resolver': { + typescript: { + alwaysTryTypes: true, + project: './tsconfig.json', + }, + node: true, + }, + }, + rules: { + 'import/no-cycle': 'error', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + }, + ignorePatterns: ['.eslintrc.cjs'], +}; diff --git a/services/websocket-service/.gitignore b/services/websocket-service/.gitignore new file mode 100644 index 0000000000..398d03bf47 --- /dev/null +++ b/services/websocket-service/.gitignore @@ -0,0 +1,9 @@ +upload/* +.vscode +.idea +.env* +!.env.example +node_modules +dist +*.tgz +*.log diff --git a/services/websocket-service/.prettierignore b/services/websocket-service/.prettierignore new file mode 100644 index 0000000000..6f4d8b5826 --- /dev/null +++ b/services/websocket-service/.prettierignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +package-lock.json +coverage/ diff --git a/services/websocket-service/.prettierrc.cjs b/services/websocket-service/.prettierrc.cjs new file mode 100644 index 0000000000..25f61d0363 --- /dev/null +++ b/services/websocket-service/.prettierrc.cjs @@ -0,0 +1 @@ +module.exports = require('../../packages/config/prettierrc.base.cjs'); diff --git a/services/websocket-service/README.md b/services/websocket-service/README.md new file mode 100644 index 0000000000..f5aa86c5dc --- /dev/null +++ b/services/websocket-service/README.md @@ -0,0 +1,73 @@ +<p align="center"> + <a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a> +</p> + +[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 +[circleci-url]: https://circleci.com/gh/nestjs/nest + + <p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p> + <p align="center"> +<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a> +<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a> +<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a> +<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a> +<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a> +<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a> +<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a> +<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a> + <a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a> + <a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a> + <a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a> +</p> + <!--[](https://opencollective.com/nest#backer) + [](https://opencollective.com/nest#sponsor)--> + +## Description + +[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. + +## Installation + +```bash +$ pnpm install +``` + +## Running the app + +```bash +# development +$ pnpm run start + +# watch mode +$ pnpm run start:dev + +# production mode +$ pnpm run start:prod +``` + +## Test + +```bash +# unit tests +$ pnpm run test + +# e2e tests +$ pnpm run test:e2e + +# test coverage +$ pnpm run test:cov +``` + +## Support + +Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). + +## Stay in touch + +- Author - [Kamil MyΕliwiec](https://kamilmysliwiec.com) +- Website - [https://nestjs.com](https://nestjs.com/) +- Twitter - [@nestframework](https://twitter.com/nestframework) + +## License + +Nest is [MIT licensed](LICENSE). diff --git a/services/websocket-service/jest.config.cjs b/services/websocket-service/jest.config.cjs new file mode 100644 index 0000000000..d86a46fe11 --- /dev/null +++ b/services/websocket-service/jest.config.cjs @@ -0,0 +1,14 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + modulePathIgnorePatterns: ['<rootDir>/dist/'], + moduleNameMapper: { + '^@/(.*)$': '<rootDir>/src/$1', + axios: 'axios/dist/node/axios.cjs', + }, + globals: { + 'ts-jest': { + tsconfig: './tsconfig.test.json', + }, + }, +}; diff --git a/services/websocket-service/package.json b/services/websocket-service/package.json new file mode 100644 index 0000000000..9b77559e41 --- /dev/null +++ b/services/websocket-service/package.json @@ -0,0 +1,57 @@ +{ + "name": "@ballerine/websocket-service", + "version": "0.0.1", + "description": "websocket-service", + "private": false, + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "dev": "npm run start:watch", + "start:watch": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json" + }, + "dependencies": { + "@nestjs/common": "^9.3.12", + "@nestjs/core": "^9.3.12", + "@nestjs/platform-express": "^9.3.12", + "@nestjs/platform-ws": "^9.4.2", + "@nestjs/websockets": "^9.4.2", + "@t3-oss/env-core": "^0.3.1", + "@types/ws": "^8.5.4", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.8.0", + "ws": "^8.13.0", + "zod": "^3.21.4" + }, + "devDependencies": { + "@nestjs/cli": "^9.3.0", + "@nestjs/schematics": "^9.0.0", + "@nestjs/testing": "^9.3.12", + "@types/express": "4.17.9", + "@types/jest": "^26.0.19", + "@types/node": "^18.14.6", + "@types/supertest": "2.0.11", + "@typescript-eslint/eslint-plugin": "^5.54.1", + "@typescript-eslint/parser": "^5.54.1", + "eslint": "^8.35.0", + "eslint-config-prettier": "^8.7.0", + "eslint-import-resolver-typescript": "^3.5.3", + "eslint-plugin-import": "^2.27.5", + "jest": "29.5.0", + "prettier": "^2.8.4", + "supertest": "^6.1.3", + "ts-jest": "29.1.0", + "ts-loader": "^9.2.3", + "ts-node": "^10.9.1", + "tsconfig-paths": "4.2.0", + "typescript": "^4.9.3" + } +} diff --git a/services/websocket-service/src/app.controller.test.ts b/services/websocket-service/src/app.controller.test.ts new file mode 100644 index 0000000000..d22f3890a3 --- /dev/null +++ b/services/websocket-service/src/app.controller.test.ts @@ -0,0 +1,22 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; + +describe('AppController', () => { + let appController: AppController; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + controllers: [AppController], + providers: [AppService], + }).compile(); + + appController = app.get<AppController>(AppController); + }); + + describe('root', () => { + it('should return "Hello World!"', () => { + expect(appController.getHello()).toBe('Hello World!'); + }); + }); +}); diff --git a/services/websocket-service/src/app.controller.ts b/services/websocket-service/src/app.controller.ts new file mode 100644 index 0000000000..cce879ee62 --- /dev/null +++ b/services/websocket-service/src/app.controller.ts @@ -0,0 +1,12 @@ +import { Controller, Get } from '@nestjs/common'; +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) {} + + @Get() + getHello(): string { + return this.appService.getHello(); + } +} diff --git a/services/websocket-service/src/app.gateway.ts b/services/websocket-service/src/app.gateway.ts new file mode 100644 index 0000000000..f599ac2789 --- /dev/null +++ b/services/websocket-service/src/app.gateway.ts @@ -0,0 +1,33 @@ +import { Logger } from '@nestjs/common'; +import { + WebSocketGateway, + WebSocketServer, + OnGatewayConnection, + OnGatewayDisconnect, +} from '@nestjs/websockets'; +import { Server } from 'ws'; + +@WebSocketGateway({ transport: ['websocket'] }) +export class AppGateway implements OnGatewayConnection, OnGatewayDisconnect { + @WebSocketServer() + server: Server | undefined; + + private logger = new Logger('AppGateway'); + + handleConnection() { + this.logger.log('New client connected'); + } + + handleDisconnect() { + this.logger.log('Client disconnected'); + } + + broadcast(event: string, message: string) { + const broadCastMessage = JSON.stringify(message); + if (this.server) { + for (const client of this.server.clients) { + client.send(event + broadCastMessage); + } + } + } +} diff --git a/services/websocket-service/src/app.module.ts b/services/websocket-service/src/app.module.ts new file mode 100644 index 0000000000..97de660e2c --- /dev/null +++ b/services/websocket-service/src/app.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; +import { AppGateway } from './app.gateway'; +import { NotifyController } from '@/notifications/notify.controller'; + +@Module({ + imports: [], + controllers: [AppController, NotifyController], + providers: [AppService, AppGateway], +}) +export class AppModule {} diff --git a/services/websocket-service/src/app.service.ts b/services/websocket-service/src/app.service.ts new file mode 100644 index 0000000000..927d7cca0b --- /dev/null +++ b/services/websocket-service/src/app.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AppService { + getHello(): string { + return 'Hello World!'; + } +} diff --git a/services/websocket-service/src/env.ts b/services/websocket-service/src/env.ts new file mode 100644 index 0000000000..977013b5a6 --- /dev/null +++ b/services/websocket-service/src/env.ts @@ -0,0 +1,23 @@ +import 'dotenv/config'; +import { createEnv } from '@t3-oss/env-core'; +import { z } from 'zod'; + +export const env = createEnv({ + /* + * clientPrefix is required. + */ + clientPrefix: 'PUBLIC_', + server: { + NODE_ENV: z.enum(['development', 'production', 'test']), + ENV_FILE_NAME: z.string().optional(), + COMPOSE_PROJECT_NAME: z.string(), + PORT: z.coerce.number(), + }, + client: {}, + /** + * What object holds the environment variables at runtime. + * Often `process.env` or `import.meta.env` + */ + runtimeEnv: process.env, + skipValidation: !!process.env.CI, +}); diff --git a/services/websocket-service/src/main.ts b/services/websocket-service/src/main.ts new file mode 100644 index 0000000000..c8379f5595 --- /dev/null +++ b/services/websocket-service/src/main.ts @@ -0,0 +1,15 @@ +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; +import { WsAdapter } from '@nestjs/platform-ws'; +import { env } from '@/env'; + +async function main() { + const app = await NestFactory.create(AppModule); + app.useWebSocketAdapter(new WsAdapter(app)); + void app.listen(env.PORT); + console.log(`Listening on port ${env.PORT}`); + + return app; +} + +module.exports = main(); diff --git a/services/websocket-service/src/notifications/notify.controller.ts b/services/websocket-service/src/notifications/notify.controller.ts new file mode 100644 index 0000000000..5bb5185ec3 --- /dev/null +++ b/services/websocket-service/src/notifications/notify.controller.ts @@ -0,0 +1,13 @@ +import { Controller, Post } from '@nestjs/common'; +import { AppGateway } from '@/app.gateway'; + +@Controller('notify') +export class NotifyController { + constructor(private readonly appGateway: AppGateway) {} + + @Post() + notifyAllConnectedWebsockets(): string { + this.appGateway.broadcast('notify', 'Hello World!'); + return '200'; + } +} diff --git a/services/websocket-service/test/app.e2e-spec.ts b/services/websocket-service/test/app.e2e-spec.ts new file mode 100644 index 0000000000..1a013bef82 --- /dev/null +++ b/services/websocket-service/test/app.e2e-spec.ts @@ -0,0 +1,21 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import * as request from 'supertest'; +import { AppModule } from './../src/app.module'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('/ (GET)', () => { + return request(app.getHttpServer()).get('/').expect(200).expect('Hello World!'); + }); +}); diff --git a/services/websocket-service/test/jest-e2e.json b/services/websocket-service/test/jest-e2e.json new file mode 100644 index 0000000000..e9d912f3e3 --- /dev/null +++ b/services/websocket-service/test/jest-e2e.json @@ -0,0 +1,9 @@ +{ + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": ".", + "testEnvironment": "node", + "testRegex": ".e2e-spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + } +} diff --git a/services/websocket-service/tsconfig.build.json b/services/websocket-service/tsconfig.build.json new file mode 100644 index 0000000000..edb35ed3a1 --- /dev/null +++ b/services/websocket-service/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts", "**/*.test.ts"] +} diff --git a/services/websocket-service/tsconfig.json b/services/websocket-service/tsconfig.json new file mode 100644 index 0000000000..f2b403d7aa --- /dev/null +++ b/services/websocket-service/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": false, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "es2017", + "lib": ["ES2020"], + "sourceMap": true, + "inlineSources": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false, + "strictBindCallApply": false, + "forceConsistentCasingInFileNames": false, + "noFallthroughCasesInSwitch": false, + "strict": true, + "noUncheckedIndexedAccess": true, + "checkJs": false, + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"] +} diff --git a/services/websocket-service/tsconfig.test.json b/services/websocket-service/tsconfig.test.json new file mode 100644 index 0000000000..483e75cd0f --- /dev/null +++ b/services/websocket-service/tsconfig.test.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noImplicitAny": false + } +} diff --git a/services/workflows-service/.env.example b/services/workflows-service/.env.example index b9934f2fb7..155deace84 100644 --- a/services/workflows-service/.env.example +++ b/services/workflows-service/.env.example @@ -1,6 +1,6 @@ BCRYPT_SALT=10 COMPOSE_PROJECT_NAME=ballerine-x -JWT_SECRET_KEY=secret +JWT_SECRET_KEY=demo JWT_EXPIRATION=10d PORT=3000 DB_USER=admin @@ -8,10 +8,11 @@ DB_PASSWORD=admin DB_PORT=5432 DB_URL=postgres://admin:admin@localhost:5432/postgres SESSION_SECRET=iGdnj4A0YOhj8dHJK7IWSvQKEZsG7P70FFehuddhFPjtg/bSkzFejYILk4Xue6Ilx9y3IAwzR8pV1gb4 +WORKFLOW_DASHBOARD_CORS_ORIGIN=http://localhost:5200 BACKOFFICE_CORS_ORIGIN=http://localhost:5137 HEADLESS_EXAMPLE_CORS_ORIGIN=http://localhost:5173 API_KEY=secret -NODE_ENV=development +NODE_ENV=local SENTRY_DSN= WEBHOOK_URL= WEBHOOK_SECRET=webhook_secret diff --git a/services/workflows-service/CHANGELOG.md b/services/workflows-service/CHANGELOG.md index aa223c6713..51eff2033a 100644 --- a/services/workflows-service/CHANGELOG.md +++ b/services/workflows-service/CHANGELOG.md @@ -1,5 +1,55 @@ # @ballerine/workflows-service +## 0.4.17 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.6 + +## 0.4.16 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.5 + +## 0.4.15 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.4 + +## 0.4.14 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.3 + +## 0.4.13 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.2 + +## 0.4.12 + +### Patch Changes + +- Updated dependencies + - @ballerine/common@0.5.1 + +## 0.4.11 + +### Patch Changes + +- Updated dependencies +- Updated dependencies [be5c9bc4] + - @ballerine/common@0.5.0 + ## 0.4.10 ### Patch Changes diff --git a/services/workflows-service/docker-compose.db.yml b/services/workflows-service/docker-compose.db.yml index 7f95afee36..8ae3428933 100644 --- a/services/workflows-service/docker-compose.db.yml +++ b/services/workflows-service/docker-compose.db.yml @@ -1,13 +1,13 @@ version: '3' services: db: - image: postgres:12 + image: sibedge/postgres-plv8:15.3-3.1.7 ports: - ${DB_PORT}:5432 environment: POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - - postgres:/var/lib/postgresql/data + - postgres15:/var/lib/postgresql/data volumes: - postgres: ~ + postgres15: ~ diff --git a/services/workflows-service/docker-compose.yml b/services/workflows-service/docker-compose.yml index ef40357e68..3e39f15884 100644 --- a/services/workflows-service/docker-compose.yml +++ b/services/workflows-service/docker-compose.yml @@ -12,6 +12,17 @@ services: JWT_SECRET_KEY: ${JWT_SECRET_KEY} JWT_EXPIRATION: ${JWT_EXPIRATION} DB_URL: postgres://${DB_USER}:${DB_PASSWORD}@db:${DB_PORT} + PORT: ${PORT} + DB_USER: ${DB_USER} + DB_PASSWORD: ${DB_PASSWORD} + DB_PORT: ${DB_PORT} + SESSION_SECRET: ${SESSION_SECRET}iGdnj4A0YOhj8dHJK7IWSvQKEZsG7P70FFehuddhFPjtg/bSkzFejYILk4Xue6Ilx9y3IAwzR8pV1gb4 + WORKFLOW_DASHBOARD_CORS_ORIGIN: ${WORKFLOW_DASHBOARD_CORS_ORIGIN} + BACKOFFICE_CORS_ORIGIN: ${BACKOFFICE_CORS_ORIGIN} + HEADLESS_EXAMPLE_CORS_ORIGIN: ${BACKOFFICE_CORS_ORIGIN} + COMPOSE_PROJECT_NAME: ${COMPOSE_PROJECT_NAME} + API_KEY: ${API_KEY} + NODE_ENV: ${NODE_ENV} depends_on: - migrate migrate: @@ -28,14 +39,14 @@ services: db: condition: service_healthy db: - image: postgres:12 + image: docker pull sibedge/postgres-plv8:15.3-3.1.7 ports: - ${DB_PORT}:5432 environment: POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - - postgres:/var/lib/postgresql/data + - postgres15:/var/lib/postgresql/data healthcheck: test: - CMD @@ -49,4 +60,4 @@ services: interval: 10s retries: 10 volumes: - postgres: ~ + postgres15: ~ diff --git a/services/workflows-service/jest.config.cjs b/services/workflows-service/jest.config.cjs index d86a46fe11..891161efb1 100644 --- a/services/workflows-service/jest.config.cjs +++ b/services/workflows-service/jest.config.cjs @@ -2,6 +2,7 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', modulePathIgnorePatterns: ['<rootDir>/dist/'], + testRegex: '(/__tests__/.*|(\\.|/)(unit|e2e|intg)\\.test)\\.ts$', moduleNameMapper: { '^@/(.*)$': '<rootDir>/src/$1', axios: 'axios/dist/node/axios.cjs', @@ -11,4 +12,6 @@ module.exports = { tsconfig: './tsconfig.test.json', }, }, + globalSetup: '<rootDir>/src/test/db-setup.ts', + globalTeardown: '<rootDir>/src/test/db-teardown.ts', }; diff --git a/services/workflows-service/package.json b/services/workflows-service/package.json index 0b5da4314b..76f1d4e117 100644 --- a/services/workflows-service/package.json +++ b/services/workflows-service/package.json @@ -1,7 +1,7 @@ { "name": "@ballerine/workflows-service", "private": false, - "version": "0.4.10", + "version": "0.4.17", "description": "workflow-service", "scripts": { "setup": "npm run docker:db && npm run db:clean && npm run db:migrate-save && npm run seed", @@ -15,6 +15,9 @@ "start:debug": "nest start --debug --watch", "build": "nest build --path=tsconfig.build.json", "test": "jest --verbose", + "test:unit": "cross-env SKIP_DB_SETUP_TEARDOWN=true jest --testRegex '.*\\.unit\\.test\\.ts$'", + "test:integration": "jest --testRegex '.*\\.intg\\.test\\.ts$'", + "test:e2e": "jest --testRegex '.*\\.e2e\\.test\\.ts$'", "test:watch": "jest --verbose --watch", "seed": "ts-node scripts/seed.ts", "db:migrate-save": "prisma migrate dev", @@ -35,8 +38,8 @@ "dependencies": { "@aws-sdk/client-s3": "3.325.0", "@aws-sdk/lib-storage": "3.325.0", - "@ballerine/common": "0.4.4", - "@ballerine/workflow-node-sdk": "^0.4.2", + "@ballerine/common": "0.5.6", + "@ballerine/workflow-node-sdk": "0.4.3", "@faker-js/faker": "^7.6.0", "@nestjs/axios": "^2.0.0", "@nestjs/common": "^9.3.12", @@ -59,6 +62,7 @@ "accesscontrol": "^2.2.1", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0", "axios": "^1.4.0", "bcrypt": "5.1.0", "class-transformer": "0.5.1", @@ -69,6 +73,7 @@ "lodash": "^4.17.21", "multer-s3": "3.0.1", "nest-access-control": "2.2.0", + "nestjs-cls": "^3.5.0", "nestjs-prisma": "0.20.0", "passport": "0.6.0", "passport-http": "0.3.0", @@ -78,6 +83,7 @@ "rxjs": "^7.8.0", "swagger-ui-express": "4.6.2", "tmp": "^0.2.1", + "winston": "^3.9.0", "zod": "^3.21.4" }, "devDependencies": { @@ -99,17 +105,20 @@ "@types/supertest": "2.0.11", "@typescript-eslint/eslint-plugin": "^5.54.1", "@typescript-eslint/parser": "^5.54.1", + "dayjs": "^1.11.6", "dotenv": "^16.0.3", "eslint": "^8.35.0", "eslint-config-prettier": "^8.7.0", "eslint-import-resolver-typescript": "^3.5.3", "eslint-plugin-import": "^2.27.5", + "eslint-plugin-unused-imports": "^2.0.0", "jest": "27.0.6", "jest-mock-extended": "^2.0.4", "json-schema-to-typescript": "^13.0.1", "prettier": "^2.8.4", "prisma": "4.13.0", "supertest": "4.0.2", + "testcontainers": "^9.8.0", "ts-jest": "27.0.3", "ts-node": "^10.9.1", "type-fest": "0.11.0", diff --git a/services/workflows-service/prisma/migrations/20230607122555_add_entity_fields/migration.sql b/services/workflows-service/prisma/migrations/20230607122555_add_entity_fields/migration.sql new file mode 100644 index 0000000000..5be70b58cf --- /dev/null +++ b/services/workflows-service/prisma/migrations/20230607122555_add_entity_fields/migration.sql @@ -0,0 +1,21 @@ +/* + Warnings: + + - You are about to drop the column `jsonData` on the `EndUser` table. All the data in the column will be lost. + - You are about to drop the column `verificationId` on the `EndUser` table. All the data in the column will be lost. + - Made the column `firstName` on table `EndUser` required. This step will fail if there are existing NULL values in that column. + - Made the column `lastName` on table `EndUser` required. This step will fail if there are existing NULL values in that column. + +*/ +-- AlterTable +ALTER TABLE "Business" ADD COLUMN "additionalInfo" JSONB, +ADD COLUMN "avatarUrl" TEXT, +ADD COLUMN "country" TEXT; + +-- AlterTable +ALTER TABLE "EndUser" DROP COLUMN "jsonData", +DROP COLUMN "verificationId", +ADD COLUMN "country" VARCHAR, +ADD COLUMN "nationalId" VARCHAR, +ALTER COLUMN "firstName" SET NOT NULL, +ALTER COLUMN "lastName" SET NOT NULL; diff --git a/services/workflows-service/prisma/migrations/20230628134448_add_last_active_at_to_user/migration.sql b/services/workflows-service/prisma/migrations/20230628134448_add_last_active_at_to_user/migration.sql new file mode 100644 index 0000000000..c464b0826d --- /dev/null +++ b/services/workflows-service/prisma/migrations/20230628134448_add_last_active_at_to_user/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "lastActiveAt" TIMESTAMP(3); diff --git a/services/workflows-service/prisma/migrations/20230629141759_added_assigned_at_to_workflow_runtime_data/migration.sql b/services/workflows-service/prisma/migrations/20230629141759_added_assigned_at_to_workflow_runtime_data/migration.sql new file mode 100644 index 0000000000..e92a54e656 --- /dev/null +++ b/services/workflows-service/prisma/migrations/20230629141759_added_assigned_at_to_workflow_runtime_data/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "WorkflowRuntimeData" ADD COLUMN "assignedAt" TIMESTAMP(3); diff --git a/services/workflows-service/prisma/migrations/20230711224250_add_enduser_type_change_business_address/migration.sql b/services/workflows-service/prisma/migrations/20230711224250_add_enduser_type_change_business_address/migration.sql new file mode 100644 index 0000000000..05f7635c88 --- /dev/null +++ b/services/workflows-service/prisma/migrations/20230711224250_add_enduser_type_change_business_address/migration.sql @@ -0,0 +1,14 @@ +/* + Warnings: + + - The `address` column on the `Business` table would be dropped and recreated. This will lead to data loss if there is data in the column. + +*/ +-- AlterTable +ALTER TABLE "Business" ADD COLUMN "bankInformation" JSONB, +DROP COLUMN "address", +ADD COLUMN "address" JSONB; + +-- AlterTable +ALTER TABLE "EndUser" ADD COLUMN "isContactPerson" BOOLEAN NOT NULL DEFAULT false, +ALTER COLUMN "endUserType" SET DEFAULT 'individual'; diff --git a/services/workflows-service/prisma/migrations/20230723212353_add_plv8/migration.sql b/services/workflows-service/prisma/migrations/20230723212353_add_plv8/migration.sql new file mode 100644 index 0000000000..ca04ab63ad --- /dev/null +++ b/services/workflows-service/prisma/migrations/20230723212353_add_plv8/migration.sql @@ -0,0 +1 @@ +CREATE EXTENSION IF NOT EXISTS plv8; \ No newline at end of file diff --git a/services/workflows-service/prisma/migrations/20230723221706_add_jsonb_deep_merge_with_options/migration.sql b/services/workflows-service/prisma/migrations/20230723221706_add_jsonb_deep_merge_with_options/migration.sql new file mode 100644 index 0000000000..ca2360aca3 --- /dev/null +++ b/services/workflows-service/prisma/migrations/20230723221706_add_jsonb_deep_merge_with_options/migration.sql @@ -0,0 +1,101 @@ +CREATE OR REPLACE FUNCTION jsonb_deep_merge_with_options(obj1 jsonb, obj2 jsonb, array_merge_option text DEFAULT 'concat') +RETURNS jsonb AS $$ + const mergeObjects = (obj1, obj2) => { + const result = { ...obj1 }; + + for (let key in obj2) { + if (typeof obj2[key] === 'object' && obj2[key] !== null && !Array.isArray(obj2[key]) && key in result) { + result[key] = mergeObjects(result[key], obj2[key]); + } else { + result[key] = obj2[key]; + } + } + + return result; + }; + + const mergeArraysById = (arr1, arr2) => { + const combined = [...arr1, ...arr2]; + const ids = Array.from(new Set(combined.map(item => item.id))); + + return ids.map(id => { + const sameIdItems = combined.filter(item => item.id === id); + return sameIdItems.reduce(mergeObjects, {}); + }); + }; + + const mergeArraysByIndex = (arr1, arr2) => { + const maxLength = Math.max(arr1.length, arr2.length); + const result = new Array(maxLength); + + for (let i = 0; i < maxLength; i++) { + if (i < arr1.length && i < arr2.length) { + // Change here: Check if the elements are basic values or JSON objects + if (typeof arr1[i] !== 'object' && typeof arr2[i] !== 'object') { + result[i] = arr2[i]; + } else { + result[i] = mergeObjects(arr1[i], arr2[i]); + } + } else { + result[i] = arr1[i] || arr2[i]; + } + } + + return result; + }; + + const deepMergeWithOptions = (val1, val2, array_merge_option = 'concat') => { + // Handle array inputs + if (Array.isArray(val1) && Array.isArray(val2)) { + if (array_merge_option === 'replace') { + return val2; + } else { + switch (array_merge_option) { + case 'by_id': + return mergeArraysById(val1, val2); + case 'by_index': + return mergeArraysByIndex(val1, val2); + case 'concat': + default: + return [...val1, ...val2]; + } + } + } + // Handle object inputs + else if (typeof val1 === 'object' && val1 !== null && typeof val2 === 'object' && val2 !== null) { + const result = { ...val1 }; + + for (let key in val2) { + if (Array.isArray(val2[key])) { + if (array_merge_option === 'replace') { + result[key] = val2[key]; + } else { + switch (array_merge_option) { + case 'by_id': + result[key] = mergeArraysById(val1[key] || [], val2[key]); + break; + case 'by_index': + result[key] = mergeArraysByIndex(val1[key] || [], val2[key]); + break; + case 'concat': + default: + result[key] = [...(val1[key] || []), ...(val2[key])]; + } + } + } else if (typeof val2[key] === 'object' && val2[key] !== null && !Array.isArray(val2[key]) && key in result) { + result[key] = deepMergeWithOptions(result[key], val2[key], array_merge_option); + } else { + result[key] = val2[key]; + } + } + + return result; + } + // For all other types of inputs, return the second value + else { + return val2; + } + }; + + return deepMergeWithOptions(obj1, obj2, array_merge_option); +$$ LANGUAGE plv8 IMMUTABLE STRICT; \ No newline at end of file diff --git a/services/workflows-service/prisma/schema.prisma b/services/workflows-service/prisma/schema.prisma index dbca4669a5..d0596c3abb 100644 --- a/services/workflows-service/prisma/schema.prisma +++ b/services/workflows-service/prisma/schema.prisma @@ -18,6 +18,7 @@ model User { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + lastActiveAt DateTime? workflowRuntimeData WorkflowRuntimeData[] } @@ -43,21 +44,22 @@ enum ApprovalState { model EndUser { id String @id @default(cuid()) + isContactPerson Boolean @default(false) - correlationId String? @unique @db.VarChar - verificationId String? @db.VarChar + correlationId String? @unique @db.VarChar - endUserType String? + endUserType String? @default("individual") // Add userType: can be 'EndUser', 'CompanyContact', 'Stakeholder' approvalState ApprovalState @default(NEW) stateReason String? @db.VarChar - jsonData Json? - firstName String? @db.VarChar - lastName String? @db.VarChar + firstName String @db.VarChar + lastName String @db.VarChar email String? @db.Text phone String? @db.VarChar + country String? @db.VarChar dateOfBirth DateTime? avatarUrl String? + nationalId String? @db.VarChar additionalInfo Json? workflowRuntimeData WorkflowRuntimeData[] @@ -93,9 +95,10 @@ model Business { companyName String // Official registered name of the business entity registrationNumber String? // Unique registration number assigned by the relevant authorities legalForm String? // Legal structure of the business entity, e.g., LLC, corporation, partnership + country String? countryOfIncorporation String? // Country where the business entity is incorporated dateOfIncorporation DateTime? // Date when the business entity was incorporated - address String? // Registered address of the business entity + address Json? // Registered address of the business entity phoneNumber String? // Contact phone number of the business entity email String? // Contact email of the business entity website String? // Official website of the business entity @@ -106,6 +109,9 @@ model Business { numberOfEmployees Int? // Number of employees working for the business entity businessPurpose String? // Brief description of the business entity's purpose or main activities documents Json? // Collection of documents required for the KYB process, e.g., registration documents, financial statements + avatarUrl String? + additionalInfo Json? + bankInformation Json? approvalState ApprovalState @default(NEW) // Current status of the KYB process for the business entity workflowRuntimeData WorkflowRuntimeData[] @@ -158,7 +164,7 @@ model WorkflowRuntimeData { workflowDefinitionId String workflowDefinitionVersion Int context Json - config Json? + config Json? // history state String? status WorkflowRuntimeDataStatus @default(active) @@ -166,6 +172,7 @@ model WorkflowRuntimeData { updatedAt DateTime @updatedAt createdBy String @default("SYSTEM") resolvedAt DateTime? + assignedAt DateTime? @@index([assigneeId, status]) @@index([endUserId, status]) diff --git a/services/workflows-service/scripts/generate-end-user.ts b/services/workflows-service/scripts/generate-end-user.ts index fd8bb5b127..75d81f939c 100644 --- a/services/workflows-service/scripts/generate-end-user.ts +++ b/services/workflows-service/scripts/generate-end-user.ts @@ -13,12 +13,29 @@ export const endUserIds = [ 'ckkt3st0r0008qxtt5rxj0wgg', 'ckkt3swf20009qxttgx0p6x60', 'ckkt3t2bw000aqxtt0hj4pw4c', + 'ckkt3t2bw000aqxtt0hj4pw4d', + 'ckkt3t2bw000aqxtt0hj4pw4e', + 'ckkt3t2bw000aqxtt0hj4pw4f', + 'ckkt3t2bw000aqxtt0hj4pw4g', + 'ckkt3t2bw000aqxtt0hj4pw4h', + 'ckkt3t2bw000aqxtt0hj4pw4i', ]; export const businessRiskIds = [ 'ckkt3qnv41001qxtt7nmj9r26', 'ckkt3r0v42002qxtt8sxk7fv9', 'ckkt3rhxr3003qxtt5x6h5j18', + 'ckkt3rv4z4004qxtte4vz9e19', + 'ckkt3s3ha5005qxttdz5yxg20', + 'ckkt3sc1n6006qxtt9e9u7y21', + 'ckkt3sjz70007qxtt3nqj80j22', + 'ckkt3st0r8008qxtt5rxj0wg23', + 'ckkt3swf90009qxttgx0p6x24', + 'ckkt3t2bw000aqxtt0hj4pw25', + 'ckkt3t2bw000aqxtt0hj4pw26', + 'ckkt3t2bw000aqxtt0hj4pw27', + 'ckkt3t2bw000aqxtt0hj4pw28', + 'ckkt3t2bw000aqxtt0hj4pw29', ]; export const businessIds = [ 'ckkt3rv4z4004qxtte4vz9e97', diff --git a/services/workflows-service/scripts/seed.ts b/services/workflows-service/scripts/seed.ts index 7afec76b9e..d95d5ae512 100644 --- a/services/workflows-service/scripts/seed.ts +++ b/services/workflows-service/scripts/seed.ts @@ -1,6 +1,6 @@ import * as dotenv from 'dotenv'; import { faker } from '@faker-js/faker'; -import { Business, EndUser, PrismaClient } from '@prisma/client'; +import { Business, EndUser, Prisma, PrismaClient } from '@prisma/client'; import { hash } from 'bcrypt'; import { customSeed } from './custom-seed'; import { @@ -10,10 +10,11 @@ import { generateBusiness, generateEndUser, } from './generate-end-user'; -import defaultContextSchema from '../src/workflow/schemas/default-context-schema.json'; +import { defaultContextSchema } from '@ballerine/common'; import { Salt } from '../src/auth/password/password.service'; import { env } from '../src/env'; import { generateUserNationalId } from './generate-user-national-id'; +import { generateDynamicDefinitionForE2eTest } from './workflows/e2e-dynamic-url-example'; if (require.main === module) { dotenv.config(); @@ -36,17 +37,22 @@ const persistImageFile = async (client: PrismaClient, uri: string) => { return file.id; }; -function generateAvatarImageUri(imageTemplate: string, countOfBusiness: number) { +function generateAvatarImageUri(imageTemplate: string, countOfBusiness: number, pdf = false) { + if (pdf) { + return `https://backoffice-demo.ballerine.app/images/mock-documents/set_1_doc_pdf.pdf`; + } + if (countOfBusiness < 4) { return `https://backoffice-demo.ballerine.app/images/mock-documents/${imageTemplate}`; - } else { - return faker.image.people(1000, 2000, true); } + + return faker.image.people(1000, 2000, true); } async function seed(bcryptSalt: Salt) { console.info('Seeding database...'); const client = new PrismaClient(); + await generateDynamicDefinitionForE2eTest(client); const users = [ { email: 'agent1@ballerine.com', @@ -113,7 +119,11 @@ async function seed(bcryptSalt: Salt) { `set_${countOfBusiness}_doc_face.png`, countOfBusiness, ); - const imageUri3 = generateAvatarImageUri(`set_${countOfBusiness}_selfie.png`, countOfBusiness); + const imageUri3 = generateAvatarImageUri( + `set_${countOfBusiness}_selfie.png`, + countOfBusiness, + true, + ); const mockData = { entity: { @@ -135,13 +145,14 @@ async function seed(bcryptSalt: Salt) { numberOfEmployees: faker.datatype.number(1000), businessPurpose: faker.company.catchPhraseDescriptor(), approvalState: 'NEW', + additionalInfo: { customParam: 'customValue' }, } satisfies Partial<Business>, - additionalDetails: {}, ballerineEntityId: businessId, id: correlationId, }, documents: [ { + id: faker.datatype.uuid(), category: 'proof_of_employment', type: 'payslip', issuer: { @@ -149,7 +160,7 @@ async function seed(bcryptSalt: Salt) { name: 'Government', country: 'GH', city: faker.address.city(), - additionalDetails: {}, + additionalInfo: { customParam: 'customValue' }, }, issuingVersion: 1, @@ -179,15 +190,16 @@ async function seed(bcryptSalt: Salt) { }, ], properties: { - userNationalId: generateUserNationalId(), - docNumber: faker.finance.account(9), - userAddress: faker.address.streetAddress(), - website: faker.internet.url(), - expiryDate: faker.date.future(10).toISOString().split('T')[0], - email: faker.internet.email(), + nationalIdNumber: generateUserNationalId(), + docNumber: faker.random.alphaNumeric(9), + employeeName: faker.name.fullName(), + position: faker.name.jobTitle(), + salaryAmount: faker.finance.amount(1000, 10000), + issuingDate: faker.date.past(10).toISOString().split('T')[0], }, }, { + id: faker.datatype.uuid(), category: 'proof_of_address', type: 'mortgage_statement', issuer: { @@ -195,7 +207,7 @@ async function seed(bcryptSalt: Salt) { name: 'Government', country: 'GH', city: faker.address.city(), - additionalDetails: {}, + additionalInfo: { customParam: 'customValue' }, }, issuingVersion: 1, @@ -211,12 +223,12 @@ async function seed(bcryptSalt: Salt) { }, ], properties: { - userNationalId: generateUserNationalId(), - docNumber: faker.finance.account(9), - userAddress: faker.address.streetAddress(), - website: faker.internet.url(), - expiryDate: faker.date.future(10).toISOString().split('T')[0], - email: faker.internet.email(), + nationalIdNumber: generateUserNationalId(), + docNumber: faker.random.alphaNumeric(9), + employeeName: faker.name.fullName(), + position: faker.name.jobTitle(), + salaryAmount: faker.finance.amount(1000, 10000), + issuingDate: faker.date.past(10).toISOString().split('T')[0], }, }, ], @@ -238,6 +250,7 @@ async function seed(bcryptSalt: Salt) { const imageUri3 = generateAvatarImageUri( `set_${countOfIndividual}_selfie.png`, countOfIndividual, + true, ); const mockData = { @@ -252,21 +265,22 @@ async function seed(bcryptSalt: Salt) { stateReason: 'Poor quality of documents', // @ts-expect-error - end user type expects a date and not a string. dateOfBirth: faker.date.past(20).toISOString(), + additionalInfo: { customParam: 'customValue' }, } satisfies Partial<EndUser>, - additionalDetails: {}, ballerineEntityId: endUserId, id: correlationId, }, documents: [ { - category: 'ID', + id: faker.datatype.uuid(), + category: 'id', type: 'photo', issuer: { type: 'government', name: 'Government', - country: faker.address.country(), + country: 'CA', city: faker.address.city(), - additionalDetails: {}, + additionalInfo: { customParam: 'customValue' }, }, issuingVersion: 1, @@ -296,23 +310,28 @@ async function seed(bcryptSalt: Salt) { }, ], properties: { - userNationalId: generateUserNationalId(), - docNumber: faker.finance.account(9), - userAddress: faker.address.streetAddress(), - website: faker.internet.url(), - expiryDate: faker.date.future(10).toISOString().split('T')[0], - email: faker.internet.email(), + firstName: faker.name.firstName(), + middleName: faker.name.firstName(), + lastName: faker.name.lastName(), + authority: faker.company.name(), + placeOfIssue: faker.address.city(), + issueDate: faker.date.past(10).toISOString().split('T')[0], + expires: faker.date.future(10).toISOString().split('T')[0], + dateOfBirth: faker.date.past(20).toISOString().split('T')[0], + placeOfBirth: faker.address.city(), + sex: faker.helpers.arrayElement(['male', 'female', 'other']), }, }, { + id: faker.datatype.uuid(), category: 'selfie', - type: 'certificate', + type: 'photo', issuer: { type: 'government', name: 'Government', - country: faker.address.country(), + country: 'CA', city: faker.address.city(), - additionalDetails: {}, + additionalInfo: { customParam: 'customValue' }, }, issuingVersion: 1, @@ -328,12 +347,16 @@ async function seed(bcryptSalt: Salt) { }, ], properties: { - userNationalId: generateUserNationalId(), - docNumber: faker.finance.account(9), - userAddress: faker.address.streetAddress(), - website: faker.internet.url(), - expiryDate: faker.date.future(10).toISOString().split('T')[0], - email: faker.internet.email(), + firstName: faker.name.firstName(), + middleName: faker.name.firstName(), + lastName: faker.name.lastName(), + authority: faker.company.name(), + placeOfIssue: faker.address.city(), + issueDate: faker.date.past(10).toISOString().split('T')[0], + expires: faker.date.future(10).toISOString().split('T')[0], + dateOfBirth: faker.date.past(20).toISOString().split('T')[0], + placeOfBirth: faker.address.city(), + sex: faker.helpers.arrayElement(['male', 'female', 'other']), }, }, ], @@ -342,6 +365,20 @@ async function seed(bcryptSalt: Salt) { return mockData; } + function createFilter( + name: string, + entity: 'individuals' | 'businesses', + query: Prisma.WorkflowRuntimeDataFindManyArgs, + ) { + return client.filter.create({ + data: { + entity, + name, + query: query as any, + }, + }); + } + // Risk score improvement await client.workflowDefinition.create({ data: { @@ -352,6 +389,7 @@ async function seed(bcryptSalt: Salt) { config: { completedWhenTasksResolved: true, workflowLevelResolution: false, + allowMultipleActiveWorkflows: true, }, contextSchema: { type: 'json-schema', @@ -596,19 +634,81 @@ async function seed(bcryptSalt: Salt) { }, }); - await client.filter.create({ - data: { - entity: 'individuals', - name: 'Risk Score Improvement - Individuals', - query: { + await createFilter('Onboarding - Businesses with enriched data', 'businesses', { + select: { + id: true, + status: true, + assigneeId: true, + createdAt: true, + context: true, + workflowDefinition: { + select: { + id: true, + name: true, + contextSchema: true, + config: true, + }, + }, + business: { + select: { + id: true, + companyName: true, + registrationNumber: true, + legalForm: true, + countryOfIncorporation: true, + dateOfIncorporation: true, + address: true, + phoneNumber: true, + email: true, + website: true, + industry: true, + taxIdentificationNumber: true, + vatNumber: true, + shareholderStructure: true, + numberOfEmployees: true, + businessPurpose: true, + documents: true, + approvalState: true, + createdAt: true, + updatedAt: true, + }, + }, + assignee: { + select: { + id: true, + firstName: true, + lastName: true, + }, + }, + }, + where: { + workflowDefinitionId: 'dynamic_external_request_example', + businessId: { not: null }, + }, + }); + + await createFilter('Onboarding - Individuals', 'individuals', { + select: { + id: true, + status: true, + assigneeId: true, + context: true, + createdAt: true, + workflowDefinition: { + select: { + id: true, + name: true, + contextSchema: true, + config: true, + }, + }, + endUser: { select: { id: true, correlationId: true, - verificationId: true, endUserType: true, approvalState: true, stateReason: true, - jsonData: true, firstName: true, lastName: true, email: true, @@ -618,41 +718,85 @@ async function seed(bcryptSalt: Salt) { additionalInfo: true, createdAt: true, updatedAt: true, - workflowRuntimeData: { - select: { - id: true, - status: true, - assigneeId: true, - createdAt: true, - workflowDefinition: { - select: { - id: true, - name: true, - }, - }, - }, - }, }, - where: { - workflowRuntimeData: { - some: { - workflowDefinition: { - is: { - id: manualMachineId, - }, - }, - }, - }, + }, + assignee: { + select: { + id: true, + firstName: true, + lastName: true, }, }, }, + where: { + workflowDefinitionId: manualMachineId, + endUserId: { not: null }, + }, }); - await client.filter.create({ - data: { - entity: 'businesses', - name: 'Risk Score Improvement - Businesses', - query: { + await createFilter('Risk Score Improvement - Individuals', 'individuals', { + select: { + id: true, + status: true, + assigneeId: true, + createdAt: true, + context: true, + workflowDefinition: { + select: { + id: true, + name: true, + contextSchema: true, + config: true, + }, + }, + endUser: { + select: { + id: true, + correlationId: true, + endUserType: true, + approvalState: true, + stateReason: true, + firstName: true, + lastName: true, + email: true, + phone: true, + dateOfBirth: true, + avatarUrl: true, + additionalInfo: true, + createdAt: true, + updatedAt: true, + }, + }, + assignee: { + select: { + id: true, + firstName: true, + lastName: true, + }, + }, + }, + where: { + workflowDefinitionId: riskScoreMachineKybId, + endUserId: { not: null }, + }, + }); + + await createFilter('Risk Score Improvement - Businesses', 'businesses', { + select: { + id: true, + status: true, + assigneeId: true, + createdAt: true, + context: true, + workflowDefinition: { + select: { + id: true, + name: true, + contextSchema: true, + config: true, + }, + }, + business: { select: { id: true, companyName: true, @@ -672,36 +816,75 @@ async function seed(bcryptSalt: Salt) { businessPurpose: true, documents: true, approvalState: true, - workflowRuntimeData: { - select: { - id: true, - status: true, - assigneeId: true, - createdAt: true, - workflowDefinition: { - select: { - id: true, - name: true, - }, - }, - }, - }, createdAt: true, updatedAt: true, }, - where: { - workflowRuntimeData: { - some: { - workflowDefinition: { - is: { - id: riskScoreMachineKybId, - }, - }, - }, - }, + }, + assignee: { + select: { + id: true, + firstName: true, + lastName: true, }, }, }, + where: { + workflowDefinitionId: riskScoreMachineKybId, + businessId: { not: null }, + }, + }); + + await createFilter('KYB', 'businesses', { + select: { + id: true, + status: true, + assigneeId: true, + createdAt: true, + context: true, + workflowDefinition: { + select: { + id: true, + name: true, + contextSchema: true, + config: true, + }, + }, + business: { + select: { + id: true, + companyName: true, + registrationNumber: true, + legalForm: true, + countryOfIncorporation: true, + dateOfIncorporation: true, + address: true, + phoneNumber: true, + email: true, + website: true, + industry: true, + taxIdentificationNumber: true, + vatNumber: true, + shareholderStructure: true, + numberOfEmployees: true, + businessPurpose: true, + documents: true, + approvalState: true, + createdAt: true, + updatedAt: true, + }, + }, + assignee: { + select: { + id: true, + firstName: true, + lastName: true, + }, + }, + }, + where: { + workflowDefinitionId: manualMachineId, + businessId: { not: null }, + }, }); await client.$transaction(async () => diff --git a/services/workflows-service/scripts/workflows/e2e-dynamic-url-example.ts b/services/workflows-service/scripts/workflows/e2e-dynamic-url-example.ts new file mode 100644 index 0000000000..94cb2f582c --- /dev/null +++ b/services/workflows-service/scripts/workflows/e2e-dynamic-url-example.ts @@ -0,0 +1,219 @@ +import { PrismaClient } from '@prisma/client'; + +export const kybWithDynamicExternalRequestWorkflowExample = { + id: 'dynamic_external_request_example', + name: 'dynamic_external_request_example', + version: 1, + definitionType: 'statechart-json', + definition: { + id: 'kyb_example_v1', + predictableActionArguments: true, + initial: 'idle', + context: { + documents: [], + }, + states: { + idle: { + on: { + start: 'check_business_details', + }, + }, + check_business_details: { + on: { + API_CALL_SUCCESS: [ + { + target: 'auto_approve', + cond: { + type: 'json-logic', + options: { + rule: { + or: [ + { + '==': [ + { var: 'context.entity.companyName' }, + { var: 'response.data.registered_name' }, + ], + }, + // { + // '>=': [ + // { var: 'context.external_request_example.data.name_fuzziness_score' }, + // 0.91, + // ], + // }, + ], + }, + }, + }, + }, + { + target: 'manual_review', + cond: { + type: 'json-logic', + options: { + rule: { + '>': [{ var: 'pluginsOutput.business_data_vendor.name_fuzziness_score' }, 0.5], + }, + onFailed: { manualReviewReason: 'name not matching ... ' }, + }, + }, + }, + { + target: 'auto_reject', + cond: { + type: 'json-logic', + options: { + rule: { + '<': [ + { var: 'pluginsOutput.external_request_example.name_fuzziness_score' }, + 0.5, + ], + }, + onFailed: { manualReviewReason: 'Fuzzy fail and does not match' }, + }, + }, + }, + ], + API_CALL_ERROR: [ + { + target: 'manual_review', + cond: { + type: 'json-logic', + options: { + rule: { + '>=': [{ var: 'pluginsOutput.business_data_vendor.httpStatus' }, 400], + }, + }, + }, + }, + { + target: 'auto_reject', + }, + ], + }, + }, + manual_review: { + on: { + approve: 'approve', + reject: 'reject', + revision: 'revision', + }, + }, + auto_approve: { + type: 'final' as 'final', + }, + auto_reject: { + type: 'final' as 'final', + }, + reject: { + type: 'final' as 'final', + }, + approve: { + type: 'final' as 'final', + }, + revision: { + on: { + data_updated: 'check_business_details', + }, + }, + }, + }, + extensions: { + apiPlugins: [ + { + name: 'business_data_vendor', + url: 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_eu.json', + logo: 'https://uploads-ssl.webflow.com/62a3bad46800eb4715b2faf1/649435882f9b2819873035d7_companyVendorLogo.png', + method: 'GET', + stateNames: ['check_business_details'], + successAction: 'API_CALL_SUCCESS', + errorAction: 'API_CALL_ERROR', + request: { + transform: { + transformer: 'jmespath', + mapping: + '{ business_name: entity.data.companyName, registration_number: entity.data.registrationNumber}', + }, // jmespath + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + business_name: { + type: 'string', + }, + registration_number: { + type: 'string', + }, + }, + required: ['business_name', 'registration_number'], + }, // Schema is OPTIONAL, but if provided, it will be used to validate the request body + }, + response: { + transform: { + transformer: 'jmespath', + mapping: '@', // jmespath + }, + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + business_details: { + type: 'object', + properties: { + registered_name: { + type: 'string', + }, + registration_number: { + type: 'string', + }, + address: { + type: 'object', + }, + contact_number: { + type: 'string', + }, + }, + }, + name_fuzziness_score: { + type: 'number', + minimum: 0, + maximum: 1, + }, + }, + }, // OPTIONAL + }, + }, + { + name: 'finish_webhook', + url: 'https://webhook.site/3c48b14f-1a70-4f73-9385-fab2d0db0db8', + method: 'POST', + stateNames: ['auto_approve', 'approve', 'reject'], + headers: { + authorization: 'Bearer {secret.BUSINESS_DATA__VENDOR_API_KEY}', + }, + request: { + transform: { + transformer: 'jmespath', + mapping: '{success_result: pluginsOutput.business_data_vendor}', + }, + }, + }, + { + name: 'fail_webhook', + url: 'https://webhook.site/3c48b14f-1a70-4f73-9385-fab2d0db0db8', + method: 'POST', + stateNames: ['auto_reject'], + request: { + transform: { + transformer: 'jmespath', + mapping: '{failing_result: @}', + }, + }, + }, + ], + }, +}; +export const generateDynamicDefinitionForE2eTest = async (prismaClient: PrismaClient) => { + return await prismaClient.workflowDefinition.create({ + data: kybWithDynamicExternalRequestWorkflowExample, + }); +}; diff --git a/services/workflows-service/scripts/workflows/index.ts b/services/workflows-service/scripts/workflows/index.ts new file mode 100644 index 0000000000..2a0cece527 --- /dev/null +++ b/services/workflows-service/scripts/workflows/index.ts @@ -0,0 +1,173 @@ +export const kybWithExternalRequestWorkflowExample = { + id: 'external_request_example', + name: 'external_request_example', + version: 1, + definitionType: 'statechart-json', + definition: { + id: 'kyb_example_v1', + predictableActionArguments: true, + initial: 'idle', + context: { + documents: [], + }, + states: { + idle: { + on: { + start: 'check_business_details', + }, + }, + check_business_details: { + on: { + API_CALL_SUCCESS: [ + { + target: 'auto_approve', + cond: { + type: 'json-logic', + rule: { + or: [ + { + '==': [ + { var: 'context.entity.companyName' }, + { var: 'response.data.registered_name' }, + ], + }, + { + '>=': [ + { var: 'context.external_request_example.data.name_fuzziness_score' }, + 0.8, + ], + }, + ], + }, + }, + }, + { + // company dosent match but similar + target: 'manual_review', + cond: { + type: 'json-logic', + options: { + rule: { + '>': [ + { var: 'context.external_request_example.data.name_fuzziness_score' }, + 0.5, + ], + }, + onFailed: { manualReviewReason: 'name not matching ... ' }, + }, + }, + }, + { + // company dosent match but similar + target: 'auto_reject', + cond: { + type: 'json-logic', + rule: { + '<': [{ var: 'context.external_request_example.data.name_fuzziness_score' }, 0.5], + }, + }, + }, + ], + API_CALL_ERROR: [ + { + target: 'manual_review', + cond: { + type: 'json-logic', + rule: { + '>=': [{ var: 'context.external_request_example.httpStatus' }, 400], + }, + }, + }, + ], + }, + }, + manual_review: { + on: { + approve: 'approve', + reject: 'reject', + revision: 'revision', + }, + }, + auto_approve: { + type: 'final', + }, + auto_reject: { + type: 'final', + }, + approve: { + type: 'final', + }, + revision: { + on: { + data_updated: 'check_business_details', + }, + }, + }, + }, + externalRequests: [ + { + name: 'external_request_example', + strategy: 'direct-api', + successCallback: 'API_CALL_SUCCESS', + errorCallback: 'API_CALL_FAILURE', + url: 'https://mocks.ballerine.dev/api/businesses/risk/{context.entity.regestrationNumber}', + when: 'pre', + method: 'POST', + request: { + transform: { + transformer: 'jq', + mapping: + '{ business_name: .context .entity .companyName, regestration_number: .context .entity .registrationNumber}', + }, // JQ + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + business_name: { + type: 'string', + }, + registration_number: { + type: 'string', + }, + }, + required: ['business_name', 'registration_number'], + }, // Schema is OPTIONAL, but if provided, it will be used to validate the request body + }, + response: { + transform: {}, // JQ + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + business_details: { + type: 'object', + properties: { + registered_name: { + type: 'string', + }, + registration_number: { + type: 'string', + }, + address: { + type: 'string', + }, + contact_number: { + type: 'string', + }, + }, + }, + name_fuzziness_score: { + type: 'number', + minimum: 0, + maximum: 1, + }, + }, + }, // OPTIONAL + }, + headers: { + authorization: 'Bearer {secrets.BUSINESS_DATA__VENDOR_API_KEY}', + }, + states: ['check_company_data'], + }, + ], +}; diff --git a/services/workflows-service/src/app.module.ts b/services/workflows-service/src/app.module.ts index 45562aeccf..8c0706f53c 100644 --- a/services/workflows-service/src/app.module.ts +++ b/services/workflows-service/src/app.module.ts @@ -1,4 +1,4 @@ -import { MiddlewareConsumer, Module, Scope } from '@nestjs/common'; +import { MiddlewareConsumer, Module } from '@nestjs/common'; import { APP_INTERCEPTOR, APP_GUARD } from '@nestjs/core'; import { UserModule } from './user/user.module'; import { WorkflowModule } from './workflow/workflow.module'; @@ -21,9 +21,15 @@ import { env } from '@/env'; import { SentryModule } from '@/sentry/sentry.module'; import { RequestIdMiddleware } from '@/common/middlewares/request-id.middleware'; import { LogRequestInterceptor } from '@/common/interceptors/log-request.interceptor'; +import { AppLoggerModule } from '@/common/app-logger/app-logger.module'; +import { ClsModule } from 'nestjs-cls'; +import { FiltersModule } from '@/common/filters/filters.module'; +import { UserSessionAuditMiddleware } from '@/common/middlewares/user-session-audit.middleware'; +import { MetricsController } from '@/metrics/metrics.controller'; +import { MetricsModule } from '@/metrics/metrics.module'; @Module({ - controllers: [], + controllers: [MetricsController], imports: [ SentryModule, MulterModule.register({ @@ -54,6 +60,12 @@ import { LogRequestInterceptor } from '@/common/interceptors/log-request.interce ServeStaticModule.forRootAsync({ useClass: ServeStaticOptionsService, }), + ClsModule.forRoot({ + global: true, + }), + AppLoggerModule, + FiltersModule, + MetricsModule, ], providers: [ { @@ -68,6 +80,6 @@ import { LogRequestInterceptor } from '@/common/interceptors/log-request.interce }) export class AppModule { configure(consumer: MiddlewareConsumer) { - consumer.apply(RequestIdMiddleware).forRoutes('*'); + consumer.apply(RequestIdMiddleware, UserSessionAuditMiddleware).forRoutes('*'); } } diff --git a/services/workflows-service/src/auth/assignee-asigned-guard.service.ts b/services/workflows-service/src/auth/assignee-asigned-guard.service.ts new file mode 100644 index 0000000000..a3dd60ab0e --- /dev/null +++ b/services/workflows-service/src/auth/assignee-asigned-guard.service.ts @@ -0,0 +1,16 @@ +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { Request } from 'express'; +import { WorkflowService } from '@/workflow/workflow.service'; + +@Injectable() +export class WorkflowAssigneeGuard implements CanActivate { + constructor(private service: WorkflowService) {} + async canActivate(context: ExecutionContext) { + const request = context.switchToHttp().getRequest<Request>(); + const workflowId = request.params.id; + const requestingUserId = request.user!.id; + const workflowRuntime = await this.service.getWorkflowRuntimeDataById(workflowId as string); + + return workflowRuntime.assigneeId === requestingUserId; + } +} diff --git a/services/workflows-service/src/auth/auth.service.test.ts b/services/workflows-service/src/auth/auth.service.unit.test.ts similarity index 100% rename from services/workflows-service/src/auth/auth.service.test.ts rename to services/workflows-service/src/auth/auth.service.unit.test.ts diff --git a/services/workflows-service/src/auth/basic/basic.strategy.test.ts b/services/workflows-service/src/auth/basic/basic.strategy.unit.test.ts similarity index 100% rename from services/workflows-service/src/auth/basic/basic.strategy.test.ts rename to services/workflows-service/src/auth/basic/basic.strategy.unit.test.ts diff --git a/services/workflows-service/src/auth/local/local.strategy.test.ts b/services/workflows-service/src/auth/local/local.strategy.unit.test.ts similarity index 100% rename from services/workflows-service/src/auth/local/local.strategy.test.ts rename to services/workflows-service/src/auth/local/local.strategy.unit.test.ts diff --git a/services/workflows-service/src/auth/password/password.service.test.ts b/services/workflows-service/src/auth/password/password.service.unit.test.ts similarity index 100% rename from services/workflows-service/src/auth/password/password.service.test.ts rename to services/workflows-service/src/auth/password/password.service.unit.test.ts diff --git a/services/workflows-service/src/auth/token/token.service.test.ts b/services/workflows-service/src/auth/token/token.service.unit.test.ts similarity index 100% rename from services/workflows-service/src/auth/token/token.service.test.ts rename to services/workflows-service/src/auth/token/token.service.unit.test.ts diff --git a/services/workflows-service/src/business/business.controller.external.ts b/services/workflows-service/src/business/business.controller.external.ts index c9759d3335..2b28b85183 100644 --- a/services/workflows-service/src/business/business.controller.external.ts +++ b/services/workflows-service/src/business/business.controller.external.ts @@ -1,5 +1,6 @@ import { ApiNestedQuery } from '@/common/decorators/api-nested-query.decorator'; import * as common from '@nestjs/common'; +import { Param } from '@nestjs/common'; import * as swagger from '@nestjs/swagger'; import { plainToClass } from 'class-transformer'; import { Request } from 'express'; @@ -11,12 +12,18 @@ import { BusinessModel } from './business.model'; import { BusinessService } from './business.service'; import { isRecordNotFoundError } from '@/prisma/prisma.util'; import { BusinessCreateDto } from './dtos/business-create'; +import { UseKeyAuthInDevGuard } from '@/common/decorators/use-key-auth-in-dev-guard.decorator'; +import { WorkflowDefinitionModel } from '@/workflow/workflow-definition.model'; +import { WorkflowDefinitionFindManyArgs } from '@/workflow/dtos/workflow-definition-find-many-args'; +import { WorkflowService } from '@/workflow/workflow.service'; +import { makeFullWorkflow } from '@/workflow/utils/make-full-workflow'; @swagger.ApiTags('external/businesses') @common.Controller('external/businesses') export class BusinessControllerExternal { constructor( protected readonly service: BusinessService, + protected readonly workflowService: WorkflowService, @nestAccessControl.InjectRolesBuilder() protected readonly rolesBuilder: nestAccessControl.RolesBuilder, ) {} @@ -24,6 +31,7 @@ export class BusinessControllerExternal { @common.Post() @swagger.ApiCreatedResponse({ type: [BusinessModel] }) @swagger.ApiForbiddenResponse() + @UseKeyAuthInDevGuard() async create( @common.Body() data: BusinessCreateDto, ): Promise<Pick<BusinessModel, 'id' | 'companyName'>> { @@ -57,6 +65,7 @@ export class BusinessControllerExternal { @swagger.ApiOkResponse({ type: BusinessModel }) @swagger.ApiNotFoundResponse({ type: errors.NotFoundException }) @swagger.ApiForbiddenResponse() + @UseKeyAuthInDevGuard() async getById(@common.Param() params: BusinessWhereUniqueInput): Promise<BusinessModel | null> { try { const business = await this.service.getById(params.id); @@ -70,4 +79,21 @@ export class BusinessControllerExternal { throw err; } } + + // curl -v http://localhost:3000/api/v1/external/businesses/:businessId/workflows + @common.Get('/:businessId/workflows') + @swagger.ApiOkResponse({ type: [WorkflowDefinitionModel] }) + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) + @common.HttpCode(200) + @ApiNestedQuery(WorkflowDefinitionFindManyArgs) + @UseKeyAuthInDevGuard() + async listWorkflowRuntimeDataByBusinessId(@Param('businessId') businessId: string) { + const workflowRuntimeDataWithDefinition = + await this.workflowService.listFullWorkflowDataByUserId({ + entityId: businessId, + entity: 'business', + }); + + return makeFullWorkflow(workflowRuntimeDataWithDefinition); + } } diff --git a/services/workflows-service/src/business/business.controller.internal.ts b/services/workflows-service/src/business/business.controller.internal.ts index ed67c4866e..203d15ff1f 100644 --- a/services/workflows-service/src/business/business.controller.internal.ts +++ b/services/workflows-service/src/business/business.controller.internal.ts @@ -1,6 +1,5 @@ import { ApiNestedQuery } from '@/common/decorators/api-nested-query.decorator'; import * as common from '@nestjs/common'; -import { UsePipes } from '@nestjs/common'; import * as swagger from '@nestjs/swagger'; import * as errors from '../errors'; import { BusinessWhereUniqueInput } from './dtos/business-where-unique-input'; @@ -11,22 +10,14 @@ import { Request } from 'express'; import * as nestAccessControl from 'nest-access-control'; import { BusinessService } from './business.service'; import { isRecordNotFoundError } from '@/prisma/prisma.util'; -import { ZodValidationPipe } from '@/common/pipes/zod.pipe'; -import { BusinessFilterCreateSchema } from '@/filter/dtos/temp-zod-schemas'; import { InputJsonValue } from '@/types'; -import { FilterService } from '@/filter/filter.service'; -import { BusinessFilterModel } from '@/business/dtos/business-filter.model'; -import { BusinessFilterCreateDto } from '@/business/dtos/business-filter-create'; import { JsonValue } from 'type-fest'; -import { BusinessFindUniqueArgs } from '@/business/dtos/business-find-unique-args'; -import { TBusinessFilter } from '@/business/types'; @swagger.ApiTags('internal/businesses') @common.Controller('internal/businesses') export class BusinessControllerInternal { constructor( protected readonly service: BusinessService, - protected readonly filterService: FilterService, @nestAccessControl.InjectRolesBuilder() protected readonly rolesBuilder: nestAccessControl.RolesBuilder, ) {} @@ -36,13 +27,8 @@ export class BusinessControllerInternal { @swagger.ApiForbiddenResponse() @ApiNestedQuery(BusinessFindManyArgs) async list(@common.Req() request: Request): Promise<BusinessModel[]> { - const { filterId, ...args } = plainToClass(BusinessFindManyArgs, request.query); - let query: JsonValue = {}; - - if (filterId) { - const filter = await this.filterService.getById(filterId); - query = filter.query; - } + const args = plainToClass(BusinessFindManyArgs, request.query); + const query: JsonValue = {}; return this.service.list({ ...args, @@ -54,29 +40,9 @@ export class BusinessControllerInternal { @swagger.ApiOkResponse({ type: BusinessModel }) @swagger.ApiNotFoundResponse({ type: errors.NotFoundException }) @swagger.ApiForbiddenResponse() - @ApiNestedQuery(BusinessFindUniqueArgs) - async getById( - @common.Param() params: BusinessWhereUniqueInput, - @common.Req() request: Request, - ): Promise<BusinessModel | null> { + async getById(@common.Param() params: BusinessWhereUniqueInput): Promise<BusinessModel | null> { try { - const { filterId, ...args } = plainToClass(BusinessFindUniqueArgs, request.query); - let query: TBusinessFilter['query'] = {}; - - if (filterId) { - const filter = await this.filterService.getById(filterId); - // findUnique does not support `where`. - const { where: _where, ...restQuery } = filter?.query as TBusinessFilter['query']; - - query = restQuery; - } - - const business = await this.service.getById(params?.id, { - ...args, - ...(query as InputJsonValue), - }); - - return business; + return await this.service.getById(params?.id); } catch (err) { if (isRecordNotFoundError(err)) { throw new errors.NotFoundException(`No resource was found for ${JSON.stringify(params)}`); @@ -85,20 +51,4 @@ export class BusinessControllerInternal { throw err; } } - - @common.Post('filters') - @swagger.ApiCreatedResponse({ type: BusinessFilterModel }) - @swagger.ApiForbiddenResponse() - @UsePipes(new ZodValidationPipe(BusinessFilterCreateSchema)) - async createFilter(@common.Body() data: BusinessFilterCreateDto): Promise<BusinessFilterModel> { - const filter = await this.filterService.create({ - data: { - ...data, - entity: 'businesses', - query: data?.query as InputJsonValue, - }, - }); - - return filter as BusinessFilterModel; - } } diff --git a/services/workflows-service/src/business/business.module.ts b/services/workflows-service/src/business/business.module.ts index 7ccec2d237..07c0dd8027 100644 --- a/services/workflows-service/src/business/business.module.ts +++ b/services/workflows-service/src/business/business.module.ts @@ -5,9 +5,30 @@ import { BusinessControllerInternal } from './business.controller.internal'; import { BusinessControllerExternal } from './business.controller.external'; import { FilterService } from '@/filter/filter.service'; import { FilterRepository } from '@/filter/filter.repository'; +import { WorkflowDefinitionRepository } from '@/workflow/workflow-definition.repository'; +import { WorkflowRuntimeDataRepository } from '@/workflow/workflow-runtime-data.repository'; +import { WorkflowService } from '@/workflow/workflow.service'; +import { FileRepository } from '@/storage/storage.repository'; +import { FileService } from '@/providers/file/file.service'; +import { StorageService } from '@/storage/storage.service'; +import { WorkflowEventEmitterService } from '@/workflow/workflow-event-emitter.service'; +import { EndUserRepository } from '@/end-user/end-user.repository'; @Module({ controllers: [BusinessControllerInternal, BusinessControllerExternal], - providers: [BusinessRepository, BusinessService, FilterRepository, FilterService], + providers: [ + BusinessRepository, + BusinessService, + FilterRepository, + FilterService, + FileRepository, + FileService, + StorageService, + EndUserRepository, + WorkflowEventEmitterService, + WorkflowDefinitionRepository, + WorkflowRuntimeDataRepository, + WorkflowService, + ], }) export class BusinessModule {} diff --git a/services/workflows-service/src/business/dtos/business-filter-create.ts b/services/workflows-service/src/business/dtos/business-filter-create.ts deleted file mode 100644 index a0d947218b..0000000000 --- a/services/workflows-service/src/business/dtos/business-filter-create.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { BusinessQueryDto } from '@/business/dtos/business-filter-query'; - -export class BusinessFilterCreateDto { - @ApiProperty({ - required: true, - type: String, - }) - name!: string; - - @ApiProperty({ - required: true, - type: String, - }) - entity!: string; - - @ApiProperty({ - required: true, - type: () => BusinessQueryDto, - }) - query!: BusinessQueryDto; -} diff --git a/services/workflows-service/src/business/dtos/business-find-many-args.ts b/services/workflows-service/src/business/dtos/business-find-many-args.ts index 0c7f0e557d..2b2af1391c 100644 --- a/services/workflows-service/src/business/dtos/business-find-many-args.ts +++ b/services/workflows-service/src/business/dtos/business-find-many-args.ts @@ -31,11 +31,4 @@ export class BusinessFindManyArgs { }) @Type(() => Number) take?: number; - - @ApiProperty({ - required: false, - type: String, - }) - @Type(() => String) - filterId?: string; } diff --git a/services/workflows-service/src/business/dtos/business-find-unique-args.ts b/services/workflows-service/src/business/dtos/business-find-unique-args.ts deleted file mode 100644 index 44f9104932..0000000000 --- a/services/workflows-service/src/business/dtos/business-find-unique-args.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; - -export class BusinessFindUniqueArgs { - @ApiProperty({ - required: false, - type: String, - }) - @Type(() => String) - filterId?: string; -} diff --git a/services/workflows-service/src/business/types.ts b/services/workflows-service/src/business/types.ts deleted file mode 100644 index 799f1e25af..0000000000 --- a/services/workflows-service/src/business/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { z } from 'zod'; -import { BusinessFilterSchema } from '@/filter/dtos/temp-zod-schemas'; - -export type TBusinessFilter = z.output<typeof BusinessFilterSchema>; diff --git a/services/workflows-service/src/common/abstract-logger/abstract-logger.ts b/services/workflows-service/src/common/abstract-logger/abstract-logger.ts new file mode 100644 index 0000000000..4417af2dda --- /dev/null +++ b/services/workflows-service/src/common/abstract-logger/abstract-logger.ts @@ -0,0 +1,11 @@ +export interface LogPayload { + [k: string]: any; +} + +export abstract class IAppLogger { + abstract log(message: string, payload: LogPayload): void; + abstract info(message: string, payload: LogPayload): void; + abstract warn(message: string, payload: LogPayload): void; + abstract debug(message: string, payload: LogPayload): void; + abstract error(message: string, payload: LogPayload): void; +} diff --git a/services/workflows-service/src/common/app-logger/app-logger.module.ts b/services/workflows-service/src/common/app-logger/app-logger.module.ts new file mode 100644 index 0000000000..b711f4d53e --- /dev/null +++ b/services/workflows-service/src/common/app-logger/app-logger.module.ts @@ -0,0 +1,10 @@ +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; +import { WinstonLogger } from '@/common/utils/winston-logger/winston-logger'; +import { Global, Module } from '@nestjs/common'; + +@Global() +@Module({ + providers: [{ useClass: WinstonLogger, provide: 'LOGGER' }, AppLoggerService], + exports: [AppLoggerService], +}) +export class AppLoggerModule {} diff --git a/services/workflows-service/src/common/app-logger/app-logger.service.ts b/services/workflows-service/src/common/app-logger/app-logger.service.ts new file mode 100644 index 0000000000..8a38d7c5f3 --- /dev/null +++ b/services/workflows-service/src/common/app-logger/app-logger.service.ts @@ -0,0 +1,33 @@ +import { IAppLogger, LogPayload } from '@/common/abstract-logger/abstract-logger'; +import { Inject, Injectable, LoggerService } from '@nestjs/common'; +import { ClsService } from 'nestjs-cls'; + +@Injectable() +export class AppLoggerService implements LoggerService { + constructor( + @Inject('LOGGER') private readonly logger: IAppLogger, + private readonly cls: ClsService, + ) {} + + log(message: string, payload: LogPayload = {}) { + this.logger.info(message, { ...this.getLogMetadata(), logData: payload }); + } + + error(message: string, payload: LogPayload = {}) { + this.logger.error(message, { ...this.getLogMetadata(), logData: payload }); + } + + warn(message: string, payload: LogPayload = {}) { + this.logger.warn(message, { ...this.getLogMetadata(), logData: payload }); + } + + debug(message: string, payload: LogPayload = {}) { + this.logger.debug(message, { ...this.getLogMetadata(), logData: payload }); + } + + private getLogMetadata() { + return { + requestId: this.cls.get('requestId'), + }; + } +} diff --git a/services/workflows-service/src/common/decorators/is-nullable.decorator.ts b/services/workflows-service/src/common/decorators/is-nullable.decorator.ts new file mode 100644 index 0000000000..72e80afe68 --- /dev/null +++ b/services/workflows-service/src/common/decorators/is-nullable.decorator.ts @@ -0,0 +1,5 @@ +import { ValidationOptions, ValidateIf } from 'class-validator'; + +export function IsNullable(validationOptions?: ValidationOptions) { + return ValidateIf((_object, value) => value !== null, validationOptions); +} diff --git a/services/workflows-service/src/common/decorators/one-of.decorator.ts b/services/workflows-service/src/common/decorators/one-of.decorator.ts new file mode 100644 index 0000000000..eb1c3a7508 --- /dev/null +++ b/services/workflows-service/src/common/decorators/one-of.decorator.ts @@ -0,0 +1,19 @@ +import { registerDecorator, ValidationOptions } from 'class-validator'; + +export function oneOf(list: (string | number)[], options?: ValidationOptions) { + return function (obj: object, paramName: string) { + registerDecorator({ + name: 'oneOf', + target: obj.constructor, + propertyName: paramName, + constraints: list, + options: { + message: `Value must be one of ${JSON.stringify(list)}`, + ...options, + }, + validator: { + validate: (value: string | number) => list.some(val => val === value), + }, + }); + }; +} diff --git a/services/workflows-service/src/common/decorators/use-key-auth-in-dev-guard.decorator.ts b/services/workflows-service/src/common/decorators/use-key-auth-in-dev-guard.decorator.ts new file mode 100644 index 0000000000..b39783c150 --- /dev/null +++ b/services/workflows-service/src/common/decorators/use-key-auth-in-dev-guard.decorator.ts @@ -0,0 +1,12 @@ +import { UseKeyAuthGuard } from '@/common/decorators/use-key-auth-guard.decorator'; +import { env } from '@/env'; + +export const UseKeyAuthInDevGuard = () => { + if (env.NODE_ENV !== 'development' && env.NODE_ENV !== 'local') { + return () => { + return; + }; + } + + return UseKeyAuthGuard(); +}; diff --git a/services/workflows-service/src/common/decorators/use-key-auth-or-session-guard.decorator.ts b/services/workflows-service/src/common/decorators/use-key-auth-or-session-guard.decorator.ts new file mode 100644 index 0000000000..554827e7cd --- /dev/null +++ b/services/workflows-service/src/common/decorators/use-key-auth-or-session-guard.decorator.ts @@ -0,0 +1,4 @@ +import { applyDecorators, UseGuards } from '@nestjs/common'; +import { KeyAuthGuard } from '@/auth/key-auth.guard'; + +export const UseKeyAuthOrSessionGuard = () => applyDecorators(UseGuards(KeyAuthGuard)); diff --git a/services/workflows-service/src/common/filters/AllExceptions.filter.ts b/services/workflows-service/src/common/filters/AllExceptions.filter.ts index 3d5f8d1660..79fe76cd88 100644 --- a/services/workflows-service/src/common/filters/AllExceptions.filter.ts +++ b/services/workflows-service/src/common/filters/AllExceptions.filter.ts @@ -1,10 +1,13 @@ -import { Catch, ArgumentsHost, Logger } from '@nestjs/common'; -import { BaseExceptionFilter } from '@nestjs/core'; +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; +import { Catch, ArgumentsHost } from '@nestjs/common'; +import { BaseExceptionFilter, HttpAdapterHost } from '@nestjs/core'; import { Response } from 'express'; @Catch() export class AllExceptionsFilter extends BaseExceptionFilter { - private readonly logger = new Logger(AllExceptionsFilter.name); + constructor(private readonly logger: AppLoggerService, applicationRef?: HttpAdapterHost) { + super(applicationRef?.httpAdapter); + } catch(exception: unknown, host: ArgumentsHost) { // if (host.getType() === 'http') return; diff --git a/services/workflows-service/src/common/filters/HttpExceptions.filter.ts b/services/workflows-service/src/common/filters/HttpExceptions.filter.ts index 07d45025f7..adbb608169 100644 --- a/services/workflows-service/src/common/filters/HttpExceptions.filter.ts +++ b/services/workflows-service/src/common/filters/HttpExceptions.filter.ts @@ -1,5 +1,5 @@ import { ArgumentsHost, Catch, HttpException, HttpServer, HttpStatus } from '@nestjs/common'; -import { BaseExceptionFilter } from '@nestjs/core'; +import { BaseExceptionFilter, HttpAdapterHost } from '@nestjs/core'; import { Prisma } from '@prisma/client'; import { Response } from 'express'; @@ -28,8 +28,8 @@ export class HttpExceptionFilter extends BaseExceptionFilter { * @param applicationRef */ // eslint-disable-next-line @typescript-eslint/no-useless-constructor - constructor(applicationRef?: HttpServer) { - super(applicationRef); + constructor(applicationRef?: HttpAdapterHost) { + super(applicationRef?.httpAdapter); } /** diff --git a/services/workflows-service/src/common/filters/filters.module.ts b/services/workflows-service/src/common/filters/filters.module.ts new file mode 100644 index 0000000000..f99396087e --- /dev/null +++ b/services/workflows-service/src/common/filters/filters.module.ts @@ -0,0 +1,14 @@ +import { AllExceptionsFilter } from '@/common/filters/AllExceptions.filter'; +import { HttpExceptionFilter } from '@/common/filters/HttpExceptions.filter'; +import { PrismaClientValidationFilter } from '@/common/filters/prisma-client-validation-filter/PrismaClientValidation.filter'; +import { Module } from '@nestjs/common'; +import { APP_FILTER } from '@nestjs/core'; + +@Module({ + providers: [ + { provide: APP_FILTER, useClass: AllExceptionsFilter }, + { provide: APP_FILTER, useClass: HttpExceptionFilter }, + { provide: APP_FILTER, useClass: PrismaClientValidationFilter }, + ], +}) +export class FiltersModule {} diff --git a/services/workflows-service/src/common/filters/prisma-client-validation-filter/PrismaClientValidation.filter.ts b/services/workflows-service/src/common/filters/prisma-client-validation-filter/PrismaClientValidation.filter.ts new file mode 100644 index 0000000000..96b7ae4521 --- /dev/null +++ b/services/workflows-service/src/common/filters/prisma-client-validation-filter/PrismaClientValidation.filter.ts @@ -0,0 +1,42 @@ +import { PrismaValidationExceptionParser } from './utils/PrismaValidationExceptionParser'; +import { ArgumentsHost, Catch, HttpStatus, Injectable, Logger } from '@nestjs/common'; +import { BaseExceptionFilter } from '@nestjs/core'; +import { Prisma } from '@prisma/client'; +import { PrismaClientValidationError } from '@prisma/client/runtime'; +import { PrismaValidationExceptionParseResult } from '@/common/filters/prisma-client-validation-filter/utils/types'; +import { InvalidArgumentParser } from '@/common/filters/prisma-client-validation-filter/utils/parsers/invalid-argument-parser/invalid-argument.parser'; +import { UnknownArgumentParser } from '@/common/filters/prisma-client-validation-filter/utils/parsers/unknown-argument-parser/unknown-argument-parser'; +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; + +@Catch(Prisma.PrismaClientValidationError) +export class PrismaClientValidationFilter extends BaseExceptionFilter { + constructor(private readonly logger: AppLoggerService) { + super(); + } + + catch(exception: PrismaClientValidationError, host: ArgumentsHost) { + const context = host.switchToHttp(); + const response = context.getResponse(); + + this.logger.error(`Prisma validation failed.\n ${exception.message}`); + + response.status(HttpStatus.BAD_REQUEST); + + response.json({ + statusCode: HttpStatus.BAD_REQUEST, + message: 'Validation failed.', + result: this.parseException(exception), + }); + } + + private parseException( + exception: PrismaClientValidationError, + ): PrismaValidationExceptionParseResult { + const parser = new PrismaValidationExceptionParser( + [InvalidArgumentParser, UnknownArgumentParser], + exception, + ); + + return parser.parse(); + } +} diff --git a/services/workflows-service/src/common/filters/prisma-client-validation-filter/PrismaClientValidation.module.ts b/services/workflows-service/src/common/filters/prisma-client-validation-filter/PrismaClientValidation.module.ts new file mode 100644 index 0000000000..318e5d98eb --- /dev/null +++ b/services/workflows-service/src/common/filters/prisma-client-validation-filter/PrismaClientValidation.module.ts @@ -0,0 +1,8 @@ +import { PrismaClientValidationFilter } from '@/common/filters/prisma-client-validation-filter/PrismaClientValidation.filter'; +import { Module } from '@nestjs/common'; +import { APP_FILTER } from '@nestjs/core'; + +@Module({ + providers: [{ provide: APP_FILTER, useClass: PrismaClientValidationFilter }], +}) +export class PrismaClientValidationModule {} diff --git a/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/PrismaValidationExceptionParser.ts b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/PrismaValidationExceptionParser.ts new file mode 100644 index 0000000000..cae4f9517c --- /dev/null +++ b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/PrismaValidationExceptionParser.ts @@ -0,0 +1,37 @@ +import { PrismaValidationExceptionParseResult } from './types'; +import { Prisma } from '@prisma/client'; +import { IParser } from '@/common/filters/prisma-client-validation-filter/utils/parsers/parser/IParser'; +import { removeAnsiEscapeCodes } from './remove-ansi-escape-codes'; + +/** + * PrismaValidationExceptionParser + * @constructor + * @param {Prisma.PrismaClientValidationError} exception - Prisma validation exception + * @description Parsing exception for errors and convert them to JSON format + * Excluding referernces to prisma and application internals + * @method parse @returns {PrismaValidationExceptionParseResult} record of entries where key is paramName and its value errorReason + */ + +export class PrismaValidationExceptionParser { + constructor( + private readonly parsers: (new (message: string) => IParser)[], + private readonly exception: Prisma.PrismaClientValidationError, + ) {} + + parse(): PrismaValidationExceptionParseResult { + const { message } = this.exception; + const parseResult: PrismaValidationExceptionParseResult = this.parsers.reduce( + (parseResult, Parser) => { + const parser = new Parser(removeAnsiEscapeCodes(message)); + + return { + ...parseResult, + ...parser.parse(), + }; + }, + {}, + ); + + return parseResult; + } +} diff --git a/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/invalid-argument-parser/invalid-argument.parser.ts b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/invalid-argument-parser/invalid-argument.parser.ts new file mode 100644 index 0000000000..99b1595840 --- /dev/null +++ b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/invalid-argument-parser/invalid-argument.parser.ts @@ -0,0 +1,26 @@ +import { + IParser, + IParserResult, +} from '@/common/filters/prisma-client-validation-filter/utils/parsers/parser/IParser'; + +export class InvalidArgumentParser extends IParser { + // Looking for matches in following string format: Argument {{paramName}}: Got {{errorReason}} on prisma + // private pattern = new RegExp(/Argument ([^:]+):[^]+?Got ([^]+?) on prisma/, 'g'); + + pattern = new RegExp(/Argument ([^:]+):[^]+?Got ([^]+?) on prisma/, 'g'); + + parse(): IParserResult { + const { message } = this; + if (!message) return {}; + + return this.execPattern(this.pattern, (result, match) => { + const [_, paramName, errorReason] = match; + + if (paramName && errorReason) { + result[paramName] = errorReason; + } + + return result; + }); + } +} diff --git a/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/invalid-argument-parser/invalid-arguments.parser.unit.test.ts b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/invalid-argument-parser/invalid-arguments.parser.unit.test.ts new file mode 100644 index 0000000000..ff48705653 --- /dev/null +++ b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/invalid-argument-parser/invalid-arguments.parser.unit.test.ts @@ -0,0 +1,51 @@ +import { InvalidArgumentParser } from './invalid-argument.parser'; + +describe('InvalidArgumentParser', () => { + let parser: InvalidArgumentParser; + + describe('when exception message is empty', () => { + beforeEach(() => { + parser = new InvalidArgumentParser(''); + }); + + it('will return empty object', () => { + expect(parser.parse()).toEqual({}); + }); + }); + + describe('when provided message matches format', () => { + it('will parse string with single error', () => { + const errorReason = `some-error`; + const signleExceptionMessage = `Argument paramName: Got ${errorReason} on prisma`; + parser = new InvalidArgumentParser(signleExceptionMessage); + + expect(parser.parse()).toEqual({ paramName: errorReason }); + }); + + it('will parse string with multiple errors', () => { + const reasonOne = 'reasonOne'; + const reasonTwo = 'reasonTwo'; + + const message = `Argument paramOne: Got ${reasonOne} on prisma.Argument paramTwo: Got ${reasonTwo} on prisma`; + parser = new InvalidArgumentParser(message); + + expect(parser.parse()).toEqual({ paramOne: reasonOne, paramTwo: reasonTwo }); + }); + + it('will parse rich string with multiple errors between lines', () => { + const reasonOne = 'reasonOne'; + const reasonTwo = 'reasonTwo'; + + const message = `Lorem Ipsum is simply dummy text of the printing and typesetting industry. + Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, + Argument paramOne: Got ${reasonOne} on prisma. + when an unknown printer took a galley of type and scrambled + Argument paramTwo: Got ${reasonTwo} on prisma + it to make a type specimen book. It has survived not only five centuries,`; + + parser = new InvalidArgumentParser(message); + + expect(parser.parse()).toEqual({ paramOne: reasonOne, paramTwo: reasonTwo }); + }); + }); +}); diff --git a/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/parser/IParser.ts b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/parser/IParser.ts new file mode 100644 index 0000000000..b766758854 --- /dev/null +++ b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/parser/IParser.ts @@ -0,0 +1,22 @@ +export type IParserResult = Record<string, string>; +export type IParserResolver = (result: IParserResult, execResult: RegExpExecArray) => IParserResult; +export abstract class IParser { + constructor(readonly message: string) {} + + abstract parse(): IParserResult; + + execPattern(pattern: RegExp, resolver: IParserResolver) { + const { message } = this; + let parseResult: IParserResult = {}; + + let match: RegExpExecArray | null = null; + + if (!message) return {}; + + while ((match = pattern.exec(message))) { + parseResult = resolver(parseResult, match); + } + + return parseResult; + } +} diff --git a/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/unknown-argument-parser/unknown-argument-parser.ts b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/unknown-argument-parser/unknown-argument-parser.ts new file mode 100644 index 0000000000..dea3c84731 --- /dev/null +++ b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/unknown-argument-parser/unknown-argument-parser.ts @@ -0,0 +1,27 @@ +import { + IParser, + IParserResult, +} from '@/common/filters/prisma-client-validation-filter/utils/parsers/parser/IParser'; + +export class UnknownArgumentParser extends IParser { + // Looking for matches in following string format: Unknown arg {{paramName}} in {{failedOnPath}} for type {{EntityType}}. + pattern = new RegExp(/Unknown arg `(.+?)` in (.+?) for type (.+?)\./, 'gi'); + + parse(): IParserResult { + if (!this.message) return {}; + + return this.execPattern(this.pattern, (result, match) => { + const [_, fieldName, failedOnPath, type] = match; + + if (fieldName && failedOnPath && type) { + result[fieldName] = this.buildMessage(fieldName, failedOnPath, type); + } + + return result; + }); + } + + buildMessage = (paramName: string, path: string, type: string) => { + return `Provided parameter: ${paramName} on ${path} is not supported in type: ${type}`; + }; +} diff --git a/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/unknown-argument-parser/unknown-argument.parser.unit.test.ts b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/unknown-argument-parser/unknown-argument.parser.unit.test.ts new file mode 100644 index 0000000000..6f8e41779d --- /dev/null +++ b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/parsers/unknown-argument-parser/unknown-argument.parser.unit.test.ts @@ -0,0 +1,82 @@ +import { UnknownArgumentParser } from './unknown-argument-parser'; + +describe('UnknownArgumentParser', () => { + let parser: UnknownArgumentParser; + + describe('when exception message is empty', () => { + beforeEach(() => { + parser = new UnknownArgumentParser(''); + }); + + it('will return empty object', () => { + expect(parser.parse()).toEqual({}); + }); + }); + + describe('when provided message matches format', () => { + it('will parse string with single error', () => { + const argumentName = 'someParam'; + const failedAtPath = `data.${argumentName}`; + const type = 'SomeEntityDefinition'; + const signleExceptionMessage = `Unknown arg \`${argumentName}\` in ${failedAtPath} for type ${type}.`; + parser = new UnknownArgumentParser(signleExceptionMessage); + + expect(parser.parse()).toEqual({ + [argumentName]: parser.buildMessage(argumentName, failedAtPath, type), + }); + }); + + describe('when working with multiple errors in message', () => { + let errorParams: { argumentName: string; failedAtPath: string; type: string }[]; + let expectResult: Record<string, string>; + + beforeAll(() => { + errorParams = [ + { argumentName: 'arg1', failedAtPath: 'some.where1', type: 'Entity1' }, + { argumentName: 'arg2', failedAtPath: 'some.where2', type: 'Entity2' }, + ]; + expectResult = errorParams.reduce((result, params) => { + result[params.argumentName] = parser.buildMessage( + params.argumentName, + params.failedAtPath, + params.type, + ); + return result; + }, {} as Record<string, string>); + }); + + it('will parse string with multiple errors', () => { + const message = errorParams + .map( + params => + `Unknown arg \`${params.argumentName}\` in ${params.failedAtPath} for type ${params.type}.`, + ) + .join('.'); + + parser = new UnknownArgumentParser(message); + + expect(parser.parse()).toEqual(expectResult); + }); + + it('will parse rich string with multiple errors between lines', () => { + const error1Params = errorParams[0]; + const error2Params = errorParams[1]; + + const message = `Lorem Ipsum is simply dummy text of the printing and typesetting industry. + Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, + Unknown arg \`${error1Params!.argumentName}\` in ${error1Params!.failedAtPath} for type ${ + error1Params!.type + }. + when an unknown printer took a galley of type and scrambled + Unknown arg \`${error2Params!.argumentName}\` in ${error2Params!.failedAtPath} for type ${ + error2Params!.type + }. + it to make a type specimen book. It has survived not only five centuries,`; + + parser = new UnknownArgumentParser(message); + + expect(parser.parse()).toEqual(expectResult); + }); + }); + }); +}); diff --git a/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/remove-ansi-escape-codes.ts b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/remove-ansi-escape-codes.ts new file mode 100644 index 0000000000..028ac78105 --- /dev/null +++ b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/remove-ansi-escape-codes.ts @@ -0,0 +1,4 @@ +export function removeAnsiEscapeCodes(string: string): string { + // eslint-disable-next-line no-control-regex + return string.replace(/\x1B\[[0-9;]*[A-Za-z]/g, ''); +} diff --git a/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/remove-ansi-escape-codes.unit.test.ts b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/remove-ansi-escape-codes.unit.test.ts new file mode 100644 index 0000000000..925873ea79 --- /dev/null +++ b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/remove-ansi-escape-codes.unit.test.ts @@ -0,0 +1,21 @@ +import { removeAnsiEscapeCodes } from './remove-ansi-escape-codes'; + +describe('removeAnsiEscapeCodes', () => { + describe("when string does't include ansi codes", () => { + it('will keep it untouched', () => { + expect(removeAnsiEscapeCodes('Lorem ipsum dolor sit amet.')).toBe( + 'Lorem ipsum dolor sit amet.', + ); + }); + }); + + describe('when string includes ANSI', () => { + it('will remove ANSI escape codes from string', () => { + const ansiEncodedString = `Lorem \x1B[35mipsum\x1B[0m dolor sit amet, \x1B[33mconsectetur\x1B[0m adipiscing elit. Lorem \x1B[36mipsum\x1B[0m dolor sit amet, \x1B[31mconsectetur\x1B[0m adipiscing elit. Lorem \x1B[34mipsum\x1B[0m dolor sit amet, \x1B[32mconsectetur\x1B[0m adipiscing elit. Lorem \x1B[35mipsum\x1B[0m dolor sit amet, consectetur adipiscing elit.`; + + expect(removeAnsiEscapeCodes(ansiEncodedString)).toBe( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + ); + }); + }); +}); diff --git a/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/types.ts b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/types.ts new file mode 100644 index 0000000000..6656773062 --- /dev/null +++ b/services/workflows-service/src/common/filters/prisma-client-validation-filter/utils/types.ts @@ -0,0 +1,4 @@ +type FieldName = string; +type ErrorReason = string; + +export type PrismaValidationExceptionParseResult = Record<FieldName, ErrorReason>; diff --git a/services/workflows-service/src/common/interceptors/log-request.interceptor.ts b/services/workflows-service/src/common/interceptors/log-request.interceptor.ts index 362e37c753..baaae5a64a 100644 --- a/services/workflows-service/src/common/interceptors/log-request.interceptor.ts +++ b/services/workflows-service/src/common/interceptors/log-request.interceptor.ts @@ -1,10 +1,11 @@ import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common'; import { tap } from 'rxjs'; import { Response } from 'express'; +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; @Injectable() export class LogRequestInterceptor implements NestInterceptor { - private readonly logger = new Logger(LogRequestInterceptor.name); + constructor(private readonly logger: AppLoggerService) {} intercept(context: ExecutionContext, next: CallHandler) { return next.handle().pipe( diff --git a/services/workflows-service/src/common/middlewares/request-id.middleware.ts b/services/workflows-service/src/common/middlewares/request-id.middleware.ts index ca5455ff32..563153b0f3 100644 --- a/services/workflows-service/src/common/middlewares/request-id.middleware.ts +++ b/services/workflows-service/src/common/middlewares/request-id.middleware.ts @@ -1,10 +1,12 @@ -import { Injectable, Logger, NestMiddleware, Scope } from '@nestjs/common'; +import { Injectable, NestMiddleware, Scope } from '@nestjs/common'; import { NextFunction, Request, Response } from 'express'; import { randomUUID } from 'crypto'; +import { ClsService } from 'nestjs-cls'; +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; @Injectable({ scope: Scope.REQUEST }) export class RequestIdMiddleware implements NestMiddleware { - private readonly logger = new Logger(RequestIdMiddleware.name); + constructor(private readonly cls: ClsService, private readonly logger: AppLoggerService) {} use(request: Request, response: Response, next: NextFunction) { request.id = randomUUID(); @@ -14,6 +16,8 @@ export class RequestIdMiddleware implements NestMiddleware { delete cleanHeaders.authorization; delete cleanHeaders.cookie; + this.cls.set('requestId', request.id); + this.logger.log(`Incoming request`, { request: { id: request.id, diff --git a/services/workflows-service/src/common/middlewares/user-session-audit.middleware.intg.test.ts b/services/workflows-service/src/common/middlewares/user-session-audit.middleware.intg.test.ts new file mode 100644 index 0000000000..330c651abd --- /dev/null +++ b/services/workflows-service/src/common/middlewares/user-session-audit.middleware.intg.test.ts @@ -0,0 +1,134 @@ +import { UserSessionAuditMiddleware } from '@/common/middlewares/user-session-audit.middleware'; +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; +import { PrismaModule } from '@/prisma/prisma.module'; +import { UserService } from '@/user/user.service'; +import { UserRepository } from '@/user/user.repository'; +import { Test, TestingModule } from '@nestjs/testing'; +import { User } from '@prisma/client'; +import { Request, Response } from 'express'; +import dayjs from 'dayjs'; +import { commonTestingModules } from '@/test/helpers/nest-app-helper'; +import { Injectable } from '@nestjs/common'; +import { PasswordService } from '@/auth/password/password.service'; + +@Injectable() +class FakePasswordService { + async compare(password: string, encrypted: string): Promise<boolean> { + return Promise.resolve(true); + } + + async hash(password: string): Promise<string> { + return Promise.resolve(password); + } +} + +describe('UserSessionAuditMiddleware', () => { + const testUserPayload = { + firstName: 'Test', + lastName: 'User', + password: '', + email: 'example@mail.com', + roles: [], + } as unknown as User; + let app: TestingModule; + let testUser: User; + let middleware: UserSessionAuditMiddleware; + let userService: UserService; + let callback: jest.Mock; + + beforeEach(async () => { + app = await Test.createTestingModule({ + imports: [PrismaModule, ...commonTestingModules], + providers: [ + { + provide: PasswordService, + useClass: FakePasswordService, + }, + UserService, + UserRepository, + ], + }).compile(); + middleware = new UserSessionAuditMiddleware(app.get(AppLoggerService), app.get(UserService)); + userService = app.get(UserService); + callback = jest.fn(() => null); + }); + + describe('when request not includes session and user', () => { + it('will call callback', async () => { + await middleware.use({} as Request, {} as Response, callback); + + expect(callback).toHaveBeenCalledTimes(1); + }); + }); + + describe('when session and user in request', () => { + describe('when lastActiveAt unset', () => { + beforeEach(async () => { + testUser = await app.get(UserService).create({ data: testUserPayload as any }); + }); + + afterEach(async () => { + await app.get(UserService).deleteById(testUser.id); + }); + + it('will be set on middleware call', async () => { + await middleware.use( + { user: testUser, session: testUser } as any, + {} as Response, + callback, + ); + + const updatedUser = await app.get(UserService).getById(testUser.id); + + expect(updatedUser.lastActiveAt).toBeTruthy(); + expect(callback).toHaveBeenCalledTimes(1); + }); + }); + + describe('when lastActiveAt is set', () => { + beforeEach(async () => { + testUser = await app.get(UserService).create({ data: testUserPayload as any }); + }); + + afterEach(async () => { + await app.get(UserService).deleteById(testUser.id); + }); + + it('will not be changed when lastActiveAt not expired', async () => { + const nonExpiredDate = dayjs().subtract(middleware.UPDATE_INTERVAL - 10, 'ms'); + + testUser = await userService.updateById(testUser.id, { + data: { lastActiveAt: nonExpiredDate.toDate() }, + }); + + await middleware.use( + { user: testUser, session: testUser } as any, + {} as Response, + callback, + ); + + const user = await userService.getById(testUser.id); + + expect(user.lastActiveAt).toEqual(nonExpiredDate.toDate()); + expect(callback).toBeCalledTimes(1); + }); + + it('will be updated when lastActiveAt expired', async () => { + const expiredDate = dayjs().subtract(middleware.UPDATE_INTERVAL + 10, 'ms'); + + testUser.lastActiveAt = expiredDate.toDate(); + + await middleware.use( + { user: testUser, session: testUser } as any, + {} as Response, + callback, + ); + + const updatedUser = await userService.getById(testUser.id); + + expect(Number(updatedUser.lastActiveAt)).toBeGreaterThan(Number(expiredDate.toDate())); + expect(callback).toBeCalledTimes(1); + }); + }); + }); +}); diff --git a/services/workflows-service/src/common/middlewares/user-session-audit.middleware.ts b/services/workflows-service/src/common/middlewares/user-session-audit.middleware.ts new file mode 100644 index 0000000000..5e7de201bd --- /dev/null +++ b/services/workflows-service/src/common/middlewares/user-session-audit.middleware.ts @@ -0,0 +1,44 @@ +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; +import { UserService } from '@/user/user.service'; +import { Injectable, NestMiddleware } from '@nestjs/common'; +import { User } from '@prisma/client'; +import { Request, Response } from 'express'; + +@Injectable() +export class UserSessionAuditMiddleware implements NestMiddleware { + private FIVE_MINUTES_IN_MS = 1000 * 60 * 5; + UPDATE_INTERVAL = this.FIVE_MINUTES_IN_MS; + + constructor( + private readonly logger: AppLoggerService, + private readonly userService: UserService, + ) {} + + async use(req: Request, res: Response, next: (error?: any) => void) { + if (req.session && req.user) { + if (this.isUpdateCanBePerformed((req.user as User).lastActiveAt)) { + await this.trackAuthorizedAction(req.user as User); + } + } + + next(); + } + + private isUpdateCanBePerformed( + lastUpdate: Date | null, + updateIntervalInMs: number = this.UPDATE_INTERVAL, + ) { + if (!lastUpdate) return true; + + const now = Date.now(); + const pastDate = Number(new Date(lastUpdate)); + + return now - pastDate >= updateIntervalInMs; + } + + private async trackAuthorizedAction(user: User, activeDate = new Date()) { + this.logger.log(`Updating user presence`, { userId: user.id }); + await this.userService.updateById(user.id, { data: { lastActiveAt: activeDate } }); + this.logger.log(`Updated user presence`, { userId: user.id }); + } +} diff --git a/services/workflows-service/src/common/pipes/zod.pipe.ts b/services/workflows-service/src/common/pipes/zod.pipe.ts index f8ed8dbf34..e9c61c4e5a 100644 --- a/services/workflows-service/src/common/pipes/zod.pipe.ts +++ b/services/workflows-service/src/common/pipes/zod.pipe.ts @@ -1,11 +1,16 @@ import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; import { ZodSchema } from 'zod'; +import { Paramtype } from '@nestjs/common/interfaces/features/paramtype.interface'; @Injectable() export class ZodValidationPipe implements PipeTransform { - constructor(private schema: ZodSchema) {} + constructor(private readonly schema: ZodSchema, private readonly type: Paramtype) {} transform(value: unknown, metadata: ArgumentMetadata) { + if (this.type !== metadata.type) { + return value; + } + const result = this.schema.safeParse(value); if (!result.success) { diff --git a/services/workflows-service/src/common/utils/winston-logger/winston-logger.ts b/services/workflows-service/src/common/utils/winston-logger/winston-logger.ts new file mode 100644 index 0000000000..060613ca77 --- /dev/null +++ b/services/workflows-service/src/common/utils/winston-logger/winston-logger.ts @@ -0,0 +1,40 @@ +import { IAppLogger, LogPayload } from '@/common/abstract-logger/abstract-logger'; +import { createLogger, format, transports, Logger as TWinstonLogger } from 'winston'; + +export class WinstonLogger implements IAppLogger { + private logger: TWinstonLogger; + + constructor() { + this.logger = createLogger({ + format: format.combine(format.json(), format.timestamp()), + transports: [ + new transports.Console({ + format: + process.env.NODE_ENV === 'production' + ? format.simple() + : format.combine(format.colorize(), format.simple()), + }), + ], + }); + } + + log(message: string, payload: LogPayload = {}) { + this.logger.info(message, payload); + } + + info(message: string, payload: LogPayload = {}) { + this.logger.info(message, payload); + } + + error(message: string, payload: LogPayload = {}) { + this.logger.error(message, payload); + } + + warn(message: string, payload: LogPayload = {}) { + this.logger.warn(message, payload); + } + + debug(message: string, payload: LogPayload = {}) { + this.logger.debug(message, payload); + } +} diff --git a/services/workflows-service/src/documents/schemas/index.ts b/services/workflows-service/src/documents/schemas/index.ts deleted file mode 100644 index 87fd8123f7..0000000000 --- a/services/workflows-service/src/documents/schemas/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { getDocumentId } from '@/documents/utils'; -import { ghanaDocuments } from './GH'; -import { Document } from '../types'; -import { countryCodes } from '@/common/countries'; - -const createDocumentIdToDocumentMap = (documents: Document[]) => { - const result = {} as Record<string, Document>; - for (const document of documents) { - const id = getDocumentId(document); - result[id] = document; - } - return result; -}; - -const documentIdsByCountry: Partial<Record<(typeof countryCodes)[number], any>> = { - GH: createDocumentIdToDocumentMap(ghanaDocuments), -}; - -export const getDocumentsByCountry = ( - countryCode: (typeof countryCodes)[number], -): Record<string, Document> => { - const documents = documentIdsByCountry[countryCode]; - if (!documents) return {}; - - return documents; -}; diff --git a/services/workflows-service/src/documents/types.ts b/services/workflows-service/src/documents/types.ts deleted file mode 100644 index f91de20c32..0000000000 --- a/services/workflows-service/src/documents/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { DefaultContextSchema } from '@/workflow/schemas/context'; - -export type Document = Omit<DefaultContextSchema['documents'][number], 'pages' | 'properties'> & { - propertiesSchema: any; -}; diff --git a/services/workflows-service/src/documents/utils.ts b/services/workflows-service/src/documents/utils.ts deleted file mode 100644 index fb54ed3de8..0000000000 --- a/services/workflows-service/src/documents/utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { DefaultContextSchema } from '@/workflow/schemas/context'; -import { Document } from './types'; - -export const getDocumentId = ( - document: Document | DefaultContextSchema['documents'][number], -): string => { - let id = `${document?.category}-${document?.type}-${document?.issuer?.country}`; - - if (document.version) { - id = `${id}-v${document.version}`; - } - - return id.toLowerCase(); -}; diff --git a/services/workflows-service/src/end-user/dtos/end-user-filter-create.ts b/services/workflows-service/src/end-user/dtos/end-user-filter-create.ts deleted file mode 100644 index a19eecf164..0000000000 --- a/services/workflows-service/src/end-user/dtos/end-user-filter-create.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { EndUserQueryDto } from '@/end-user/dtos/end-user-filter-query'; - -export class EndUserFilterCreateDto { - @ApiProperty({ - required: true, - type: String, - }) - name!: string; - - @ApiProperty({ - required: true, - type: () => EndUserQueryDto, - }) - query!: EndUserQueryDto; -} diff --git a/services/workflows-service/src/end-user/dtos/end-user-find-many-args.ts b/services/workflows-service/src/end-user/dtos/end-user-find-many-args.ts index 54a8c7a88f..a483a258fe 100644 --- a/services/workflows-service/src/end-user/dtos/end-user-find-many-args.ts +++ b/services/workflows-service/src/end-user/dtos/end-user-find-many-args.ts @@ -31,11 +31,4 @@ export class EndUserFindManyArgs { }) @Type(() => Number) take?: number; - - @ApiProperty({ - required: false, - type: String, - }) - @Type(() => String) - filterId?: string; } diff --git a/services/workflows-service/src/end-user/dtos/end-user-find-unique-args.ts b/services/workflows-service/src/end-user/dtos/end-user-find-unique-args.ts deleted file mode 100644 index ec8855385f..0000000000 --- a/services/workflows-service/src/end-user/dtos/end-user-find-unique-args.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; - -export class EndUserFindUniqueArgs { - @ApiProperty({ - required: false, - type: String, - }) - @Type(() => String) - filterId?: string; -} diff --git a/services/workflows-service/src/end-user/dtos/end-user-where-input.ts b/services/workflows-service/src/end-user/dtos/end-user-where-input.ts index 5fb429d97b..627d7e712f 100644 --- a/services/workflows-service/src/end-user/dtos/end-user-where-input.ts +++ b/services/workflows-service/src/end-user/dtos/end-user-where-input.ts @@ -48,20 +48,18 @@ export class EndUserWhereInput { stateReason?: StringNullableFilter; @ApiProperty({ - required: false, - type: StringNullableFilter, + required: true, + type: String, }) - @Type(() => StringNullableFilter) - @IsOptional() - firstName?: StringNullableFilter; + @Type(() => String) + firstName!: string; @ApiProperty({ - required: false, - type: StringNullableFilter, + required: true, + type: String, }) - @Type(() => StringNullableFilter) - @IsOptional() - lastName?: StringNullableFilter; + @Type(() => String) + lastName!: string; @ApiProperty({ required: false, diff --git a/services/workflows-service/src/end-user/end-user.controller.external.intg.test.ts b/services/workflows-service/src/end-user/end-user.controller.external.intg.test.ts new file mode 100644 index 0000000000..2315812918 --- /dev/null +++ b/services/workflows-service/src/end-user/end-user.controller.external.intg.test.ts @@ -0,0 +1,84 @@ +import request from 'supertest'; +import { cleanupDatabase, tearDownDatabase } from '@/test/helpers/database-helper'; +import { INestApplication } from '@nestjs/common'; +import { fetchServiceFromModule, initiateNestApp } from '@/test/helpers/nest-app-helper'; +import { EndUserControllerExternal } from '@/end-user/end-user.controller.external'; +import { faker } from '@faker-js/faker'; +import { EndUserService } from '@/end-user/end-user.service'; +import { PrismaModule } from 'nestjs-prisma'; +import { EndUserRepository } from '@/end-user/end-user.repository'; +import { FilterService } from '@/filter/filter.service'; +import { FilterRepository } from '@/filter/filter.repository'; +import { FileRepository } from '@/storage/storage.repository'; +import { FileService } from '@/providers/file/file.service'; +import { StorageService } from '@/storage/storage.service'; +import { WorkflowEventEmitterService } from '@/workflow/workflow-event-emitter.service'; +import { BusinessRepository } from '@/business/business.repository'; +import { WorkflowDefinitionRepository } from '@/workflow/workflow-definition.repository'; +import { WorkflowRuntimeDataRepository } from '@/workflow/workflow-runtime-data.repository'; +import { WorkflowService } from '@/workflow/workflow.service'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { PrismaService } from '@/prisma/prisma.service'; +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; +import { AppLoggerModule } from '@/common/app-logger/app-logger.module'; + +describe('#EndUserControllerExternal', () => { + let app: INestApplication; + let endUserService: EndUserService; + beforeEach(cleanupDatabase); + afterEach(tearDownDatabase); + + beforeAll(async () => { + const servicesProviders = [ + EndUserRepository, + FilterService, + FilterRepository, + FileRepository, + FileService, + StorageService, + WorkflowEventEmitterService, + BusinessRepository, + WorkflowDefinitionRepository, + WorkflowRuntimeDataRepository, + WorkflowService, + EventEmitter2, + PrismaService, + ]; + endUserService = (await fetchServiceFromModule(EndUserService, servicesProviders, [ + PrismaModule, + ])) as unknown as EndUserService; + app = await initiateNestApp( + app, + [ + { + provide: EndUserService, + useValue: endUserService, + }, + ...servicesProviders, + ], + [EndUserControllerExternal], + [PrismaModule], + ); + }); + + describe('POST /end-user', () => { + it('it creates an end-user', async () => { + expect(await endUserService.list({})).toHaveLength(0); + + const response = await request(app.getHttpServer()).post('/external/end-users').send({ + correlationId: faker.datatype.uuid(), + endUserType: faker.random.word(), + approvalState: 'APPROVED', + firstName: 'test', + lastName: 'lastName', + }); + + expect(response.status).toBe(201); + const allEndUsers = await endUserService.list({}); + expect(allEndUsers[0]).toMatchObject({ + firstName: 'test', + lastName: 'lastName', + }); + }); + }); +}); diff --git a/services/workflows-service/src/end-user/end-user.controller.external.ts b/services/workflows-service/src/end-user/end-user.controller.external.ts index ff3a2b133d..b73deaf756 100644 --- a/services/workflows-service/src/end-user/end-user.controller.external.ts +++ b/services/workflows-service/src/end-user/end-user.controller.external.ts @@ -1,6 +1,7 @@ import { ApiNestedQuery } from '@/common/decorators/api-nested-query.decorator'; import { faker } from '@faker-js/faker'; import * as common from '@nestjs/common'; +import { Param } from '@nestjs/common'; import * as swagger from '@nestjs/swagger'; import { plainToClass } from 'class-transformer'; import { Request } from 'express'; @@ -12,12 +13,18 @@ import { EndUserWhereUniqueInput } from './dtos/end-user-where-unique-input'; import { EndUserModel } from './end-user.model'; import { EndUserService } from './end-user.service'; import { isRecordNotFoundError } from '@/prisma/prisma.util'; +import { UseKeyAuthInDevGuard } from '@/common/decorators/use-key-auth-in-dev-guard.decorator'; +import { WorkflowDefinitionModel } from '@/workflow/workflow-definition.model'; +import { WorkflowDefinitionFindManyArgs } from '@/workflow/dtos/workflow-definition-find-many-args'; +import { WorkflowService } from '@/workflow/workflow.service'; +import { makeFullWorkflow } from '@/workflow/utils/make-full-workflow'; @swagger.ApiTags('external/end-users') @common.Controller('external/end-users') export class EndUserControllerExternal { constructor( protected readonly service: EndUserService, + protected readonly workflowService: WorkflowService, @nestAccessControl.InjectRolesBuilder() protected readonly rolesBuilder: nestAccessControl.RolesBuilder, ) {} @@ -25,6 +32,7 @@ export class EndUserControllerExternal { @common.Post() @swagger.ApiCreatedResponse({ type: [EndUserModel] }) @swagger.ApiForbiddenResponse() + @UseKeyAuthInDevGuard() async create( @common.Body() data: EndUserCreateDto, ): Promise<Pick<EndUserModel, 'id' | 'firstName' | 'lastName' | 'avatarUrl'>> { @@ -59,6 +67,7 @@ export class EndUserControllerExternal { @swagger.ApiOkResponse({ type: EndUserModel }) @swagger.ApiNotFoundResponse({ type: errors.NotFoundException }) @swagger.ApiForbiddenResponse() + @UseKeyAuthInDevGuard() async getById(@common.Param() params: EndUserWhereUniqueInput): Promise<EndUserModel | null> { try { const endUser = await this.service.getById(params.id); @@ -72,4 +81,21 @@ export class EndUserControllerExternal { throw err; } } + + // curl -v http://localhost:3000/api/v1/external/end-users/:endUserId/workflows + @common.Get('/:endUserId/workflows') + @swagger.ApiOkResponse({ type: [WorkflowDefinitionModel] }) + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) + @common.HttpCode(200) + @ApiNestedQuery(WorkflowDefinitionFindManyArgs) + @UseKeyAuthInDevGuard() + async listWorkflowRuntimeDataByEndUserId(@Param('endUserId') endUserId: string) { + const workflowRuntimeDataWithDefinition = + await this.workflowService.listFullWorkflowDataByUserId({ + entityId: endUserId, + entity: 'endUser', + }); + + return makeFullWorkflow(workflowRuntimeDataWithDefinition); + } } diff --git a/services/workflows-service/src/end-user/end-user.controller.internal.ts b/services/workflows-service/src/end-user/end-user.controller.internal.ts index c05067362f..bdc964329f 100644 --- a/services/workflows-service/src/end-user/end-user.controller.internal.ts +++ b/services/workflows-service/src/end-user/end-user.controller.internal.ts @@ -1,6 +1,5 @@ import { ApiNestedQuery } from '@/common/decorators/api-nested-query.decorator'; import * as common from '@nestjs/common'; -import { Logger, UsePipes } from '@nestjs/common'; import * as swagger from '@nestjs/swagger'; import * as errors from '../errors'; import { EndUserWhereUniqueInput } from './dtos/end-user-where-unique-input'; @@ -11,25 +10,14 @@ import { Request } from 'express'; import * as nestAccessControl from 'nest-access-control'; import { EndUserService } from './end-user.service'; import { isRecordNotFoundError } from '@/prisma/prisma.util'; -import { EndUserFilterModel } from '@/end-user/dtos/end-user-filter.model'; -import { FilterService } from '@/filter/filter.service'; import { InputJsonValue } from '@/types'; -import { EndUserFilterCreateDto } from '@/end-user/dtos/end-user-filter-create'; -import { ZodValidationPipe } from '@/common/pipes/zod.pipe'; -import { EndUserFilterCreateSchema } from '@/filter/dtos/temp-zod-schemas'; import { JsonValue } from 'type-fest'; -import { EndUserFindUniqueArgs } from '@/end-user/dtos/end-user-find-unique-args'; -import { TEndUserFilter } from '@/end-user/types'; -import { UseKeyAuthGuard } from '@/common/decorators/use-key-auth-guard.decorator'; @swagger.ApiTags('internal/end-users') @common.Controller('internal/end-users') export class EndUserControllerInternal { - private readonly logger = new Logger(EndUserControllerInternal.name); - constructor( protected readonly service: EndUserService, - protected readonly filterService: FilterService, @nestAccessControl.InjectRolesBuilder() protected readonly rolesBuilder: nestAccessControl.RolesBuilder, ) {} @@ -39,13 +27,8 @@ export class EndUserControllerInternal { @swagger.ApiForbiddenResponse() @ApiNestedQuery(EndUserFindManyArgs) async list(@common.Req() request: Request): Promise<EndUserModel[]> { - const { filterId, ...args } = plainToClass(EndUserFindManyArgs, request.query); - let query: JsonValue = {}; - - if (filterId) { - const filter = await this.filterService.getById(filterId); - query = filter.query; - } + const args = plainToClass(EndUserFindManyArgs, request.query); + const query: JsonValue = {}; return this.service.list({ ...args, @@ -57,29 +40,9 @@ export class EndUserControllerInternal { @swagger.ApiOkResponse({ type: EndUserModel }) @swagger.ApiNotFoundResponse({ type: errors.NotFoundException }) @swagger.ApiForbiddenResponse() - @ApiNestedQuery(EndUserFindUniqueArgs) - async getById( - @common.Param() params: EndUserWhereUniqueInput, - @common.Req() request: Request, - ): Promise<EndUserModel | null> { + async getById(@common.Param() params: EndUserWhereUniqueInput): Promise<EndUserModel | null> { try { - const { filterId, ...args } = plainToClass(EndUserFindUniqueArgs, request.query); - let query: TEndUserFilter['query'] = {}; - - if (filterId) { - const filter = await this.filterService.getById(filterId); - // findUnique does not support `where`. - const { where: _where, ...restQuery } = filter?.query as TEndUserFilter['query']; - - query = restQuery; - } - - const endUser = await this.service.getById(params?.id, { - ...args, - ...(query as InputJsonValue), - }); - - return endUser; + return await this.service.getById(params?.id); } catch (err) { if (isRecordNotFoundError(err)) { throw new errors.NotFoundException(`No resource was found for ${JSON.stringify(params)}`); @@ -88,21 +51,4 @@ export class EndUserControllerInternal { throw err; } } - - @common.Post('filters') - @UseKeyAuthGuard() - @swagger.ApiCreatedResponse({ type: EndUserFilterModel }) - @swagger.ApiForbiddenResponse() - @UsePipes(new ZodValidationPipe(EndUserFilterCreateSchema)) - async createFilter(@common.Body() data: EndUserFilterCreateDto): Promise<EndUserFilterModel> { - const filter = await this.filterService.create({ - data: { - ...data, - entity: 'individuals', - query: data?.query as InputJsonValue, - }, - }); - - return filter as EndUserFilterModel; - } } diff --git a/services/workflows-service/src/end-user/end-user.module.ts b/services/workflows-service/src/end-user/end-user.module.ts index 84204c3e6c..8f3ce76245 100644 --- a/services/workflows-service/src/end-user/end-user.module.ts +++ b/services/workflows-service/src/end-user/end-user.module.ts @@ -5,9 +5,30 @@ import { EndUserRepository } from './end-user.repository'; import { EndUserService } from './end-user.service'; import { FilterService } from '@/filter/filter.service'; import { FilterRepository } from '@/filter/filter.repository'; +import { WorkflowDefinitionRepository } from '@/workflow/workflow-definition.repository'; +import { WorkflowRuntimeDataRepository } from '@/workflow/workflow-runtime-data.repository'; +import { WorkflowService } from '@/workflow/workflow.service'; +import { BusinessRepository } from '@/business/business.repository'; +import { StorageService } from '@/storage/storage.service'; +import { FileService } from '@/providers/file/file.service'; +import { FileRepository } from '@/storage/storage.repository'; +import { WorkflowEventEmitterService } from '@/workflow/workflow-event-emitter.service'; @Module({ controllers: [EndUserControllerInternal, EndUserControllerExternal], - providers: [EndUserRepository, EndUserService, FilterService, FilterRepository], + providers: [ + EndUserRepository, + EndUserService, + FilterService, + FilterRepository, + FileRepository, + FileService, + StorageService, + WorkflowEventEmitterService, + BusinessRepository, + WorkflowDefinitionRepository, + WorkflowRuntimeDataRepository, + WorkflowService, + ], }) export class EndUserModule {} diff --git a/services/workflows-service/src/end-user/types.ts b/services/workflows-service/src/end-user/types.ts deleted file mode 100644 index 1f43af57c2..0000000000 --- a/services/workflows-service/src/end-user/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { z } from 'zod'; -import { EndUserFilterSchema } from '@/filter/dtos/temp-zod-schemas'; - -export type TEndUserFilter = z.output<typeof EndUserFilterSchema>; diff --git a/services/workflows-service/src/env.ts b/services/workflows-service/src/env.ts index c2138c82b5..4eaafb05b7 100644 --- a/services/workflows-service/src/env.ts +++ b/services/workflows-service/src/env.ts @@ -8,7 +8,7 @@ export const env = createEnv({ */ clientPrefix: 'PUBLIC_', server: { - NODE_ENV: z.enum(['development', 'production', 'test']), + NODE_ENV: z.enum(['development', 'production', 'test', 'local']), ENV_FILE_NAME: z.string().optional(), BCRYPT_SALT: z.coerce.number().int().nonnegative().or(z.string()), COMPOSE_PROJECT_NAME: z.string(), @@ -22,6 +22,7 @@ export const env = createEnv({ SESSION_SECRET: z.string(), BACKOFFICE_CORS_ORIGIN: z.string().url(), HEADLESS_EXAMPLE_CORS_ORIGIN: z.string().url(), + WORKFLOW_DASHBOARD_CORS_ORIGIN: z.string().url(), AWS_S3_BUCKET_NAME: z.string().optional(), AWS_S3_BUCKET_KEY: z.string().optional(), AWS_S3_BUCKET_SECRET: z.string().optional(), diff --git a/services/workflows-service/src/events/document-changed-webhook-caller.ts b/services/workflows-service/src/events/document-changed-webhook-caller.ts index 5e52b4a7f1..eac849182d 100644 --- a/services/workflows-service/src/events/document-changed-webhook-caller.ts +++ b/services/workflows-service/src/events/document-changed-webhook-caller.ts @@ -12,64 +12,80 @@ import { ConfigService } from '@nestjs/config'; import * as Sentry from '@sentry/node'; import { AxiosInstance, isAxiosError } from 'axios'; import { WorkflowConfig } from '@/workflow/schemas/zod-schemas'; +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; +import { getDocumentId } from '@ballerine/common'; @Injectable() export class DocumentChangedWebhookCaller { - private readonly logger = new Logger(DocumentChangedWebhookCaller.name); - #__axios: AxiosInstance; constructor( private httpService: HttpService, private workflowEventEmitter: WorkflowEventEmitterService, private configService: ConfigService, + private readonly logger: AppLoggerService, ) { this.#__axios = this.httpService.axiosRef; - workflowEventEmitter.on('workflow.context.changed', data => { - void this.handleWorkflowEvent(data); + workflowEventEmitter.on('workflow.context.changed', async data => { + try { + await this.handleWorkflowEvent(data); + } catch (error) { + console.error(error); + alertWebhookFailure(error); + } }); } async handleWorkflowEvent(data: WorkflowEventRawData) { - const oldDocuments = data.runtimeData.context['documents'] || []; - const newDocuments = data.context?.['documents'] || []; - - const documentIdentifier = (doc: any) => { - return `${doc.category as string}$${doc.type as string}$${ - doc.issuer?.country as string - }`.toLowerCase(); - }; + const oldDocuments = data.oldRuntimeData.context['documents'] || []; + const newDocuments = data.updatedRuntimeData.context?.['documents'] || []; + + this.logger.log('handleWorkflowEvent:: ', { + state: data.state, + entityId: data.entityId, + correlationId: data.correlationId, + id: data.updatedRuntimeData.id, + }); const newDocumentsByIdentifier = newDocuments.reduce((accumulator: any, doc: any) => { - const id = documentIdentifier(doc); + const id = getDocumentId(doc, false); + this.logger.log('handleWorkflowEvent::newDocumentsByIdentifier::getDocumentId:: ', { + idDoc: id, + }); accumulator[id] = doc; return accumulator; }, {}); - const anyDocumentStatusChanged = (oldDocuments as Array<any>).some(oldDocument => { - const id = documentIdentifier(oldDocument); + const anyDocumentStatusChanged = oldDocuments.some((oldDocument: any) => { + const id = getDocumentId(oldDocument, false); + this.logger.log('handleWorkflowEvent::anyDocumentStatusChanged::getDocumentId:: ', { + idDoc: id, + }); return ( - (!oldDocument.decision && newDocumentsByIdentifier[id].decision) || + (!oldDocument.decision && newDocumentsByIdentifier[id]?.decision) || (oldDocument.decision && oldDocument.decision.status && id in newDocumentsByIdentifier && - oldDocument.decision.status !== newDocumentsByIdentifier[id].decision.status) + oldDocument.decision.status !== newDocumentsByIdentifier[id].decision?.status) ); }); if (!anyDocumentStatusChanged) { + this.logger.log('handleWorkflowEvent:: Skipped, ', { + anyDocumentStatusChanged, + }); return; } const id = randomUUID(); const environment = this.configService.get<string>('NODE_ENV'); const url = - getDynamicWebhookUrl(data.runtimeData?.config) || + getDynamicWebhookUrl(data.updatedRuntimeData?.config) || this.configService.get<string>('WEBHOOK_URL')!; const authSecret = this.configService.get<string>('WEBHOOK_SECRET'); - data.context.documents.forEach((doc: any) => { + data.updatedRuntimeData.context.documents.forEach((doc: any) => { delete doc.propertiesSchema; }); @@ -83,12 +99,14 @@ export class DocumentChangedWebhookCaller { eventName: 'workflow.context.document.changed', apiVersion: 1, timestamp: new Date().toISOString(), - workflowDefinitionId: data.runtimeData.workflowDefinitionId, - workflowRuntimeId: data.runtimeData.id, + workflowCreatedAt: data.updatedRuntimeData.createdAt, + workflowResolvedAt: data.updatedRuntimeData.resolvedAt, + workflowDefinitionId: data.updatedRuntimeData.workflowDefinitionId, + workflowRuntimeId: data.updatedRuntimeData.id, ballerineEntityId: data.entityId, correlationId: data.correlationId, environment, - data: data.context, + data: data.updatedRuntimeData.context, }, { headers: { @@ -103,6 +121,14 @@ export class DocumentChangedWebhookCaller { data: res.data, }); } catch (error: Error | any) { + this.logger.log('Webhook error data:: ', { + state: data.state, + entityId: data.entityId, + correlationId: data.correlationId, + id: data.updatedRuntimeData.id, + newDocumentsByIdentifier, + oldDocuments, + }); this.logger.error('Failed to send webhook', { id, message: error?.message, error }); alertWebhookFailure(error); } diff --git a/services/workflows-service/src/filter/dtos/filter-create.ts b/services/workflows-service/src/filter/dtos/filter-create.ts index 30b67f606a..90b4f68593 100644 --- a/services/workflows-service/src/filter/dtos/filter-create.ts +++ b/services/workflows-service/src/filter/dtos/filter-create.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsString } from 'class-validator'; +import { IsEnum, IsString } from 'class-validator'; export class FilterCreateDto { @ApiProperty({ @@ -11,8 +11,13 @@ export class FilterCreateDto { @ApiProperty({ required: true, - type: String, + enum: ['individuals', 'businesses'], }) - @IsString() + @IsEnum(['individuals', 'businesses']) entity!: string; + + @ApiProperty({ + required: true, + }) + query!: Record<string, any>; } diff --git a/services/workflows-service/src/filter/dtos/temp-zod-schemas.ts b/services/workflows-service/src/filter/dtos/temp-zod-schemas.ts index 186983dc0a..90fef5837a 100644 --- a/services/workflows-service/src/filter/dtos/temp-zod-schemas.ts +++ b/services/workflows-service/src/filter/dtos/temp-zod-schemas.ts @@ -185,27 +185,31 @@ export const BusinessRelationFilterSchema = z.object({ // @ts-expect-error - It is expected for z.lazy to be any. export const WorkflowRuntimeDataWhereInputSchema = z.lazy(() => - z.object({ - id: zStringFilterStringUnion.optional(), - endUserId: zStringNullableFilterStringNullUnion.optional(), - businessId: zStringNullableFilterStringNullUnion.optional(), - workflowDefinitionId: zStringFilterStringUnion.optional(), - workflowDefinitionVersion: zStringFilterStringUnion.optional(), - context: z.unknown().optional(), - config: z.unknown().optional(), - state: zStringNullableFilterStringNullUnion.optional(), - status: zStringFilterStringUnion.optional(), - createdAt: zDateTimeFilterDateStringUnion.optional(), - updatedAt: zDateTimeFilterDateStringUnion.optional(), - createdBy: zStringFilterStringUnion.optional(), - endUser: z.union([EndUserRelationFilterSchema, EndUserWhereInputSchema, z.null()]).optional(), - business: z - .union([BusinessRelationFilterSchema, BusinessWhereInputSchema, z.null()]) - .optional(), - workflowDefinition: WorkflowDefinitionRelationFilterSchema.or( - WorkflowDefinitionWhereInputSchema, - ).optional(), - }), + z + .object({ + id: zStringFilterStringUnion.optional(), + endUserId: zStringNullableFilterStringNullUnion.optional(), + businessId: zStringNullableFilterStringNullUnion.optional(), + workflowDefinitionId: zStringFilterStringUnion.optional(), + workflowDefinitionVersion: zStringFilterStringUnion.optional(), + context: z.unknown().optional(), + assigneeId: zStringNullableFilterStringNullUnion.optional(), + config: z.unknown().optional(), + state: zStringNullableFilterStringNullUnion.optional(), + status: zStringFilterStringUnion.optional(), + createdAt: zDateTimeFilterDateStringUnion.optional(), + updatedAt: zDateTimeFilterDateStringUnion.optional(), + createdBy: zStringFilterStringUnion.optional(), + endUser: z.union([EndUserRelationFilterSchema, EndUserWhereInputSchema, z.null()]).optional(), + business: z + .union([BusinessRelationFilterSchema, BusinessWhereInputSchema, z.null()]) + .optional(), + workflowDefinition: WorkflowDefinitionRelationFilterSchema.or( + WorkflowDefinitionWhereInputSchema, + ).optional(), + assignee: UserWhereInputSchema.or(UserRelationFilterSchema).optional(), + }) + .strict(), ); // @ts-ignore @@ -289,6 +293,8 @@ export const WorkflowDefinitionSelectSchema = z.object({ id: z.boolean().optional(), reviewMachineId: z.boolean().optional(), name: z.boolean().optional(), + config: z.boolean().optional(), + contextSchema: z.boolean().optional(), version: z.boolean().optional(), definitionType: z.boolean().optional(), definition: z.boolean().optional(), @@ -313,11 +319,51 @@ export const WorkflowDefinitionSelectSchema = z.object({ .optional(), }); +export const UserSelectSchema = z.object({ + id: z.boolean().optional(), + firstName: z.boolean().optional(), + lastName: z.boolean().optional(), + email: z.boolean().optional(), + phone: z.boolean().optional(), + roles: z.boolean().optional(), + createdAt: z.boolean().optional(), + updatedAt: z.boolean().optional(), + workflowRuntimeData: z + .union([ + z.boolean(), + z.object({ + select: z + .lazy(() => WorkflowRuntimeDataSelectSchema) + .nullable() + .optional(), + }), + ]) + .optional(), +}); + +export const UserWhereInputSchema = z.object({ + id: zStringFilterStringUnion.optional(), + firstName: zStringFilterStringUnion.optional(), + lastName: zStringFilterStringUnion.optional(), + email: zStringFilterStringUnion.optional(), + phone: zStringFilterStringUnion.optional(), + roles: zStringFilterStringUnion.optional(), + createdAt: zDateTimeFilterDateStringUnion.optional(), + updatedAt: zDateTimeFilterDateStringUnion.optional(), + workflowRuntimeData: WorkflowRuntimeDataListRelationFilterSchema.optional(), +}); + +export const UserRelationFilterSchema = z.object({ + is: UserWhereInputSchema.optional(), + isNot: UserWhereInputSchema.optional(), +}); + // @ts-ignore export const WorkflowRuntimeDataSelectSchema = z.object({ id: z.boolean().optional(), endUserId: z.boolean().optional(), businessId: z.boolean().optional(), + assigneeId: z.boolean().optional(), workflowDefinitionId: z.boolean().optional(), workflowDefinitionVersion: z.boolean().optional(), context: z.boolean().optional(), @@ -360,6 +406,17 @@ export const WorkflowRuntimeDataSelectSchema = z.object({ }), ]) .optional(), + assignee: z + .union([ + z.boolean(), + z.object({ + select: z + .lazy(() => UserSelectSchema.strict()) + .nullable() + .optional(), + }), + ]) + .optional(), }); // @ts-ignore @@ -395,27 +452,6 @@ export const EndUserSelectSchema = z.object({ endUsersOnBusinesses: z.boolean().optional(), }); -export const EndUserFilterSchema = FilterSchema.extend({ - query: z - .object({ - select: EndUserSelectSchema.strict() - .refine( - (v: Record<PropertyKey, unknown>) => Object.keys(v).length > 0, - 'At least one `select` field must be provided', - ) - .optional(), - where: EndUserWhereInputSchema.strict().optional(), - }) - .refine(v => v.select || v.where, 'At least `query.select` or `query.where` must be provided'), -}); - -export const EndUserFilterCreateSchema = EndUserFilterSchema.omit({ - id: true, - entity: true, - createdAt: true, - updatedAt: true, -}); - /* Businesses */ // @ts-ignore export const BusinessSelectSchema = z.object({ @@ -476,20 +512,20 @@ export const BusinessWhereInputSchema = z.object({ updatedAt: zDateTimeFilterDateStringUnion.optional(), }); -export const BusinessFilterSchema = FilterSchema.extend({ - query: z - .object({ - select: BusinessSelectSchema.strict() - .refine(v => Object.keys(v).length > 0, 'At least one `select` field must be provided') - .optional(), - where: BusinessWhereInputSchema.strict().optional(), - }) - .refine(v => v.select || v.where, 'At least `query.select` or `query.where` must be provided'), -}); +const WorkflowRuntimeDataFilterQuerySchema = z + .object({ + select: WorkflowRuntimeDataSelectSchema.strict() + .refine( + (v: Record<PropertyKey, unknown>) => Object.keys(v).length > 0, + 'At least one `select` field must be provided', + ) + .optional(), + where: WorkflowRuntimeDataWhereInputSchema.optional(), + }) + .refine(v => v.select || v.where, 'At least `query.select` or `query.where` must be provided'); -export const BusinessFilterCreateSchema = BusinessFilterSchema.omit({ - id: true, - entity: true, - createdAt: true, - updatedAt: true, +export const FilterCreateSchema = z.object({ + name: z.string(), + entity: z.enum(['individuals', 'businesses']), + query: WorkflowRuntimeDataFilterQuerySchema, }); diff --git a/services/workflows-service/src/filter/filter.controller.external.ts b/services/workflows-service/src/filter/filter.controller.external.ts index c4b7b2d2c0..8f724a630d 100644 --- a/services/workflows-service/src/filter/filter.controller.external.ts +++ b/services/workflows-service/src/filter/filter.controller.external.ts @@ -10,6 +10,12 @@ import { FilterFindManyArgs } from '@/filter/dtos/filter-find-many-args'; import { FilterModel } from '@/filter/filter.model'; import { FilterWhereUniqueInput } from '@/filter/dtos/filter-where-unique-input'; import { FilterService } from '@/filter/filter.service'; +import { UsePipes } from '@nestjs/common'; +import { ZodValidationPipe } from '@/common/pipes/zod.pipe'; +import { FilterCreateDto } from '@/filter/dtos/filter-create'; +import { FilterCreateSchema } from '@/filter/dtos/temp-zod-schemas'; +import { InputJsonValue } from '@/types'; +import { UseKeyAuthGuard } from '@/common/decorators/use-key-auth-guard.decorator'; @swagger.ApiTags('external/filters') @common.Controller('external/filters') @@ -46,4 +52,18 @@ export class FilterControllerExternal { throw err; } } + + @common.Post() + @UseKeyAuthGuard() + @swagger.ApiCreatedResponse({ type: FilterModel }) + @swagger.ApiForbiddenResponse() + @UsePipes(new ZodValidationPipe(FilterCreateSchema, 'body')) + async createFilter(@common.Body() data: FilterCreateDto) { + return await this.service.create({ + data: { + ...data, + query: data?.query as InputJsonValue, + }, + }); + } } diff --git a/services/workflows-service/src/filter/filter.model.ts b/services/workflows-service/src/filter/filter.model.ts index 88e9adac54..0b5cef573d 100644 --- a/services/workflows-service/src/filter/filter.model.ts +++ b/services/workflows-service/src/filter/filter.model.ts @@ -1,31 +1,29 @@ -import { StringFilter } from '@/common/query-filters/string-filter'; import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsObject, IsString } from 'class-validator'; +import { IsEnum, IsObject, IsString } from 'class-validator'; import { JsonValue } from 'type-fest'; export class FilterModel { @ApiProperty({ required: true, - type: StringFilter, + type: String, }) - @Type(() => StringFilter) + @Type(() => String) id!: string; @ApiProperty({ required: true, - type: StringFilter, + type: String, }) - @Type(() => StringFilter) + @Type(() => String) @IsString() name!: string; @ApiProperty({ required: true, - type: StringFilter, + enum: ['individuals', 'businesses'], }) - @Type(() => StringFilter) - @IsString() + @IsEnum(['individuals', 'businesses']) entity!: string; @ApiProperty({ diff --git a/services/workflows-service/src/health/health.service.test.ts b/services/workflows-service/src/health/health.service.unit.test.ts similarity index 100% rename from services/workflows-service/src/health/health.service.test.ts rename to services/workflows-service/src/health/health.service.unit.test.ts diff --git a/services/workflows-service/src/main.ts b/services/workflows-service/src/main.ts index 7cd079ccc7..3027f9cfd4 100644 --- a/services/workflows-service/src/main.ts +++ b/services/workflows-service/src/main.ts @@ -1,8 +1,7 @@ import passport from 'passport'; import cookieSession from 'cookie-session'; -import { HttpAdapterHost, NestFactory } from '@nestjs/core'; +import { NestFactory } from '@nestjs/core'; import { SwaggerModule } from '@nestjs/swagger'; -import { HttpExceptionFilter } from '@/common/filters/HttpExceptions.filter'; import { AppModule } from './app.module'; import { swaggerDocumentOptions, swaggerPath, swaggerSetupOptions } from './swagger'; import { ValidationPipe, VersioningType } from '@nestjs/common'; @@ -11,8 +10,8 @@ import { PathItemObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.in // @ts-ignore - there is an issue with helemet types import helmet from 'helmet'; import { env } from '@/env'; -import { AllExceptionsFilter } from '@/common/filters/AllExceptions.filter'; import { NextFunction, Request, Response } from 'express'; +import { ClsMiddleware } from 'nestjs-cls'; // This line is used to improve Sentry's stack traces // https://docs.sentry.io/platforms/node/typescript/#changing-events-frames @@ -20,10 +19,11 @@ global.__rootdir__ = __dirname || process.cwd(); const corsOrigins = env.NODE_ENV === 'production' - ? [env.BACKOFFICE_CORS_ORIGIN, /\.ballerine\.app$/] + ? [env.BACKOFFICE_CORS_ORIGIN, env.WORKFLOW_DASHBOARD_CORS_ORIGIN, /\.ballerine\.app$/] : [ env.BACKOFFICE_CORS_ORIGIN, env.HEADLESS_EXAMPLE_CORS_ORIGIN, + env.WORKFLOW_DASHBOARD_CORS_ORIGIN, /\.ballerine\.dev$/, /\.ballerine\.app$/, ]; @@ -37,18 +37,20 @@ async function main() { }, }); + app.use(new ClsMiddleware({}).use); + app.use(helmet()); app.use( cookieSession({ name: 'session', keys: [env.SESSION_SECRET], httpOnly: true, - domain: env.NODE_ENV === 'production' ? '.ballerine.app' : undefined, - secure: env.NODE_ENV === 'production', + secure: false, sameSite: 'strict', - maxAge: 1000 * 60 * 60 * 1, // 1 hour(s) + maxAge: 1000 * 60 * 60 * 1, // 1 hour(s), }), ); + app.use((req: Request, res: Response, next: NextFunction) => { if (!req.session) return next(); @@ -98,10 +100,6 @@ async function main() { SwaggerModule.setup(swaggerPath, app, document, swaggerSetupOptions); - const { httpAdapter } = app.get(HttpAdapterHost); - app.useGlobalFilters(new AllExceptionsFilter(httpAdapter)); - app.useGlobalFilters(new HttpExceptionFilter(httpAdapter)); - app.enableShutdownHooks(); void app.listen(env.PORT); diff --git a/services/workflows-service/src/metrics/common/dto/date-query-params.dto.ts b/services/workflows-service/src/metrics/common/dto/date-query-params.dto.ts new file mode 100644 index 0000000000..6be577baa3 --- /dev/null +++ b/services/workflows-service/src/metrics/common/dto/date-query-params.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsDate } from 'class-validator'; + +export class DateQueryParamsDto { + @ApiProperty({ type: Number, description: 'UNIX timestamp' }) + @Transform(({ value }) => new Date(value ? +value : 0)) + @IsDate() + fromDate!: Date; +} diff --git a/services/workflows-service/src/metrics/common/types/base-query.params.ts b/services/workflows-service/src/metrics/common/types/base-query.params.ts new file mode 100644 index 0000000000..9affd04009 --- /dev/null +++ b/services/workflows-service/src/metrics/common/types/base-query.params.ts @@ -0,0 +1,3 @@ +export interface BaseParams { + fromDate?: Date; +} diff --git a/services/workflows-service/src/metrics/dto/get-user-cases-resolved-daily.dto.ts b/services/workflows-service/src/metrics/dto/get-user-cases-resolved-daily.dto.ts new file mode 100644 index 0000000000..f0c25d8d9b --- /dev/null +++ b/services/workflows-service/src/metrics/dto/get-user-cases-resolved-daily.dto.ts @@ -0,0 +1,7 @@ +import { DateQueryParamsDto } from '@/metrics/common/dto/date-query-params.dto'; +import { ApiProperty } from '@nestjs/swagger'; + +export class GetUserCasesResolvedDailyDto extends DateQueryParamsDto { + @ApiProperty() + userId!: string; +} diff --git a/services/workflows-service/src/metrics/dto/get-user-workflow-processing-statistic.dto.ts b/services/workflows-service/src/metrics/dto/get-user-workflow-processing-statistic.dto.ts new file mode 100644 index 0000000000..216730986e --- /dev/null +++ b/services/workflows-service/src/metrics/dto/get-user-workflow-processing-statistic.dto.ts @@ -0,0 +1,3 @@ +import { DateQueryParamsDto } from '@/metrics/common/dto/date-query-params.dto'; + +export class GetUserWorkflowProcessingStatisticDto extends DateQueryParamsDto {} diff --git a/services/workflows-service/src/metrics/dto/get-users-assigned-cases-statistic.dto.ts b/services/workflows-service/src/metrics/dto/get-users-assigned-cases-statistic.dto.ts new file mode 100644 index 0000000000..1e66af5b83 --- /dev/null +++ b/services/workflows-service/src/metrics/dto/get-users-assigned-cases-statistic.dto.ts @@ -0,0 +1,7 @@ +import { DateQueryParamsDto } from '@/metrics/common/dto/date-query-params.dto'; +import { IsOptional } from 'class-validator'; + +export class GetUsersAssignedCasesStatisticDto extends DateQueryParamsDto { + @IsOptional() + fromDate!: Date; +} diff --git a/services/workflows-service/src/metrics/dto/get-users-resolved-cases-statistic.dto.ts b/services/workflows-service/src/metrics/dto/get-users-resolved-cases-statistic.dto.ts new file mode 100644 index 0000000000..a1dbb29e26 --- /dev/null +++ b/services/workflows-service/src/metrics/dto/get-users-resolved-cases-statistic.dto.ts @@ -0,0 +1,3 @@ +import { DateQueryParamsDto } from '@/metrics/common/dto/date-query-params.dto'; + +export class GetUsersResolvedCasesStatisticDto extends DateQueryParamsDto {} diff --git a/services/workflows-service/src/metrics/dto/get-workflow-runtimes-status-count.dto.ts b/services/workflows-service/src/metrics/dto/get-workflow-runtimes-status-count.dto.ts new file mode 100644 index 0000000000..b975036ad2 --- /dev/null +++ b/services/workflows-service/src/metrics/dto/get-workflow-runtimes-status-count.dto.ts @@ -0,0 +1,7 @@ +import { DateQueryParamsDto } from '@/metrics/common/dto/date-query-params.dto'; +import { IsOptional } from 'class-validator'; + +export class GetWorkflowRuntimesStatusCountDto extends DateQueryParamsDto { + @IsOptional() + fromDate!: Date; +} diff --git a/services/workflows-service/src/metrics/metrics.controller.ts b/services/workflows-service/src/metrics/metrics.controller.ts new file mode 100644 index 0000000000..1c218a662f --- /dev/null +++ b/services/workflows-service/src/metrics/metrics.controller.ts @@ -0,0 +1,86 @@ +import { NotFoundException } from '@/errors'; +import { GetUserCasesResolvedDailyDto } from '@/metrics/dto/get-user-cases-resolved-daily.dto'; +import { GetUserWorkflowProcessingStatisticDto } from '@/metrics/dto/get-user-workflow-processing-statistic.dto'; +import { GetUsersAssignedCasesStatisticDto } from '@/metrics/dto/get-users-assigned-cases-statistic.dto'; +import { GetUsersResolvedCasesStatisticDto } from '@/metrics/dto/get-users-resolved-cases-statistic.dto'; +import { GetWorkflowRuntimesStatusCountDto } from '@/metrics/dto/get-workflow-runtimes-status-count.dto'; +import { MetricsUserModel } from '@/metrics/repository/models/metrics-user.model'; +import { UserAssignedCasesStatisticModel } from '@/metrics/repository/models/user-assigned-cases-statistic.model'; +import { CasesResolvedInDay } from '@/metrics/repository/models/cases-resolved-daily.model'; +import { UserResolvedCasesStatisticModel } from '@/metrics/repository/models/user-resolved-cases-statistic.model'; +import { WorkflowRuntimeStatisticModel } from '@/metrics/repository/models/workflow-runtime-statistic.model'; +import { WorkflowRuntimeStatusCaseCountModel } from '@/metrics/repository/models/workflow-runtime-status-case-count.model'; +import { MetricsService } from '@/metrics/service/metrics.service'; +import { UserWorkflowProcessingStatisticModel } from '@/metrics/service/models/user-workflow-processing-statistic.model'; +import * as common from '@nestjs/common'; +import { Controller } from '@nestjs/common'; +import { ApiNoContentResponse, ApiNotFoundResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +@ApiTags('/metrics') +@Controller('/metrics') +export class MetricsController { + constructor(private readonly metricsService: MetricsService) {} + + @ApiOkResponse({ type: [WorkflowRuntimeStatisticModel] }) + @common.HttpCode(200) + @common.Get('/workflows/runtimes-statistic') + async getRuntimesStatistic(): Promise<WorkflowRuntimeStatisticModel[]> { + return await this.metricsService.listRuntimesStatistic(); + } + + @ApiOkResponse({ type: WorkflowRuntimeStatusCaseCountModel }) + @common.HttpCode(200) + @common.Get('/workflows/runtimes-status-count') + async getRuntimesStatusCount( + @common.Query() query: GetWorkflowRuntimesStatusCountDto, + ): Promise<WorkflowRuntimeStatusCaseCountModel> { + return await this.metricsService.getRuntimesStatusCaseCount(query); + } + @ApiOkResponse({ type: [UserAssignedCasesStatisticModel] }) + @ApiNotFoundResponse({ type: NotFoundException }) + @common.HttpCode(200) + @common.Get('/users/users-assigned-cases-statistic') + async getUsersAssignedCasesStatistic(@common.Query() query: GetUsersAssignedCasesStatisticDto) { + return await this.metricsService.listUsersAssignedCasesStatistic(query); + } + + @ApiOkResponse({ type: [UserResolvedCasesStatisticModel] }) + @ApiNotFoundResponse({ type: NotFoundException }) + @common.HttpCode(200) + @common.Get('/users/users-resolved-cases-statistic') + async getUsersResolvedCasesStatistic(@common.Query() query: GetUsersResolvedCasesStatisticDto) { + return await this.metricsService.listUsersResolvedCasesStatistic(query); + } + + @ApiOkResponse({ type: UserWorkflowProcessingStatisticModel }) + @ApiNotFoundResponse({ type: NotFoundException }) + @ApiNoContentResponse({ type: Object }) + @common.HttpCode(200) + @common.Get('/users/workflow-processing-statistic') + async getUserWorkflowProcessingStatistic( + @common.Query() query: GetUserWorkflowProcessingStatisticDto, + ): Promise<UserWorkflowProcessingStatisticModel> { + return await this.metricsService.getUserWorkflowProcessingStatistic({ + fromDate: query.fromDate, + }); + } + + @ApiOkResponse({ type: [CasesResolvedInDay] }) + @common.HttpCode(200) + @common.Get('/users/cases-resolved-daily') + async getUserCasesResolvedDaily( + @common.Query() query: GetUserCasesResolvedDailyDto, + ): Promise<CasesResolvedInDay[]> { + return await this.metricsService.listUserCasesResolvedDaily({ + fromDate: query.fromDate, + userId: query.userId, + }); + } + + @ApiOkResponse({ type: [MetricsUserModel] }) + @common.HttpCode(200) + @common.Get('/users') + async getActiveUsers(): Promise<MetricsUserModel[]> { + return await this.metricsService.listActiveUsers(); + } +} diff --git a/services/workflows-service/src/metrics/metrics.module.ts b/services/workflows-service/src/metrics/metrics.module.ts new file mode 100644 index 0000000000..d27925862a --- /dev/null +++ b/services/workflows-service/src/metrics/metrics.module.ts @@ -0,0 +1,11 @@ +import { MetricsRepository } from '@/metrics/repository/metrics.repository'; +import { MetricsService } from '@/metrics/service/metrics.service'; +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'nestjs-prisma'; + +@Module({ + imports: [PrismaModule], + providers: [MetricsRepository, MetricsService], + exports: [MetricsService], +}) +export class MetricsModule {} diff --git a/services/workflows-service/src/metrics/repository/metrics.repository.ts b/services/workflows-service/src/metrics/repository/metrics.repository.ts new file mode 100644 index 0000000000..02911bd2dc --- /dev/null +++ b/services/workflows-service/src/metrics/repository/metrics.repository.ts @@ -0,0 +1,181 @@ +import { WorkflowRuntimeStatisticModel } from '@/metrics/repository/models/workflow-runtime-statistic.model'; +import { IAggregateWorkflowRuntimeStatistic } from '@/metrics/repository/types/aggregate-workflow-runtime-statistic'; +import { PrismaService } from '@/prisma/prisma.service'; +import { Injectable } from '@nestjs/common'; +import { plainToClass } from 'class-transformer'; +import { IAggregateUsersWithCasesCount } from '@/metrics/repository/types/aggregate-users-with-cases-count'; +import { WorkflowRuntimeStatusCaseCountModel } from '@/metrics/repository/models/workflow-runtime-status-case-count.model'; +import { aggregateWorkflowRuntimeStatisticQuery } from './sql/aggregate-workflow-runtime-statistic.sql'; +import { aggregateWorkflowRuntimeStatusCaseCountQuery } from './sql/aggregate-workflow-runtime-status-case-count.sql'; +import { IAggregateWorkflowRuntimeStatusCaseCount } from '@/metrics/repository/types/aggregate-workflow-runtime-status-case-count'; +import { GetRuntimeStatusCaseCountParams } from '@/metrics/repository/types/get-runtime-status-case-count.params'; +import { GetUserApprovalRateParams } from '@/metrics/repository/types/get-user-approval-rate.params'; +import { ApprovalRateModel } from '@/metrics/repository/models/approval-rate.model'; +import { IAggregateApprovalRate } from '@/metrics/repository/types/aggregate-approval-rate'; +import { GetUserAverageResolutionTimeParams } from '@/metrics/repository/types/get-user-average-resolution-time.params'; +import { AverageResolutionTimeModel } from '@/metrics/repository/models/average-resolution-time.model'; +import { aggregateAverageResolutionTimeQuery } from './sql/aggregate-average-resolution-time.sql'; +import { IAggregateAverageResolutionTime } from '@/metrics/repository/types/aggregate-average-resolution-time'; +import { GetUserAverageAssignmentTimeParams } from '@/metrics/repository/types/get-user-average-assignment-time.params'; +import { IAggregateAverageAssignmentTime } from '@/metrics/repository/types/aggregate-average-assignment-time'; +import { GetUserAverageReviewTimeParams } from '@/metrics/repository/types/get-user-average-review-time.params'; +import { AverageReviewTimeModel } from '@/metrics/repository/models/average-review-time.model'; +import { IAggregateAverageReviewTime } from '@/metrics/repository/types/aggregate-average-review-time'; +import { ListUserCasesResolvedDailyParams } from '@/metrics/repository/types/list-user-cases-resolved-daily.params'; +import { CasesResolvedInDay } from '@/metrics/repository/models/cases-resolved-daily.model'; +import { IAggregateCasesResolvedDaily } from '@/metrics/repository/types/aggregate-cases-resolved-daily'; +import { aggregateDailyCasesResolvedQuery } from '@/metrics/repository/sql/aggregate-daily-cases-resolved.sql'; +import { MetricsUserModel } from '@/metrics/repository/models/metrics-user.model'; +import { ISelectActiveUser } from '@/metrics/repository/types/select-active-user'; +import { selectActiveUsersQuery } from '@/metrics/repository/sql/select-active-users.sql'; +import { FindUsersAssignedCasesStatisticParams } from '@/metrics/repository/types/find-users-assigned-cases-statistic.params'; +import { UserAssignedCasesStatisticModel } from '@/metrics/repository/models/user-assigned-cases-statistic.model'; +import { aggregateUsersAssignedCasesStatisticQuery } from '@/metrics/repository/sql/aggregate-users-assigned-cases-statistic.sql'; +import { FindUsersResolvedCasesStatisticParams } from '@/metrics/repository/types/find-users-resolved-cases-statistic.params'; +import { UserResolvedCasesStatisticModel } from '@/metrics/repository/models/user-resolved-cases-statistic.model'; +import { IAggregateUserResolvedCasesStatistic } from '@/metrics/repository/types/aggregate-user-resolved-cases-statistic'; +import { aggregateUsersResolvedCasesStatisticQuery } from '@/metrics/repository/sql/aggregate-users-resolved-cases-statistic.sql'; +import { aggregateApprovalRateQuery } from '@/metrics/repository/sql/aggregate-approval-rate.sql'; +import { aggregateAverageAssignmentTimeQuery } from '@/metrics/repository/sql/aggregate-average-assignment-time.sql'; +import { AverageAssignmentTimeModel } from '@/metrics/repository/models/average-assignment-time.model'; +import { aggregateAverageReviewTimeQuery } from '@/metrics/repository/sql/aggregate-average-review-time.sql'; + +@Injectable() +export class MetricsRepository { + constructor(private readonly prismaService: PrismaService) {} + + async getRuntimeStatusCaseCount( + params: GetRuntimeStatusCaseCountParams, + ): Promise<WorkflowRuntimeStatusCaseCountModel> { + const results = await this.prismaService.$queryRawUnsafe< + IAggregateWorkflowRuntimeStatusCaseCount[] + >(aggregateWorkflowRuntimeStatusCaseCountQuery, params.fromDate); + + return plainToClass( + WorkflowRuntimeStatusCaseCountModel, + results.length ? results.at(-1) : { active: 0, failed: 0, completed: 0 }, + ); + } + + async findRuntimeStatistic(): Promise<WorkflowRuntimeStatisticModel[]> { + const results = await this.prismaService.$queryRawUnsafe<IAggregateWorkflowRuntimeStatistic[]>( + aggregateWorkflowRuntimeStatisticQuery, + ); + + return results.map(result => + plainToClass(WorkflowRuntimeStatisticModel, { + id: result.workflowDefinitionId, + name: result.workflowDefinitionName, + active: result.active || 0, + completed: result.completed || 0, + failed: result.failed, + }), + ); + } + + async findUsersAssignedCasesStatistic( + params: FindUsersAssignedCasesStatisticParams, + ): Promise<UserAssignedCasesStatisticModel[]> { + const results = await this.prismaService.$queryRawUnsafe<IAggregateUsersWithCasesCount[]>( + aggregateUsersAssignedCasesStatisticQuery, + params.fromDate, + ); + + return results.map(result => plainToClass(UserAssignedCasesStatisticModel, result)); + } + + async findUsersResolvedCasesStatistic( + params: FindUsersResolvedCasesStatisticParams, + ): Promise<UserResolvedCasesStatisticModel[]> { + const results = await this.prismaService.$queryRawUnsafe< + IAggregateUserResolvedCasesStatistic[] + >(aggregateUsersResolvedCasesStatisticQuery, params.fromDate); + + return results.map(result => + plainToClass(UserResolvedCasesStatisticModel, { + id: result.id, + firstName: result.firstName, + lastName: result.lastName, + casesCount: result.casesCount, + email: result.email, + }), + ); + } + + async getUserApprovalRate(params: GetUserApprovalRateParams): Promise<ApprovalRateModel | null> { + const results = await this.prismaService.$queryRawUnsafe<IAggregateApprovalRate[]>( + aggregateApprovalRateQuery, + params.fromDate, + ); + + return results.length + ? plainToClass(ApprovalRateModel, { approvalRate: results.at(-1)?.approvalRate }) + : null; + } + + async getUserAverageResolutionTime( + params: GetUserAverageResolutionTimeParams, + ): Promise<AverageResolutionTimeModel | null> { + const results = await this.prismaService.$queryRawUnsafe<IAggregateAverageResolutionTime[]>( + aggregateAverageResolutionTimeQuery, + params.fromDate, + ); + + return results.length + ? plainToClass(AverageResolutionTimeModel, { time: results.at(-1)?.average_time }) + : null; + } + + async getUserAverageAssignmentTime( + params: GetUserAverageAssignmentTimeParams, + ): Promise<AverageAssignmentTimeModel | null> { + const results = await this.prismaService.$queryRawUnsafe<IAggregateAverageAssignmentTime[]>( + aggregateAverageAssignmentTimeQuery, + params.fromDate, + ); + + return results.length + ? plainToClass(AverageAssignmentTimeModel, { time: results.at(-1)?.average_time }) + : null; + } + + async getUserAverageReviewTime( + params: GetUserAverageReviewTimeParams, + ): Promise<AverageReviewTimeModel | null> { + const results = await this.prismaService.$queryRawUnsafe<IAggregateAverageReviewTime[]>( + aggregateAverageReviewTimeQuery, + params.fromDate, + ); + + return results.length + ? plainToClass(AverageReviewTimeModel, { time: results.at(-1)?.average_time }) + : null; + } + + async listCasesResolvedDaily( + params: ListUserCasesResolvedDailyParams, + ): Promise<CasesResolvedInDay[]> { + const results = await this.prismaService.$queryRawUnsafe<IAggregateCasesResolvedDaily[]>( + aggregateDailyCasesResolvedQuery, + params.fromDate, + params.userId, + ); + + if (!results.length) return []; + + return results.map(result => + plainToClass(CasesResolvedInDay, { + date: result.date, + count: result.cases, + }), + ); + } + + async listUsers(): Promise<MetricsUserModel[]> { + const results = await this.prismaService.$queryRawUnsafe<ISelectActiveUser[]>( + selectActiveUsersQuery, + ); + + return results.map(result => plainToClass(MetricsUserModel, result)); + } +} diff --git a/services/workflows-service/src/metrics/repository/models/approval-rate.model.ts b/services/workflows-service/src/metrics/repository/models/approval-rate.model.ts new file mode 100644 index 0000000000..8ba822dbb6 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/models/approval-rate.model.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform, Type } from 'class-transformer'; + +export class ApprovalRateModel { + @ApiProperty() + @Transform(({ value }) => (!value ? '0' : value)) + approvalRate!: string; +} diff --git a/services/workflows-service/src/metrics/repository/models/average-assignment-time.model.ts b/services/workflows-service/src/metrics/repository/models/average-assignment-time.model.ts new file mode 100644 index 0000000000..18753d8b77 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/models/average-assignment-time.model.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class AverageAssignmentTimeModel { + @ApiProperty({ description: 'Average assignment time in milliseconds' }) + @Transform(({ value }) => (!value ? 0 : value.split('.')[0])) + time!: string; +} diff --git a/services/workflows-service/src/metrics/repository/models/average-resolution-time.model.ts b/services/workflows-service/src/metrics/repository/models/average-resolution-time.model.ts new file mode 100644 index 0000000000..873a6ceb49 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/models/average-resolution-time.model.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class AverageResolutionTimeModel { + @ApiProperty({ description: 'Average resolution time in milliseconds.' }) + @Transform(({ value }) => (!value ? 0 : value.split('.')[0])) + time!: string; +} diff --git a/services/workflows-service/src/metrics/repository/models/average-review-time.model.ts b/services/workflows-service/src/metrics/repository/models/average-review-time.model.ts new file mode 100644 index 0000000000..8733327abf --- /dev/null +++ b/services/workflows-service/src/metrics/repository/models/average-review-time.model.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class AverageReviewTimeModel { + @ApiProperty({ description: 'Average review time in milliseconds' }) + @Transform(({ value }) => (!value ? 0 : value.split('.')[0])) + time!: string; +} diff --git a/services/workflows-service/src/metrics/repository/models/cases-resolved-daily.model.ts b/services/workflows-service/src/metrics/repository/models/cases-resolved-daily.model.ts new file mode 100644 index 0000000000..3e98ee3618 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/models/cases-resolved-daily.model.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class CasesResolvedInDay { + @ApiProperty() + @Transform(({ value }) => (value === null ? 0 : value)) + count!: number; + + @ApiProperty() + date!: string; +} diff --git a/services/workflows-service/src/metrics/repository/models/metrics-user.model.ts b/services/workflows-service/src/metrics/repository/models/metrics-user.model.ts new file mode 100644 index 0000000000..d088317425 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/models/metrics-user.model.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class MetricsUserModel { + @ApiProperty() + id!: string; + + @ApiProperty() + firstName!: string; + + @ApiProperty() + lastName!: string; + + @ApiProperty() + lastActiveAt!: string; +} diff --git a/services/workflows-service/src/metrics/repository/models/user-assigned-cases-statistic.model.ts b/services/workflows-service/src/metrics/repository/models/user-assigned-cases-statistic.model.ts new file mode 100644 index 0000000000..b8eb3f58af --- /dev/null +++ b/services/workflows-service/src/metrics/repository/models/user-assigned-cases-statistic.model.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class UserAssignedCasesStatisticModel { + @ApiProperty() + id!: string; + + @ApiProperty() + firstName!: string; + + @ApiProperty() + lastName!: string; + + @ApiProperty() + email!: string; + + @ApiProperty() + @Transform(({ value }) => (value === null ? 0 : value)) + casesCount!: number; +} diff --git a/services/workflows-service/src/metrics/repository/models/user-resolved-cases-statistic.model.ts b/services/workflows-service/src/metrics/repository/models/user-resolved-cases-statistic.model.ts new file mode 100644 index 0000000000..631d84e2a4 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/models/user-resolved-cases-statistic.model.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class UserResolvedCasesStatisticModel { + @ApiProperty() + id!: string; + + @ApiProperty() + firstName!: string; + + @ApiProperty() + lastName!: string; + + @ApiProperty() + @Transform(({ value }) => (value === null ? 0 : value)) + casesCount!: number; + + @ApiProperty() + email!: string; +} diff --git a/services/workflows-service/src/metrics/repository/models/workflow-runtime-statistic.model.ts b/services/workflows-service/src/metrics/repository/models/workflow-runtime-statistic.model.ts new file mode 100644 index 0000000000..f7503cfdc4 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/models/workflow-runtime-statistic.model.ts @@ -0,0 +1,24 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class WorkflowRuntimeStatusStatistic {} + +export class WorkflowRuntimeStatisticModel { + @ApiProperty({ description: 'Workflow Runtime Definition Id' }) + id!: string; + + @ApiProperty({ description: 'Workflow Runtime Definition name' }) + name!: string; + + @ApiProperty() + @Transform(({ value }) => (value === null ? 0 : value)) + active!: number; + + @ApiProperty() + @Transform(({ value }) => (value === null ? 0 : value)) + failed!: number; + + @ApiProperty() + @Transform(({ value }) => (value === null ? 0 : value)) + completed!: number; +} diff --git a/services/workflows-service/src/metrics/repository/models/workflow-runtime-status-case-count.model.ts b/services/workflows-service/src/metrics/repository/models/workflow-runtime-status-case-count.model.ts new file mode 100644 index 0000000000..2ccf4449e5 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/models/workflow-runtime-status-case-count.model.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class WorkflowRuntimeStatusCaseCountModel { + @ApiProperty() + @Transform(({ value }) => (value === null ? 0 : value)) + active!: number; + + @ApiProperty() + @Transform(({ value }) => (value === null ? 0 : value)) + completed!: number; + + @ApiProperty() + @Transform(({ value }) => (value === null ? 0 : value)) + failed!: number; +} diff --git a/services/workflows-service/src/metrics/repository/sql/aggregate-approval-rate.sql.ts b/services/workflows-service/src/metrics/repository/sql/aggregate-approval-rate.sql.ts new file mode 100644 index 0000000000..4a15bb2594 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/sql/aggregate-approval-rate.sql.ts @@ -0,0 +1,18 @@ +export const aggregateApprovalRateQuery = ` +SELECT + (CASE + WHEN counts."resolvedCount" > 0 AND counts."approvedCount" > 0 + THEN (counts."approvedCount"::int / counts."resolvedCount"::int * 100) + ELSE 0 + END)::numeric(5, 2)::varchar AS "approvalRate" +FROM ( + SELECT + (SELECT COUNT(*) + FROM "WorkflowRuntimeData" + WHERE "resolvedAt" >= $1) AS "resolvedCount", + (SELECT COUNT(*) + FROM "WorkflowRuntimeData" + WHERE context -> 'documents' @> '[{"decision": {"status": "approved"}}]' + AND "resolvedAt" >= $1) AS "approvedCount" +) AS counts; +`; diff --git a/services/workflows-service/src/metrics/repository/sql/aggregate-average-assignment-time.sql.ts b/services/workflows-service/src/metrics/repository/sql/aggregate-average-assignment-time.sql.ts new file mode 100644 index 0000000000..77141bdbe9 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/sql/aggregate-average-assignment-time.sql.ts @@ -0,0 +1,20 @@ +export const aggregateAverageAssignmentTimeQuery = ` +SELECT AVG(time)::varchar as average_time +FROM ( + SELECT + "User".id, + COALESCE( + EXTRACT(EPOCH FROM ("WorkflowRuntimeData"."assignedAt" - "WorkflowRuntimeData"."createdAt")) * 1000, + 0 + ) AS time + FROM + "User" + LEFT JOIN + "WorkflowRuntimeData" + ON "User".id = "WorkflowRuntimeData"."assigneeId" + WHERE + "WorkflowRuntimeData"."createdAt" IS NOT NULL + AND + "WorkflowRuntimeData"."assignedAt" >= $1 +) AS T +`; diff --git a/services/workflows-service/src/metrics/repository/sql/aggregate-average-resolution-time.sql.ts b/services/workflows-service/src/metrics/repository/sql/aggregate-average-resolution-time.sql.ts new file mode 100644 index 0000000000..a098176cc9 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/sql/aggregate-average-resolution-time.sql.ts @@ -0,0 +1,20 @@ +export const aggregateAverageResolutionTimeQuery = ` +SELECT AVG(time)::varchar as average_time +FROM ( + SELECT + "User".id, + COALESCE( + EXTRACT(EPOCH FROM ("WorkflowRuntimeData"."resolvedAt" - "WorkflowRuntimeData"."createdAt")) * 1000, + 0 + ) AS time + FROM + "User" + LEFT JOIN + "WorkflowRuntimeData" + ON "User".id = "WorkflowRuntimeData"."assigneeId" + WHERE + "WorkflowRuntimeData"."createdAt" IS NOT NULL + AND + "WorkflowRuntimeData"."resolvedAt" >= $1 +) AS T + `; diff --git a/services/workflows-service/src/metrics/repository/sql/aggregate-average-review-time.sql.ts b/services/workflows-service/src/metrics/repository/sql/aggregate-average-review-time.sql.ts new file mode 100644 index 0000000000..185a8c6fde --- /dev/null +++ b/services/workflows-service/src/metrics/repository/sql/aggregate-average-review-time.sql.ts @@ -0,0 +1,20 @@ +export const aggregateAverageReviewTimeQuery = ` +SELECT AVG(time)::varchar as average_time +FROM ( + SELECT + "User".id, + COALESCE( + EXTRACT(EPOCH FROM ("WorkflowRuntimeData"."resolvedAt" - "WorkflowRuntimeData"."assignedAt")) * 1000, + 0 + ) AS time + FROM + "User" + LEFT JOIN + "WorkflowRuntimeData" + ON "User".id = "WorkflowRuntimeData"."assigneeId" + WHERE + "WorkflowRuntimeData"."assignedAt" IS NOT NULL + AND + "WorkflowRuntimeData"."resolvedAt" >= $1 +) AS T +`; diff --git a/services/workflows-service/src/metrics/repository/sql/aggregate-daily-cases-resolved.sql.ts b/services/workflows-service/src/metrics/repository/sql/aggregate-daily-cases-resolved.sql.ts new file mode 100644 index 0000000000..f92e66f083 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/sql/aggregate-daily-cases-resolved.sql.ts @@ -0,0 +1,14 @@ +export const aggregateDailyCasesResolvedQuery = ` +select + date, + sum(( + SELECT COUNT(*) + FROM "WorkflowRuntimeData" + WHERE "assigneeId" = coalesce($2, "WorkflowRuntimeData"."assigneeId") + AND date_trunc('day', "resolvedAt"::timestamp) = date_trunc('day', date::timestamp) + ))::int AS cases +from +generate_series($1::timestamp, now()::timestamp, interval '1 day') as date +group by date +order by date asc +`; diff --git a/services/workflows-service/src/metrics/repository/sql/aggregate-users-assigned-cases-statistic.sql.ts b/services/workflows-service/src/metrics/repository/sql/aggregate-users-assigned-cases-statistic.sql.ts new file mode 100644 index 0000000000..1e0e0654f2 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/sql/aggregate-users-assigned-cases-statistic.sql.ts @@ -0,0 +1,21 @@ +export const aggregateUsersAssignedCasesStatisticQuery = ` +select + id, + "firstName", + "lastName", + "casesCount"::int, + email +from + "User" +inner join ( + select + "WorkflowRuntimeData"."assigneeId", + count(*) as "casesCount" + from + "WorkflowRuntimeData" + where "assignedAt" >= coalesce($1, '1900-01-01'::timestamp) + group by "assigneeId" +) as agent_workflow_runtime on +agent_workflow_runtime."assigneeId" = "id" +order by "casesCount" desc +`; diff --git a/services/workflows-service/src/metrics/repository/sql/aggregate-users-resolved-cases-statistic.sql.ts b/services/workflows-service/src/metrics/repository/sql/aggregate-users-resolved-cases-statistic.sql.ts new file mode 100644 index 0000000000..04e0694e07 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/sql/aggregate-users-resolved-cases-statistic.sql.ts @@ -0,0 +1,21 @@ +export const aggregateUsersResolvedCasesStatisticQuery = ` +select + id, + "firstName", + "lastName", + "casesCount"::int, + email +from + "User" +inner join ( + select + "WorkflowRuntimeData"."assigneeId", + count(*) as "casesCount" + from + "WorkflowRuntimeData" + where "resolvedAt" >= $1 + group by "assigneeId" +) as agent_workflow_runtime on +agent_workflow_runtime."assigneeId" = "id" +order by "casesCount" desc +`; diff --git a/services/workflows-service/src/metrics/repository/sql/aggregate-workflow-runtime-statistic.sql.ts b/services/workflows-service/src/metrics/repository/sql/aggregate-workflow-runtime-statistic.sql.ts new file mode 100644 index 0000000000..b003bfde0a --- /dev/null +++ b/services/workflows-service/src/metrics/repository/sql/aggregate-workflow-runtime-statistic.sql.ts @@ -0,0 +1,36 @@ +export const aggregateWorkflowRuntimeStatisticQuery = ` +select + "workflowDefinitionId", + "name" as "workflowDefinitionName", +sum( + case + when workflow_runtime_data."status" = 'active' then workflow_runtime_data.status_count + else 0 + end +)::int as active, +sum( + case + when workflow_runtime_data."status" = 'completed' then workflow_runtime_data.status_count + else 0 + end +)::int as completed, +sum( + case + when workflow_runtime_data."status" = 'failed' then workflow_runtime_data.status_count + else 0 + end +)::int as failed +from +( + select + "status", + "workflowDefinitionId", + count("status") as status_count + from + "WorkflowRuntimeData" + group by + "workflowDefinitionId", + "status" +) as workflow_runtime_data +inner join (select * from "WorkflowDefinition") as workflowDefinition on workflowDefinition."id" = "workflowDefinitionId" +group by "workflowDefinitionId","workflowDefinitionName"`; diff --git a/services/workflows-service/src/metrics/repository/sql/aggregate-workflow-runtime-status-case-count.sql.ts b/services/workflows-service/src/metrics/repository/sql/aggregate-workflow-runtime-status-case-count.sql.ts new file mode 100644 index 0000000000..4041d9f4e4 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/sql/aggregate-workflow-runtime-status-case-count.sql.ts @@ -0,0 +1,31 @@ +export const aggregateWorkflowRuntimeStatusCaseCountQuery = ` +select + sum( + case + when workflow_runtime_data."status" = 'active' then workflow_runtime_data.status_count + else 0 + end + )::int as active, + sum( + case + when workflow_runtime_data."status" = 'completed' then workflow_runtime_data.status_count + else 0 + end + )::int as completed, + sum( + case + when workflow_runtime_data."status" = 'failed' then workflow_runtime_data.status_count + else 0 + end + )::int as failed +from +( + select + "status", + count("status") as status_count + from + "WorkflowRuntimeData" + where "createdAt" >= coalesce($1, '1900-01-01'::timestamp) + group by + "status" +) as workflow_runtime_data`; diff --git a/services/workflows-service/src/metrics/repository/sql/select-active-users.sql.ts b/services/workflows-service/src/metrics/repository/sql/select-active-users.sql.ts new file mode 100644 index 0000000000..7b671aaf85 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/sql/select-active-users.sql.ts @@ -0,0 +1,5 @@ +export const selectActiveUsersQuery = ` +select +id, "firstName", "lastName", "lastActiveAt" +from "User" +order by "lastActiveAt" desc nulls last`; diff --git a/services/workflows-service/src/metrics/repository/types/aggregate-approval-rate.ts b/services/workflows-service/src/metrics/repository/types/aggregate-approval-rate.ts new file mode 100644 index 0000000000..da2d515a22 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/aggregate-approval-rate.ts @@ -0,0 +1,3 @@ +export interface IAggregateApprovalRate { + approvalRate: string | null; +} diff --git a/services/workflows-service/src/metrics/repository/types/aggregate-average-assignment-time.ts b/services/workflows-service/src/metrics/repository/types/aggregate-average-assignment-time.ts new file mode 100644 index 0000000000..73d9985804 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/aggregate-average-assignment-time.ts @@ -0,0 +1,3 @@ +export interface IAggregateAverageAssignmentTime { + average_time: string | null; +} diff --git a/services/workflows-service/src/metrics/repository/types/aggregate-average-resolution-time.ts b/services/workflows-service/src/metrics/repository/types/aggregate-average-resolution-time.ts new file mode 100644 index 0000000000..0169130aee --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/aggregate-average-resolution-time.ts @@ -0,0 +1,3 @@ +export interface IAggregateAverageResolutionTime { + average_time: string | null; +} diff --git a/services/workflows-service/src/metrics/repository/types/aggregate-average-review-time.ts b/services/workflows-service/src/metrics/repository/types/aggregate-average-review-time.ts new file mode 100644 index 0000000000..149b1857a4 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/aggregate-average-review-time.ts @@ -0,0 +1,3 @@ +export interface IAggregateAverageReviewTime { + average_time: string | null; +} diff --git a/services/workflows-service/src/metrics/repository/types/aggregate-cases-resolved-daily.ts b/services/workflows-service/src/metrics/repository/types/aggregate-cases-resolved-daily.ts new file mode 100644 index 0000000000..ba84a5f054 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/aggregate-cases-resolved-daily.ts @@ -0,0 +1,4 @@ +export interface IAggregateCasesResolvedDaily { + date: string | null; + cases: number | null; +} diff --git a/services/workflows-service/src/metrics/repository/types/aggregate-user-resolved-cases-statistic.ts b/services/workflows-service/src/metrics/repository/types/aggregate-user-resolved-cases-statistic.ts new file mode 100644 index 0000000000..9ca0c2a787 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/aggregate-user-resolved-cases-statistic.ts @@ -0,0 +1,7 @@ +export interface IAggregateUserResolvedCasesStatistic { + id: string; + firstName: string; + lastName: string; + casesCount: number; + email: string; +} diff --git a/services/workflows-service/src/metrics/repository/types/aggregate-users-with-cases-count.ts b/services/workflows-service/src/metrics/repository/types/aggregate-users-with-cases-count.ts new file mode 100644 index 0000000000..6b0707d233 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/aggregate-users-with-cases-count.ts @@ -0,0 +1,6 @@ +export interface IAggregateUsersWithCasesCount { + id: string; + firstName: string; + lastName: string; + casesCount: number | null; +} diff --git a/services/workflows-service/src/metrics/repository/types/aggregate-workflow-runtime-statistic.ts b/services/workflows-service/src/metrics/repository/types/aggregate-workflow-runtime-statistic.ts new file mode 100644 index 0000000000..5b1d62053d --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/aggregate-workflow-runtime-statistic.ts @@ -0,0 +1,7 @@ +import { WorkflowRuntimeDataStatus } from '@prisma/client'; + +export interface IAggregateWorkflowRuntimeStatistic + extends Record<WorkflowRuntimeDataStatus, number> { + workflowDefinitionId: string; + workflowDefinitionName: string; +} diff --git a/services/workflows-service/src/metrics/repository/types/aggregate-workflow-runtime-status-case-count.ts b/services/workflows-service/src/metrics/repository/types/aggregate-workflow-runtime-status-case-count.ts new file mode 100644 index 0000000000..c655a81546 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/aggregate-workflow-runtime-status-case-count.ts @@ -0,0 +1,5 @@ +export interface IAggregateWorkflowRuntimeStatusCaseCount { + active: number | null; + failed: number | null; + completed: number | null; +} diff --git a/services/workflows-service/src/metrics/repository/types/find-users-assigned-cases-statistic.params.ts b/services/workflows-service/src/metrics/repository/types/find-users-assigned-cases-statistic.params.ts new file mode 100644 index 0000000000..bcfec41ec7 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/find-users-assigned-cases-statistic.params.ts @@ -0,0 +1,3 @@ +import { BaseParams } from '@/metrics/common/types/base-query.params'; + +export type FindUsersAssignedCasesStatisticParams = BaseParams; diff --git a/services/workflows-service/src/metrics/repository/types/find-users-resolved-cases-statistic.params.ts b/services/workflows-service/src/metrics/repository/types/find-users-resolved-cases-statistic.params.ts new file mode 100644 index 0000000000..564a90a63b --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/find-users-resolved-cases-statistic.params.ts @@ -0,0 +1,3 @@ +import { BaseParams } from '@/metrics/common/types/base-query.params'; + +export type FindUsersResolvedCasesStatisticParams = BaseParams; diff --git a/services/workflows-service/src/metrics/repository/types/get-runtime-status-case-count.params.ts b/services/workflows-service/src/metrics/repository/types/get-runtime-status-case-count.params.ts new file mode 100644 index 0000000000..bb0b8a9b71 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/get-runtime-status-case-count.params.ts @@ -0,0 +1,3 @@ +import { BaseParams } from '@/metrics/common/types/base-query.params'; + +export type GetRuntimeStatusCaseCountParams = BaseParams; diff --git a/services/workflows-service/src/metrics/repository/types/get-user-approval-rate.params.ts b/services/workflows-service/src/metrics/repository/types/get-user-approval-rate.params.ts new file mode 100644 index 0000000000..c64f6318b7 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/get-user-approval-rate.params.ts @@ -0,0 +1,5 @@ +import { BaseParams } from '@/metrics/common/types/base-query.params'; + +export interface GetUserApprovalRateParams extends BaseParams { + userId?: string; +} diff --git a/services/workflows-service/src/metrics/repository/types/get-user-average-assignment-time.params.ts b/services/workflows-service/src/metrics/repository/types/get-user-average-assignment-time.params.ts new file mode 100644 index 0000000000..504918c825 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/get-user-average-assignment-time.params.ts @@ -0,0 +1,3 @@ +import { BaseParams } from '@/metrics/common/types/base-query.params'; + +export type GetUserAverageAssignmentTimeParams = BaseParams; diff --git a/services/workflows-service/src/metrics/repository/types/get-user-average-resolution-time.params.ts b/services/workflows-service/src/metrics/repository/types/get-user-average-resolution-time.params.ts new file mode 100644 index 0000000000..7e3ccf2c35 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/get-user-average-resolution-time.params.ts @@ -0,0 +1,3 @@ +import { BaseParams } from '@/metrics/common/types/base-query.params'; + +export type GetUserAverageResolutionTimeParams = BaseParams; diff --git a/services/workflows-service/src/metrics/repository/types/get-user-average-review-time.params.ts b/services/workflows-service/src/metrics/repository/types/get-user-average-review-time.params.ts new file mode 100644 index 0000000000..b7b2b56e59 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/get-user-average-review-time.params.ts @@ -0,0 +1,3 @@ +import { BaseParams } from '@/metrics/common/types/base-query.params'; + +export type GetUserAverageReviewTimeParams = BaseParams; diff --git a/services/workflows-service/src/metrics/repository/types/list-user-cases-resolved-daily.params.ts b/services/workflows-service/src/metrics/repository/types/list-user-cases-resolved-daily.params.ts new file mode 100644 index 0000000000..564ce184e9 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/list-user-cases-resolved-daily.params.ts @@ -0,0 +1,5 @@ +import { BaseParams } from '@/metrics/common/types/base-query.params'; + +export interface ListUserCasesResolvedDailyParams extends BaseParams { + userId: string; +} diff --git a/services/workflows-service/src/metrics/repository/types/list-users-resolved-cases-statistic.params.ts b/services/workflows-service/src/metrics/repository/types/list-users-resolved-cases-statistic.params.ts new file mode 100644 index 0000000000..fd81c4ef04 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/list-users-resolved-cases-statistic.params.ts @@ -0,0 +1,3 @@ +import { BaseParams } from '@/metrics/common/types/base-query.params'; + +export type ListUsersResolvedCasesStatistic = BaseParams; diff --git a/services/workflows-service/src/metrics/repository/types/select-active-user.ts b/services/workflows-service/src/metrics/repository/types/select-active-user.ts new file mode 100644 index 0000000000..5ebef912c2 --- /dev/null +++ b/services/workflows-service/src/metrics/repository/types/select-active-user.ts @@ -0,0 +1,6 @@ +export interface ISelectActiveUser { + id: string; + firstName: string; + lastName: string; + lastActiveAt: string; +} diff --git a/services/workflows-service/src/metrics/service/metrics.service.ts b/services/workflows-service/src/metrics/service/metrics.service.ts new file mode 100644 index 0000000000..f217837af3 --- /dev/null +++ b/services/workflows-service/src/metrics/service/metrics.service.ts @@ -0,0 +1,84 @@ +import { MetricsRepository } from '@/metrics/repository/metrics.repository'; +import { MetricsUserModel } from '@/metrics/repository/models/metrics-user.model'; +import { UserAssignedCasesStatisticModel } from '@/metrics/repository/models/user-assigned-cases-statistic.model'; +import { CasesResolvedInDay } from '@/metrics/repository/models/cases-resolved-daily.model'; +import { UserResolvedCasesStatisticModel } from '@/metrics/repository/models/user-resolved-cases-statistic.model'; +import { WorkflowRuntimeStatisticModel } from '@/metrics/repository/models/workflow-runtime-statistic.model'; +import { WorkflowRuntimeStatusCaseCountModel } from '@/metrics/repository/models/workflow-runtime-status-case-count.model'; +import { FindUsersAssignedCasesStatisticParams } from '@/metrics/repository/types/find-users-assigned-cases-statistic.params'; +import { FindUsersResolvedCasesStatisticParams } from '@/metrics/repository/types/find-users-resolved-cases-statistic.params'; +import { GetRuntimeStatusCaseCountParams } from '@/metrics/repository/types/get-runtime-status-case-count.params'; +import { ListUserCasesResolvedDailyParams } from '@/metrics/repository/types/list-user-cases-resolved-daily.params'; +import { UserWorkflowProcessingStatisticModel } from '@/metrics/service/models/user-workflow-processing-statistic.model'; +import { GetUserWorkflowProcessingStatisticParams } from '@/metrics/service/types/get-user-workflow-processing-statistic.params'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class MetricsService { + constructor(private readonly metricsRepository: MetricsRepository) {} + + async getRuntimesStatusCaseCount( + params: GetRuntimeStatusCaseCountParams, + ): Promise<WorkflowRuntimeStatusCaseCountModel> { + return await this.metricsRepository.getRuntimeStatusCaseCount(params); + } + + async listRuntimesStatistic(): Promise<WorkflowRuntimeStatisticModel[]> { + return await this.metricsRepository.findRuntimeStatistic(); + } + + async listUsersAssignedCasesStatistic( + params: FindUsersAssignedCasesStatisticParams, + ): Promise<UserAssignedCasesStatisticModel[]> { + return await this.metricsRepository.findUsersAssignedCasesStatistic(params); + } + + async listUsersResolvedCasesStatistic( + params: FindUsersResolvedCasesStatisticParams, + ): Promise<UserResolvedCasesStatisticModel[]> { + return await this.metricsRepository.findUsersResolvedCasesStatistic(params); + } + + async getUserWorkflowProcessingStatistic( + params: GetUserWorkflowProcessingStatisticParams, + ): Promise<UserWorkflowProcessingStatisticModel> { + const commonParams = { + fromDate: params.fromDate, + }; + + const results = await Promise.all([ + this.metricsRepository.getUserApprovalRate(commonParams), + this.metricsRepository.getUserAverageAssignmentTime(commonParams), + this.metricsRepository.getUserAverageResolutionTime(commonParams), + + this.metricsRepository.getUserAverageReviewTime(commonParams), + ]); + + const [ + approvalRateModel, + averageAssignmentTimeModel, + averageResolutionTimeModel, + averageReviewTimeModel, + ] = results; + + const statisticModel: UserWorkflowProcessingStatisticModel = + new UserWorkflowProcessingStatisticModel(); + + statisticModel.approvalRate = approvalRateModel?.approvalRate || '0'; + statisticModel.averageAssignmentTime = averageAssignmentTimeModel?.time || '0'; + statisticModel.averageResolutionTime = averageResolutionTimeModel?.time || '0'; + statisticModel.averageReviewTime = averageReviewTimeModel?.time || '0'; + + return statisticModel; + } + + async listUserCasesResolvedDaily( + params: ListUserCasesResolvedDailyParams, + ): Promise<CasesResolvedInDay[]> { + return await this.metricsRepository.listCasesResolvedDaily(params); + } + + async listActiveUsers(): Promise<MetricsUserModel[]> { + return await this.metricsRepository.listUsers(); + } +} diff --git a/services/workflows-service/src/metrics/service/models/user-workflow-processing-statistic.model.ts b/services/workflows-service/src/metrics/service/models/user-workflow-processing-statistic.model.ts new file mode 100644 index 0000000000..5c7cf6ce71 --- /dev/null +++ b/services/workflows-service/src/metrics/service/models/user-workflow-processing-statistic.model.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class UserWorkflowProcessingStatisticModel { + @ApiProperty() + id!: string; + + @ApiProperty() + approvalRate!: string; + + @ApiProperty({ description: 'Average resolution time in milliseconds' }) + averageResolutionTime!: string; + + @ApiProperty({ description: 'Average assignment time in milliseconds' }) + averageAssignmentTime!: string; + + @ApiProperty({ description: 'Average review time in milliseconds' }) + averageReviewTime!: string; +} diff --git a/services/workflows-service/src/metrics/service/types/get-user-workflow-processing-statistic.params.ts b/services/workflows-service/src/metrics/service/types/get-user-workflow-processing-statistic.params.ts new file mode 100644 index 0000000000..dfa48f3afe --- /dev/null +++ b/services/workflows-service/src/metrics/service/types/get-user-workflow-processing-statistic.params.ts @@ -0,0 +1,9 @@ +import { GetUserApprovalRateParams } from '@/metrics/repository/types/get-user-approval-rate.params'; +import { GetUserAverageAssignmentTimeParams } from '@/metrics/repository/types/get-user-average-assignment-time.params'; +import { GetUserAverageResolutionTimeParams } from '@/metrics/repository/types/get-user-average-resolution-time.params'; +import { GetUserAverageReviewTimeParams } from '@/metrics/repository/types/get-user-average-review-time.params'; + +export type GetUserWorkflowProcessingStatisticParams = GetUserApprovalRateParams & + GetUserAverageAssignmentTimeParams & + GetUserAverageResolutionTimeParams & + GetUserAverageReviewTimeParams; diff --git a/services/workflows-service/src/prisma/prisma.service.ts b/services/workflows-service/src/prisma/prisma.service.ts index 3882b9a57f..30ee0efdb9 100644 --- a/services/workflows-service/src/prisma/prisma.service.ts +++ b/services/workflows-service/src/prisma/prisma.service.ts @@ -3,6 +3,12 @@ import { PrismaClient } from '@prisma/client'; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit { + constructor() { + super({ + errorFormat: 'pretty', + }); + } + async onModuleInit() { await this.$connect(); } diff --git a/services/workflows-service/src/prisma/prisma.util.test.ts b/services/workflows-service/src/prisma/prisma.util.unit.test.ts similarity index 100% rename from services/workflows-service/src/prisma/prisma.util.test.ts rename to services/workflows-service/src/prisma/prisma.util.unit.test.ts diff --git a/services/workflows-service/src/serve-static-options.service.ts b/services/workflows-service/src/serve-static-options.service.ts index a8caccda60..b2cb7c0ea5 100644 --- a/services/workflows-service/src/serve-static-options.service.ts +++ b/services/workflows-service/src/serve-static-options.service.ts @@ -2,6 +2,7 @@ import * as path from 'path'; import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { ServeStaticModuleOptions, ServeStaticModuleOptionsFactory } from '@nestjs/serve-static'; +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; const SERVE_STATIC_ROOT_PATH_VAR = 'SERVE_STATIC_ROOT_PATH'; const DEFAULT_STATIC_MODULE_OPTIONS_LIST: ServeStaticModuleOptions[] = [ @@ -13,9 +14,10 @@ const DEFAULT_STATIC_MODULE_OPTIONS_LIST: ServeStaticModuleOptions[] = [ @Injectable() export class ServeStaticOptionsService implements ServeStaticModuleOptionsFactory { - private readonly logger = new Logger(ServeStaticOptionsService.name); - - constructor(private readonly configService: ConfigService) {} + constructor( + private readonly configService: ConfigService, + private readonly logger: AppLoggerService, + ) {} createLoggerOptions(): ServeStaticModuleOptions[] { const serveStaticRootPath = this.configService.get(SERVE_STATIC_ROOT_PATH_VAR) as string; diff --git a/services/workflows-service/src/storage/storage.controller.external.ts b/services/workflows-service/src/storage/storage.controller.external.ts index b65efe442f..c9b2ceb382 100644 --- a/services/workflows-service/src/storage/storage.controller.external.ts +++ b/services/workflows-service/src/storage/storage.controller.external.ts @@ -12,6 +12,7 @@ import { downloadFileFromS3, manageFileByProvider } from '@/storage/get-file-sto import { AwsS3FileConfig } from '@/providers/file/file-provider/aws-s3-file.config'; import * as os from 'os'; import * as path from 'path'; +import { UseKeyAuthInDevGuard } from '@/common/decorators/use-key-auth-in-dev-guard.decorator'; // Temporarily identical to StorageControllerInternal @swagger.ApiTags('Storage') @@ -42,6 +43,7 @@ export class StorageControllerExternal { }, }, }) + @UseKeyAuthInDevGuard() async uploadFile(@UploadedFile() file: Partial<Express.MulterS3.File>) { const id = await this.service.createFileLink({ uri: file.location || String(file.path), @@ -56,6 +58,7 @@ export class StorageControllerExternal { // curl -v http://localhost:3000/api/v1/storage/1679322938093 @common.Get('/:id') + @UseKeyAuthInDevGuard() async getFileById(@Param('id') id: string, @Res() res: Response) { // currently ignoring user id due to no user info const persistedFile = await this.service.getFileNameById({ @@ -70,6 +73,7 @@ export class StorageControllerExternal { // curl -v http://localhost:3000/api/v1/storage/content/1679322938093 @common.Get('/content/:id') + @UseKeyAuthInDevGuard() async fetchFileContent(@Param('id') id: string, @Res() res: Response) { // currently ignoring user id due to no user info const persistedFile = await this.service.getFileNameById({ diff --git a/services/workflows-service/src/swagger.ts b/services/workflows-service/src/swagger.ts index 78dd2d1412..18b85ee7c5 100644 --- a/services/workflows-service/src/swagger.ts +++ b/services/workflows-service/src/swagger.ts @@ -3,9 +3,9 @@ import { DocumentBuilder, SwaggerCustomOptions } from '@nestjs/swagger'; export const swaggerPath = 'api'; export const swaggerDocumentOptions = new DocumentBuilder() - .setTitle('Workflow API Service') - .setDescription('Workflow API Service') - .setVersion('20') + .setTitle('Workflows API Service') + .setDescription('Workflows API Service') + .setVersion('1') .addBearerAuth() .build(); @@ -15,5 +15,5 @@ export const swaggerSetupOptions: SwaggerCustomOptions = { }, customCssUrl: '../common/swagger/swagger.css', customfavIcon: '../common/swagger/favicon.png', - customSiteTitle: 'Sample service', + customSiteTitle: 'Workflow Service', }; diff --git a/services/workflows-service/src/test/db-setup.ts b/services/workflows-service/src/test/db-setup.ts new file mode 100644 index 0000000000..59b3c85db8 --- /dev/null +++ b/services/workflows-service/src/test/db-setup.ts @@ -0,0 +1,30 @@ +import { PostgreSqlContainer } from 'testcontainers'; +import console from 'console'; +import { TestGlobal } from '@/test/test-global'; +import { execSync } from 'child_process'; + +module.exports = async () => { + if (process.env.SKIP_DB_SETUP_TEARDOWN) return; + + const container = await new PostgreSqlContainer('sibedge/postgres-plv8:15.3-3.1.7') + .withDatabase('test') + .start(); + process.env.TEST_DATABASE_SCHEMA_NAME = container.getDatabase(); + process.env.DB_URL = container.getConnectionUri(); + console.log('\nStarting database container on: ' + container.getConnectionUri()); + + runPrismaMigrations(); + + (globalThis as TestGlobal).__DB_CONTAINER__ = container; +}; + +const runPrismaMigrations = () => { + if (process.env.SKIP_DB_SETUP_TEARDOWN) return; + + try { + execSync('npx prisma migrate dev --preview-feature', { stdio: 'inherit' }); + } catch (error) { + console.error('Prisma migration failed:'); + console.error(error); + } +}; diff --git a/services/workflows-service/src/test/db-teardown.ts b/services/workflows-service/src/test/db-teardown.ts new file mode 100644 index 0000000000..a3834a1b5a --- /dev/null +++ b/services/workflows-service/src/test/db-teardown.ts @@ -0,0 +1,11 @@ +import { TestGlobal } from '@/test/test-global'; + +export async function teardown() { + const globalThisTest = globalThis as TestGlobal; + + if (!globalThisTest.__DB_CONTAINER__) return; + + await globalThisTest.__DB_CONTAINER__.stop(); +} + +module.exports = teardown; diff --git a/services/workflows-service/src/test/helpers/database-helper.ts b/services/workflows-service/src/test/helpers/database-helper.ts new file mode 100644 index 0000000000..885bd08ca9 --- /dev/null +++ b/services/workflows-service/src/test/helpers/database-helper.ts @@ -0,0 +1,33 @@ +import { PrismaClient } from '@prisma/client'; +import { z } from 'zod'; +const databaseHelper = new PrismaClient(); +// should never be unset - default test in order not to delete default db +const TEST_DATABASE_SCHEMA_NAME = z + .string() + .default('test') + .parse(process.env.DATABASE_SCHEMA_NAME); + +//should be implemented in BeforeEach hook +export const cleanupDatabase = async () => { + const tableNames = await __getTables(databaseHelper); + await __removeAllTableContent(databaseHelper, tableNames); +}; + +//should be implemented in AfterEach hook +export const tearDownDatabase = async () => { + await databaseHelper.$disconnect(); +}; + +async function __getTables(prisma: PrismaClient): Promise<string[]> { + const results: Array<{ + tablename: string; + }> = + await prisma.$queryRaw`SELECT tablename from pg_tables where schemaname = '${TEST_DATABASE_SCHEMA_NAME}';`; + return results.map(result => result.tablename); +} + +const __removeAllTableContent = async (prisma: PrismaClient, tableNames: Array<string>) => { + for (const table of tableNames) { + await prisma.$executeRawUnsafe(`DELETE FROM ${TEST_DATABASE_SCHEMA_NAME}."${table}" CASCADE;`); + } +}; diff --git a/services/workflows-service/src/test/helpers/nest-app-helper.ts b/services/workflows-service/src/test/helpers/nest-app-helper.ts new file mode 100644 index 0000000000..1b18814ea0 --- /dev/null +++ b/services/workflows-service/src/test/helpers/nest-app-helper.ts @@ -0,0 +1,73 @@ +import { Test } from '@nestjs/testing'; +import { ACLModule } from '@/common/access-control/acl.module'; +import { ACGuard } from 'nest-access-control'; +import { AclFilterResponseInterceptor } from '@/common/access-control/interceptors/acl-filter-response.interceptor'; +import { AclValidateRequestInterceptor } from '@/common/access-control/interceptors/acl-validate-request.interceptor'; +import { CallHandler, ExecutionContext, INestApplication, Provider, Type } from '@nestjs/common'; +import console from 'console'; +import { AppLoggerModule } from '@/common/app-logger/app-logger.module'; +import { ClsModule } from 'nestjs-cls'; + +export const commonTestingModules = [ + ClsModule.forRoot({ + global: true, + }), + AppLoggerModule, +]; + +const acGuard = { + canActivate: () => { + return true; + }, +}; + +const aclValidateRequestInterceptor = { + intercept: (_context: ExecutionContext, next: CallHandler) => { + return next.handle(); + }, +}; + +const aclFilterResponseInterceptor = { + intercept: (_context: ExecutionContext, next: CallHandler) => { + return next.handle(); + }, +}; + +export const fetchServiceFromModule = async <T>( + service: Type<T>, + dependencies: Provider[] = [], + modules: Type<any>[] = [], +) => { + const moduleRef = await Test.createTestingModule({ + providers: [service, ...dependencies], + imports: [...modules, ...commonTestingModules], + }).compile(); + + return moduleRef.get<typeof service>(service); +}; + +export const initiateNestApp = async ( + app: INestApplication, + providers: Provider[] = [], + controllers: Array<Type>, + modules: Array<Type>, +) => { + console.log(JSON.stringify(modules)); + const moduleRef = await Test.createTestingModule({ + providers: providers, + controllers: controllers, + imports: [ACLModule, ...modules, ...commonTestingModules], + }) + .overrideGuard(ACGuard) + .useValue(acGuard) + .overrideInterceptor(AclFilterResponseInterceptor) + .useValue(aclFilterResponseInterceptor) + .overrideInterceptor(AclValidateRequestInterceptor) + .useValue(aclValidateRequestInterceptor) + .compile(); + + app = moduleRef.createNestApplication(); + await app.init(); + + return app; +}; diff --git a/services/workflows-service/src/test/test-global.ts b/services/workflows-service/src/test/test-global.ts new file mode 100644 index 0000000000..22602639d5 --- /dev/null +++ b/services/workflows-service/src/test/test-global.ts @@ -0,0 +1,5 @@ +import { StartedPostgreSqlContainer } from 'testcontainers'; + +export type TestGlobal = typeof globalThis & { + __DB_CONTAINER__?: StartedPostgreSqlContainer; +}; diff --git a/services/workflows-service/src/user/dtos/get-active-users.dto.ts b/services/workflows-service/src/user/dtos/get-active-users.dto.ts new file mode 100644 index 0000000000..af9b219ef8 --- /dev/null +++ b/services/workflows-service/src/user/dtos/get-active-users.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsDate, IsOptional } from 'class-validator'; + +export class GetActiveUsersDto { + @ApiProperty({ type: Number, description: 'UNIX timestamp' }) + @IsOptional() + @Transform(({ value }) => new Date(value ? +value : 0)) + @IsDate() + fromDate!: Date; +} diff --git a/services/workflows-service/src/user/dtos/get-users-case-resolving-stats-input.dto.ts b/services/workflows-service/src/user/dtos/get-users-case-resolving-stats-input.dto.ts new file mode 100644 index 0000000000..ff09305239 --- /dev/null +++ b/services/workflows-service/src/user/dtos/get-users-case-resolving-stats-input.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsDate, IsOptional } from 'class-validator'; + +export class GetUsersCaseResolvingStatsDto { + @ApiProperty({ type: Number, description: 'UNIX timestamp' }) + @IsOptional() + @Transform(({ value }) => new Date(value ? +value : 0)) + @IsDate() + fromDate!: Date; +} diff --git a/services/workflows-service/src/user/user-case-resolving-stats.model.ts b/services/workflows-service/src/user/user-case-resolving-stats.model.ts new file mode 100644 index 0000000000..c6d869d3b0 --- /dev/null +++ b/services/workflows-service/src/user/user-case-resolving-stats.model.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class UserCaseResolvingStatsModel { + @ApiProperty() + id!: string; + + @ApiProperty() + firstName!: string; + + @ApiProperty() + lastName!: string; + + @ApiProperty() + cases!: number; + + @ApiProperty() + email!: string; +} diff --git a/services/workflows-service/src/user/user.controller.internal.ts b/services/workflows-service/src/user/user.controller.internal.ts index 452c76b8b6..5c3d20bbdf 100644 --- a/services/workflows-service/src/user/user.controller.internal.ts +++ b/services/workflows-service/src/user/user.controller.internal.ts @@ -2,7 +2,6 @@ import * as common from '@nestjs/common'; import * as swagger from '@nestjs/swagger'; import { UserService } from './user.service'; import { UserModel } from './user.model'; -import { UserRepository } from '@/user/user.repository'; import { UserCreateDto } from '@/user/dtos/user-create'; @swagger.ApiTags('internal/users') diff --git a/services/workflows-service/src/user/user.model.ts b/services/workflows-service/src/user/user.model.ts index e521014c84..5c7ae5fbc5 100644 --- a/services/workflows-service/src/user/user.model.ts +++ b/services/workflows-service/src/user/user.model.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsDate, IsString, IsArray } from 'class-validator'; -import { Type } from 'class-transformer'; +import { IsDate, IsString, IsArray, IsOptional } from 'class-validator'; +import { Exclude, Type } from 'class-transformer'; import { JsonValue } from 'type-fest'; export class UserModel { @@ -18,6 +18,17 @@ export class UserModel { @IsString() email!: string; + @ApiProperty() + @IsString() + firstName!: string; + + @ApiProperty() + @IsString() + lastName!: string; + + @Exclude() + password!: string; + @ApiProperty({ required: true, }) @@ -38,4 +49,9 @@ export class UserModel { @IsDate() @Type(() => Date) createdAt!: Date; + + @ApiProperty() + @IsDate() + @IsOptional() + lastActiveAt!: Date | null; } diff --git a/services/workflows-service/src/user/user.repository.ts b/services/workflows-service/src/user/user.repository.ts index 69f9ed4ff1..1e72acce15 100644 --- a/services/workflows-service/src/user/user.repository.ts +++ b/services/workflows-service/src/user/user.repository.ts @@ -78,4 +78,8 @@ export class UserRepository { ...args, }); } + + async queryRaw<TValue>(query: string, values: any[] = []): Promise<TValue> { + return (await this.prisma.$queryRawUnsafe.apply(this.prisma, [query, ...values])) as TValue; + } } diff --git a/services/workflows-service/src/user/user.service.ts b/services/workflows-service/src/user/user.service.ts index 7abac28be1..f79518fff0 100644 --- a/services/workflows-service/src/user/user.service.ts +++ b/services/workflows-service/src/user/user.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@nestjs/common'; -import { User } from '@prisma/client'; import { PasswordService } from '../auth/password/password.service'; import { UserRepository } from './user.repository'; diff --git a/services/workflows-service/src/webhooks/webhooks.service.test.ts b/services/workflows-service/src/webhooks/webhooks.service.unit.test.ts similarity index 100% rename from services/workflows-service/src/webhooks/webhooks.service.test.ts rename to services/workflows-service/src/webhooks/webhooks.service.unit.test.ts diff --git a/services/workflows-service/src/workflow/assign-id-to-documents.ts b/services/workflows-service/src/workflow/assign-id-to-documents.ts new file mode 100644 index 0000000000..1776b5435f --- /dev/null +++ b/services/workflows-service/src/workflow/assign-id-to-documents.ts @@ -0,0 +1,12 @@ +import { randomUUID } from 'crypto'; +import { DefaultContextSchema } from '@ballerine/common'; + +export type TDocuments = DefaultContextSchema['documents']; + +export const assignIdToDocuments = (documents: TDocuments): TDocuments => + documents?.map(document => { + return { + id: randomUUID(), + ...document, + }; + }); diff --git a/services/workflows-service/src/workflow/dtos/find-workflow.dto.ts b/services/workflows-service/src/workflow/dtos/find-workflow.dto.ts new file mode 100644 index 0000000000..b9b791aead --- /dev/null +++ b/services/workflows-service/src/workflow/dtos/find-workflow.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { z } from 'zod'; + +export class FindWorkflowParamsDto { + @ApiProperty() + id!: string; +} + +export class FindWorkflowQueryDto { + @ApiProperty() + filterId!: string; +} + +export const FindWorkflowQuerySchema = z.object({ + filterId: z.string(), +}); diff --git a/services/workflows-service/src/workflow/dtos/find-workflows-list.dto.ts b/services/workflows-service/src/workflow/dtos/find-workflows-list.dto.ts new file mode 100644 index 0000000000..8bb6a31e0e --- /dev/null +++ b/services/workflows-service/src/workflow/dtos/find-workflows-list.dto.ts @@ -0,0 +1,90 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { WorkflowRuntimeDataStatus } from '@prisma/client'; +import { z } from 'zod'; + +class PageDto { + @ApiProperty() + number!: number; + + @ApiProperty() + size!: number; +} + +class FilterDto { + @ApiProperty() + assigneeId?: (string | null)[]; + + @ApiProperty() + status?: WorkflowRuntimeDataStatus[]; +} + +export class FindWorkflowsListDto { + @ApiProperty() + filterId!: string; + + @ApiProperty() + orderBy!: string; + + @ApiProperty() + page!: PageDto; + + @ApiProperty() + limit!: number; + + @ApiProperty() + filter?: FilterDto; +} + +const validateOrderBy = (value: unknown, validColumns: readonly string[]) => { + if (typeof value !== 'string') { + throw new Error('Invalid orderBy'); + } + + const [column = '', direction = ''] = value.split(':'); + + if (!validColumns.includes(column)) { + throw new Error(`Invalid column: ${column}`); + } + + // @ts-expect-error + if (!sortDirections.includes(direction)) { + throw new Error(`Invalid direction: ${direction}`); + } + + return value; +}; +export const FindWorkflowsListSchema = z.object({ + filterId: z.string(), + orderBy: z.string(), + page: z.object({ + number: z.coerce.number().int().positive(), + size: z.coerce.number().int().positive(), + }), + filter: z + .object({ + assigneeId: z + .array(z.union([z.literal('').transform(() => null), z.string().nonempty()])) + .optional(), + status: z.array(z.nativeEnum(WorkflowRuntimeDataStatus)).optional(), + }) + .optional(), +}); + +const sortDirections = ['asc', 'desc'] as const; +const sortableColumnsIndividuals = ['createdAt', 'firstName', 'lastName', 'email'] as const; +const sortableColumnsBusinesses = ['createdAt', 'companyName'] as const; + +export const FindWorkflowsListLogicSchema = { + individuals: z.object({ + orderBy: + z.custom<`${(typeof sortableColumnsIndividuals)[number]}:${(typeof sortDirections)[number]}`>( + value => validateOrderBy(value, sortableColumnsIndividuals), + ), + }), + businesses: z.object({ + orderBy: + z.custom<`${(typeof sortableColumnsBusinesses)[number]}:${(typeof sortDirections)[number]}`>( + value => validateOrderBy(value, sortableColumnsBusinesses), + ), + }), +} as const; diff --git a/services/workflows-service/src/workflow/dtos/get-workflows-runtime-cases-per-status-input.dto.ts b/services/workflows-service/src/workflow/dtos/get-workflows-runtime-cases-per-status-input.dto.ts new file mode 100644 index 0000000000..102499e951 --- /dev/null +++ b/services/workflows-service/src/workflow/dtos/get-workflows-runtime-cases-per-status-input.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsDate, IsOptional } from 'class-validator'; + +export class GetWorkflowsRuntimeAgentCases { + @ApiProperty({ type: Number, description: 'UNIX timestamp' }) + @IsOptional() + @Transform(({ value }) => new Date(value ? +value : 0)) + @IsDate() + fromDate!: Date; +} diff --git a/services/workflows-service/src/workflow/dtos/get-workflows-runtime-input.dto.ts b/services/workflows-service/src/workflow/dtos/get-workflows-runtime-input.dto.ts new file mode 100644 index 0000000000..dc6273ebbc --- /dev/null +++ b/services/workflows-service/src/workflow/dtos/get-workflows-runtime-input.dto.ts @@ -0,0 +1,44 @@ +import { oneOf } from '@/common/decorators/one-of.decorator'; +import { SortOrder } from '@/common/query-filters/sort-order'; +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { WorkflowRuntimeDataStatus } from '@prisma/client'; +import { Type } from 'class-transformer'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class GetWorkflowsRuntimeInputDto { + @IsOptional() + @oneOf(Object.values(WorkflowRuntimeDataStatus), { each: true }) + @ApiPropertyOptional() + status?: WorkflowRuntimeDataStatus[]; + + @Type(() => Number) + @IsOptional() + @IsNumber() + @ApiPropertyOptional() + page?: number; + + @Type(() => Number) + @IsOptional() + @IsNumber() + @ApiPropertyOptional() + limit?: number; + + @Type(() => String) + @ApiPropertyOptional({ + enum: [ + 'workflowDefinitionName', + 'status', + 'state', + 'assignee', + 'resolvedAt', + 'createdBy', + 'createdAt', + ], + }) + orderBy?: string; + + @ApiPropertyOptional({ + enum: ['asc', 'desc'], + }) + orderDirection?: SortOrder; +} diff --git a/services/workflows-service/src/workflow/dtos/get-workflows-runtime-output.dto.ts b/services/workflows-service/src/workflow/dtos/get-workflows-runtime-output.dto.ts new file mode 100644 index 0000000000..2145792cab --- /dev/null +++ b/services/workflows-service/src/workflow/dtos/get-workflows-runtime-output.dto.ts @@ -0,0 +1,20 @@ +import { WorkflowRuntimeListItemModel } from '@/workflow/workflow-runtime-list-item.model'; +import { ApiProperty } from '@nestjs/swagger'; +import { ValidateNested } from 'class-validator'; + +export class Pagination { + @ApiProperty({ type: () => Number }) + pages!: number; + + @ApiProperty({ type: () => Number }) + total!: number; +} + +export class GetWorkflowsRuntimeOutputDto { + @ApiProperty({ type: () => WorkflowRuntimeListItemModel, isArray: true }) + @ValidateNested() + results!: WorkflowRuntimeListItemModel[]; + + @ApiProperty({ type: () => Pagination }) + meta!: Pagination; +} diff --git a/services/workflows-service/src/workflow/dtos/intent.ts b/services/workflows-service/src/workflow/dtos/intent.ts index 2ab8e62115..77d42e7bab 100644 --- a/services/workflows-service/src/workflow/dtos/intent.ts +++ b/services/workflows-service/src/workflow/dtos/intent.ts @@ -4,5 +4,5 @@ export class IntentDto { @ApiProperty() intentName!: string; @ApiProperty() - userId!: string; + entityId!: string; } diff --git a/services/workflows-service/src/workflow/dtos/workflow-assignee-id.ts b/services/workflows-service/src/workflow/dtos/workflow-assignee-id.ts new file mode 100644 index 0000000000..ddae23e1b0 --- /dev/null +++ b/services/workflows-service/src/workflow/dtos/workflow-assignee-id.ts @@ -0,0 +1,7 @@ +import { IsOptional, IsString, ValidateIf } from 'class-validator'; + +export class WorkflowAssigneeId { + @IsString() + @ValidateIf((object, value) => value !== null) // no nullable class validator - skip validation if null + assigneeId!: string | null; +} diff --git a/services/workflows-service/src/workflow/dtos/workflow-definition-update-input.ts b/services/workflows-service/src/workflow/dtos/workflow-definition-update-input.ts index 3bf03cfe03..20cf3b6c8b 100644 --- a/services/workflows-service/src/workflow/dtos/workflow-definition-update-input.ts +++ b/services/workflows-service/src/workflow/dtos/workflow-definition-update-input.ts @@ -1,8 +1,6 @@ -import { InputJsonValue } from '@/types'; import { ApiProperty } from '@nestjs/swagger'; import { IsString, IsOptional, IsObject } from 'class-validator'; import { WorkflowRuntimeDataStatus } from '@prisma/client'; -import { DefaultContextSchema } from '../schemas/context'; export class WorkflowDefinitionUpdateInput { @ApiProperty({ diff --git a/services/workflows-service/src/workflow/dtos/workflow-run.ts b/services/workflows-service/src/workflow/dtos/workflow-run.ts index 3d37546759..25c54a395e 100644 --- a/services/workflows-service/src/workflow/dtos/workflow-run.ts +++ b/services/workflows-service/src/workflow/dtos/workflow-run.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { DefaultContextSchema } from '../schemas/context'; import { IsNotEmpty, IsObject, IsOptional } from 'class-validator'; +import { DefaultContextSchema } from '@ballerine/common'; export class WorkflowRunDto { @ApiProperty({ diff --git a/services/workflows-service/src/workflow/enrich-workflow-runtime-data.ts b/services/workflows-service/src/workflow/enrich-workflow-runtime-data.ts deleted file mode 100644 index b6df7c7424..0000000000 --- a/services/workflows-service/src/workflow/enrich-workflow-runtime-data.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { WorkflowRuntimeData } from '@prisma/client'; -import { DefaultContextSchema } from './schemas/context'; -import { getDocumentId } from '@/documents/utils'; -import { getDocumentsByCountry } from '@/documents/schemas'; -import { Document } from '@/documents/types'; -import { countryCodes } from '@/common/countries'; - -export const enrichWorkflowRuntimeData = (workflowRuntimeData: WorkflowRuntimeData) => { - if (workflowRuntimeData?.context?.documents?.length) { - const documents = workflowRuntimeData?.context?.documents as DefaultContextSchema['documents']; - const result = documents.map(document => { - const documents = getDocumentsByCountry( - document.issuer.country as (typeof countryCodes)[number], - ); - const id = getDocumentId(document as unknown as Document); - const documentSchema = documents[id]; - - return { - ...document, - propertiesSchema: documentSchema ? documentSchema.propertiesSchema : {}, - }; - }); - - workflowRuntimeData.context.documents = result; - } - - return workflowRuntimeData; -}; diff --git a/services/workflows-service/src/workflow/schemas/zod-schemas.ts b/services/workflows-service/src/workflow/schemas/zod-schemas.ts index ffc4c5de31..171519c697 100644 --- a/services/workflows-service/src/workflow/schemas/zod-schemas.ts +++ b/services/workflows-service/src/workflow/schemas/zod-schemas.ts @@ -13,7 +13,9 @@ export const ConfigSchema = z subscriptions: z.array(SubscriptionSchema).optional(), completedWhenTasksResolved: z.boolean().optional(), workflowLevelResolution: z.boolean().optional(), + allowMultipleActiveWorkflows: z.boolean().optional(), }) - .strict(); + .strict() + .optional(); export type WorkflowConfig = z.infer<typeof ConfigSchema>; diff --git a/services/workflows-service/src/workflow/types/index.ts b/services/workflows-service/src/workflow/types/index.ts index 7d55ffc9c3..7facb2d544 100644 --- a/services/workflows-service/src/workflow/types/index.ts +++ b/services/workflows-service/src/workflow/types/index.ts @@ -1,4 +1,13 @@ -import { WorkflowDefinition, WorkflowRuntimeData } from '@prisma/client'; +import { SortOrder } from '@/common/query-filters/sort-order'; +import { WorkflowRuntimeListItemModel } from '@/workflow/workflow-runtime-list-item.model'; +import { + Business, + EndUser, + WorkflowDefinition, + WorkflowRuntimeData, + WorkflowRuntimeDataStatus, +} from '@prisma/client'; +import { User } from '@sentry/node'; export interface RunnableWorkflowData { workflowDefinition: WorkflowDefinition; @@ -11,3 +20,29 @@ export type CompleteWorkflowData = WorkflowRuntimeData & { }; export type TEntityType = 'endUser' | 'business'; + +export type TWorkflowWithRelations = WorkflowRuntimeData & { + workflowDefinition: WorkflowDefinition; + assignee: User; +} & ({ endUser: EndUser } | { business: Business }); + +export interface ListWorkflowsRuntimeParams { + page?: number; + size?: number; + status?: WorkflowRuntimeDataStatus[]; + orderBy?: string; + orderDirection?: SortOrder; +} + +export interface ListRuntimeDataResult { + results: WorkflowRuntimeListItemModel[]; + meta: { + pages: number; + total: number; + }; +} + +export type WorkflowRuntimeListQueryResult = WorkflowRuntimeData & { + workflowDefinition: WorkflowDefinition; + assignee: User | null; +}; diff --git a/services/workflows-service/src/workflow/update-documents.ts b/services/workflows-service/src/workflow/update-documents.ts index 029fe0343a..759ad88e88 100644 --- a/services/workflows-service/src/workflow/update-documents.ts +++ b/services/workflows-service/src/workflow/update-documents.ts @@ -1,5 +1,4 @@ -import { DefaultContextSchema } from '@/workflow/schemas/context'; -import { getDocumentId } from '@/documents/utils'; +import { DefaultContextSchema } from '@ballerine/common'; type Documents = DefaultContextSchema['documents']; type Document = Documents[number]; @@ -10,16 +9,14 @@ export const updateDocuments = ( ): Documents => { const updatedDocumentsMap = new Map<string, Document>(); - existingDocuments.forEach(document => { - const documentId = getDocumentId(document); - - updatedDocumentsMap.set(documentId, document); + // @ts-ignore + existingDocuments?.forEach(document => { + updatedDocumentsMap.set(document.id!, document); }); - documentsToUpdate.forEach(document => { - const documentId = getDocumentId(document); - - updatedDocumentsMap.set(documentId, document); + // @ts-ignore + documentsToUpdate?.forEach(document => { + updatedDocumentsMap.set(document.id!, document); }); return Array.from(updatedDocumentsMap.values()); diff --git a/services/workflows-service/src/workflow/update-documents.test.ts b/services/workflows-service/src/workflow/update-documents.unit.test.ts similarity index 98% rename from services/workflows-service/src/workflow/update-documents.test.ts rename to services/workflows-service/src/workflow/update-documents.unit.test.ts index 2fad0dc519..152fac9c2e 100644 --- a/services/workflows-service/src/workflow/update-documents.test.ts +++ b/services/workflows-service/src/workflow/update-documents.unit.test.ts @@ -1,5 +1,5 @@ -import { DefaultContextSchema } from '@/workflow/schemas/context'; import { updateDocuments } from '@/workflow/update-documents'; +import { DefaultContextSchema } from '@ballerine/common'; type Documents = DefaultContextSchema['documents']; @@ -56,6 +56,7 @@ describe('updateDocuments', () => { const documents = getMockDocuments(); const documentsToUpdate: Documents = [ { + id: 'id-1', version: 1, decision: { revisionReason: '', @@ -112,6 +113,7 @@ describe('updateDocuments', () => { function getMockDocuments(): Documents { return [ { + id: 'id-1', version: 1, decision: { revisionReason: 'Blurry image', @@ -167,6 +169,7 @@ function getMockDocuments(): Documents { }, }, { + id: 'id-2', version: 1, decision: { revisionReason: 'Blurry image', @@ -214,6 +217,7 @@ function getMockDocuments(): Documents { }, }, { + id: 'id-3', version: 1, decision: { revisionReason: 'Blurry image', diff --git a/services/workflows-service/src/workflow/utils/make-full-workflow.ts b/services/workflows-service/src/workflow/utils/make-full-workflow.ts new file mode 100644 index 0000000000..4cf99124b9 --- /dev/null +++ b/services/workflows-service/src/workflow/utils/make-full-workflow.ts @@ -0,0 +1,9 @@ +import { WorkflowDefinition, WorkflowRuntimeData } from '@prisma/client'; + +export const makeFullWorkflow = ( + data: Array<WorkflowRuntimeData & { workflowDefinition: WorkflowDefinition }>, +) => + data?.map(({ workflowDefinition, ...workflowRuntimeData }) => ({ + workflowRuntimeData, + workflowDefinition, + })); diff --git a/services/workflows-service/src/workflow/utils/toPrismaOrderBy.ts b/services/workflows-service/src/workflow/utils/toPrismaOrderBy.ts new file mode 100644 index 0000000000..a7f7044eb5 --- /dev/null +++ b/services/workflows-service/src/workflow/utils/toPrismaOrderBy.ts @@ -0,0 +1,43 @@ +import { Prisma } from '@prisma/client'; + +type Direction = 'asc' | 'desc'; +type IndividualsColumns = 'firstName' | 'lastName' | 'email'; +type BusinessesColumns = 'companyName'; +type WorkflowColumns = 'createdAt'; + +type AvailableColumns<T extends 'individuals' | 'businesses'> = T extends 'individuals' + ? IndividualsColumns | WorkflowColumns + : BusinessesColumns | WorkflowColumns; + +type EntityType = 'individuals' | 'businesses'; + +export const toPrismaOrderBy = < + TEntityType extends EntityType, + TColumn extends AvailableColumns<TEntityType>, + TDirection extends Direction, +>( + orderBy: `${TColumn}:${TDirection}`, + entityType: TEntityType, +): Prisma.WorkflowRuntimeDataOrderByWithRelationInput => { + const [column, direction] = orderBy.split(':') as [TColumn, TDirection]; + + if (column === 'createdAt') { + return { + [column]: direction, + }; + } + + if (entityType === 'individuals') { + return { + endUser: { + [column]: direction, + }, + }; + } + + return { + business: { + [column]: direction, + }, + }; +}; diff --git a/services/workflows-service/src/workflow/utils/toPrismaWhere.ts b/services/workflows-service/src/workflow/utils/toPrismaWhere.ts new file mode 100644 index 0000000000..1732bd5dfc --- /dev/null +++ b/services/workflows-service/src/workflow/utils/toPrismaWhere.ts @@ -0,0 +1,31 @@ +import { Prisma, WorkflowRuntimeDataStatus } from '@prisma/client'; + +type Filters = { + assigneeId?: (string | null)[]; + status?: WorkflowRuntimeDataStatus[]; +}; + +export const toPrismaWhere = (filters: Filters): Prisma.WorkflowRuntimeDataWhereInput => { + const where: Prisma.WorkflowRuntimeDataWhereInput = {}; + + if (filters.assigneeId) { + where.OR = [ + { + assigneeId: { + in: filters.assigneeId.filter((id): id is string => id !== null), + }, + }, + { + assigneeId: filters.assigneeId.includes(null) ? null : undefined, + }, + ]; + } + + if (filters.status) { + where.status = { + in: filters.status, + }; + } + + return where; +}; diff --git a/services/workflows-service/src/workflow/workflow-event-emitter.service.ts b/services/workflows-service/src/workflow/workflow-event-emitter.service.ts index a90ae104f5..4ae251cfff 100644 --- a/services/workflows-service/src/workflow/workflow-event-emitter.service.ts +++ b/services/workflows-service/src/workflow/workflow-event-emitter.service.ts @@ -3,12 +3,9 @@ import { Injectable } from '@nestjs/common'; import { WorkflowRuntimeData } from '@prisma/client'; export interface WorkflowEventRawData { - runtimeData: WorkflowRuntimeData; + oldRuntimeData: WorkflowRuntimeData; + updatedRuntimeData: WorkflowRuntimeData; state: string; - context: { - documents: any[]; - entity: any; - }; entityId: string; correlationId: string; } @@ -35,11 +32,12 @@ export class WorkflowEventEmitterService { this.eventEmitter.emit(eventName, eventData); } - on(eventName: string, listener: (eventData: WorkflowEventRawData) => void) { + on(eventName: string, listener: (eventData: WorkflowEventRawData) => Promise<void>) { if (!eventName) { throw new Error('Event name is required'); } + // eslint-disable-next-line @typescript-eslint/no-misused-promises this.eventEmitter.on(eventName, listener); } } diff --git a/services/workflows-service/src/workflow/workflow-runtime-data.repository.intg.test.ts b/services/workflows-service/src/workflow/workflow-runtime-data.repository.intg.test.ts new file mode 100644 index 0000000000..3add161a5d --- /dev/null +++ b/services/workflows-service/src/workflow/workflow-runtime-data.repository.intg.test.ts @@ -0,0 +1,866 @@ +import { cleanupDatabase, tearDownDatabase } from '@/test/helpers/database-helper'; +import { fetchServiceFromModule, initiateNestApp } from '@/test/helpers/nest-app-helper'; +import { PrismaModule } from 'nestjs-prisma'; +import { EndUserRepository } from '@/end-user/end-user.repository'; +import { FilterService } from '@/filter/filter.service'; +import { FilterRepository } from '@/filter/filter.repository'; +import { FileRepository } from '@/storage/storage.repository'; +import { FileService } from '@/providers/file/file.service'; +import { StorageService } from '@/storage/storage.service'; +import { WorkflowEventEmitterService } from '@/workflow/workflow-event-emitter.service'; +import { BusinessRepository } from '@/business/business.repository'; +import { WorkflowDefinitionRepository } from '@/workflow/workflow-definition.repository'; +import { + ArrayMergeOption, + WorkflowRuntimeDataRepository, +} from '@/workflow/workflow-runtime-data.repository'; +import { WorkflowService } from '@/workflow/workflow.service'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { PrismaService } from '@/prisma/prisma.service'; + +describe('#Workflow Runtime Repository Integration Tests', () => { + let workflowRuntimeRepository: WorkflowRuntimeDataRepository; + let workflowDefinitionRepository: WorkflowDefinitionRepository; + beforeEach(cleanupDatabase); + afterEach(tearDownDatabase); + + beforeAll(async () => { + const servicesProviders = [ + EndUserRepository, + FilterService, + FilterRepository, + FileRepository, + FileService, + StorageService, + WorkflowEventEmitterService, + BusinessRepository, + WorkflowDefinitionRepository, + WorkflowRuntimeDataRepository, + WorkflowService, + EventEmitter2, + PrismaService, + ]; + + workflowRuntimeRepository = (await fetchServiceFromModule( + WorkflowRuntimeDataRepository, + servicesProviders, + [PrismaModule], + )) as unknown as WorkflowRuntimeDataRepository; + + workflowDefinitionRepository = (await fetchServiceFromModule( + WorkflowDefinitionRepository, + servicesProviders, + [PrismaModule], + )) as unknown as WorkflowDefinitionRepository; + }); + describe('Workflow Runtime Data Repository: Jsonb Merge', () => { + beforeAll(async () => { + await workflowDefinitionRepository.create({ + data: { + id: 'test-definition', + name: 'test', + version: 1, + definitionType: 'statechart-json', + definition: { + id: 'Manual Review', + }, + }, + }); + }); + it('updateById: Merge context with nested entities - will preserve "replacment" behaviour for merging arrays', async () => { + // Set up initial data + + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { + entity: { + id: '1', + name: 'TestEntity', + }, + documents: ['file1', 'file2'], + }, + }, + }); + + // Update the context with a new object + const updatedContext = { + entity: { + id: '2', + name: 'UpdatedEntity', + }, + documents: ['file3'], + }; + + const res = await workflowRuntimeRepository.updateById(createRes.id, { + data: { + context: updatedContext, + }, + }); + + // The expected result should be the merged version of initial and updated context + expect(res.context).toMatchObject({ + entity: { + id: '2', + name: 'UpdatedEntity', + }, + documents: ['file3'], + }); + }); + + it('updateById: Merge context with nested entities - will preserve "replacment" behaviour for merging objects', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { + entity: { + id: '1', + }, + }, + }, + }); + + const res = await workflowRuntimeRepository.updateById(createRes.id, { + data: { + context: { + entity: { + id: '2', + }, + documents: [], + }, + }, + }); + + expect(res).toMatchObject({ + endUserId: null, + businessId: null, + assigneeId: null, + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { entity: { id: '2' }, documents: [] }, + config: null, + state: null, + status: 'active', + createdBy: 'SYSTEM', + resolvedAt: null, + assignedAt: null, + }); + }); + + it('should merge the existing and new context data when updateContextById is called', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { key1: 'value1', key2: 'value2', documents: [{ id: '1', name: 'doc1' }] }, + }, + }); + const newContext = { + key2: 'new-value', + key3: 'value3', + documents: [ + { id: '1', name: 'doc2' }, + { id: '2', name: 'doc3' }, + ], + }; + + const arrayMergeOption: ArrayMergeOption = 'by_id'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: 'value1', + key2: 'new-value', + key3: 'value3', + documents: [ + { id: '1', name: 'doc2' }, + { id: '2', name: 'doc3' }, + ], + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should not change existing context when the new context is empty', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { key1: 'value1', key2: 'value2', documents: [{ id: '1', name: 'doc1' }] }, + }, + }); + const newContext = {}; + + const arrayMergeOption: ArrayMergeOption = 'by_id'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: 'value1', + key2: 'value2', + documents: [{ id: '1', name: 'doc1' }], + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should add new key from the new context to the existing context', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { key1: 'value1', key2: 'value2', documents: [{ id: '1', name: 'doc1' }] }, + }, + }); + const newContext = { key3: 'value3' }; + + const arrayMergeOption: ArrayMergeOption = 'by_id'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: 'value1', + key2: 'value2', + key3: 'value3', + documents: [{ id: '1', name: 'doc1' }], + }; + expect(updatedContext).toEqual(expectedContext); + }); + + it('should update the value of an existing key when the new context has a different value for that key', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { key1: 'value1', key2: 'value2', documents: [{ id: '1', name: 'doc1' }] }, + }, + }); + const newContext = { key2: 'new-value2' }; + + const arrayMergeOption: ArrayMergeOption = 'by_id'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: 'value1', + key2: 'new-value2', + documents: [{ id: '1', name: 'doc1' }], + }; + expect(updatedContext).toEqual(expectedContext); + }); + }); + it('should merge nested objects in the context', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { + key1: 'value1', + key2: { nestedKey1: 'nestedValue1' }, + documents: [{ id: '1', name: 'doc1' }], + }, + }, + }); + const newContext = { key2: { nestedKey2: 'nestedValue2' } }; + + const arrayMergeOption: ArrayMergeOption = 'by_id'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: 'value1', + key2: { nestedKey1: 'nestedValue1', nestedKey2: 'nestedValue2' }, + documents: [{ id: '1', name: 'doc1' }], + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should update values in nested objects in the context', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { + key1: 'value1', + key2: { nestedKey1: 'nestedValue1' }, + documents: [{ id: '1', name: 'doc1' }], + }, + }, + }); + const newContext = { key2: { nestedKey1: 'new-nestedValue1' } }; + + const arrayMergeOption: ArrayMergeOption = 'by_id'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: 'value1', + key2: { nestedKey1: 'new-nestedValue1' }, + documents: [{ id: '1', name: 'doc1' }], + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should add a new element to an array in the context', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { key1: 'value1', key2: ['element1'], documents: [{ id: '1', name: 'doc1' }] }, + }, + }); + const newContext = { key2: ['element2'] }; + + const arrayMergeOption: ArrayMergeOption = 'concat'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: 'value1', + key2: ['element1', 'element2'], + documents: [{ id: '1', name: 'doc1' }], + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should replace an element from an array in the context when the new context have it on the same index', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { + key1: 'value1', + key2: ['element1', 'element2'], + documents: [{ id: '1', name: 'doc1' }], + }, + }, + }); + const newContext = { key2: ['element3'] }; + + const arrayMergeOption: ArrayMergeOption = 'by_index'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: 'value1', + key2: ['element3', 'element2'], + documents: [{ id: '1', name: 'doc1' }], + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should throw an error when trying to update context of a non-existing ID', async () => { + const newContext = { key1: 'value1' }; + + const arrayMergeOption: ArrayMergeOption = 'by_id'; + await expect( + workflowRuntimeRepository.updateContextById('non-existing-id', newContext, arrayMergeOption), + ).rejects.toThrow(); + }); + + it('should be able to handle large context objects', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { key1: 'value1', largeKey: new Array(1000).fill('value').join('') }, + }, + }); + const newContext = { key2: 'value2' }; + + const arrayMergeOption: ArrayMergeOption = 'by_id'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: 'value1', + key2: 'value2', + largeKey: new Array(1000).fill('value').join(''), + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should concatenate array in a nested object when array_merge_option is "concat"', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { + key1: 'value1', + key2: { nestedKey: ['value2'] }, + }, + }, + }); + + const newContext = { + key2: { nestedKey: ['value3'] }, + }; + + const arrayMergeOption: ArrayMergeOption = 'concat'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: 'value1', + key2: { nestedKey: ['value2', 'value3'] }, + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should concatenate array of objects in a nested object when array_merge_option is "concat"', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { + key1: 'value1', + key2: { nestedKey: [{ id: '1', value: 'value2' }] }, + }, + }, + }); + + const newContext = { + key2: { nestedKey: [{ id: '2', value: 'value3' }] }, + }; + + const arrayMergeOption: ArrayMergeOption = 'concat'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: 'value1', + key2: { + nestedKey: [ + { id: '1', value: 'value2' }, + { id: '2', value: 'value3' }, + ], + }, + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should concatenate deeply nested arrays when array_merge_option is "concat"', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { + key1: 'value1', + key2: { nestedKey: { anotherNestedKey: ['value2'] } }, + }, + }, + }); + + const newContext = { + key2: { nestedKey: { anotherNestedKey: ['value3'] } }, + }; + + const arrayMergeOption: ArrayMergeOption = 'concat'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: 'value1', + key2: { nestedKey: { anotherNestedKey: ['value2', 'value3'] } }, + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should correctly merge context data with high nesting level', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { + key1: { key2: { key3: { key4: { key5: 'value1' } } } }, + }, + }, + }); + + const newContext = { + key1: { key2: { key3: { key4: { key5: 'value2', key6: 'value3' } } } }, + }; + + const arrayMergeOption: ArrayMergeOption = 'concat'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: { key2: { key3: { key4: { key5: 'value2', key6: 'value3' } } } }, + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should correctly merge context data with mixed data types', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { + key1: 'value1', + key2: { nestedKey1: 'value2', nestedKey2: ['value3'] }, + }, + }, + }); + + const newContext = { + key1: { nestedKey1: 'new-value1' }, + key2: { nestedKey1: 'new-value2', nestedKey3: 'value4' }, + }; + + const arrayMergeOption: ArrayMergeOption = 'concat'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: { nestedKey1: 'new-value1' }, + key2: { nestedKey1: 'new-value2', nestedKey2: ['value3'], nestedKey3: 'value4' }, + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should correctly merge deeply nested arrays with the by_id strategy', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { key1: [{ id: '1', data: 'data1' }], key2: [{ id: '1', data: 'data1' }] }, + }, + }); + const newContext = { key1: [{ id: '1', data: 'data2' }], key2: [{ id: '2', data: 'data2' }] }; + + const arrayMergeOption: ArrayMergeOption = 'by_id'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: [{ id: '1', data: 'data2' }], + key2: [ + { id: '1', data: 'data1' }, + { id: '2', data: 'data2' }, + ], + }; + expect(updatedContext).toEqual(expectedContext); + }); + + it('should correctly merge deeply nested arrays with the by_index strategy', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { key1: ['element1', 'element2'], key2: ['element1', 'element2'] }, + }, + }); + const newContext = { key1: ['element3'], key2: ['element3', 'element4'] }; + + const arrayMergeOption: ArrayMergeOption = 'by_index'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: ['element3', 'element2'], + key2: ['element3', 'element4'], + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should correctly merge deeply nested arrays with the concat strategy', async () => { + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: { key1: ['element1', 'element2'], key2: ['element1', 'element2'] }, + }, + }); + const newContext = { key1: ['element3'], key2: ['element3', 'element4'] }; + + const arrayMergeOption: ArrayMergeOption = 'concat'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + + const expectedContext = { + key1: ['element1', 'element2', 'element3'], + key2: ['element1', 'element2', 'element3', 'element4'], + }; + expect(updatedContext).toEqual(expectedContext); + }); + it('should merge the nested entity data when updateContextById is called', async () => { + const initialContext = { + entity: { + type: 'individual', + data: { + name: 'John Doe', + age: 30, + additionalInfo: { + hobbies: ['running', 'reading'], + }, + }, + id: '123', + }, + documents: [], + }; + + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: initialContext, + }, + }); + const newContext = { + entity: { + data: { + age: 35, + additionalInfo: { + hobbies: ['cycling'], + occupation: 'engineer', + }, + }, + }, + }; + + const arrayMergeOption = 'concat'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const expectedContext = { + entity: { + type: 'individual', + data: { + name: 'John Doe', + age: 35, + additionalInfo: { + hobbies: ['running', 'reading', 'cycling'], + occupation: 'engineer', + }, + }, + id: '123', + }, + documents: [], + }; + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + expect(updatedContext).toEqual(expectedContext); + }); + it('should merge the documents array by id when updateContextById is called', async () => { + const initialContext = { + entity: { + type: 'individual', + id: '123', + }, + documents: [ + { + id: 'doc1', + category: 'category1', + // other properties... + }, + ], + }; + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: initialContext, + }, + }); + const newContext = { + documents: [ + { + id: 'doc1', + category: 'category2', + // other properties... + }, + ], + }; + + const arrayMergeOption = 'by_id'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const expectedContext = { + entity: { + type: 'individual', + id: '123', + }, + documents: [ + { + id: 'doc1', + category: 'category2', + // other properties... + }, + ], + }; + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + expect(updatedContext).toEqual(expectedContext); + }); + it('should merge nested arrays in additionalInfo when updateContextById is called', async () => { + const initialContext = { + entity: { + type: 'individual', + data: { + additionalInfo: { + hobbies: ['running', 'reading'], + }, + }, + id: '123', + }, + documents: [ + { + id: 'doc1', + issuer: { + additionalInfo: { + requirements: ['photo', 'signature'], + }, + }, + // other properties... + }, + ], + }; + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: initialContext, + }, + }); + const newContext = { + entity: { + data: { + additionalInfo: { + hobbies: ['cycling'], + }, + }, + }, + documents: [ + { + id: 'doc1', + issuer: { + additionalInfo: { + requirements: ['address'], + }, + }, + // other properties... + }, + ], + }; + + const arrayMergeOption: ArrayMergeOption = 'concat'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const expectedContext = { + entity: { + type: 'individual', + data: { + additionalInfo: { + hobbies: ['running', 'reading', 'cycling'], + }, + }, + id: '123', + }, + documents: [ + { + id: 'doc1', + issuer: { + additionalInfo: { + requirements: ['photo', 'signature'], + }, + }, + }, + { + id: 'doc1', + issuer: { + additionalInfo: { + requirements: ['address'], + }, + }, + }, + ], + }; + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + expect(updatedContext).toEqual(expectedContext); + }); + it('should merge nested arrays in additionalInfo when updateContextById is called', async () => { + const initialContext = { + entity: { + type: 'individual', + data: { + additionalInfo: { + hobbies: ['running', 'reading'], + }, + }, + id: '123', + }, + documents: [ + { + id: 'doc1', + issuer: { + additionalInfo: { + requirements: ['photo', 'signature'], + }, + }, + // other properties... + }, + ], + }; + const createRes = await workflowRuntimeRepository.create({ + data: { + workflowDefinitionId: 'test-definition', + workflowDefinitionVersion: 1, + context: initialContext, + }, + }); + const newContext = { + entity: { + data: { + additionalInfo: { + hobbies: ['cycling'], + }, + }, + }, + documents: [ + { + id: 'doc1', + issuer: { + additionalInfo: { + requirements: ['address'], + }, + }, + // other properties... + }, + ], + }; + + const arrayMergeOption: ArrayMergeOption = 'replace'; + await workflowRuntimeRepository.updateContextById(createRes.id, newContext, arrayMergeOption); + + const expectedContext = { + entity: { + type: 'individual', + data: { + additionalInfo: { + hobbies: ['cycling'], + }, + }, + id: '123', + }, + documents: [ + { + id: 'doc1', + issuer: { + additionalInfo: { + requirements: ['address'], + }, + }, + // other properties... + }, + ], + }; + + const updatedContext = await workflowRuntimeRepository.findContext(createRes.id); + expect(updatedContext).toEqual(expectedContext); + }); +}); diff --git a/services/workflows-service/src/workflow/workflow-runtime-data.repository.ts b/services/workflows-service/src/workflow/workflow-runtime-data.repository.ts index c39c3f4010..bb165209bf 100644 --- a/services/workflows-service/src/workflow/workflow-runtime-data.repository.ts +++ b/services/workflows-service/src/workflow/workflow-runtime-data.repository.ts @@ -2,6 +2,10 @@ import { PrismaService } from '@/prisma/prisma.service'; import { Injectable } from '@nestjs/common'; import { Prisma, WorkflowRuntimeData, WorkflowRuntimeDataStatus } from '@prisma/client'; import { TEntityType } from '@/workflow/types'; +import { merge } from 'lodash'; +import { assignIdToDocuments } from '@/workflow/assign-id-to-documents'; + +export type ArrayMergeOption = 'by_id' | 'by_index' | 'concat' | 'replace'; @Injectable() export class WorkflowRuntimeDataRepository { @@ -10,12 +14,21 @@ export class WorkflowRuntimeDataRepository { async create<T extends Prisma.WorkflowRuntimeDataCreateArgs>( args: Prisma.SelectSubset<T, Prisma.WorkflowRuntimeDataCreateArgs>, ): Promise<WorkflowRuntimeData> { - return await this.prisma.workflowRuntimeData.create<T>(args); + return await this.prisma.workflowRuntimeData.create<T>({ + ...args, + data: { + ...args.data, + context: { + ...((args.data?.context ?? {}) as any), + documents: assignIdToDocuments((args.data?.context as any)?.documents), + }, + }, + }); } async findMany<T extends Prisma.WorkflowRuntimeDataFindManyArgs>( args?: Prisma.SelectSubset<T, Prisma.WorkflowRuntimeDataFindManyArgs>, - ): Promise<WorkflowRuntimeData[]> { + ) { return await this.prisma.workflowRuntimeData.findMany(args); } @@ -29,10 +42,7 @@ export class WorkflowRuntimeDataRepository { id: string, args?: Prisma.SelectSubset<T, Omit<Prisma.WorkflowRuntimeDataFindUniqueOrThrowArgs, 'where'>>, ): Promise<WorkflowRuntimeData> { - return await this.prisma.workflowRuntimeData.findUniqueOrThrow({ - where: { id }, - ...args, - }); + return await this.prisma.workflowRuntimeData.findFirstOrThrow(merge(args, { where: { id } })); } async updateById<T extends Omit<Prisma.WorkflowRuntimeDataUpdateArgs, 'where'>>( @@ -45,6 +55,23 @@ export class WorkflowRuntimeDataRepository { }); } + async updateContextById( + id: string, + newContext: any, + arrayMergeOption: ArrayMergeOption = 'by_id', + ): Promise<WorkflowRuntimeData> { + const stringifiedContext = JSON.stringify(newContext); + const affectedRows = await this.prisma + .$executeRaw`UPDATE "WorkflowRuntimeData" SET "context" = jsonb_deep_merge_with_options("context", ${stringifiedContext}::jsonb, ${arrayMergeOption}) WHERE "id" = ${id}`; + + // Retrieve and return the updated record + if (affectedRows === 0) { + throw new Error(`No workflowRuntimeData found with the id "${id}"`); + } + + return this.findById(id); + } + async deleteById<T extends Omit<Prisma.WorkflowRuntimeDataDeleteArgs, 'where'>>( id: string, args: Prisma.SelectSubset<T, Omit<Prisma.WorkflowRuntimeDataDeleteArgs, 'where'>>, @@ -99,4 +126,20 @@ export class WorkflowRuntimeDataRepository { }) )?.context; } + + async count<T extends Prisma.WorkflowRuntimeDataFindManyArgs>( + args?: Prisma.SelectSubset<T, Prisma.WorkflowRuntimeDataFindManyArgs>, + ): Promise<number> { + return await this.prisma.workflowRuntimeData.count(args); + } + + async groupBy<T extends Prisma.WorkflowRuntimeDataGroupByArgs>( + args: Prisma.SubsetIntersection<T, Prisma.WorkflowRuntimeDataGroupByArgs, any>, + ) { + return await this.prisma.workflowRuntimeData.groupBy(args); + } + + async queryRaw<TValue>(query: string, values: any[] = []): Promise<TValue> { + return (await this.prisma.$queryRawUnsafe.apply(this.prisma, [query, ...values])) as TValue; + } } diff --git a/services/workflows-service/src/workflow/workflow-runtime-list-item.model.ts b/services/workflows-service/src/workflow/workflow-runtime-list-item.model.ts new file mode 100644 index 0000000000..7e0d31aeeb --- /dev/null +++ b/services/workflows-service/src/workflow/workflow-runtime-list-item.model.ts @@ -0,0 +1,73 @@ +import { IsNullable } from '@/common/decorators/is-nullable.decorator'; +import { ApiProperty } from '@nestjs/swagger'; +import { WorkflowRuntimeDataStatus } from '@prisma/client'; +import { Expose } from 'class-transformer'; +import { IsDate, IsJSON, IsString, ValidateNested } from 'class-validator'; + +export class WorkflowAssignee { + @Expose() + @IsNullable() + @IsString() + firstName!: string; + + @Expose() + @IsNullable() + @IsString() + lastName!: string; +} + +export class WorkflowRuntimeListItemModel { + @Expose() + @ApiProperty() + @IsString() + id!: string; + + @Expose() + @ApiProperty() + @IsString() + workflowDefinitionName!: string; + + @Expose() + @ApiProperty() + @IsString() + workflowDefinitionId!: string; + + @Expose() + @ApiProperty() + @IsString() + status!: WorkflowRuntimeDataStatus; + + @Expose() + @ApiProperty() + @IsJSON() + context!: JSON; + + @Expose() + @IsNullable() + @IsString() + state!: string | null; + + @Expose() + @IsNullable() + @ValidateNested() + assignee!: WorkflowAssignee | null; + + @Expose() + @IsString() + createdBy!: string; + + @Expose() + @ApiProperty() + @IsDate() + createdAt!: Date; + + @Expose() + @ApiProperty() + @IsDate() + resolvedAt!: Date; + + @Expose() + @ApiProperty() + @IsDate() + updatedAt!: Date; +} diff --git a/services/workflows-service/src/workflow/workflow.controller.external.ts b/services/workflows-service/src/workflow/workflow.controller.external.ts index a0caeb02d6..3d9e4b54b6 100644 --- a/services/workflows-service/src/workflow/workflow.controller.external.ts +++ b/services/workflows-service/src/workflow/workflow.controller.external.ts @@ -2,26 +2,28 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ import { UserData } from '@/user/user-data.decorator'; import { UserInfo } from '@/user/user-info'; -import { ApiNestedQuery } from '@/common/decorators/api-nested-query.decorator'; import { isRecordNotFoundError } from '@/prisma/prisma.util'; import * as common from '@nestjs/common'; -import { Headers, NotFoundException, Res } from '@nestjs/common'; +import { NotFoundException, Query, Res } from '@nestjs/common'; import * as swagger from '@nestjs/swagger'; import { WorkflowRuntimeData } from '@prisma/client'; import * as nestAccessControl from 'nest-access-control'; import * as errors from '../errors'; import { IntentDto } from './dtos/intent'; -import { WorkflowDefinitionFindManyArgs } from './dtos/workflow-definition-find-many-args'; import { WorkflowDefinitionUpdateInput } from './dtos/workflow-definition-update-input'; import { WorkflowEventInput } from './dtos/workflow-event-input'; import { WorkflowDefinitionWhereUniqueInput } from './dtos/workflow-where-unique-input'; import { RunnableWorkflowData } from './types'; import { WorkflowDefinitionModel } from './workflow-definition.model'; import { IntentResponse, WorkflowService } from './workflow.service'; -import { EventEmitter2 } from '@nestjs/event-emitter'; import { Response } from 'express'; import { WorkflowRunDto } from './dtos/workflow-run'; import { UseKeyAuthGuard } from '@/common/decorators/use-key-auth-guard.decorator'; +import { UseKeyAuthInDevGuard } from '@/common/decorators/use-key-auth-in-dev-guard.decorator'; +import { plainToClass } from 'class-transformer'; +import { GetWorkflowsRuntimeInputDto } from '@/workflow/dtos/get-workflows-runtime-input.dto'; +import { GetWorkflowsRuntimeOutputDto } from '@/workflow/dtos/get-workflows-runtime-output.dto'; +import { ApiOkResponse } from '@nestjs/swagger'; @swagger.ApiBearerAuth() @swagger.ApiTags('external/workflows') @@ -31,33 +33,38 @@ export class WorkflowControllerExternal { protected readonly service: WorkflowService, @nestAccessControl.InjectRolesBuilder() protected readonly rolesBuilder: nestAccessControl.RolesBuilder, - private eventEmitter: EventEmitter2, ) {} - // GET /workflows - @common.Get() - @swagger.ApiOkResponse({ type: [WorkflowDefinitionModel] }) + @common.Get('/') + @swagger.ApiOkResponse({ type: [GetWorkflowsRuntimeOutputDto] }) @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) @common.HttpCode(200) - @ApiNestedQuery(WorkflowDefinitionFindManyArgs) - async listWorkflowRuntimeDataByUserId( - @UserData() - _userInfo: UserInfo, - @Headers('no_auth_user_id') no_auth_user_id: string, - ): Promise<RunnableWorkflowData[]> { - const completeWorkflowData = await this.service.listFullWorkflowDataByUserId(no_auth_user_id); - const response = completeWorkflowData.map(({ workflowDefinition, ...rest }) => ({ - workflowRuntimeData: rest, - workflowDefinition, - })); + async listWorkflowRuntimeData( + @Query() query: GetWorkflowsRuntimeInputDto, + ): Promise<GetWorkflowsRuntimeOutputDto> { + const results = await this.service.listRuntimeData({ + page: query.page, + size: query.limit, + status: query.status, + orderBy: query.orderBy, + orderDirection: query.orderDirection, + }); + + return plainToClass(GetWorkflowsRuntimeOutputDto, results); + } - return response; + @common.Get('/workflow-definition/:id') + @ApiOkResponse({ type: WorkflowDefinitionModel }) + @swagger.ApiNotFoundResponse({ type: errors.NotFoundException }) + async getWorkflowDefinition(@common.Param() params: WorkflowDefinitionWhereUniqueInput) { + return await this.service.getWorkflowDefinitionById(params.id); } @common.Get('/:id') @swagger.ApiOkResponse({ type: WorkflowDefinitionModel }) @swagger.ApiNotFoundResponse({ type: errors.NotFoundException }) @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) + @UseKeyAuthInDevGuard() async getRunnableWorkflowDataById( @common.Param() params: WorkflowDefinitionWhereUniqueInput, ): Promise<RunnableWorkflowData> { @@ -81,6 +88,7 @@ export class WorkflowControllerExternal { @swagger.ApiOkResponse({ type: WorkflowDefinitionModel }) @swagger.ApiNotFoundResponse({ type: errors.NotFoundException }) @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) + @UseKeyAuthInDevGuard() async updateById( @common.Param() params: WorkflowDefinitionWhereUniqueInput, @common.Body() data: WorkflowDefinitionUpdateInput, @@ -100,13 +108,11 @@ export class WorkflowControllerExternal { @swagger.ApiOkResponse() @common.HttpCode(200) @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) - async intent( - @common.Body() intent: IntentDto, - @Headers('no_auth_user_id') no_auth_user_id: string, - ): Promise<IntentResponse> { + @UseKeyAuthInDevGuard() + async intent(@common.Body() { intentName, entityId }: IntentDto): Promise<IntentResponse> { // Rename to intent or getRunnableWorkflowDataByIntent? - const entityType = intent.intentName === 'kycSignup' ? 'endUser' : 'business'; - return await this.service.resolveIntent(intent.intentName, no_auth_user_id, entityType); + const entityType = intentName === 'kycSignup' ? 'endUser' : 'business'; + return await this.service.resolveIntent(intentName, entityId, entityType); } @common.Post('/run') @@ -153,6 +159,22 @@ export class WorkflowControllerExternal { }); } + // POST /event + @common.Post('/:id/send-event') + @swagger.ApiOkResponse() + @UseKeyAuthGuard() + @common.HttpCode(200) + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) + async sendEvent( + @UserData() _userInfo: UserInfo, + @common.Param('id') id: string, + @common.Body() data: WorkflowEventInput, + ): Promise<void> { + return await this.service.event({ + ...data, + id, + }); + } // curl -X GET -H "Content-Type: application/json" http://localhost:3000/api/v1/external/workflows/:id/context @common.Get('/:id/context') @UseKeyAuthGuard() diff --git a/services/workflows-service/src/workflow/workflow.controller.external.test.ts b/services/workflows-service/src/workflow/workflow.controller.external.unit.test.ts similarity index 80% rename from services/workflows-service/src/workflow/workflow.controller.external.test.ts rename to services/workflows-service/src/workflow/workflow.controller.external.unit.test.ts index cc91f86d50..a337ca6a8f 100644 --- a/services/workflows-service/src/workflow/workflow.controller.external.test.ts +++ b/services/workflows-service/src/workflow/workflow.controller.external.unit.test.ts @@ -9,7 +9,6 @@ import { AclValidateRequestInterceptor } from '@/common/access-control/intercept import { WorkflowControllerExternal } from './workflow.controller.external'; import { WorkflowService } from './workflow.service'; import { EventEmitter2 } from '@nestjs/event-emitter'; -import { CompleteWorkflowData } from './types'; import { WorkflowDefinition, WorkflowRuntimeData } from '@prisma/client'; const acGuard = { @@ -36,7 +35,7 @@ describe('Workflow (external)', () => { beforeEach(async () => { wfService = { - listFullWorkflowDataByUserId: jest.fn() as unknown, + listRuntimeData: jest.fn() as unknown, getWorkflowRuntimeDataById: jest.fn() as unknown, getWorkflowDefinitionById: jest.fn() as unknown, } as WorkflowService; @@ -67,29 +66,29 @@ describe('Workflow (external)', () => { await app.init(); }); - test('GET /workflows will return a two-key object containing "definition" and "runtime data" ', async () => { - const wfService = moduleRef.get<WorkflowService>(WorkflowService); - (wfService.listFullWorkflowDataByUserId as jest.Mock).mockReturnValue( - Promise.resolve([ - { - workflowDefinition: { id: 'a' }, - state: { id: 'b' } as unknown, - }, - ] as unknown as CompleteWorkflowData[]), - ); + // test('GET /workflows will return a two-key object containing "definition" and "runtime data" ', async () => { + // const wfService = moduleRef.get<WorkflowService>(WorkflowService); + // (wfService.listRuntimeData as jest.Mock).mockReturnValue( + // Promise.resolve([ + // { + // workflowDefinition: { id: 'a' }, + // state: { id: 'b' } as unknown, + // }, + // ] as unknown as CompleteWorkflowData[]), + // ); - await request(app.getHttpServer()) - .get('/external/workflows') - .expect(HttpStatus.OK) - .expect([ - { - workflowDefinition: { id: 'a' }, - workflowRuntimeData: { - state: { id: 'b' }, - }, - }, - ]); - }); + // await request(app.getHttpServer()) + // .get('/external/workflows') + // .expect(HttpStatus.OK) + // .expect([ + // { + // workflowDefinition: { id: 'a' }, + // workflowRuntimeData: { + // state: { id: 'b' }, + // }, + // }, + // ]); + // }); test('GET /workflows/:id non existing', async () => { const wfService = moduleRef.get<WorkflowService>(WorkflowService); diff --git a/services/workflows-service/src/workflow/workflow.controller.internal.ts b/services/workflows-service/src/workflow/workflow.controller.internal.ts index e3f275c923..3255b3028a 100644 --- a/services/workflows-service/src/workflow/workflow.controller.internal.ts +++ b/services/workflows-service/src/workflow/workflow.controller.internal.ts @@ -2,6 +2,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ import * as common from '@nestjs/common'; +import { UseGuards, UsePipes } from '@nestjs/common'; import * as swagger from '@nestjs/swagger'; import * as nestAccessControl from 'nest-access-control'; import { isRecordNotFoundError } from '../prisma/prisma.util'; @@ -14,19 +15,29 @@ import { WorkflowEventInput } from './dtos/workflow-event-input'; import { UserData } from '@/user/user-data.decorator'; import { UserInfo } from '@/user/user-info'; import { WorkflowDefinition, WorkflowRuntimeData } from '@prisma/client'; -import { RunnableWorkflowData } from './types'; import { ApiNestedQuery } from '@/common/decorators/api-nested-query.decorator'; -import { plainToClass } from 'class-transformer'; -import { Request } from 'express'; -import { WorkflowDefinitionFindManyArgs } from './dtos/workflow-definition-find-many-args'; import { WorkflowDefinitionUpdateInput } from '@/workflow/dtos/workflow-definition-update-input'; -import { enrichWorkflowRuntimeData } from './enrich-workflow-runtime-data'; +import { + FindWorkflowsListDto, + FindWorkflowsListLogicSchema, + FindWorkflowsListSchema, +} from '@/workflow/dtos/find-workflows-list.dto'; +import { ZodValidationPipe } from '@/common/pipes/zod.pipe'; +import { FilterService } from '@/filter/filter.service'; +import { + FindWorkflowParamsDto, + FindWorkflowQueryDto, + FindWorkflowQuerySchema, +} from '@/workflow/dtos/find-workflow.dto'; +import { WorkflowAssigneeGuard } from '@/auth/assignee-asigned-guard.service'; +import { WorkflowAssigneeId } from '@/workflow/dtos/workflow-assignee-id'; @swagger.ApiTags('internal/workflows') @common.Controller('internal/workflows') export class WorkflowControllerInternal { constructor( protected readonly service: WorkflowService, + protected readonly filterService: FilterService, @nestAccessControl.InjectRolesBuilder() protected readonly rolesBuilder: nestAccessControl.RolesBuilder, ) {} @@ -42,16 +53,42 @@ export class WorkflowControllerInternal { } @common.Get() - @swagger.ApiOkResponse({ type: [WorkflowDefinitionModel] }) - @swagger.ApiForbiddenResponse() - @ApiNestedQuery(WorkflowDefinitionFindManyArgs) - async listWorkflowDefinitions( - @UserData() userInfo: UserInfo, - @common.Req() request: Request, - ): Promise<WorkflowDefinition[]> { - const args = plainToClass(WorkflowDefinitionFindManyArgs, request.query); + @swagger.ApiOkResponse() + @swagger.ApiNotFoundResponse({ type: errors.NotFoundException }) + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) + @ApiNestedQuery(FindWorkflowsListDto) + @UsePipes(new ZodValidationPipe(FindWorkflowsListSchema, 'query')) + async listWorkflowRuntimeData( + @common.Query() { filterId, page, filter: filters, ...queryParams }: FindWorkflowsListDto, + ) { + const filter = await this.filterService.getById(filterId); - return await this.service.listWorkflowDefinitions(args); + const entityType = filter.entity as 'individuals' | 'businesses'; + + const { orderBy } = FindWorkflowsListLogicSchema[entityType].parse(queryParams); + + return await this.service.listWorkflowRuntimeDataWithRelations({ + args: filter.query as any, + entityType, + orderBy, + page, + filters, + }); + } + + @common.Get('/:id') + @swagger.ApiOkResponse() + @swagger.ApiNotFoundResponse({ type: errors.NotFoundException }) + @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) + @ApiNestedQuery(FindWorkflowQueryDto) + @UsePipes(new ZodValidationPipe(FindWorkflowQuerySchema, 'query')) + async getRunnableWorkflowDataById( + @common.Param() { id }: FindWorkflowParamsDto, + @common.Query() { filterId }: FindWorkflowQueryDto, + ) { + const filter = await this.filterService.getById(filterId); + + return await this.service.getWorkflowByIdWithRelations(id, filter.query as any); } @common.Get('/active-states') @@ -83,34 +120,41 @@ export class WorkflowControllerInternal { }); } - @common.Get('/:id') + // PATCH /workflows/:id + @common.Patch('/:id') @swagger.ApiOkResponse({ type: WorkflowDefinitionModel }) @swagger.ApiNotFoundResponse({ type: errors.NotFoundException }) @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) - async getRunnableWorkflowDataById( + @UseGuards(WorkflowAssigneeGuard) + async updateById( @common.Param() params: WorkflowDefinitionWhereUniqueInput, - ): Promise<RunnableWorkflowData | null> { - const workflowRuntimeData = await this.service.getWorkflowRuntimeDataById(params.id); - const workflowDefinition = await this.service.getWorkflowDefinitionById( - workflowRuntimeData.workflowDefinitionId, - ); - return { - workflowDefinition, - workflowRuntimeData: enrichWorkflowRuntimeData(workflowRuntimeData), - }; + @common.Body() data: WorkflowDefinitionUpdateInput, + ): Promise<WorkflowRuntimeData> { + try { + return await this.service.updateWorkflowRuntimeData(params.id, data); + } catch (error) { + if (isRecordNotFoundError(error)) { + throw new errors.NotFoundException(`No resource was found for ${JSON.stringify(params)}`); + } + throw error; + } } - // PATCH /workflows/:id - @common.Patch('/:id') + // PATCH /workflows/assign/:id + // curl -X PATCH http://localhost:3000/api/v1/internal/workflows/assign/:workflowId \ + // -H 'Content-Type: application/json' \ + // -H 'Cookie: session=[SESSION]; session.sig=[SESSION_SIG]' \ + // -d '{"assigneeId": "[ASSIGNEE_ID]"}' + @common.Patch('/assign/:id') @swagger.ApiOkResponse({ type: WorkflowDefinitionModel }) @swagger.ApiNotFoundResponse({ type: errors.NotFoundException }) @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException }) - async updateById( + async assignWorkflowById( @common.Param() params: WorkflowDefinitionWhereUniqueInput, - @common.Body() data: WorkflowDefinitionUpdateInput, + @common.Body() data: WorkflowAssigneeId, ): Promise<WorkflowRuntimeData> { try { - return await this.service.updateWorkflowRuntimeData(params.id, data); + return await this.service.assignWorkflowToUser(params.id, data); } catch (error) { if (isRecordNotFoundError(error)) { throw new errors.NotFoundException(`No resource was found for ${JSON.stringify(params)}`); diff --git a/services/workflows-service/src/workflow/workflow.controller.internal.test.ts b/services/workflows-service/src/workflow/workflow.controller.internal.unit.test.ts similarity index 77% rename from services/workflows-service/src/workflow/workflow.controller.internal.test.ts rename to services/workflows-service/src/workflow/workflow.controller.internal.unit.test.ts index 04ca02b2b7..35510e68ca 100644 --- a/services/workflows-service/src/workflow/workflow.controller.internal.test.ts +++ b/services/workflows-service/src/workflow/workflow.controller.internal.unit.test.ts @@ -11,6 +11,9 @@ import { WorkflowService } from './workflow.service'; import { WorkflowDefinitionModel } from './workflow-definition.model'; import { EndUserModel } from '@/end-user/end-user.model'; import { BusinessModel } from '@/business/business.model'; +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; +import { commonTestingModules } from '@/test/helpers/nest-app-helper'; +import { Test, TestingModule } from '@nestjs/testing'; class FakeWorkflowRuntimeDataRepo extends BaseFakeRepository { constructor() { @@ -71,6 +74,13 @@ describe('WorkflowControllerInternal', () => { let endUserRepo; let eventEmitterSpy; const numbUserInfo = Symbol(); + let testingModule: TestingModule; + + beforeAll(async () => { + testingModule = await Test.createTestingModule({ + imports: commonTestingModules, + }).compile(); + }); beforeEach(() => { const workflowDefinitionRepo = new FakeWorkflowDefinitionRepo(); @@ -93,43 +103,12 @@ describe('WorkflowControllerInternal', () => { {} as any, {} as any, eventEmitterSpy, + testingModule.get(AppLoggerService), ); + const filterService = {} as any; + const rolesBuilder = {} as any; - controller = new WorkflowControllerInternal(service, {} as any); - }); - - describe('.listWorkflowDefinitions', () => { - it('returns workflows by query', async () => { - await controller.createWorkflowDefinition(numbUserInfo, buildWorkflowDeifintion(2)); - await controller.createWorkflowDefinition(numbUserInfo, buildWorkflowDeifintion(3)); - await controller.createWorkflowDefinition(numbUserInfo, buildWorkflowDeifintion(4)); - - const definitions = await controller.listWorkflowDefinitions(numbUserInfo, { - query: { - where: { - name: 'name 3', - }, - }, - }); - - expect(definitions).toHaveLength(1); - expect(definitions[0]).toMatchObject({ id: '3', name: 'name 3' }); - expect(definitions[0]).not.toHaveProperty('updatedAt'); - }); - - it('filters out certain fields', async () => { - await controller.createWorkflowDefinition(numbUserInfo, buildWorkflowDeifintion(3)); - - const definitions = await controller.listWorkflowDefinitions(numbUserInfo, { - query: { - where: { - name: 'name 3', - }, - }, - }); - - expect(definitions[0]).not.toHaveProperty('updatedAt'); - }); + controller = new WorkflowControllerInternal(service, filterService, rolesBuilder); }); describe('.event', () => { diff --git a/services/workflows-service/src/workflow/workflow.module.ts b/services/workflows-service/src/workflow/workflow.module.ts index af55410b20..f9eef5dd77 100644 --- a/services/workflows-service/src/workflow/workflow.module.ts +++ b/services/workflows-service/src/workflow/workflow.module.ts @@ -5,16 +5,19 @@ import { WorkflowControllerInternal } from './workflow.controller.internal'; import { ACLModule } from '@/common/access-control/acl.module'; import { AuthModule } from '../auth/auth.module'; import { WorkflowDefinitionRepository } from './workflow-definition.repository'; -import { WorkflowRuntimeDataRepository } from './workflow-runtime-data.repository'; import { EndUserRepository } from '@/end-user/end-user.repository'; import { WorkflowEventEmitterService } from './workflow-event-emitter.service'; import { DocumentChangedWebhookCaller } from '@/events/document-changed-webhook-caller'; import { BusinessRepository } from '@/business/business.repository'; import { FileService } from '@/providers/file/file.service'; import { StorageService } from '@/storage/storage.service'; -import { StorageModule } from '@/storage/storage.module'; import { FileRepository } from '@/storage/storage.repository'; -import { HttpModule, HttpService } from '@nestjs/axios'; +import { HttpModule } from '@nestjs/axios'; +import { FilterRepository } from '@/filter/filter.repository'; +import { FilterService } from '@/filter/filter.service'; +import { WorkflowRuntimeDataRepository } from '@/workflow/workflow-runtime-data.repository'; +import { UserService } from '@/user/user.service'; +import { UserRepository } from '@/user/user.repository'; @Module({ imports: [ACLModule, forwardRef(() => AuthModule), HttpModule], @@ -30,6 +33,10 @@ import { HttpModule, HttpService } from '@nestjs/axios'; FileService, WorkflowEventEmitterService, DocumentChangedWebhookCaller, + FilterRepository, + FilterService, + UserService, + UserRepository, ], exports: [WorkflowService, ACLModule, AuthModule, StorageService, FileRepository], }) diff --git a/services/workflows-service/src/workflow/workflow.service.ts b/services/workflows-service/src/workflow/workflow.service.ts index 70fb953106..2ee66521f2 100644 --- a/services/workflows-service/src/workflow/workflow.service.ts +++ b/services/workflows-service/src/workflow/workflow.service.ts @@ -5,17 +5,25 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { ApprovalState, + Business, + EndUser, Prisma, WorkflowDefinition, WorkflowRuntimeData, WorkflowRuntimeDataStatus, } from '@prisma/client'; import { WorkflowEventInput } from './dtos/workflow-event-input'; -import { CompleteWorkflowData, RunnableWorkflowData } from './types'; +import { + ListRuntimeDataResult, + ListWorkflowsRuntimeParams, + RunnableWorkflowData, + TWorkflowWithRelations, + WorkflowRuntimeListQueryResult, +} from './types'; import { createWorkflow } from '@ballerine/workflow-node-sdk'; import { WorkflowDefinitionUpdateInput } from './dtos/workflow-definition-update-input'; import { isEqual, merge } from 'lodash'; -import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { WorkflowDefinitionRepository } from './workflow-definition.repository'; import { WorkflowDefinitionCreateDto } from './dtos/workflow-definition-create'; import { WorkflowDefinitionFindManyArgs } from './dtos/workflow-definition-find-many-args'; @@ -24,10 +32,9 @@ import { EndUserRepository } from '@/end-user/end-user.repository'; import { InputJsonValue, IObjectWithId } from '@/types'; import { WorkflowEventEmitterService } from './workflow-event-emitter.service'; import { BusinessRepository } from '@/business/business.repository'; -import Ajv, { Schema } from 'ajv'; +import Ajv from 'ajv'; import addFormats from 'ajv-formats'; -import { DefaultContextSchema } from './schemas/context'; -import * as console from 'console'; +import addKeywords from 'ajv-keywords'; import { TRemoteFileConfig, TS3BucketConfig } from '@/providers/file/types/files-types'; import { z } from 'zod'; import { HttpFileService } from '@/providers/file/file-provider/http-file.service'; @@ -37,13 +44,27 @@ import { StorageService } from '@/storage/storage.service'; import { FileService } from '@/providers/file/file.service'; import * as process from 'process'; import * as crypto from 'crypto'; -import { TDefaultSchemaDocumentPage } from '@/workflow/schemas/default-context-page-schema'; import { AwsS3FileConfig } from '@/providers/file/file-provider/aws-s3-file.config'; import { TFileServiceProvider } from '@/providers/file/types'; import { updateDocuments } from '@/workflow/update-documents'; -import { getDocumentId } from '@/documents/utils'; - +import { WorkflowAssigneeId } from '@/workflow/dtos/workflow-assignee-id'; import { ConfigSchema, WorkflowConfig } from './schemas/zod-schemas'; +import { toPrismaOrderBy } from '@/workflow/utils/toPrismaOrderBy'; +import { toPrismaWhere } from '@/workflow/utils/toPrismaWhere'; +import { + DefaultContextSchema, + getDocumentId, + getDocumentsByCountry, + TDefaultSchemaDocumentPage, +} from '@ballerine/common'; +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; +import { assignIdToDocuments } from '@/workflow/assign-id-to-documents'; +import { + WorkflowAssignee, + WorkflowRuntimeListItemModel, +} from '@/workflow/workflow-runtime-list-item.model'; +import { plainToClass } from 'class-transformer'; +import { SortOrder } from '@/common/query-filters/sort-order'; type TEntityId = string; @@ -52,6 +73,7 @@ const ajv = new Ajv({ coerceTypes: true, }); addFormats(ajv, { formats: ['email', 'uri', 'date'] }); +addKeywords(ajv); export const ResubmissionReason = { BLURRY_IMAGE: 'BLURRY_IMAGE', @@ -84,8 +106,6 @@ const policies = { @Injectable() export class WorkflowService { - private readonly logger = new Logger(WorkflowService.name); - constructor( protected readonly workflowDefinitionRepository: WorkflowDefinitionRepository, protected readonly workflowRuntimeDataRepository: WorkflowRuntimeDataRepository, @@ -94,6 +114,7 @@ export class WorkflowService { protected readonly storageService: StorageService, protected readonly fileService: FileService, protected readonly workflowEventEmitter: WorkflowEventEmitterService, + private readonly logger: AppLoggerService, ) {} async createWorkflowDefinition(data: WorkflowDefinitionCreateDto) { @@ -121,6 +142,63 @@ export class WorkflowService { return await this.workflowRuntimeDataRepository.findById(id, args); } + async getWorkflowByIdWithRelations( + id: string, + args?: Parameters<WorkflowRuntimeDataRepository['findById']>[1], + ) { + const workflow = (await this.workflowRuntimeDataRepository.findById( + id, + args, + )) as TWorkflowWithRelations; + + return this.formatWorkflow(workflow); + } + + private formatWorkflow(workflow: TWorkflowWithRelations) { + const isIndividual = 'endUser' in workflow; + const service = createWorkflow({ + definition: workflow.workflowDefinition as any, + definitionType: workflow.workflowDefinition.definitionType, + workflowContext: { + machineContext: workflow.context, + state: workflow.state, + }, + }); + + return { + ...workflow, + context: { + ...workflow.context, + documents: workflow.context?.documents?.map( + (document: DefaultContextSchema['documents'][number]) => { + const documentsByCountry = getDocumentsByCountry(document?.issuer?.country); + const documentByCountry = documentsByCountry?.find( + doc => getDocumentId(doc, false) === getDocumentId(document, false), + ); + + return { + ...document, + propertiesSchema: documentByCountry?.propertiesSchema ?? {}, + }; + }, + ), + }, + entity: { + id: isIndividual ? workflow.endUser.id : workflow.business.id, + name: isIndividual + ? `${String(workflow.endUser.firstName)} ${String(workflow.endUser.lastName)}` + : workflow.business.companyName, + avatarUrl: isIndividual ? workflow.endUser.avatarUrl : null, + approvalState: isIndividual + ? workflow.endUser.approvalState + : workflow.business.approvalState, + }, + endUser: undefined, + business: undefined, + nextEvents: service.getSnapshot().nextEvents, + }; + } + async getWorkflowRuntimeDataByCorrelationId( id: string, args?: Parameters<WorkflowRuntimeDataRepository['findById']>[1], @@ -144,7 +222,97 @@ export class WorkflowService { assigneeId: true, id: true, status: true, + workflowDefinitionId: true, + }, + }); + } + + async listWorkflowRuntimeDataWithRelations({ + args, + entityType, + orderBy, + page, + filters, + }: { + args: Parameters<WorkflowRuntimeDataRepository['findMany']>[0]; + entityType: 'individuals' | 'businesses'; + orderBy: Parameters<typeof toPrismaOrderBy>[0]; + page: { + number: number; + size: number; + }; + filters?: { + assigneeId?: (string | null)[]; + status?: WorkflowRuntimeDataStatus[]; + }; + }) { + const query = merge( + args, + { + orderBy: toPrismaOrderBy(orderBy, entityType), + where: filters ? toPrismaWhere(filters) : {}, + skip: (page.number - 1) * page.size, + take: page.size, + }, + { + where: + entityType === 'individuals' + ? { + endUserId: { not: null }, + } + : { + businessId: { not: null }, + }, }, + ); + + const totalWorkflowsCount = await this.workflowRuntimeDataRepository.count({ + where: query.where, + }); + + if (page.number > 1 && totalWorkflowsCount < (page.number - 1) * page.size + 1) { + throw new NotFoundException('Page not found'); + } + + const workflows = await this.workflowRuntimeDataRepository.findMany(query); + + return { + data: this.formatWorkflowsRuntimeData(workflows as unknown as TWorkflowWithRelations[]), + meta: { + totalItems: totalWorkflowsCount, + totalPages: Math.max(Math.ceil(totalWorkflowsCount / page.size), 1), + }, + }; + } + + private formatWorkflowsRuntimeData(workflows: TWorkflowWithRelations[]) { + return workflows.map(workflow => { + const isIndividual = 'endUser' in workflow; + + console.log('workflow', workflow); + + return { + id: workflow?.id, + status: workflow?.status, + createdAt: workflow?.createdAt, + entity: { + id: isIndividual ? workflow?.endUser?.id : workflow?.business?.id, + name: isIndividual + ? `${String(workflow?.endUser?.firstName)} ${String(workflow?.endUser?.lastName)}` + : workflow?.business?.companyName, + avatarUrl: isIndividual ? workflow?.endUser?.avatarUrl : null, + approvalState: isIndividual + ? workflow?.endUser?.approvalState + : workflow?.business?.approvalState, + }, + assignee: workflow?.assigneeId + ? { + id: workflow?.assigneeId, + firstName: workflow?.assignee?.firstName, + lastName: workflow?.assignee?.lastName, + } + : null, + }; }); } @@ -154,12 +322,102 @@ export class WorkflowService { }); } - async listFullWorkflowDataByUserId(userId: string): Promise<CompleteWorkflowData[]> { - return (await this.workflowRuntimeDataRepository.findMany({ - // todo refactor - where: { businessId: userId }, + async listFullWorkflowDataByUserId({ + entityId, + entity, + }: { + entityId: string; + entity: TEntityType; + }) { + return await this.workflowRuntimeDataRepository.findMany({ + where: { + [`${entity}Id`]: entityId, + }, include: { workflowDefinition: true }, - })) as CompleteWorkflowData[]; + }); + } + + async listRuntimeData({ + page, + size, + status, + orderBy, + orderDirection, + }: ListWorkflowsRuntimeParams): Promise<ListRuntimeDataResult> { + const query = { + where: { + ...(status ? { status: { in: status } } : undefined), + }, + }; + + const [workflowsRuntimeCount, workflowsRuntime] = await Promise.all([ + this.workflowRuntimeDataRepository.count(query), + this.workflowRuntimeDataRepository.findMany({ + ...query, + skip: page && size ? (page - 1) * size : undefined, + take: size, + include: { + workflowDefinition: true, + assignee: true, + }, + orderBy: this._resolveOrderByParams(orderBy, orderDirection), + }), + ]); + + const result: ListRuntimeDataResult = { + results: this.workflowsRuntimeListItemsFactory(workflowsRuntime), + meta: { + pages: size ? Math.max(Math.ceil(workflowsRuntimeCount / size)) : 0, + total: workflowsRuntimeCount, + }, + }; + + return result; + } + + private _resolveOrderByParams( + orderBy: string | undefined, + orderDirection: SortOrder | undefined, + ): object { + if (!orderBy && !orderDirection) return {}; + + if (orderBy === 'assignee') { + return { + assignee: { + firstName: orderDirection, + }, + }; + } + + if (orderBy === 'workflowDefinitionName') { + return { + workflowDefinition: { + name: orderDirection, + }, + }; + } + + return { + [orderBy as string]: orderDirection, + }; + } + + private workflowsRuntimeListItemsFactory( + workflows: WorkflowRuntimeListQueryResult[], + ): WorkflowRuntimeListItemModel[] { + return workflows.map(workflow => { + const { assignee, workflowDefinition } = workflow; + + return plainToClass( + WorkflowRuntimeListItemModel, + { + ...workflow, + assignee: assignee ? plainToClass(WorkflowAssignee, assignee) : null, + workflowDefinitionName: workflowDefinition?.name || null, + }, + { excludeExtraneousValues: true }, + ); + }); } async listWorkflowDefinitions(args: WorkflowDefinitionFindManyArgs) { @@ -189,34 +447,26 @@ export class WorkflowService { let contextHasChanged, mergedContext; if (data.context) { + data.context.documents = assignIdToDocuments(data.context.documents); contextHasChanged = !isEqual(data.context, runtimeData.context); mergedContext = merge({}, runtimeData.context, data.context); + const context = { ...mergedContext, // @ts-ignore documents: mergedContext?.documents?.map( // @ts-ignore - ({ propertiesSchema: _propertiesSchema, id: _id, ...document }) => document, + // Validating the context should be done without the propertiesSchema + ({ propertiesSchema: _propertiesSchema, ...document }) => document, ), }; - const validateContextSchema = ajv.compile( - (workflowDef?.contextSchema?.schema as Schema) ?? {}, - ); - const isValidContextSchema = validateContextSchema(context); - - if (!isValidContextSchema) { - throw new BadRequestException( - validateContextSchema.errors?.map(({ instancePath, message, ...rest }) => ({ - ...rest, - instancePath, - message: `${instancePath} ${message}`, - })), - ); - } + this.__validateWorkflowDefinitionContext(workflowDef, context); // @ts-ignore - data?.context?.documents?.forEach(({ propertiesSchema, id: _id, ...document }) => { + data?.context?.documents?.forEach(({ propertiesSchema, ...document }) => { + if (!Object.keys(propertiesSchema ?? {})?.length) return; + const validatePropertiesSchema = ajv.compile(propertiesSchema ?? {}); const isValidPropertiesSchema = validatePropertiesSchema(document?.properties); @@ -250,11 +500,12 @@ export class WorkflowService { ['active'].includes(data.status! || runtimeData.status) && workflowDef.config?.completedWhenTasksResolved ) { - const allDocumentsResolved = data.context?.documents?.every( - (document: DefaultContextSchema['documents'][number]) => { + // TODO: Check against `contextSchema` or a policy if the length of documents is equal to the number of tasks defined. + const allDocumentsResolved = + data.context?.documents?.length && + data.context?.documents?.every((document: DefaultContextSchema['documents'][number]) => { return ['approved', 'rejected'].includes(document?.decision?.status as string); - }, - ); + }); data.status = allDocumentsResolved ? 'completed' : data.status! || runtimeData.status; } @@ -265,33 +516,109 @@ export class WorkflowService { this.logger.log('Workflow resolved', { id: workflowRuntimeId }); } - const updateResult = await this.workflowRuntimeDataRepository.updateById(workflowRuntimeId, { - data: { - ...data, - resolvedAt: isResolved ? new Date() : null, - }, - }); + const documentToRevise = data.context?.documents?.find( + ({ decision }: { decision: DefaultContextSchema['documents'][number]['decision'] }) => + decision?.status === 'revision', + ); + let updatedResult; + + if (documentToRevise && !workflowDef.reviewMachineId) { + const parentMachine = await this.workflowRuntimeDataRepository.findById( + runtimeData?.context?.parentMachine?.id, + { + include: { + workflowDefinition: { + select: { + definition: true, + }, + }, + }, + }, + ); + + // Updates the collect documents workflow with the manual review workflow's decision. + await this.workflowRuntimeDataRepository.updateById(parentMachine?.id, { + data: { + status: 'active', + state: parentMachine?.workflowDefinition?.definition?.initial as string, + context: { + ...parentMachine?.context, + documents: parentMachine?.context?.documents?.map((document: any) => { + if (document.id !== documentToRevise.id) return document; + + return { + ...document, + decision: documentToRevise.decision, + }; + }), + }, + }, + }); + + updatedResult = await this.workflowRuntimeDataRepository.updateById(workflowRuntimeId, { + data: { + ...data, + context: { + ...data.context, + parentMachine: { + id: parentMachine?.id, + status: 'active', + }, + }, + resolvedAt: isResolved ? new Date().toISOString() : null, + }, + }); + } else { + updatedResult = await this.workflowRuntimeDataRepository.updateById(workflowRuntimeId, { + data: { + ...data, + resolvedAt: isResolved ? new Date().toISOString() : null, + }, + }); + } if (contextHasChanged) { this.workflowEventEmitter.emit('workflow.context.changed', { - runtimeData: runtimeData, + oldRuntimeData: runtimeData, + updatedRuntimeData: updatedResult, state: currentState as string, - context: mergedContext, entityId: (runtimeData.businessId || runtimeData.endUserId) as string, correlationId: correlationId, }); } // TODO: Move to a separate method - if (data.state) { - if (isFinal && workflowDef.reviewMachineId) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - await this.handleRuntimeFinalState(runtimeData, data.context, workflowDef); - } + if (data.state && isFinal && workflowDef.reviewMachineId) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await this.handleRuntimeFinalState(runtimeData, data.context, workflowDef); } - return updateResult; + return updatedResult; + } + + async assignWorkflowToUser(workflowRuntimeId: string, { assigneeId }: WorkflowAssigneeId) { + const workflowRuntimeData = await this.workflowRuntimeDataRepository.findById( + workflowRuntimeId, + ); + const hasDecision = + workflowRuntimeData?.context?.documents?.length && + workflowRuntimeData?.context?.documents?.every( + (document: DefaultContextSchema['documents'][number]) => !!document?.decision?.status, + ); + + if (hasDecision) { + throw new BadRequestException( + `Workflow with the id of "${workflowRuntimeId}" already has a decision`, + ); + } + + const updatedWorkflowRuntimeData = await this.workflowRuntimeDataRepository.updateById( + workflowRuntimeId, + { data: { assigneeId: assigneeId, assignedAt: new Date() } }, + ); + + return updatedWorkflowRuntimeData; } private async getCorrelationIdFromWorkflow(runtimeData: WorkflowRuntimeData) { @@ -362,7 +689,7 @@ export class WorkflowService { entitySearch.endUserId = runtime.endUserId as string; } - const workflowRuntimeDataExists = await this.workflowRuntimeDataRepository.findOne({ + const manualReviewWorkflow = await this.workflowRuntimeDataRepository.findOne({ where: { ...entitySearch, context: { @@ -372,7 +699,7 @@ export class WorkflowService { }, }); - if (!workflowRuntimeDataExists) { + if (!manualReviewWorkflow) { await this.workflowRuntimeDataRepository.create({ data: { ...entitySearch, @@ -382,34 +709,27 @@ export class WorkflowService { ...context, parentMachine: { id: runtime.id, + status: 'completed', }, }, status: 'active', }, }); } else { - await this.workflowRuntimeDataRepository.updateById(workflowRuntimeDataExists.id, { + await this.workflowRuntimeDataRepository.updateById(manualReviewWorkflow.id, { data: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - context: context as any, + context: { + ...manualReviewWorkflow.context, + parentMachine: { + id: runtime.id, + status: 'completed', + }, + }, }, }); } await this.updateWorkflowRuntimeData(runtime.id, { - ...((runtime.context as { resubmissionReason: string })?.resubmissionReason - ? { - ...entitySearch, - workflowDefinitionVersion: workflow.version, - workflowDefinitionId: workflow.reviewMachineId, - context: { - ...context, - parentMachine: { - id: runtime.id, - }, - }, - } - : {}), status: 'completed', }); } @@ -417,14 +737,36 @@ export class WorkflowService { async resolveIntent( intent: string, entityId: string, - tempEntityType: TEntityType, + entityType: TEntityType, ): Promise<RunnableWorkflowData[]> { const workflowDefinitionResolver = policies[intent as keyof typeof policies]; + const entity = await (async () => { + if (entityType === 'business') return await this.businessRepository.findById(entityId); + if (entityType === 'endUser') return await this.endUserRepository.findById(entityId); + + throw new BadRequestException(`Invalid entity type ${entityType}`); + })(); + const isBusinessEntity = (entity: EndUser | Business): entity is Business => + entityType === 'business'; // TODO: implement logic for multiple workflows const { workflowDefinitionId } = workflowDefinitionResolver()[0]; const context: DefaultContextSchema = { - entity: { ballerineEntityId: entityId, entityType: tempEntityType }, + entity: { + ballerineEntityId: entityId, + type: entityType, + data: { + ...(isBusinessEntity(entity) + ? { + companyName: entity?.companyName, + registrationNumber: entity?.registrationNumber, + } + : { + firstName: entity?.firstName, + lastName: entity?.lastName, + }), + }, + }, documents: [], }; return this.createOrUpdateWorkflowRuntime({ workflowDefinitionId, context }); @@ -442,6 +784,7 @@ export class WorkflowService { const workflowDefinition = await this.workflowDefinitionRepository.findById( workflowDefinitionId, ); + config = merge(workflowDefinition.config, config); let validatedConfig: WorkflowConfig; try { validatedConfig = ConfigSchema.parse(config); @@ -461,24 +804,16 @@ export class WorkflowService { let contextToInsert = structuredClone(context); - if (existingWorkflowRuntimeData) { - contextToInsert.documents = updateDocuments( - existingWorkflowRuntimeData.context.documents, - context.documents, - ); - } - - contextToInsert = await this.__copyFileAndCreate(contextToInsert, entityId); - const entityConnect = { [entityType]: { connect: { id: entityId }, }, }; - let workflowRuntimeData: WorkflowRuntimeData; + let workflowRuntimeData: WorkflowRuntimeData, newWorkflowCreated: boolean; - if (!existingWorkflowRuntimeData) { + if (!existingWorkflowRuntimeData || config?.allowMultipleActiveWorkflows) { + contextToInsert = await this.__copyFileAndCreate(contextToInsert, entityId); workflowRuntimeData = await this.workflowRuntimeDataRepository.create({ data: { ...entityConnect, @@ -493,7 +828,14 @@ export class WorkflowService { }, }, }); + newWorkflowCreated = true; } else { + contextToInsert.documents = updateDocuments( + existingWorkflowRuntimeData.context.documents, + context.documents, + ); + + contextToInsert = await this.__copyFileAndCreate(contextToInsert, entityId); workflowRuntimeData = await this.workflowRuntimeDataRepository.updateById( existingWorkflowRuntimeData.id, { @@ -507,12 +849,14 @@ export class WorkflowService { }, }, ); + newWorkflowCreated = false; } this.logger.log(existingWorkflowRuntimeData ? 'Workflow updated' : 'Workflow created', { workflowRuntimeDataId: workflowRuntimeData.id, entityId, entityType, + newWorkflowCreated, }); return [ @@ -523,6 +867,7 @@ export class WorkflowService { }, ]; } + private async __copyFileAndCreate( context: DefaultContextSchema, entityId: TEntityId, @@ -536,7 +881,6 @@ export class WorkflowService { return { ...context, documents: documentsWithPersistedImages }; } - private async __persistDocumentPagesFiles( document: DefaultContextSchema['documents'][number], entityId: string, @@ -557,8 +901,7 @@ export class WorkflowService { entityId: string, documentPage: TDefaultSchemaDocumentPage, ) { - const documentContext = getDocumentId(document).toLowerCase(); - const remoteFileName = `${documentContext}_${crypto.randomUUID()}.${documentPage.type}`; + const remoteFileName = `${document.id!}_${crypto.randomUUID()}.${documentPage.type}`; const { fromServiceProvider, fromRemoteFileConfig } = this.__fetchFromServiceProviders(documentPage); @@ -648,15 +991,20 @@ export class WorkflowService { workflowDefinition: WorkflowDefinition, context: DefaultContextSchema, ) { - if (workflowDefinition.contextSchema && Object.keys(workflowDefinition.contextSchema).length) { - const validate = ajv.compile((workflowDefinition.contextSchema as any).schema); // TODO: fix type - const validationResult = validate(context); + if (!Object.keys(workflowDefinition?.contextSchema ?? {}).length) return; - if (!validationResult) { - console.log(validate.errors); - throw new BadRequestException('Invalid context', JSON.stringify(validate.errors)); - } - } + const validate = ajv.compile(workflowDefinition?.contextSchema?.schema); // TODO: fix type + const isValid = validate(context); + + if (isValid) return; + + throw new BadRequestException( + validate.errors?.map(({ instancePath, message, ...rest }) => ({ + ...rest, + instancePath, + message: `${instancePath} ${message}`, + })), + ); } async event({ @@ -673,13 +1021,14 @@ export class WorkflowService { const service = createWorkflow({ // eslint-disable-next-line @typescript-eslint/no-explicit-any - definition: workflow.definition as any, + definition: workflow.definition, // eslint-disable-next-line @typescript-eslint/no-explicit-any - definitionType: workflow.definitionType as any, + definitionType: workflow.definitionType, workflowContext: { machineContext: runtimeData.context, state: runtimeData.state, }, + extensions: workflow.extensions, }); await service.sendEvent({ @@ -697,6 +1046,7 @@ export class WorkflowService { to: currentState, }); + // TODO: Update to work with changes related to revision if (type === 'resubmit' && document) { switch (resubmissionReason) { case ResubmissionReason.BLURRY_IMAGE: diff --git a/services/workflows-service/src/workflow/workflow.service.test.ts b/services/workflows-service/src/workflow/workflow.service.unit.test.ts similarity index 93% rename from services/workflows-service/src/workflow/workflow.service.test.ts rename to services/workflows-service/src/workflow/workflow.service.unit.test.ts index 84c75a33b1..f01f07d41b 100644 --- a/services/workflows-service/src/workflow/workflow.service.test.ts +++ b/services/workflows-service/src/workflow/workflow.service.unit.test.ts @@ -11,6 +11,9 @@ import { BaseFakeRepository } from '../../../../test-utils/src/base-fake-reposit import { WorkflowService } from './workflow.service'; import { WorkflowDefinitionModel } from './workflow-definition.model'; import { DocumentChangedWebhookCaller } from '../events/document-changed-webhook-caller'; +import { Test, TestingModule } from '@nestjs/testing'; +import { commonTestingModules } from '@/test/helpers/nest-app-helper'; +import { AppLoggerService } from '@/common/app-logger/app-logger.service'; class FakeWorkflowRuntimeDataRepo extends BaseFakeRepository { constructor() { @@ -80,6 +83,13 @@ describe('WorkflowService', () => { let endUserRepo; const numbUserInfo = Symbol(); let fakeHttpService; + let testingModule: TestingModule; + + beforeAll(async () => { + testingModule = await Test.createTestingModule({ + imports: commonTestingModules, + }).compile(); + }); beforeEach(() => { const workflowDefinitionRepo = new FakeWorkflowDefinitionRepo(); @@ -124,6 +134,7 @@ describe('WorkflowService', () => { fakeHttpService, eventEmitter as any, env as any, + testingModule.get(AppLoggerService), ); service = new WorkflowService( @@ -134,6 +145,7 @@ describe('WorkflowService', () => { {} as any, {} as any, eventEmitter as any, + testingModule.get(AppLoggerService), ); }); @@ -218,6 +230,8 @@ describe('WorkflowService', () => { id: expect.stringMatching(/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}/), eventName: 'workflow.context.document.changed', workflowDefinitionId: '2', + workflowCreatedAt: undefined, + workflowResolvedAt: null, ballerineEntityId: undefined, correlationId: '', apiVersion: 1, @@ -263,6 +277,8 @@ describe('WorkflowService', () => { id: expect.stringMatching(/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}/), eventName: 'workflow.context.document.changed', ballerineEntityId: undefined, + workflowResolvedAt: null, + workflowCreatedAt: undefined, correlationId: '', apiVersion: 1, timestamp: expect.stringMatching(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/), diff --git a/websites/docs/astro.config.mjs b/websites/docs/astro.config.mjs index 94eaa0cae2..c076f3b697 100644 --- a/websites/docs/astro.config.mjs +++ b/websites/docs/astro.config.mjs @@ -14,7 +14,7 @@ export default defineConfig({ solidJs(), mdx(), ], - site: `https://ballerine.io`, + site: `https://ballerine.com`, markdown: { shikiConfig: { wrap: true, diff --git a/websites/docs/package.json b/websites/docs/package.json index 890c07d9f7..7b80c5cb34 100644 --- a/websites/docs/package.json +++ b/websites/docs/package.json @@ -1,5 +1,5 @@ { - "name": "@ballerine/docs", + "name": "@ballerine/docs-site", "type": "module", "version": "0.4.1", "private": true, @@ -19,17 +19,20 @@ }, "dependencies": { "@algolia/client-search": "^4.13.1", - "@astrojs/mdx": "^0.18.3", - "@astrojs/react": "^1.2.2", + "@astrojs/mdx": "0.19.7", + "@astrojs/react": "^2.2.1", "@astrojs/solid-js": "^1.2.3", - "@ballerine/common": "0.0.1", + "@astrojs/tailwind": "^4.0.0", + "@ballerine/common": "0.5.6", "@docsearch/css": "^3.1.0", "@docsearch/react": "^3.1.0", "@types/node": "^18.0.0", "@types/react": "^17.0.45", "@types/react-dom": "^18.0.0", - "astro": "^1.6.10", - "react-dom": "^18.1.0", + "astro": "^2.6.6", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "shiki": "^0.14.2", "solid-js": "^1.4.3" }, "devDependencies": { @@ -39,8 +42,9 @@ "eslint-config-prettier": "^8.5.0", "eslint-config-standard-with-typescript": "^23.0.0", "eslint-plugin-astro": "^0.21.0", + "eslint-plugin-unused-imports": "^2.0.0", "html-escaper": "^3.0.3", "prettier": "^2.8.0", - "prettier-plugin-astro": "^0.7.0" + "prettier-plugin-astro": "^0.10.0" } } diff --git a/websites/docs/public/ballerine-logo.png b/websites/docs/public/ballerine-logo.png index 95f79a16ac..159bd576d7 100644 Binary files a/websites/docs/public/ballerine-logo.png and b/websites/docs/public/ballerine-logo.png differ diff --git a/websites/docs/public/ballerine-logo1.png b/websites/docs/public/ballerine-logo1.png new file mode 100644 index 0000000000..3f5cc9deac Binary files /dev/null and b/websites/docs/public/ballerine-logo1.png differ diff --git a/websites/docs/public/favicon.ico b/websites/docs/public/favicon.ico index 0a8fae6f1f..90cf4eda8e 100644 Binary files a/websites/docs/public/favicon.ico and b/websites/docs/public/favicon.ico differ diff --git a/websites/docs/src/components/Footer/AvatarList.astro b/websites/docs/src/components/Footer/AvatarList.astro index b032516fb2..5663bcf8f4 100644 --- a/websites/docs/src/components/Footer/AvatarList.astro +++ b/websites/docs/src/components/Footer/AvatarList.astro @@ -7,7 +7,7 @@ type Props = { }; const { path } = Astro.props as Props; // Change to the path of the docs repo -const resolvedPath = `examples/docs/${path}`; +const resolvedPath = `websites/docs/${path}`; const url = `https://api.github.com/repos/ballerine-io/ballerine/commits?path=${resolvedPath}`; const commitsURL = `https://github.com/ballerine-io/ballerine/commits/main/${resolvedPath}`; diff --git a/websites/docs/src/components/Header/Logo.astro b/websites/docs/src/components/Header/Logo.astro index 4fcf1d8908..6202acdd9c 100644 --- a/websites/docs/src/components/Header/Logo.astro +++ b/websites/docs/src/components/Header/Logo.astro @@ -5,131 +5,15 @@ type Props = { const { size } = Astro.props as Props; --- -<svg - width={size} - height={size} - viewBox="0 0 29.536709 52.331005" - fill="none" - xmlns="http://www.w3.org/2000/svg" -> - <title>Logo</title> - <path - fill-rule="evenodd" - clip-rule="evenodd" - d="m 0.604457,21.2253 v -1.754 L 0.783188,17.3265 C 0.985753,14.8957 1.50376,12.5016 2.32415,10.2045 L 2.43348,9.8984 C 4.81082,3.24186 11.5869,-0.782381 18.5694,0.315392 l 0.3145,0.049441 C 25.284,1.37104 29.9996,6.88592 29.9996,13.3647 c 0,5.1951 -2.4083,9.8277 -6.1694,12.8422 -0.2185,0.1752 -0.2532,0.4969 -0.0775,0.715 2.6424,3.2799 3.9484,7.6461 3.1642,12.1984 -1.1185,6.4924 -6.2852,11.5427 -12.8034,12.4999 L 8.42571,52.4555 C 8.01008,52.5165 7.59771,52.3258 7.37507,51.9696 2.87349,44.7671 0.511154,36.4337 0.56326,27.9403 L 0.462891,24.5828 Z M 17.2355,8.79989 17.55,8.84933 c 2.223,0.34949 3.8609,2.26507 3.8609,4.51537 0,4.3413 -3.5193,7.8606 -7.8606,7.8606 H 9.67704 c -0.26723,0 -0.48387,-0.2166 -0.48387,-0.4839 V 19.8285 L 9.34223,18.0398 C 9.48292,16.3515 9.8427,14.6887 10.4125,13.0932 l 0.1093,-0.3061 C 11.511,10.0175 14.3303,8.34314 17.2355,8.79989 Z M 11.8612,29.814 H 9.19317 c -0.00368,0 -0.00662,0.0031 -0.00646,0.0068 0.19992,4.5079 1.23989,8.9293 3.05599,13.0391 0.0872,0.1972 0.2959,0.3108 0.5092,0.2795 l 0.1137,-0.0167 c 2.8359,-0.4165 5.0979,-2.6199 5.5873,-5.4605 0.7072,-4.1055 -2.4574,-7.8482 -6.5917,-7.8482 z" - fill="url(#paint0_linear_926_2922)" - id="path95" - clip-path="url(#clipPath473)" - style="fill:url(#paint0_linear_926_2922)" - transform="translate(-0.462891,-0.13574204)"></path> - <path - fill-rule="evenodd" - clip-rule="evenodd" - d="m 0.604457,21.2253 v -1.754 L 0.783188,17.3265 C 0.985753,14.8957 1.50376,12.5016 2.32415,10.2045 L 2.43348,9.8984 C 4.81082,3.24186 11.5869,-0.782381 18.5694,0.315392 l 0.3145,0.049441 C 25.284,1.37104 29.9996,6.88592 29.9996,13.3647 c 0,5.1951 -2.4083,9.8277 -6.1694,12.8422 -0.2185,0.1752 -0.2532,0.4969 -0.0775,0.715 2.6424,3.2799 3.9484,7.6461 3.1642,12.1984 -1.1185,6.4924 -6.2852,11.5427 -12.8034,12.4999 L 8.42571,52.4555 C 8.01008,52.5165 7.59771,52.3258 7.37507,51.9696 2.87349,44.7671 0.511154,36.4337 0.56326,27.9403 L 0.462891,24.5828 Z M 17.2355,8.79989 17.55,8.84933 c 2.223,0.34949 3.8609,2.26507 3.8609,4.51537 0,4.3413 -3.5193,7.8606 -7.8606,7.8606 H 9.67704 c -0.26723,0 -0.48387,-0.2166 -0.48387,-0.4839 V 19.8285 L 9.34223,18.0398 C 9.48292,16.3515 9.8427,14.6887 10.4125,13.0932 l 0.1093,-0.3061 C 11.511,10.0175 14.3303,8.34314 17.2355,8.79989 Z M 11.8612,29.814 H 9.19317 c -0.00368,0 -0.00662,0.0031 -0.00646,0.0068 0.19992,4.5079 1.23989,8.9293 3.05599,13.0391 0.0872,0.1972 0.2959,0.3108 0.5092,0.2795 l 0.1137,-0.0167 c 2.8359,-0.4165 5.0979,-2.6199 5.5873,-5.4605 0.7072,-4.1055 -2.4574,-7.8482 -6.5917,-7.8482 z" - fill="url(#paint1_linear_926_2922)" - id="path97" - clip-path="url(#clipPath469)" - style="fill:url(#paint1_linear_926_2922)" - transform="translate(-0.462891,-0.13574204)"></path> - <path - fill-rule="evenodd" - clip-rule="evenodd" - d="m 0.604457,21.2253 v -1.754 L 0.783188,17.3265 C 0.985753,14.8957 1.50376,12.5016 2.32415,10.2045 L 2.43348,9.8984 C 4.81082,3.24186 11.5869,-0.782381 18.5694,0.315392 l 0.3145,0.049441 C 25.284,1.37104 29.9996,6.88592 29.9996,13.3647 c 0,5.1951 -2.4083,9.8277 -6.1694,12.8422 -0.2185,0.1752 -0.2532,0.4969 -0.0775,0.715 2.6424,3.2799 3.9484,7.6461 3.1642,12.1984 -1.1185,6.4924 -6.2852,11.5427 -12.8034,12.4999 L 8.42571,52.4555 C 8.01008,52.5165 7.59771,52.3258 7.37507,51.9696 2.87349,44.7671 0.511154,36.4337 0.56326,27.9403 L 0.462891,24.5828 Z M 17.2355,8.79989 17.55,8.84933 c 2.223,0.34949 3.8609,2.26507 3.8609,4.51537 0,4.3413 -3.5193,7.8606 -7.8606,7.8606 H 9.67704 c -0.26723,0 -0.48387,-0.2166 -0.48387,-0.4839 V 19.8285 L 9.34223,18.0398 C 9.48292,16.3515 9.8427,14.6887 10.4125,13.0932 l 0.1093,-0.3061 C 11.511,10.0175 14.3303,8.34314 17.2355,8.79989 Z M 11.8612,29.814 H 9.19317 c -0.00368,0 -0.00662,0.0031 -0.00646,0.0068 0.19992,4.5079 1.23989,8.9293 3.05599,13.0391 0.0872,0.1972 0.2959,0.3108 0.5092,0.2795 l 0.1137,-0.0167 c 2.8359,-0.4165 5.0979,-2.6199 5.5873,-5.4605 0.7072,-4.1055 -2.4574,-7.8482 -6.5917,-7.8482 z" - fill="url(#paint2_linear_926_2922)" - id="path99" - clip-path="url(#clipPath465)" - style="fill:url(#paint2_linear_926_2922)" - transform="translate(-0.462891,-0.13574204)"></path> - <path - fill-rule="evenodd" - clip-rule="evenodd" - d="m 0.604457,21.2253 v -1.754 L 0.783188,17.3265 C 0.985753,14.8957 1.50376,12.5016 2.32415,10.2045 L 2.43348,9.8984 C 4.81082,3.24186 11.5869,-0.782381 18.5694,0.315392 l 0.3145,0.049441 C 25.284,1.37104 29.9996,6.88592 29.9996,13.3647 c 0,5.1951 -2.4083,9.8277 -6.1694,12.8422 -0.2185,0.1752 -0.2532,0.4969 -0.0775,0.715 2.6424,3.2799 3.9484,7.6461 3.1642,12.1984 -1.1185,6.4924 -6.2852,11.5427 -12.8034,12.4999 L 8.42571,52.4555 C 8.01008,52.5165 7.59771,52.3258 7.37507,51.9696 2.87349,44.7671 0.511154,36.4337 0.56326,27.9403 L 0.462891,24.5828 Z M 17.2355,8.79989 17.55,8.84933 c 2.223,0.34949 3.8609,2.26507 3.8609,4.51537 0,4.3413 -3.5193,7.8606 -7.8606,7.8606 H 9.67704 c -0.26723,0 -0.48387,-0.2166 -0.48387,-0.4839 V 19.8285 L 9.34223,18.0398 C 9.48292,16.3515 9.8427,14.6887 10.4125,13.0932 l 0.1093,-0.3061 C 11.511,10.0175 14.3303,8.34314 17.2355,8.79989 Z M 11.8612,29.814 H 9.19317 c -0.00368,0 -0.00662,0.0031 -0.00646,0.0068 0.19992,4.5079 1.23989,8.9293 3.05599,13.0391 0.0872,0.1972 0.2959,0.3108 0.5092,0.2795 l 0.1137,-0.0167 c 2.8359,-0.4165 5.0979,-2.6199 5.5873,-5.4605 0.7072,-4.1055 -2.4574,-7.8482 -6.5917,-7.8482 z" - fill="url(#paint3_linear_926_2922)" - style="mix-blend-mode:color;fill:url(#paint3_linear_926_2922)" - id="path101" - clip-path="url(#clipPath461)" - transform="translate(-0.462891,-0.13574204)"></path> - <defs id="defs129"> - <linearGradient - id="paint0_linear_926_2922" - x1="15.2415" - y1="26.305901" - x2="5.1407099" - y2="26.305901" - gradientUnits="userSpaceOnUse" - > - <stop stop-color="#DADADA" id="stop107"></stop> - <stop offset="0.598958" stop-color="#C2C2C2" id="stop109"></stop> - <stop offset="0.599058" stop-color="#DADADA" id="stop111"></stop> - </linearGradient> - <linearGradient - id="paint1_linear_926_2922" - x1="11.3101" - y1="20.9228" - x2="11.2496" - y2="21.346201" - gradientUnits="userSpaceOnUse" - > - <stop stop-color="#DADADA" id="stop114"></stop> - <stop offset="1" stop-color="#DADADA" stop-opacity="0" id="stop116"></stop> - </linearGradient> - <linearGradient - id="paint2_linear_926_2922" - x1="11.7335" - y1="30.055901" - x2="11.673" - y2="29.7535" - gradientUnits="userSpaceOnUse" - > - <stop stop-color="#DADADA" id="stop119"></stop> - <stop offset="1" stop-color="#DEDEDE" stop-opacity="0" id="stop121"></stop> - </linearGradient> - <linearGradient - id="paint3_linear_926_2922" - x1="15.2313" - y1="0.13574199" - x2="15.2313" - y2="52.466801" - gradientUnits="userSpaceOnUse" - > - <stop offset="0.15625" stop-color="#FF00E5" id="stop124"></stop> - <stop offset="1" stop-color="#7000FF" id="stop126"></stop> - </linearGradient> - <clipPath clipPathUnits="userSpaceOnUse" id="clipPath461"> - <path - fill-rule="evenodd" - clip-rule="evenodd" - d="m 0.604457,21.2253 v -1.754 L 0.783188,17.3265 C 0.985753,14.8957 1.50376,12.5016 2.32415,10.2045 L 2.43348,9.8984 C 4.81082,3.24186 11.5869,-0.782381 18.5694,0.315392 l 0.3145,0.049441 C 25.284,1.37104 29.9996,6.88592 29.9996,13.3647 c 0,5.1951 -2.4083,9.8277 -6.1694,12.8422 -0.2185,0.1752 -0.2532,0.4969 -0.0775,0.715 2.6424,3.2799 3.9484,7.6461 3.1642,12.1984 -1.1185,6.4924 -6.2852,11.5427 -12.8034,12.4999 L 8.42571,52.4555 C 8.01008,52.5165 7.59771,52.3258 7.37507,51.9696 2.87349,44.7671 0.511154,36.4337 0.56326,27.9403 L 0.462891,24.5828 Z M 17.2355,8.79989 17.55,8.84933 c 2.223,0.34949 3.8609,2.26507 3.8609,4.51537 0,4.3413 -3.5193,7.8606 -7.8606,7.8606 H 9.67704 c -0.26723,0 -0.48387,-0.2166 -0.48387,-0.4839 V 19.8285 L 9.34223,18.0398 C 9.48292,16.3515 9.8427,14.6887 10.4125,13.0932 l 0.1093,-0.3061 C 11.511,10.0175 14.3303,8.34314 17.2355,8.79989 Z M 11.8612,29.814 H 9.19317 c -0.00368,0 -0.00662,0.0031 -0.00646,0.0068 0.19992,4.5079 1.23989,8.9293 3.05599,13.0391 0.0872,0.1972 0.2959,0.3108 0.5092,0.2795 l 0.1137,-0.0167 c 2.8359,-0.4165 5.0979,-2.6199 5.5873,-5.4605 0.7072,-4.1055 -2.4574,-7.8482 -6.5917,-7.8482 z" - fill="#5200ff" - fill-opacity="0.2" - id="path463"></path> - </clipPath> - <clipPath clipPathUnits="userSpaceOnUse" id="clipPath465"> - <path - fill-rule="evenodd" - clip-rule="evenodd" - d="m 0.604457,21.2253 v -1.754 L 0.783188,17.3265 C 0.985753,14.8957 1.50376,12.5016 2.32415,10.2045 L 2.43348,9.8984 C 4.81082,3.24186 11.5869,-0.782381 18.5694,0.315392 l 0.3145,0.049441 C 25.284,1.37104 29.9996,6.88592 29.9996,13.3647 c 0,5.1951 -2.4083,9.8277 -6.1694,12.8422 -0.2185,0.1752 -0.2532,0.4969 -0.0775,0.715 2.6424,3.2799 3.9484,7.6461 3.1642,12.1984 -1.1185,6.4924 -6.2852,11.5427 -12.8034,12.4999 L 8.42571,52.4555 C 8.01008,52.5165 7.59771,52.3258 7.37507,51.9696 2.87349,44.7671 0.511154,36.4337 0.56326,27.9403 L 0.462891,24.5828 Z M 17.2355,8.79989 17.55,8.84933 c 2.223,0.34949 3.8609,2.26507 3.8609,4.51537 0,4.3413 -3.5193,7.8606 -7.8606,7.8606 H 9.67704 c -0.26723,0 -0.48387,-0.2166 -0.48387,-0.4839 V 19.8285 L 9.34223,18.0398 C 9.48292,16.3515 9.8427,14.6887 10.4125,13.0932 l 0.1093,-0.3061 C 11.511,10.0175 14.3303,8.34314 17.2355,8.79989 Z M 11.8612,29.814 H 9.19317 c -0.00368,0 -0.00662,0.0031 -0.00646,0.0068 0.19992,4.5079 1.23989,8.9293 3.05599,13.0391 0.0872,0.1972 0.2959,0.3108 0.5092,0.2795 l 0.1137,-0.0167 c 2.8359,-0.4165 5.0979,-2.6199 5.5873,-5.4605 0.7072,-4.1055 -2.4574,-7.8482 -6.5917,-7.8482 z" - fill="#5200ff" - fill-opacity="0.2" - id="path467"></path> - </clipPath> - <clipPath clipPathUnits="userSpaceOnUse" id="clipPath469"> - <path - fill-rule="evenodd" - clip-rule="evenodd" - d="m 0.604457,21.2253 v -1.754 L 0.783188,17.3265 C 0.985753,14.8957 1.50376,12.5016 2.32415,10.2045 L 2.43348,9.8984 C 4.81082,3.24186 11.5869,-0.782381 18.5694,0.315392 l 0.3145,0.049441 C 25.284,1.37104 29.9996,6.88592 29.9996,13.3647 c 0,5.1951 -2.4083,9.8277 -6.1694,12.8422 -0.2185,0.1752 -0.2532,0.4969 -0.0775,0.715 2.6424,3.2799 3.9484,7.6461 3.1642,12.1984 -1.1185,6.4924 -6.2852,11.5427 -12.8034,12.4999 L 8.42571,52.4555 C 8.01008,52.5165 7.59771,52.3258 7.37507,51.9696 2.87349,44.7671 0.511154,36.4337 0.56326,27.9403 L 0.462891,24.5828 Z M 17.2355,8.79989 17.55,8.84933 c 2.223,0.34949 3.8609,2.26507 3.8609,4.51537 0,4.3413 -3.5193,7.8606 -7.8606,7.8606 H 9.67704 c -0.26723,0 -0.48387,-0.2166 -0.48387,-0.4839 V 19.8285 L 9.34223,18.0398 C 9.48292,16.3515 9.8427,14.6887 10.4125,13.0932 l 0.1093,-0.3061 C 11.511,10.0175 14.3303,8.34314 17.2355,8.79989 Z M 11.8612,29.814 H 9.19317 c -0.00368,0 -0.00662,0.0031 -0.00646,0.0068 0.19992,4.5079 1.23989,8.9293 3.05599,13.0391 0.0872,0.1972 0.2959,0.3108 0.5092,0.2795 l 0.1137,-0.0167 c 2.8359,-0.4165 5.0979,-2.6199 5.5873,-5.4605 0.7072,-4.1055 -2.4574,-7.8482 -6.5917,-7.8482 z" - fill="#5200ff" - fill-opacity="0.2" - id="path471"></path> - </clipPath> - <clipPath clipPathUnits="userSpaceOnUse" id="clipPath473"> - <path - fill-rule="evenodd" - clip-rule="evenodd" - d="m 0.604457,21.2253 v -1.754 L 0.783188,17.3265 C 0.985753,14.8957 1.50376,12.5016 2.32415,10.2045 L 2.43348,9.8984 C 4.81082,3.24186 11.5869,-0.782381 18.5694,0.315392 l 0.3145,0.049441 C 25.284,1.37104 29.9996,6.88592 29.9996,13.3647 c 0,5.1951 -2.4083,9.8277 -6.1694,12.8422 -0.2185,0.1752 -0.2532,0.4969 -0.0775,0.715 2.6424,3.2799 3.9484,7.6461 3.1642,12.1984 -1.1185,6.4924 -6.2852,11.5427 -12.8034,12.4999 L 8.42571,52.4555 C 8.01008,52.5165 7.59771,52.3258 7.37507,51.9696 2.87349,44.7671 0.511154,36.4337 0.56326,27.9403 L 0.462891,24.5828 Z M 17.2355,8.79989 17.55,8.84933 c 2.223,0.34949 3.8609,2.26507 3.8609,4.51537 0,4.3413 -3.5193,7.8606 -7.8606,7.8606 H 9.67704 c -0.26723,0 -0.48387,-0.2166 -0.48387,-0.4839 V 19.8285 L 9.34223,18.0398 C 9.48292,16.3515 9.8427,14.6887 10.4125,13.0932 l 0.1093,-0.3061 C 11.511,10.0175 14.3303,8.34314 17.2355,8.79989 Z M 11.8612,29.814 H 9.19317 c -0.00368,0 -0.00662,0.0031 -0.00646,0.0068 0.19992,4.5079 1.23989,8.9293 3.05599,13.0391 0.0872,0.1972 0.2959,0.3108 0.5092,0.2795 l 0.1137,-0.0167 c 2.8359,-0.4165 5.0979,-2.6199 5.5873,-5.4605 0.7072,-4.1055 -2.4574,-7.8482 -6.5917,-7.8482 z" - fill="#5200ff" - fill-opacity="0.2" - id="path475"></path> - </clipPath> +<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> + <rect width="40" height="40" rx="3.76471" fill="black"/> + <g clip-path="url(#clip0_2893_10083)"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M13.8199 21.2926C13.9411 22.1339 14.671 22.7385 15.5052 22.7385C15.5869 22.7385 15.6687 22.7329 15.7504 22.7217C16.6832 22.588 17.3315 21.7328 17.1962 20.8107C17.0081 19.5157 16.8787 18.2787 16.8075 17.085C17.2534 17.0133 17.7229 16.9774 18.2123 16.9774C21.6985 16.9774 25.0946 18.7408 26.6615 21.3622L26.6925 21.4124C27.5042 22.666 27.8057 23.733 27.8057 25.3432C27.8057 28.5719 24.5394 31.7645 21.0842 31.9149C19.0409 32.004 17.3837 30.9789 16.5805 30.3604C15.8393 29.7893 14.7656 29.9203 14.1878 30.6557C13.6101 31.3912 13.7425 32.4526 14.4866 33.0236C15.6077 33.8868 17.8861 35.2927 20.8291 35.2941C20.9602 35.294 21.0912 35.2912 21.2251 35.2857C26.4502 35.0601 31.2102 30.3214 31.2102 25.3432C31.2102 23.0839 30.7368 21.4235 29.5784 19.6211C29.2952 19.1502 28.9745 18.7008 28.6206 18.2752C29.5807 16.8185 30.1574 15.3228 30.1574 12.9993C30.1574 8.42495 26.3753 4.70589 21.7674 4.70589C17.1595 4.70589 13.4056 8.39988 13.3802 12.9519C13.2393 15.565 13.383 18.2923 13.8199 21.2926ZM26.0506 15.9695C26.5211 15.1358 26.7473 14.3006 26.7473 12.9993C26.7473 10.2859 24.5152 8.07951 21.7702 8.07951C19.0252 8.07951 16.7932 10.2859 16.7932 12.9993V13.0912C16.782 13.2894 16.7726 13.4885 16.7651 13.6885C17.2345 13.634 17.7166 13.6065 18.2095 13.6065C21.0652 13.6065 23.8278 14.4732 26.0506 15.9695ZM13.4295 28.2934C9.93199 28.2934 7.29408 25.7333 7.29408 22.3401C7.29408 21.0559 7.76473 19.7521 8.69476 18.4651L8.72858 18.4205C8.8385 18.2645 8.95968 18.1113 9.08932 17.9581C9.17184 17.8586 9.42421 17.5587 9.82703 17.1627C9.84186 17.1403 9.85821 17.1175 9.87614 17.0945C10.4482 16.5624 11.0936 16.1835 11.6798 15.9885C12.1251 15.8408 12.6916 15.8269 12.7311 16.4092C12.742 16.5756 12.7545 16.7481 12.767 16.9197C12.8013 17.3925 12.8351 17.8591 12.8354 18.1765C12.8587 18.644 12.6858 19.1188 12.3191 19.4707C11.975 19.8005 11.7708 20.0445 11.7398 20.0816L11.7357 20.0864L11.6962 20.1338C11.6314 20.2062 11.575 20.2786 11.5271 20.3483L11.4708 20.4291L11.4678 20.4332C11.276 20.6991 10.7042 21.4921 10.7042 22.3429C10.7042 23.8612 11.8231 24.9226 13.4295 24.9226C15.2163 24.9226 18.6602 24.4379 21.6391 21.1868L21.6589 21.1673L22.1129 20.6879V20.6854L23.1641 19.5376C23.3219 19.3816 23.7221 19.3677 23.911 19.5376C24.1454 19.7483 24.5762 20.2102 24.9154 21.0177C25.2401 21.6339 25.1554 22.4117 24.6462 22.9502L24.1615 23.4628C20.3033 27.6666 15.7827 28.2962 13.4323 28.2962L13.4295 28.2934Z" fill="white"/> + </g> + <defs> + <clipPath id="clip0_2893_10083"> + <rect width="23.9216" height="30.5882" fill="white" transform="translate(7.29395 4.70593)"/> + </clipPath> </defs> -</svg> + </svg> + diff --git a/websites/docs/src/components/LeftSidebar/LeftSidebar.astro b/websites/docs/src/components/LeftSidebar/LeftSidebar.astro index ad2f1241dc..79c5818a6b 100644 --- a/websites/docs/src/components/LeftSidebar/LeftSidebar.astro +++ b/websites/docs/src/components/LeftSidebar/LeftSidebar.astro @@ -3,7 +3,7 @@ import { getLanguageFromURL } from '../../languages'; import { SIDEBAR } from '../../config'; import SidebarToggleTabGroup from '../SidebarToggleTabGroup/SidebarToggleTabGroup'; import SidebarContent from '../SidebarContent/SidebarContent.astro'; -import { ETab } from '../../types'; +import { Tab } from '../../types'; type Props = { currentPage: string; @@ -16,7 +16,7 @@ const currentPageMatch = currentPage.endsWith(`/`) const langCode = getLanguageFromURL(currentPage); const sidebar = SIDEBAR[langCode]; const [, , tab] = currentPage.split(`/`); -const activeTab: ETab = tab as ETab; +const activeTab: Tab = tab as Tab; --- <nav aria-labelledby="grid-left"> @@ -31,13 +31,13 @@ const activeTab: ETab = tab as ETab; /> <ul class="nav-groups"> <SidebarContent - type={ETab.LEARN} + type={Tab.LEARN} defaultActiveTab={activeTab} sidebarSections={sidebar.learn} currentPageMatch={currentPageMatch} /> <SidebarContent - type={ETab.API} + type={Tab.API} defaultActiveTab={activeTab} sidebarSections={sidebar.api} currentPageMatch={currentPageMatch} diff --git a/websites/docs/src/config.ts b/websites/docs/src/config.ts index bcd6590328..7fb6a779a7 100644 --- a/websites/docs/src/config.ts +++ b/websites/docs/src/config.ts @@ -31,7 +31,7 @@ export const KNOWN_LANGUAGES = { } as const; export const KNOWN_LANGUAGE_CODES = Object.values(KNOWN_LANGUAGES); -export const GITHUB_EDIT_URL = `https://github.com/ballerine-io/ballerine/tree/main/examples/docs`; +export const GITHUB_EDIT_URL = `https://github.com/ballerine-io/ballerine/tree/dev/websites/docs`; export const DISCORD_URL = `https://discord.gg/e2rQE4YygA`; @@ -41,7 +41,7 @@ export const GITHUB_REPO_URL = `https://github.com/ballerine-io/ballerine/`; export const TWITTER_URL = `https://twitter.com/ballerine_io`; -export const EMAIL_ADDRESS = `oss@ballerine.io`; +export const EMAIL_ADDRESS = `oss@ballerine.com`; // See "Algolia" section of the README for more information. export const ALGOLIA = { @@ -65,21 +65,57 @@ export const SIDEBAR: Sidebar = { learn: [ { group: `Getting started`, - sections: [{ text: `UI Flows`, link: `ui-flows` }], + sections: [ + { text: `Introduction`, link: `introduction` }, + { text: `Gloassry`, link: `gloassry` }, + { text: `Installation`, link: `installation` }, + { text: `Deployment`, link: `deployment` }, + ], }, { - group: `Start Here`, + group: `Guides`, sections: [ - { text: `Introduction`, link: `introduction` }, { - text: `Style Guidelines`, - link: `style-guidelines`, + text: `KYB Workflow with External Integrations`, + link: `simple-kyb-guide`, + }, + { + text: `KYC Manual Review Workflow Guide`, + link: `kyc-manual-review-workflow-guide`, + }, + { + text: `Creating a KYC UI Flow`, + link: `creating-a-kyc-flow-and-deploying-it`, + }, + ], + }, + { + group: `Workflows`, + sections: [ + { + text: `Understanding Workflows`, + link: `understanding_workflows`, + }, + { + text: `Workflow Definitions`, + link: `workflow_definitions`, + }, + { + text: `Interacting with Workflows`, + link: `interacting_with_workflows`, + }, + { + text: `Workflows Plugins`, + link: `api-plugin`, }, - { text: `Contributing`, link: `contributing` }, ], }, + // { + // group: `Case Managment`, + // sections: [], + // }, { - group: `Basics`, + group: `UI SDK's`, sections: [ { text: `SDK Events`, link: `sdk-events` }, { text: `SDK Backend Configuration`, link: `sdk-backend-configuration` }, @@ -97,15 +133,18 @@ export const SIDEBAR: Sidebar = { }, ], }, + { - group: `Guides`, + group: `Contributing`, sections: [ { - text: `Creating a KYC flow and deploying it`, - link: `creating-a-kyc-flow-and-deploying-it`, + text: `Style Guidelines`, + link: `style-guidelines`, }, + { text: `Contributing`, link: `contributing` }, ], }, + { group: `Examples`, sections: [ diff --git a/websites/docs/src/pages/en/learn/back-office.md b/websites/docs/src/pages/en/learn/back-office.md index 57f36a4417..f69e828212 100644 --- a/websites/docs/src/pages/en/learn/back-office.md +++ b/websites/docs/src/pages/en/learn/back-office.md @@ -87,7 +87,7 @@ Open cases in the backoffice by sending API requests to the workflow service. ``` 6. Initilazie monorepo: ```sh - pnpm run api-manual-review-example + pnpm run api-flow-example ``` _Now the backoffice will run on http://localhost:5137/, and the workflow service will accept calls at http://localhost:3000/_ diff --git a/websites/docs/src/pages/en/learn/deployment.md b/websites/docs/src/pages/en/learn/deployment.md new file mode 100644 index 0000000000..a4c87cf94d --- /dev/null +++ b/websites/docs/src/pages/en/learn/deployment.md @@ -0,0 +1,122 @@ +--- +title: Deployment +description: +layout: '../../../layouts/MainLayout.astro' +--- +## Deployment Guide + +### Docker Compose Deployment + +1. **Clone the project**: Use Git to clone the Ballerine repository to your local machine: + ```shell + git clone https://github.com/ballerine-io/ballerine.git && cd ballerine + ``` + +2. **Switch to the main branch**: After cloning, switch to the main branch (or the branch you wish to deploy): + ```shell + git checkout main + ``` + +3. **Run Docker Compose**: Now, you can start all services using Docker Compose: + ```shell + docker-compose up -d + ``` + +The application should now be running at the ports defined in your Docker Compose configuration. + +### Kubernetes Deployment (Helm) +# Install ballerine using helm chart + +Ballerine is a collection of services like workflow-service, backendoffice. +In values.yaml we have sections to enable/disable them based on the necessity like below + +``` bash +workflowService: + enabled: true +``` + +## Prerequisites + +- kubernetes cluster +- [helm](https://helm.sh/docs/intro/install/) +- [kubectl](https://storage.googleapis.com/kubernetes-release/release/v1.23.6/bin/linux/amd64/kubectl) preferably 1.24 or less upto 1.23 + +### How to install + +Move to deploy directory + +```bash +cd deploy/helm +``` + +### Setup Postgresql + +#### Install postgresql along with ballerine + +- edit values.yaml + +```bash +## Postgres params +postgresql: + enabled: true + auth: + username: admin + password: admin + postgresPassword: admin + database: postgres +# Local dev purpose +# persistence: +# existingClaim: postgresql-pv-claim +# volumePermissions: +# enabled: true +``` + +#### How to use managed postgresql along with ballerine + +- edit values.yaml + +```bash +## Postgres params +postgresql: + enabled: false +. +. +. +. + applicationConfig: + BCRYPT_SALT: "10" + JWT_SECRET_KEY: "secret" + JWT_EXPIRATION: "10d" + DB_URL: "<Managed DB_URL with databasename>" + DB_USER: "<Managed DB_USER>" + DB_PASSWORD: "<Managed DB_PASSWORD>" + DB_PORT: "5432" +``` + +### Installing Ballerine helm chart + +``` bash +helm install ballerine . -n ballerine --create-namespace -f values.yaml +``` + +### Troubleshooting + +```bash +kubectl get pods -n ballerine +``` + +- Note the pod name of service you wish to trouble shoot + +```bash +kubectl logs <pod> -n ballerine +``` + +- Accessing the application + +```bash +kubectl port-forward svc/<service> -n ballerine 3000:3000 +``` + + +Always refer to the official documentation of Ballerine for more specific configuration and deployment details. + diff --git a/websites/docs/src/pages/en/learn/gloassry.md b/websites/docs/src/pages/en/learn/gloassry.md new file mode 100644 index 0000000000..17d92e1fde --- /dev/null +++ b/websites/docs/src/pages/en/learn/gloassry.md @@ -0,0 +1,91 @@ +--- +title: Gloassry +description: This page provides a glossary of key terms and concepts related to the Ballerine project, offering brief explanations of the system components, processes, etc.. +layout: ../../../layouts/MainLayout.astro +--- +# Glossary + +- **Ballerine**: An open-source user risk decisioning infrastructure that helps companies automate their decisions for customer and business account-opening (KYC, KYB), underwriting, and transaction monitoring. + +- **KYC**: Know Your Customer. It is the process of verifying the identity of customers to ensure they are legitimate and not involved in illegal activities. + +- **KYB**: Know Your Business. Similar to KYC, it is the process of verifying the identity of businesses to ensure they are legitimate and comply with regulations. + +- **Rules & Workflow Engine**: A component of Ballerine that orchestrates and automates the different parts of the system using a flexible rules and workflow engine. + +- **Plugin System**: A component of Ballerine that integrates with third-party vendors, APIs, and data sources to enhance its functionality and capabilities. + +- **Back Office**: A case management dashboard in Ballerine that allows manual decision-making and review of user profiles and documents. + +- **Frontend Headless SDK**: Ballerine's software development kit that enables real-time modification of KYC/KYB frontend user journeys. + +- **User Flows UX/UI**: Community-driven building blocks in Ballerine that provide KYC/KYB/document collection flows and user interfaces for mobile and web applications. + +- **Rule Engine**: A component of Ballerine that leverages various rule types to ensure user compliance with risk policies. + +- **Workflow**: A sequence of steps and actions defined in Ballerine to automate and manage the user risk decisioning process. + +- **Community**: The active and engaged group of developers, users, and contributors who support and contribute to the development and improvement of Ballerine. + +- **Discord**: An online communication platform where the Ballerine community gathers to discuss and collaborate on the project. + +- **Slack**: Another online communication platform used by the Ballerine community for discussions, support, and collaboration. + +- **Issues**: The section on GitHub where users can report bugs, suggest new features, and participate in discussions related to Ballerine. + +- **Pull Request**: A way for developers to contribute to Ballerine by proposing changes to the codebase. + +- **CI**: Continuous Integration. It is a development practice where changes to the codebase are frequently integrated and tested to ensure early detection of issues. + +- **Release**: A specific version of Ballerine that is marked as a stable and official release. + +- **Roadmap**: A high-level plan or timeline that outlines the future development and features of Ballerine. + +- **Contributing**: The process of actively participating and contributing to the Ballerine project, such as providing feedback, reporting bugs, or submitting code changes. + +- **Open Source**: A development approach where the source code of a software project is made freely available for anyone to view, use, modify, and distribute. + +Please note that this glossary provides a brief explanation of key terms related to Ballerine and may not cover all possible definitions or variations of the terms. + + +## Glossary + +**1. Ballerine**: A case management system that allows businesses to optimize manual work, enhance teamwork, and monitor productivity. + +**2. Workflow**: A series of interconnected tasks in Ballerine that make up a process, like KYC (Know Your Customer) or document approval. + +**3. Case**: A specific instance of a workflow in Ballerine, representing a unique process for an individual enduser or business. + +**4. Task**: An individual operation or step within a workflow in Ballerine, such as document selection or review. + +**5. Event**: An occurrence in Ballerine that can initiate tasks, change the state of a case, or trigger workflows. + +**6. UI Component**: An element of the Ballerine user interface, designed to be versatile for various workflow needs. + +**7. State**: A snapshot of a case in Ballerine at a specific point in a workflow, helping track progression and manage tasks. + +**8. User**: An operator or employee managing and operating Ballerine for their organization, such as reviewing cases or making decisions. + +**9. Enduser**: The subject of a case in Ballerine, usually a customer or an individual entity undergoing the process outlined by a workflow. + +**10. Business**: A client entity in Ballerine, undergoing a series of tasks defined in the workflow, similar to an enduser but with potential for more complexity due to multiple associated individuals or documents. + +**11. Rules Engine**: A component of Ballerine that applies predefined business rules to automate decisions within workflows. + +**12. XState**: The library upon which Ballerine's workflows are built, providing state machine and statechart capabilities to model complex workflows. + +**13. Statecharts**: A method of modeling system behavior (workflows) in Ballerine, showing the interaction between states and transitions. + +**14. Document Review**: A task in Ballerine workflows where users review documents submitted by endusers or businesses. + +**15. Manual Review**: A process in Ballerine where users manually evaluate a case, potentially approving, rejecting, or asking for more information. + +**16. Selfie Review**: A task within certain Ballerine workflows where a selfie provided by an enduser is reviewed for validation purposes. + +**17. Final State**: The concluding state of a workflow in Ballerine, marking the completion of a case. + +**18. KYC (Know Your Customer)**: A type of workflow in Ballerine that verifies the identity of endusers, ensuring that they're real and legitimate entities. + +**19. KYB (Know Your Business)**: A type of workflow in Ballerine that verifies the legitimacy of a business, considering multiple UBOs, documents, and other collected information. + +**20. RBAC (Role-Based Access Control)**: A feature in Ballerine that limits system access to authorized users based on their roles within the organization. diff --git a/websites/docs/src/pages/en/learn/installation.md b/websites/docs/src/pages/en/learn/installation.md new file mode 100644 index 0000000000..046f0678cf --- /dev/null +++ b/websites/docs/src/pages/en/learn/installation.md @@ -0,0 +1,51 @@ +--- +title: Ballerine Installation +description: his guide provides a step-by-step process for setting up and running the Ballerine stack on your local environment. +layout: '../../../layouts/MainLayout.astro' +--- + +## Installation Guide + +### Prerequisites +Before you can install and use Ballerine, you will need the following: +## Install prerequisites: + - Node.js (Minimum version 18) (you can install node via NVM: [Install NVM](https://github.com/nvm-sh/nvm)) + - Latest PNPM version (Minimum version 8.0) ([Install PNPM](https://pnpm.io/installation)) + - Docker and docker compose ([Docker](https://docs.docker.com/desktop), [Docker Compose](https://docs.docker.com/compose/install)) + +### Installation Steps + +1. **Clone the project**: Use Git to clone the Ballerine repository to your local machine: + ```shell + git clone https://github.com/ballerine-io/ballerine.git && cd ballerine + ``` + +2. **Switch to the dev branch**: After cloning, switch to the development branch: + ```shell + git checkout dev + ``` + +3. **Install dependencies**: Use pnpm to install all the required dependencies: + ```shell + pnpm install + ``` + +4. **Initialize the monorepo**: The project is set up as a monorepo. Initialize it using the following command: + ```shell + pnpm run monorepo:init + ``` + + +## Run one of the examples, or follow a guide from the [guide sections](https://docs.ballerine.com/en/learn/simple-kyb-guide/): + +**Start the KYC example**: To start the example API, use the following command: + ```shell + pnpm run kyc-manual-review-example + ``` + +After performing these steps, the backoffice should be running on http://localhost:5137/, and the workflow service will be accepting calls at http://localhost:3000/. + +The default username and password for the backoffice are: + +- Username: admin@admin.com +- Password: admin diff --git a/websites/docs/src/pages/en/learn/interacting_with_workflows.md b/websites/docs/src/pages/en/learn/interacting_with_workflows.md new file mode 100644 index 0000000000..c83e07a899 --- /dev/null +++ b/websites/docs/src/pages/en/learn/interacting_with_workflows.md @@ -0,0 +1,19 @@ +--- +title: Interacting with Workflows +description: Interacting with Workflows +layout: ../../../layouts/MainLayout.astro +--- + +Once a workflow is defined, you can create instances of it and interact with them in various ways. Each instance of a workflow is unique, with its own ID and context. + +## Workflow Instances + +A workflow instance represents an actual execution of a workflow with real data. The instance has its own state context, which is updated as it transitions through different states. This context includes any data that needs to be used or modified throughout the execution of the workflow. + +## Sending Events to Workflow Instances + +Workflow instances are event-driven. You interact with a workflow instance by sending events to it. These events could be user actions, system notifications, API responses, etc. + +When an event is sent to a workflow instance, + +--- diff --git a/websites/docs/src/pages/en/learn/introduction.md b/websites/docs/src/pages/en/learn/introduction.md index e52de0b096..0aff709540 100644 --- a/websites/docs/src/pages/en/learn/introduction.md +++ b/websites/docs/src/pages/en/learn/introduction.md @@ -1,15 +1,16 @@ --- -title: Introduction description: Docs intro layout: ../../../layouts/MainLayout.astro --- <div align="center"> +</br> -<a href="https://ballerine.io" title="Ballerine - Open-source Infrastructure for Identity and Risk management."> +<a href="https://ballerine.com" title="Ballerine - Open-source Infrastructure for Identity and Risk management."> <img src="/ballerine-logo.png" alt="Ballerine's website"> - <span class="ballerine-span">Ballerine</span> </a> +</br> +</br> # Open-source Infrastructure for User Identity and Risk Management @@ -29,132 +30,79 @@ layout: ../../../layouts/MainLayout.astro ### Description -Ballerine helps any company verify its customersβ identity while providing an amazing user experience by composing verification processes for any vertical and geography using modular building blocks, components, and 3rd party integrations. +Ballerine is an open-source user risk decisioning infrastructure that helps companies automate their decisions for customer and business account-opening (KYC, KYB), underwriting, and transaction monitoring, using a flexible rules & workflow engine, 3rd party plugin system, manual review back office, and document & information collection frontend flows. -### What can you find in this project? +## Our vision for this project -- KYC/KYB flows and UI in Mobile & web SDK - β Open Source! -- Case management dashboard for users approval/rejection - π Final stages of Open Sourcing! -- Identity & risk vendors orchestration - β³ Almost done -- No-code rule engine to control Frontend and backend flows - π§ WIP +Watch a brief video explaining what we're building. -[See Detailed Roadmap](#roadmap) - -Join our mailing list so you know whenever we release something (like liveliness or the case management back office). +[Watch now](https://youtu.be/0SppYSZOatw) <br/> -<a href="https://www.ballerine.io/mailing-list" title="Ballerine - Request Access"> - <img width="160px" src="https://blrn-staging-assets.s3.eu-central-1.amazonaws.com/email-updates.png" alt="Ballerine's Early Access"> +<a href="https://youtu.be/0SppYSZOatw" title="Ballerine Demo"> + <img src="https://uploads-ssl.webflow.com/62a3bad46800eb4715b2faf1/646cb35bbee1708f66aa223a_vision%20thumbnail.png" alt="Demo video"> </a> ---- -## Why Open Source KYC/KYB & Risk stack? +## Features +View each component's current state in the [roadmap](#roadmap) below. +#### Released - π +- [**Back office**](https://github.com/ballerine-io/ballerine/blob/main/websites/docs/src/pages/en/learn/back-office.md) - Case management dashboard for manual decision-making. -The goal is to allow any company to manage user identity and risk in a way that suits them and their unique changing needs. -Main Open Source benefits: +- **Workflow engine** - Orchestrates and automates the different system's parts. -- **Future proof** - modular and extendable building blocks. -- **Global** - Multiple vendors accessible in one UI and case management dashboard. -- **White label** - Customizable UX and UI. -- **Community** - Use what others have built, contribute yourself, and leverage community maintenance. +#### In Development/testing - π¨ +- **Plugin system** - Integrates with 3rd-party vendors, APIs, and data sources. -<details> +- **Rule engine** - Leverage various rule types to ensure user compliance with your risk policy. -<summary> -See some examples of what you can do with it -</summary> +#### Planned on roadmap - π +- **Frontend headless SDK**- Real-time modification of KYC/KYB frontend user journeys. -- Use different vendors for different audiences - all modules are 100% vendor agnostic. -- Create your own low-cost KYC with AWS Rekognition, Google vision, and other ML tools. -- Collect documents in a KYB flow. -- Implement and modify a case management for user approval/rejection. -- And more. +#### Community driven building blocks - π +- [**User flows UX/UI**](https://github.com/ballerine-io/ballerine/blob/main/websites/docs/src/pages/en/learn/kit.md) - KYC/KYB/Document collection flows and UI via mobile & web SDK. -If you currently don't have a commercial agreement with KYC vendors, you can use some of the vendors we already integrated with (Identity verification, AML check, etc.), to quickly start processing user's KYC requests. To do so please contact us at oss@ballerine.io. -</details> +## Why Open Source? -## Roadmap +We believe in enabling companies to manage user identity and risk according to their unique and evolving requirements. Ballerine empowers you to create decisioning processes that are right for you. It is flexible, future-proof, easy to implement, secure, and supported by a robust community. -Click below to tell us what we should work on next by creating feature requests or upvoting existing one. +#### Explore What You Can Do With Ballerine -</br> -<a href="https://ballerine.canny.io/" title="Ballerine - feature requests"> - <img width="180px" src="https://blrn-staging-assets.s3.eu-central-1.amazonaws.com/vote%20features.png" alt="Ballerine - feature requests"> -</a> +- **Dynamic Experience:** Adaptive user journeys that modify in real-time based on the user's risk. +- **Data Ownership:** Self-host on-premise to keep sensitive data within your infrastructure. +- **Global Orchestration:** Add/change vendors and data sources to cater to users from multiple countries. +- **Cost Reduction:** Retain control over vendor relationships, costs, and communication. +- And More. + +## Try Ballerine Now -#### General - -- [ ] Documentation Portal -- [ ] Roadmap Community Voting System - -#### Onboarding Suite (KYC/KYB/General Document Collection) - -_Please use the voting system if you think we should prioritize higher a specific chunk_ - -- [ ] SDKs _(WIP)_ - - [x] ~~Open sourcing Web SDK~~ - - [x] ~~UI Customization~~ - - [x] ~~Flow Customization~~ - - [x] ~~Document collection~~ - - [x] ~~Selfie~~ - - [x] ~~KYC/B Templates~~ - - [x] ~~Vendor/Backend Agnostic~~ - - [ ] Web liveliness step - - [x] Open-sourcing Android SDK - - [x] ~~Webview Integration~~ - - [x] ~~Native Camera Option~~ - - [ ] Native liveliness _(WIP)_ - - [ ] Open sourcing iOS SDK _(WIP)_ - - [x] ~~Webview Integration~~ - - [x] ~~Native Camera Option~~ -- [ ] Backoffice - - [ ] Open Sourcing Case Management - - [x] User Approval Queues - - [ ] Operator Collaborations - - [x] Backend Agnostic - - [x] Vendor Agnostic - - [ ] Transaction Approval Queues -- [ ] Dashboard - - [ ] Data pipelines (Orchestrator) _(WIP)_ - - [ ] Plugin System - - Integrations - - [x] Veriff - - [x] AWS Rekognition - - [x] Face Match API - - [x] Document Classification - - [ ] Google Vision - - [ ] Document Classification - - [x] Vision OCR - - [ ] No-Code Flow Builder - -#### Risk Suite - -_Please use the voting system if you think we should prioritize higher a specific chunk_ - -- [ ] SDKs _(WIP)_ - - [x] ~~Open sourcing Web SDK~~ - - [ ] Behavior data for fraud detection _(WIP)_ - - [ ] Risk-Based KYC/Step-up KYC Templates _(WIP)_ - - [x] Open-sourcing Android SDK - - [ ] Behavior data for fraud detection _(WIP)_ - - [ ] Open sourcing iOS SDK _(WIP)_ - - [x] ~~Webview Integration~~ - - [x] ~~Native Camera Option~~ -- [ ] Backoffice - - [ ] Open Sourcing Case Management - - [ ] Operator Collaborations - - [ ] Transaction Approval Queues -- [ ] Dashboard - - [ ] Data pipelines (Orchestrator) _(WIP)_ - - Integrations - - [ ] AWS - - Fraud Detection - - [ ] Account Take Over - - [ ] Rule/Risk Engine _(WIP)_ +**In the following example you can test a simple form of the following infrastructure capabilities** +1. Document collection flow controlled by our **Headless SDK**. +2. A manual review case management **Back Office**. +3. Live communication between the parts using a **Workflow Engine**, that also defines the process steps. +4. A simple JSON containing **Risk Rules** that are checked during the flow. + +**Parts of the system you might look for but are not in THIS demo:** +- Advanced Back Office - To try it out go to our [Case management back office page](https://github.com/ballerine-io/ballerine/blob/main/websites/docs/src/pages/en/learn/back-office.md). +- KYC/KYB UI flows - To try it out go to our [Community driven building blocks page](https://github.com/ballerine-io/ballerine/blob/main/websites/docs/src/pages/en/learn/kit.md). +- Our Plugin System and Rule Engine are still under construction and will soon be released. + + +**Demo example video** + +Watch a video of how the demo works, with explanations: +[Watch now](https://youtu.be/EzBXhUM7gb8) + +<br/> + +<a href="https://youtu.be/EzBXhUM7gb8" title="Ballerine Demo"> + <img src="https://uploads-ssl.webflow.com/62a3bad46800eb4715b2faf1/646b32fd3d69c9698cd511a1_vid%20thumbnail.png" alt="Demo video"> +</a> + +<br/> #### Get to know when we release more parts Leave us your email on our mailing list and we'll let you know whenever we release a feature or improvement (like liveliness on the camera or the case management back office). @@ -169,4 +117,4 @@ Leave us your email on our mailing list and we'll let you know whenever we relea --- <i>As you can see, most apps and packages are still private git submodules. We are working on migrating them to this monorepo.</i>\ -<i>If you already want to start using them or want to get involved - reach out to us at [oss@ballerine.io](mailto:oss@ballerine.io).</i> +<i>If you already want to start using them or want to get involved - reach out to us at [oss@ballerine.com](mailto:oss@ballerine.com).</i> diff --git a/websites/docs/src/pages/en/learn/kyc-manual-review-workflow-guide.md b/websites/docs/src/pages/en/learn/kyc-manual-review-workflow-guide.md new file mode 100644 index 0000000000..ed2898b85f --- /dev/null +++ b/websites/docs/src/pages/en/learn/kyc-manual-review-workflow-guide.md @@ -0,0 +1,70 @@ +--- +title: KYC Manual Review Workflow Guide +description: KYC Manual Review Workflow Guide +layout: ../../../layouts/MainLayout.astro +--- + +In this guide, we will go through the KYC Manual Review workflow step-by-step. This workflow involves collecting KYC data through a user interface, reviewing the collected data in the backoffice application, and then reflecting the decision made in the backoffice in the user interface. + +## Prerequisites + +Before we start, please make sure you have all the necessary dependencies installed. If you haven't done so already, you can find the installation instructions on our installation page. + +## Running the KYC Manual Review Workflow + +First, we need to start the backend services and the user interface for the KYC Manual Review workflow. You can do this by running the following command: + +```sh +pnpm kyc-manual-review-example +``` + +After running this command, your default browser should automatically open two tabs. One for the KYC UI, and another for the backoffice application. + +If the tabs didn't open automatically, please manually navigate to the links provided in the terminal output. + +## The KYC Data Collection Flow + +In the KYC UI tab, click on the "Start KYC" button to start the data collection process. This process involves filling in personal details, uploading required documents, and finally submitting the data for review. + +As you navigate through the data collection process, your progress will be automatically saved in the backend. This feature allows users to pick up where they left off, even if they continue the process on a different device. + +Here is a brief overview of the states involved in this process: + +- `welcome`: The initial state where the user starts the KYC process. +- `document_selection`: The user selects the documents to upload. +- `document_photo`: The user uploads photos of the selected documents. +- `document_review`: The user reviews the uploaded documents. +- `selfie`: The user takes and uploads a selfie. +- `selfie_review`: The user reviews the uploaded selfie. +- `final`: The final state indicating the completion of the data collection process. + +All these states are part of a [statechart](https://xstate.js.org/docs/guides/statecharts.html#statechart-example) defined in the client application, orchestrated by the headless SDK. + +The states `document_review`, `document_selection`, and `final` are persisted in the backend, allowing the user's progress to be saved and resumed later. The state `document_photo` is a submit state, meaning that when the user moves from this state to the next, the workflow backend is informed of the transition. + +## Reviewing KYC Data in the Backoffice + +After the user submits the KYC data, a new case will appear in the backoffice application. In the backoffice, you can review the submitted data, and decide whether to approve, reject, or ask the user to resubmit some documents. For example, you might ask the user to resubmit a document if the uploaded photo is blurry. + +To review a case: + +1. Go to the backoffice tab in your browser. +2. Click on the new case that appeared after the user submitted the KYC data. +3. Review the data and decide whether to approve, reject, or ask for resubmission. + +## Reflecting the Decision in the KYC UI + +After you make a decision in the backoffice, the workflow context will be updated accordingly. The updated context will then be reflected in the KYC UI. + +To see the result of the review: + +1. Go back to the KYC UI tab in your browser. +2. You will see the result of the review, whether the KYC data was approved, rejected, or needs to be resubmitted. + +Remember: this is an example workflow showcasing our system's capabilities. In real-world applications, the workflow can be + + much more complex, involving multiple review stages, automatic data checks, integration with external services, and much more. + +Enjoy exploring our system, and please feel free to reach out if you have any questions or run into any issues! + +--- diff --git a/websites/docs/src/pages/en/learn/plugins.md b/websites/docs/src/pages/en/learn/plugins.md new file mode 100644 index 0000000000..c9db50b6e6 --- /dev/null +++ b/websites/docs/src/pages/en/learn/plugins.md @@ -0,0 +1,176 @@ +--- +title: Plugins +description: Learn how to configure a plugin for your workflow +layout: ../../../layouts/MainLayout.astro +--- + +____ +## API Plugin +### Description + +The API Plugin is a flexible extension that can be added to your workflow definition. It allows you to configure and make API requests directly from your workflows.<br>This guide will help you understand how to set up and use the API Plugin in your workflows. + +### Overview +The API Plugin serves several functions in your workflow: + +- Request Formatting: It allows you to shape the API request in the format your endpoint requires. +- Request Validation: You can validate the formatted request before sending it. +- API Call: It performs the actual API call. +- Response Formatting: It formats the received response in a way that suits your workflow. +- Response Validation: The formatted response is validated before further use. +- State Transitioning: Depending on the API response, it can move the workflow to different states. + +### Configuration + +- `name` (Required) - The name of the plugin. the name will also be used as the Api Plugin's response key in the workflow's context. +- `url` (Required) - The URL destination which the Api Plugin will generate the call to.<br>p.s you can transform the the url by wrapping the require variable path with Braces e.g.`https://example.com/identity/{entity.id}/location` +- `method` (Required) - The method of the Api Request ('POST' | 'PUT' | 'PATCH' | 'DELETE' | 'GET') +- `headers` (Optional) - The headers which the Api Plugin uses in order generate the call with (default: `{"Content-Type": "application/json"}`). you can also use the header's values as a secret when it is set. e.g. `{"Authorization": "secret.API_AUTHORIZATION_KEY"}` +- `stateNames` (Required) - The state names from Which the api request will be made from. +- `request` (Required) - The request Transformation and Validation configuration. + - `transform` (Required) - The request wrapper of the Transformer and Mapping configuration. + - `transformer` (Required) - The transformer function which will be used to transform the request before it is sent. (default [jmespath](https://jmespath.org/)) + - `mapping` (Required) - The transformation logic which will be used in order to transform the request before it is sent. + - `schema` (Optional) - The wrapper of the Validator configuration and Validation schema. + - `validator` - The validator function which will be used to validate the request after it is transformed and before it is sent. (default [json-schema](https://json-schema.org/)) + - `schema` - The Validation schema that the validator uses. +- `response` (Required) - The response wrapper of the Transformer and Mapping configuration. + - `transform` (Required) - The response wrapper of the Transformer and Mapping configuration. + - `transformer` (Required) - The transformer function which will be used to transform the response's body after it is received. (default [jmespath](https://jmespath.org/)) + - `mapping` (Required) - The transformation logic which will be used in order to transform the response after it is received. + - `schema` (Optional) - The wrapper of the Validator configuration and Validation schema. + - `validator` - The validator function which will be used to validate the response after it is transformed it is persisted to the context. (default [json-schema](https://json-schema.org/)) + - `schema` - The Validation schema that the validator uses. +- `successAction` (Required) - The State to which will the action will be transition if the api request was successful and it passed all the validations. +- `errorAction` (Required) - The State which the workflow will transition to if the api request failed or did not passed all the validations. + +### Code Example + +The following Plugin can be used to configure an extension in the workflow [configuration](#configuration) - [API reference](/en/learn/workflow_definitions) + +```json +{ + "name": "business_data_vendor", + "url": "https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/business_test_eu.json", + "method": "GET", + "stateNames": ["check_business_details"], + "successAction": "API_CALL_SUCCESS", + "errorAction": "API_CALL_ERROR", + "request": { + "transform": { + "transformer": "jmespath", + "mapping": "{ business_name: entity.data.companyName, registration_number: entity.data.registrationNumber}" + }, + "schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "business_name": { "type": "string" }, + "registration_number": { "type": "string" } + }, + "required": ["business_name", "registration_number"] + } + }, + "response": { + "transform": { + "transformer": "jmespath", + "mapping": "@" + }, + "schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "business_details": { + "type": "object", + "properties": { + "registered_name": { "type": "string" }, + "registration_number": { "type": "string" }, + "address": { "type": "object" }, + "contact_number": { "type": "string" } + } + }, + "name_fuzziness_score": { "type": "number", "minimum": 0, "maximum": 1 } + } + } + } +} +``` + +This configuration makes a GET request to an external API, transforms and validates the request and response data, and transitions to appropriate states based on the API call's success or failure. + +Understanding and leveraging the flexibility of the API Plugin can significantly expand the capabilities of your workflows. It allows you to connect your workflows with external APIs, retrieve and manipulate data, and make dynamic decisions based on the results. + +____ +# Webhook Plugin +### Description + +The Webhook Plugin is a handy extension that can be added to your workflow definition. It allows you to configure outgoing webhooks directly from your workflows to your required destination. +This guide will help you understand how to set up and use the Webhook Plugin in your workflows. + +Overview +The Webhook Plugin serves several functions in your workflow: + +Request Formatting: It enables you to shape the webhook request to match your endpoint's needs. +Request Validation: It lets you verify the formatted request before sending it out. +Webhook Call: It makes the actual webhook call. +Webhook Monitoring: It listens for a specific state in the workflow to initiate the webhook. + +The primary difference between the [API Plugin](/en/learn/api-plugin) and the Webhook Plugin is that the Webhook Plugin does not directly handle responses or transition states. Instead, it listens to specific states in the workflow to trigger webhook calls. + +### Configuration + +- `name` (Required) - The name of the plugin. the name will also be used as the Api Plugin's response key in the workflow's context. +- `url` (Required) - The URL destination which the Api Plugin will generate the call to.<br>p.s you can transform the the url by wrapping the require variable path with Braces e.g.`https://example.com/identity/{entity.id}/location` +- `method` (Required) - The method of the Api Request ('POST' | 'PUT' | 'PATCH' | 'DELETE' | 'GET') +- `headers` (Optional) - The headers which the Api Plugin uses in order generate the call with (default: `{"Content-Type": "application/json"}`). you can also use the header's values as a secret when it is set. e.g. `{"Authorization": "secret.API_AUTHORIZATION_KEY"}` +- `stateNames` (Required) - The state names from Which the api request will be made from. +- `request` (Required) - The request Transformation and Validation configuration. + - `transform` (Required) - The request wrapper of the Transformer and Mapping configuration. + - `transformer` (Required) - The transformer function which will be used to transform the request before it is sent. (default [jmespath](https://jmespath.org/)) + - `mapping` (Required) - The transformation logic which will be used in order to transform the request before it is sent. + - `schema` (Optional) - The wrapper of the Validator configuration and Validation schema. + - `validator` - The validator function which will be used to validate the request after it is transformed and before it is sent. (default [json-schema](https://json-schema.org/)) + - `schema` - The Validation schema that the validator uses. + +### Code Example + +The following Plugin can be used to configure an extension in the workflow [configuration](#configuration) - [API reference](/en/learn/workflow_definitions) + +```json + { + "name": "finish_webhook", + "url": "https://webhook.site/3c48b14f-1a70-4f73-9385-fab2d0db0db8", + "method": "POST", + "stateNames": [ + "auto_approve", + "approve", + "reject" + ], + "headers": { + "authorization": "Bearer {secret.BUSINESS_DATA__VENDOR_API_KEY}" + }, + "request": { + "transform": { + "transformer": "jmespath", + "mapping": "{success_result: pluginsOutput.business_data_vendor}" + } + } + }, + { + "name": "fail_webhook", + "url": "https://webhook.site/3c48b14f-1a70-4f73-9385-fab2d0db0db8", + "method": "POST", + "stateNames": [ + "auto_reject" + ], + "request": { + "transform": { + "transformer": "jmespath", + "mapping": "{failing_result: @}" + } + } + } + +``` + +By integrating the Webhook Plugin into your workflows, you can easily set up outgoing webhooks to interact with external systems based on the workflow's state, thereby extending the power and flexibility of your workflows. diff --git a/websites/docs/src/pages/en/learn/simple-kyb-guide.mdx b/websites/docs/src/pages/en/learn/simple-kyb-guide.mdx new file mode 100644 index 0000000000..99a4a6f9f6 --- /dev/null +++ b/websites/docs/src/pages/en/learn/simple-kyb-guide.mdx @@ -0,0 +1,323 @@ +--- +title: KYB Workflow with External Integrations +description: Learn how to create a KYC flow, customize its UI, and deploy it +layout: ../../../layouts/MainLayout.astro +--- + +This guide will walk you through implementing a Know Your Business (KYB) workflow using Ballerine's system. + +The capabilities that will be showcased can be utilized to: +1. Create decisioning workflows. +2. Create and configure rules inside a workflow. +3. Use external sources as part of a workflow. + +We'll start with a simple onboarding workflow for a company, enriching it's data using an external source, and send it to a manual review in Ballerine's back office. Then, we'll show you how to adjust the rules for automatic approval, skipping the manual approval step. + + +## Preparing Your Environment + +Before we proceed with the workflow execution, we need to set up our environment. + +Please follow the [installation guide](/en/learn/installation) to install all the project dependencies. +This guide will walk you through all the necessary steps to get your system ready for running the Ballerine projects. + +Once you've installed all the dependencies, run the following command to start both the workflow service and the backoffice (case management project): + +```bash +pnpm api-flow-example +``` + +The `api-flow-example` is a script that runs two essential services: + +1. **Workflow service**: Handles the main business logic for workflows, from execution to transitioning between different states. + +2. **Backoffice**: Acts as the case management system where you can manually review cases, approve or reject them based on the information provided. +It will run at [http://localhost:5137/](http://localhost:5137/). +To log in, use the following credentials: + + * **Username:** admin@admin.com + * **Password:** admin + +With your environment set up and both services running, you're ready to implement and run the KYB workflow. +Let's proceed to the next section. + +## Workflow Definition + +The workflow we're going to use is predefined, meaning it is already inserted into the database during the seeding process. + +This workflow is defined using a statechart definition implemented under the hood with [XState](https://xstate.js.org/docs/). +If you're unfamiliar with state machines or XState, we recommend reviewing the XState documentation for a detailed explanation. + +Here is the complete workflow definition: +(See the full file on GitHub [Seed Workflow](https://github.com/ballerine-io/ballerine/blob/dev/services/workflows-service/scripts/workflows/e2e-dynamic-url-example.ts)) + +```json +{ + "id": "dynamic_external_request_example", + "name": "dynamic_external_request_example", + "version": 1, + "definitionType": "statechart-json", + "definition": { + "id": "kyb_example_v1", + "initial": "idle", + "states": { + "idle": { + "on": { + "start": "check_business_details" + } + }, + "check_business_details": { ... }, + "manual_review": { ... }, + "auto_approve": { ... }, + "auto_reject": { ... }, + "reject": { ... }, + "approve": { ... }, + "revision": { ... } + } + }, + "extensions": { ... } +} + +``` + +Here is the workflow vizualztion in xState visualizer: + + +Let's break down some crucial parts: + +1. **State Machine Definition**: The core of our workflow is a state machine that defines various states and the conditions for transitions between them. +This definition is based on the XState library. + +```json +"definition": { + "id": 'kyb_example_v1', + "predictableActionArguments": true, + "initial": 'idle', + ... +}, +``` + +2. **Check Business Details State**: This state is responsible for making an external call to enrich business data and calculate a fuzziness score for the company name. + +```json +"check_business_details": { + ... +{ + "target": "auto_approve", + "cond": { + "type": "json-logic", + "rule": { + "or": [ + { + "==": [ + { "var": "context.entity.companyName" }, + { "var": "response.data.registered_name" }, + ], + }, + { + ">=": [ + { "var": "context.external_request_example.data.name_fuzziness_score" }, + 0.8, + ], + }, + ], + }, + }, +} + + ... +}, +``` + +3. **API Plugins**: API plugins allow the workflow to interact with external services. For example, 'business_data_vendor' plugin fetches business data from an external URL. + +```json +"apiPlugins": [ + { + "name": 'business_data_vendor', + "url": 'https://simple-kyb-demo.s3.eu-central-1.amazonaws.com/mock-data/{api_url}.json', + ... + }, + ... +] +``` + +## Running the KYB Workflow + +### Creating a New Workflow Instance + +To create a new workflow instance, execute the following `curl` command: + +```bash +curl -X POST --location 'http://localhost:3000/api/v1/external/workflows/run' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Bearer secret' \ +--data '{ + "workflowId": "dynamic_external_request_example", + "context": { + "entity": { + "id": "company_id_22113243", + "data": { + "companyName": "ABC Company", + "registrationNumber": "123456789", + "legalForm": "Business Services", + "countryOfIncorporation": "United States", + "dateOfIncorporation": "2003-05-12T14:36:24.173Z", + "address": "123 Main Street", + "phoneNumber": "+1 (555) 123-4567", + "email": "info@abccompany.com", + "website": "http://www.abccompany.com", + "industry": "Innovative Solutions", + "taxIdentificationNumber": "987654321012", + "vatNumber": "123456789", + "numberOfEmployees": 250, + "businessPurpose": "Cutting-edge Technology", + "approvalState": "NEW", + "additionalInfo": { + "customParam": "customValue" + } + }, + "type": "business" + }, + "documents": [ + { + "category": "proof_of_address", + "type": "water_bill", + "issuer": { + "country": "GH" + }, + "pages": [ + { + "provider": "http", + "uri": "https://www.africau.edu/images/default/sample.pdf", + "metadata": { + "side": "front", + "pageNumber": "1" + }, + "type": "pdf" + } + ], + "properties": { + "docNumber": "1234", + "userAddress": "Turkey, buhgdawe" + }, + "version": "1", + "issuingVersion": 1 + } + ], + "api_config": { + "url": "business_test_eu", + "special_header": "very_special" + } + } +}' +``` + +This command creates a new workflow instance, which upon successful execution, provides you with a response containing `workflowDefinitionId`, `workflowRuntimeId`, and `ballerineEntityId`. +An example of the response: + +```json +{ + "workflowDefinitionId":"dynamic_external_request_example", + "workflowRuntimeId":"[TAKE THIS VALUE TO THE THE FOLLOWING REQUEST]", + "ballerineEntityId":"clj6uxa650006ruhvbcfvvhgh" +} +``` + +### Sending Event to a Workflow + +To initiate the workflow, send the `start` event. +This action triggers the data enrichment process. + +```bash +curl -X POST 'http://localhost:3000/api/v1/external/workflows/[WORKFLOW RUNTIME ID HERE]/send-event' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Bearer secret' \ +--data '{"name": "start"}' + +``` + +## What Happens Next? + +After you send the `start` event to the workflow, the system moves the workflow to the `check_business_details` state. +In this state, it triggers the `external_request_example` plugin, which makes a GET request to an external URL to enrich the business data. +The plugin also calculates a fuzziness score between the registered business name and the one provided in the input. + +The workflow then evaluates several conditions based on the fuzziness score. +Let's take a closer look at one of them: + +```json +{ + "target": "manual_review", + "cond": { + "type": "json-logic", + "options": { + "rule": { + ">": [ + { "var": "pluginsOutput.external_request_example.name_fuzziness_score" }, + 0.5 + ] + }, + "onFailed": { "manualReviewReason": "name not matching ... " } + } + } +} +``` +Here, the workflow checks whether the fuzziness score is above 0.5. +If the score is above this threshold, the workflow moves to the `manual_review` state, and the reason for manual review is set to "name not matching ...". + +In case the workflow move to the `manual_review` state, you can then proceed to the backoffice (http://localhost:5137/) using your login credentials (admin@admin.com, admin). +There, you will see the case awaiting your review. +You have the power to either approve or reject it based on the information provided. +Once you make your decision, the system will notify you of the outcome via a webhook event. + +Let's learn more about how to customize the workflow to suit your specific needs in the next section. +# Adjusting the Workflow + +Let's tweak the workflow for automatic approval and for sending updates to your backend on workflow completion. + +### Setting Up Webhooks + +Visit [Webhook.site](https://webhook.site) to get your webhook URLs. Replace the `url` field under the `finish_webhook` and `fail_webhook` plugins in your workflow definition with your webhook URLs. + +## Changing Approval Threshold + +By default, the rule we discussed in the Workflow Definition section will auto-approve the business if the fuzziness score is 0.8 or higher. However, you might want to adjust this threshold to be stricter or more lenient based on your needs. + +Let's make the fuzziness score threshold stricter. +Change the second condition in the `API_CALL_SUCCESS` rule from 0.8 to 0.9: + +```json +{ + "target": "auto_approve", + "cond": { + "type": "json-logic", + "rule": { + "or": [ + { + "==": [ + { "var": "context.entity.companyName" }, + { "var": "response.data.registered_name" }, + ], + }, + { + ">=": [ + { "var": "context.external_request_example.data.name_fuzziness_score" }, + 0.9, + ], + }, + ], + }, + }, +} +``` + +With this change, the rule now reads: auto-approve the business if either the company name provided matches the registered name, or if the fuzziness score is 0.9 or higher. +This makes the approval criteria more stringent, requiring a higher match level between the provided company name and the registered name. + +Remember to save the changes you've made to the workflow definition. +Once the changes are saved, you'll need to restart the services to reflect these changes. Run the command `pnpm api-flow-example` again in your terminal. + +Now, when you send a start event to a new workflow instance, it will follow the updated approval criteria. If the new criteria are met, the workflow will proceed to auto-approval and a webhook will be sent to notify about this decision. This allows for quick notifications of automated decisions, improving the speed of your KYB process. + +In the next section, we'll guide you on how to set up a webhook and receive notifications about workflow state changes. diff --git a/websites/docs/src/pages/en/learn/understanding_workflows.md b/websites/docs/src/pages/en/learn/understanding_workflows.md new file mode 100644 index 0000000000..17f1f648aa --- /dev/null +++ b/websites/docs/src/pages/en/learn/understanding_workflows.md @@ -0,0 +1,18 @@ +--- +title: Understanding Workflows +description: Understanding Workflows +layout: ../../../layouts/MainLayout.astro +--- + +A workflow, in a broad sense, is a series of steps or a process that takes an input and produces an output. It's a sequence of tasks or procedures that lead to some desired outcome. However, in the context of our system, workflows are not just mere sequences but orchestrated, complex processes that involve dynamic decision-making and interactions. A good example is the "Know Your Business (KYB)" process, as explained in our [KYB guide](../guides/KYB_Workflow_Implementation_Guide.md). + +## What are Workflows in Our System? + +In our system, workflows are defined using a State Machine model, specifically statecharts, which is implemented using the [XState library](https://xstate.js.org/docs/). In the realm of state machines, a system can be in only one state at a time. From that state, certain actions or events can lead the system to transition to other states. + +Statecharts allow you to define complex behavior using states, sub-states, and transitions between states. It's a robust way to manage and visualize the different stages of a process and the conditions that lead to state changes. + +States and transitions in our workflows are predefined and loaded into the system during startup. They define the blueprints of different processes within our system. + + +--- diff --git a/websites/docs/src/pages/en/learn/workflow_definitions.md b/websites/docs/src/pages/en/learn/workflow_definitions.md new file mode 100644 index 0000000000..24089dd02f --- /dev/null +++ b/websites/docs/src/pages/en/learn/workflow_definitions.md @@ -0,0 +1,32 @@ +--- +title: Workflow Definitions +description: Workflow Definitions +layout: ../../../layouts/MainLayout.astro +--- + +A workflow definition is a JSON object that specifies the structure and behavior of a workflow. It outlines the states, transitions, actions, and events that the workflow is comprised of. + +## Components of a Workflow Definition + +Let's explore the main components that make up a workflow definition: + +### States + +States represent different conditions that your system or process could be in. For example, 'idle', 'check_business_details', 'manual_review', and so on. States provide a snapshot of the system at any given time. + +### Transitions + +Transitions define the conditions that allow the system to move from one state to another. They are linked to actions or events and are expressed using 'on' in the statechart. Transitions determine the path of the process based on various conditions. + +### Actions + +Actions are side effects or operations that are carried out when transitioning from one state to another. These could include API calls, sending messages, updating the system state, etc. They represent the active parts of your workflows. + +### Events + +Events are the triggers that cause transitions to occur. In our workflows, events can be user actions or system events, such as API responses. Events are the stimuli to which the system responds by changing states. + +For a detailed understanding of how states and state configuration work in XState, please refer to the [XState documentation](https://xstate.js.org/docs/guides/states.html). + + +---