diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..9662b54 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + run: npm install -g pnpm && pnpm install + - name: Install Playwright Browsers + run: pnpm exec playwright install --with-deps + - name: Run Playwright tests + run: pnpm exec playwright test + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fc5299a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..46116bc --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "nyxb.vscode-styled-colors" + ] +} \ No newline at end of file diff --git a/next-js/.eslintrc.js b/next-js/.eslintrc.js new file mode 100644 index 0000000..6c411cb --- /dev/null +++ b/next-js/.eslintrc.js @@ -0,0 +1,26 @@ +module.exports = { + extends: ["custom"], + globals: { + NODE_ENV: "readonly" + }, + env: { + node: true + }, + plugins: [ + 'import' + ], + rules: { + 'turbo/no-undeclared-env-vars': 'off', + 'import/named': 'error', + }, +}; + + +// module.exports = { +// // extends: ["eslint:recommended", "next"], +// extends: ["custom"], + +// // extends: ["next", "next/core-web-vitals"], +// }; + + diff --git a/next-js/.gitignore b/next-js/.gitignore new file mode 100644 index 0000000..8393d85 --- /dev/null +++ b/next-js/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local +.env.production +.env + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +/.examples/ +/public/copydb/ \ No newline at end of file diff --git a/next-js/.vscode/launch.json b/next-js/.vscode/launch.json new file mode 100644 index 0000000..b982cdc --- /dev/null +++ b/next-js/.vscode/launch.json @@ -0,0 +1,38 @@ +{ + "version": "0.2.0", + "configurations": [ + + { + "name": "Attach to Chrome", + "port": 9222, + "request": "attach", + "type": "chrome", + "webRoot": "${workspaceFolder}" + }, + + { + "name": "Next.js: debug server-side", + "type": "node-terminal", + "request": "launch", + "command": "npm run dev" + }, + { + "name": "Next.js: debug client-side", + "type": "chrome", + "request": "launch", + "url": "http://localhost:3000" + }, + { + "name": "Next.js: debug full stack", + "type": "node-terminal", + "request": "launch", + "command": "npm run dev", + "cwd": "${workspaceFolder}/apps/web", + "serverReadyAction": { + "pattern": "- Local:.+(https?://.+)", + "uriFormat": "%s", + "action": "debugWithChrome" + } + } + ] +} diff --git a/next-js/.vscode/settings.json b/next-js/.vscode/settings.json new file mode 100644 index 0000000..3f4a09a --- /dev/null +++ b/next-js/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "files.exclude": { + ".next": true, + ".vercel": true, + ".vscode": true + } +} \ No newline at end of file diff --git a/next-js/README.md b/next-js/README.md new file mode 100644 index 0000000..498d162 --- /dev/null +++ b/next-js/README.md @@ -0,0 +1,18 @@ +# Configuration +- `pnpm install` to install dependencies +- `pnpm add next react react-dom` to add next.js and react dependencies in the Playwright test project +- `pnpm add react@canary react-dom@canary` to add the latest version of React and React DOM + + + +# Prisma with MongoDB +- `pnpx prisma generate` to generate the Prisma client +- `pnpx prisma studio` to open the Prisma Studio +- `pnpx prisma db push` Whenever you update your Prisma schema, you will need to run the + + + +# Clean up the cache and node_modules +pnpm store prune +pnpm rm --recursive node_modules +pnpm install diff --git a/next-js/app/api/index/route.ts b/next-js/app/api/index/route.ts new file mode 100644 index 0000000..dba6592 --- /dev/null +++ b/next-js/app/api/index/route.ts @@ -0,0 +1,10 @@ +import { NextResponse } from "next/server"; +import { getIndexReport } from "../../db/actions/getReportIndexAction"; + +export async function GET() { + const index = await getIndexReport(); + + return NextResponse.json({ + indexData: index, + }); +} diff --git a/next-js/app/api/reports/route.ts b/next-js/app/api/reports/route.ts new file mode 100644 index 0000000..34366c5 --- /dev/null +++ b/next-js/app/api/reports/route.ts @@ -0,0 +1,14 @@ +"use server"; + +import { NextResponse } from "next/server"; +import { getReportAction } from "../../db/actions/getReportAction"; + +export async function GET() { + const reports = await getReportAction(); + + // const reportName = reports?.filter((report: any) => report?.tests.some((test: any) => test.dirName === params.reportName)); + + return NextResponse.json({ + reports, + }); +} diff --git a/next-js/app/components/layout/Footer.tsx b/next-js/app/components/layout/Footer.tsx new file mode 100644 index 0000000..10220e0 --- /dev/null +++ b/next-js/app/components/layout/Footer.tsx @@ -0,0 +1,29 @@ +"use client"; + +import React from "react"; +import { Flex, Text, Box } from "@mantine/core"; +import { useMediaQuery } from "@mantine/hooks"; +import Image from "next/image"; +import Link from "next/link"; + +const Footer = () => { + const year = new Date().getFullYear() || ""; + const isTabletSize = useMediaQuery("(max-width: 1039px)"); + + return ( + + + + + + + + © {year} + + + + + ); +}; + +export default Footer; diff --git a/next-js/app/components/layout/Navbar.tsx b/next-js/app/components/layout/Navbar.tsx new file mode 100644 index 0000000..e310924 --- /dev/null +++ b/next-js/app/components/layout/Navbar.tsx @@ -0,0 +1,139 @@ +"use client"; + +import React, { useRef, useEffect } from "react"; +import { usePathname } from "next/navigation"; +import { useAppContext } from "@/context"; +import { useMediaQuery, useDisclosure } from "@mantine/hooks"; +import Image from "next/image"; +import { Box, Flex, Text, Transition, Burger, Paper, Anchor } from "@mantine/core"; +import SelectPreviousReport from "../shared/SelectPreviousReport"; +import HistoryBanner from "@/app/components/shared/HistoryBanner"; +import Link from "next/link"; + +const Navbar = () => { + const pathname = usePathname(); + const { selectReportContext } = useAppContext(); + const [opened, { toggle: toggleDropdown, close: closeDropdown }] = useDisclosure(false); + + const isTabletSize = useMediaQuery("(max-width: 1039px)"); + + const dropdownRef = useRef(null); // Ref for the dropdown + const burgerRef = useRef(null); // Ref for the burger icon + + // Setup event listener for outside clicks + useEffect(() => { + // Specify the type for 'event' as 'MouseEvent' + const handleOutsideClick = (event: MouseEvent) => { + // Ensure the elements exist before calling 'contains' + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node) && burgerRef.current && !burgerRef.current.contains(event.target as Node)) { + closeDropdown(); + } + }; + + if (opened) { + document.addEventListener("mousedown", handleOutsideClick); + } + + // Cleanup the event listener + return () => { + document.removeEventListener("mousedown", handleOutsideClick); + }; + }, [opened, closeDropdown]); + + const isHistoryPage = pathname.endsWith("/e2e/history"); + const isReportPage = pathname.endsWith("/e2e") && !isHistoryPage; + + return ( + <> + + + + + + + + + + + DEMO REPORT + + + + {!isTabletSize && ( + + + {/* Link to the history page of the current report */} + {isReportPage && ( + + + History View + + + )} + {isHistoryPage && ( + + + Back to Report + + + )} + + + + + )} + {isTabletSize && ( + <> + + + + {(styles) => ( + + + + {isReportPage && ( + + + History View + + + )} + {isHistoryPage && ( + + + Back to Report + + + )} + + + )} + + > + )} + + + + {selectReportContext !== 0 && ( + + + + )} + + > + ); +}; + +export default Navbar; diff --git a/next-js/app/components/shared/ChangesDetected.tsx b/next-js/app/components/shared/ChangesDetected.tsx new file mode 100644 index 0000000..426ff72 --- /dev/null +++ b/next-js/app/components/shared/ChangesDetected.tsx @@ -0,0 +1,181 @@ +import React, { useEffect, useState, useCallback } from "react"; +import { Text, Paper, Badge, Box, Title, Anchor, Accordion, Group } from "@mantine/core"; +import { ChangesDetectedProps, ChangesTestDetails } from "@/app/utils/interfaces"; +import Loading from "./Loading"; + +interface ChangesDetectedComponentProps { + testName: string | undefined; + compact?: boolean; + reportIdContextProps: string; +} + +const ChangesDetected = ({ reportIdContextProps, testName, compact }: ChangesDetectedComponentProps) => { + const [changes, setChanges] = useState(null); + const [loading, setLoading] = useState(true); + + const isCompact = compact ?? false; + + const loadChanges = useCallback(() => { + setChanges(null); // Reset changes + if (!reportIdContextProps || !testName) return; + + const fileName = `${testName}-changes-${reportIdContextProps}.json`; + setLoading(true); + fetch(`/data/${testName}/history/${fileName}`) + .then((res) => (res.ok ? res.json() : Promise.reject(res))) + .then((result) => setChanges(result)) + .catch((error) => { + console.error("Failed to load the JSON file ", error); + }) + .finally(() => setLoading(false)); + }, [reportIdContextProps, testName]); + + useEffect(() => { + loadChanges(); + }, [loadChanges]); + + if (loading) + return ( + + + + ); + + const changesDetected = changes?.changesDetected ?? []; + + const renderChanges = (changes: ChangesTestDetails[]) => { + return changes.map((change, index) => { + const { testId, title, newTest, reporter, previous, actual } = change.testDetails; + const itemBackground = index % 2 === 0 ? "#ffffff" : "#f0f0f0"; + + const textStyle = isCompact ? "xs" : "sm"; + const paddingStyle = isCompact ? "2px" : "10px"; + const boxMarginBottom = isCompact ? "md" : "md"; + const badgeVariant = isCompact ? "light" : "filled"; + + const changeContent = ( + + + + {testId} + {" "} + Reported by:{" "} + + {reporter} + {" "} + -{" "} + + {title} + + : + {!newTest && previous && ( + <> + + {previous.statusChange && ( + <> + Status changed from + + {previous.statusChange} + + to + {actual?.statusChange}. + > + )} + {previous.messageChange && ( + <> + Description changed from + + {previous.messageChange} + + to + + {actual?.messageChange}. + + > + )} + + > + )} + {newTest && ( + + New test reported: + + {actual?.messageChange} . + + + )} + + + ); + return !isCompact ? ( + + {changeContent} + + ) : ( + changeContent + ); + }); + }; + + // Changes detected style + const backGroundColor = !isCompact ? "#f8f9fa" : "#ffffff"; + const paddingXStyle = !isCompact ? "10px" : "0"; + const paddingYStyle = !isCompact ? 2 : 0; + const boxShadowStyle = !isCompact ? "0px 1px 2px 0px rgba(0, 0, 0, 0.1)" : undefined; + const borderRadiusStyle = !isCompact ? "8px" : 0; + const marginStyle = !isCompact ? "sm" : 0; + const boxMarginBottom = isCompact ? "md" : "md"; + + // No changes detected style + const textStyle = isCompact ? "sm" : "sm"; + const paddingStyle = isCompact ? "sm" : "sm"; + const boxMargin = isCompact ? 0 : 0; + const paddingXYStyle = isCompact ? 0 : "xs"; + + return ( + <> + {changesDetected && changesDetected.length === 0 ? ( + + + + No changes have been detected from the previous test. + + + + ) : ( + + <> + + + + + + Changes Detected: {changes?.changesDetected ? changes.changesDetected.length : 0} + + Report id: {reportIdContextProps} + + + + + {renderChanges(changesDetected)} + + + > + + )} + > + ); +}; + +export default ChangesDetected; diff --git a/next-js/app/components/shared/HistoryBanner.tsx b/next-js/app/components/shared/HistoryBanner.tsx new file mode 100644 index 0000000..3a3969e --- /dev/null +++ b/next-js/app/components/shared/HistoryBanner.tsx @@ -0,0 +1,74 @@ +"use client"; + +import React, { useEffect } from "react"; +import useSWR from "swr"; +import { Flex, Text, CloseButton } from "@mantine/core"; +import { IconXboxX } from "@tabler/icons-react"; +import { ApiResponse } from "../../utils/interfaces"; +import { useAppContext } from "@/context"; +import { useParams, useRouter } from "next/navigation"; + +interface HistoryBannerProps { + closeDropdown: () => void; +} + +const fetcher = (url: string) => fetch(url).then((res) => res.json()); + +const HistoryBanner: React.FC = ({ closeDropdown }) => { + const { data } = useSWR("/api/index", fetcher); + const { setSelectReportContext, setReportIdContext, reportIdContext } = useAppContext(); + + const router = useRouter(); + const params = useParams<{ reportId: string; reportName: string }>(); + + useEffect(() => { + if (params.reportId) { + setReportIdContext(params.reportId); + } + }, [params.reportId, setReportIdContext, setSelectReportContext, reportIdContext]); + + if (!data || !Array.isArray(data.indexData) || data.indexData.length === 0) { + return No report data available; + } + + const handleClose = () => { + setReportIdContext(data.indexData[0]?.reportId); + setSelectReportContext(0); + + // Logic to find the report name based on ID if necessary + const reportName = params.reportName; + // Refresh the URL with the new reportId and reportName + if (params.reportId && params.reportName) { + router.push(`/reports/${data.indexData[0]?.reportId}/${reportName}/e2e`); + } + + closeDropdown(); + }; + + return ( + + + + This is a Previous Report + + + + } /> + + ); +}; + +export default HistoryBanner; diff --git a/next-js/app/components/shared/ImageAccordionModal.tsx b/next-js/app/components/shared/ImageAccordionModal.tsx new file mode 100644 index 0000000..42c9985 --- /dev/null +++ b/next-js/app/components/shared/ImageAccordionModal.tsx @@ -0,0 +1,75 @@ +import React, { useState } from "react"; +import { useAppContext } from "@/context"; +import { useDisclosure } from "@mantine/hooks"; +import { Modal, Accordion, Button, Text, Popover } from "@mantine/core"; +import Image from "next/image"; +import { ImageAccordionModalProps } from "../../utils/interfaces"; +import Loading from "./Loading"; + +const ImageAccordionModal: React.FC = ({ imageUrl, message, title }) => { + const [opened, setOpened] = useState(false); + const [openedPopover, { close, open }] = useDisclosure(false); + const [imageLoaded, setImageLoaded] = useState(false); + + const { selectReportContext } = useAppContext(); + + return ( + <> + + + + setOpened(true)} variant='outline' size='xs' color='text1.9' style={{ height: "2rem" }} disabled={selectReportContext !== 0}> + View + + + + + {selectReportContext === 0 ? "Click to view image" : "Cannot view images from previous reports."} + + + + setOpened(false)} + size='lg' + title={title} + centered + styles={{ + title: { + fontWeight: 700, + textAlign: "center", + fontSize: "1.2rem", + }, + inner: { + marginTop: "10rem", + }, + }} + > + + {!imageLoaded && ( + + + + )} + setImageLoaded(true)} /> + + + + + {message} + + + + + > + ); +}; + +export default ImageAccordionModal; diff --git a/next-js/app/components/shared/Loading.tsx b/next-js/app/components/shared/Loading.tsx new file mode 100644 index 0000000..19e67d6 --- /dev/null +++ b/next-js/app/components/shared/Loading.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import { Loader, Center } from "@mantine/core"; + +function Loading() { + return ( + + + + ); +} + +export default Loading; diff --git a/next-js/app/components/shared/MissingTests.tsx b/next-js/app/components/shared/MissingTests.tsx new file mode 100644 index 0000000..ed09f4f --- /dev/null +++ b/next-js/app/components/shared/MissingTests.tsx @@ -0,0 +1,205 @@ +import React, { useEffect, useState } from "react"; +import { Accordion, Card, Table, TextInput, Title, Box, Badge, Flex } from "@mantine/core"; +import ImageAccordionModal from "./ImageAccordionModal"; +import { Report, Spec } from "../../utils/interfaces"; +import Loading from "./Loading"; + +const MissingTests = () => { + const [latestJsonData, setLatestJsonData] = useState(null); + const [previousJsonData, setPreviousJsonData] = useState(null); + const [currentReportId, setCurrentReportId] = useState(null); + const [previousReportId, setPreviousReportId] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [search, setSearch] = useState(""); + const [filteredTests, setFilteredTests] = useState([]); + + useEffect(() => { + fetch("/data/reportId.json") + .then((res) => { + if (!res.ok) { + throw new Error("Network response was not ok"); + } + return res.json(); + }) + .then((data) => { + const numericId = parseInt(data.reportId, 10); + const adjustedReportNumericId = numericId - 1; + const adjustedReportId = adjustedReportNumericId.toString().padStart(3, "0"); + setCurrentReportId(adjustedReportId); // Current report id + const previousAdjustedNumericId = numericId - 2; + const previousAdjustedReportId = previousAdjustedNumericId.toString().padStart(3, "0"); + setPreviousReportId(previousAdjustedReportId); // Previous report id + }) + .catch((error) => { + console.error("Failed to load reportId.json", error); + setError(`Failed to load - fetch("/data/reportId.json"): ${error.message}`); + setLoading(false); + }); + }, []); + + useEffect(() => { + if (currentReportId) { + setLoading(true); + fetch(`/public/data/WordsServedEstimatePage/WordsServedEstimatePage-${currentReportId}.json`) + .then((res) => { + if (!res.ok) { + throw new Error("Network response was not ok"); + } + return res.json(); + }) + .then( + (result) => { + setLatestJsonData(result); + setLoading(false); + }, + (error) => { + console.error("Failed to load the JSON file", error); + setError(`Failed to load - fetch(/public/data/WordsServedEstimatePage/WordsServedEstimatePage-${currentReportId}.json): ${error.message}`); + setLoading(false); + } + ); + } + }, [currentReportId]); + + useEffect(() => { + if (previousReportId) { + setLoading(true); + fetch(`/public/data/WordsServedEstimatePage/WordsServedEstimatePage-${previousReportId}.json`) + .then((res) => { + if (!res.ok) { + throw new Error("Network response was not ok"); + } + return res.json(); + }) + .then( + (result) => { + setPreviousJsonData(result); + setLoading(false); + }, + (error) => { + console.error("Failed to load the JSON file", error); + setError("Failed to load data"); + setLoading(false); + } + ); + } + }, [previousReportId]); + + // This function compares two sets of tests and returns the ones that are missing + function findMissingTests(currentTests: Spec[], previousTests: Spec[]): Spec[] { + const currentTestIds = new Set(currentTests.map((test) => test.testId)); + return previousTests.filter((test) => !currentTestIds.has(test.testId)); + } + + // Define the missingTests array and populate it with the missing tests + let missingTests: Spec[] = []; + if (latestJsonData && previousJsonData) { + missingTests = findMissingTests(latestJsonData.specs, previousJsonData.specs); + } + + // useEffect(() => { + // if (search) { + // setFilteredTests( + // missingTests.filter( + // (test) => + // test.title.toLowerCase().includes(search.toLowerCase()) || + // test.testId.toLowerCase().includes(search.toLowerCase()) || + // test.status.toLowerCase().includes(search.toLowerCase()) || + // test.message.toLowerCase().includes(search.toLowerCase()) || + // test.jiraTicket.toLowerCase().includes(search.toLowerCase()) || + // test.reporter.toLowerCase().includes(search.toLowerCase()) + // ) + // ); + // } else { + // setFilteredTests(missingTests); + // } + // }, [latestJsonData, previousJsonData, search]); + + useEffect(() => { + let missingTests: Spec[] = []; + + if (latestJsonData && previousJsonData) { + missingTests = findMissingTests(latestJsonData.specs, previousJsonData.specs); + } + + if (search) { + setFilteredTests( + missingTests.filter( + (test) => + test.title.toLowerCase().includes(search.toLowerCase()) || + test.testId.toLowerCase().includes(search.toLowerCase()) || + test.status.toLowerCase().includes(search.toLowerCase()) || + test.message.toLowerCase().includes(search.toLowerCase()) || + test.jiraTicket.toLowerCase().includes(search.toLowerCase()) || + test.reporter.toLowerCase().includes(search.toLowerCase()) + ) + ); + } else { + setFilteredTests(missingTests); + } + }, [latestJsonData, previousJsonData, search]); + + + if (loading) return ; + if (error) return Error WordsServedEstimatePage: {error}; + + const accordionContent = ( + <> + + + + + Missing Tests: {missingTests.length} + + + + setSearch(event.currentTarget.value)} mb='md' style={{ width: "16rem" }} /> + + + + Id + Status + Title + Reporter + Jira Ref + Screenshot + Description + + + + {filteredTests.map((test, index) => ( + + {test?.testId} + + {test?.status} + + {test?.title} + {test?.reporter} + {test?.jiraTicket} + + + + {test.message} + + ))} + + + + + + > + ); + + return ( + <> + {missingTests.length > 0 && ( + + {accordionContent} + + )} + > + ); +}; + +export default MissingTests; diff --git a/next-js/app/components/shared/PageHeader.tsx b/next-js/app/components/shared/PageHeader.tsx new file mode 100644 index 0000000..e61f293 --- /dev/null +++ b/next-js/app/components/shared/PageHeader.tsx @@ -0,0 +1,18 @@ +import React from "react"; +import { Flex, Text } from "@mantine/core"; + +const PageHeader = ({ slogan, title, dotColor, textColor, titleFontWeight }: { slogan: string; title: string; dotColor: string; textColor?: string; titleFontWeight?: number }) => ( + + + + + + {slogan} + + + {title} + + +); + +export default PageHeader; diff --git a/next-js/app/components/shared/SelectPreviousReport.tsx b/next-js/app/components/shared/SelectPreviousReport.tsx new file mode 100644 index 0000000..71e1a7d --- /dev/null +++ b/next-js/app/components/shared/SelectPreviousReport.tsx @@ -0,0 +1,110 @@ +import React, { useEffect, useState } from "react"; +import useSWR from "swr"; +import { Select } from "@mantine/core"; +import Loading from "./Loading"; +import { useAppContext } from "@/context"; +import { ApiResponse } from "../../utils/interfaces"; +import { useParams, useRouter, usePathname } from "next/navigation"; + +interface SelectReportProps { + closeDropdown: () => void; +} + +const fetcher = (url: string) => fetch(url).then((res) => res.json()); + +const SelectPreviousReport: React.FC = ({ closeDropdown }) => { + const [isSelectorDisabled, setSelectorDisabled] = useState(false); + const { data, error, isLoading } = useSWR("/api/index", fetcher); + const { selectReportContext, setSelectReportContext, setReportIdContext, reportIdContext } = useAppContext(); + + const router = useRouter(); + const pathname = usePathname(); + + const params = useParams<{ reportId: string; reportName: string }>(); + const reportIdParam = params.reportId; + + // if the pathname includes '/history' then disable the selector + useEffect(() => { + const isHistoryPage = pathname.includes("/history"); + setSelectorDisabled(isHistoryPage); + + if (isHistoryPage && data?.indexData && data.indexData.length > 0) { + setSelectReportContext(0); + setReportIdContext(data.indexData[0].reportId); + router.push(`/reports/${data.indexData[0]?.reportId}/${params.reportName}/e2e/history`); + closeDropdown(); + } + }, [pathname, data, setReportIdContext, closeDropdown]); + + useEffect(() => { + if (reportIdParam && data?.indexData) { + const report = data.indexData.find((report) => report.reportId === reportIdParam); + if (report) { + setReportIdContext(reportIdParam); + } else { + setReportIdContext(data.indexData[0].reportId); + } + } + }, [reportIdParam, data, reportIdContext, selectReportContext]); + + if (error) return Failed to load; + if (isLoading) return ; + if (!data || !Array.isArray(data.indexData) || data.indexData.length === 0) { + return No report data available; + } + + const selectedReport = () => { + if (reportIdContext) { + return data.indexData.find((report) => report.reportId === reportIdContext); + } else { + return data.indexData[0]; + } + }; + + if (!selectedReport) { + console.error("Selected report is undefined"); + setReportIdContext(data.indexData[0].reportId); + setSelectReportContext(0); + return Report not found; + } + + const handleReportChange = (value: string | null) => { + if (value) { + // Find the index of the selected report so you can display Previous Reports Banner + const reportIndex = data.indexData.findIndex((report) => report.reportId === value); + setSelectReportContext(reportIndex); + + setReportIdContext(value); + + // Logic to find the report name based on ID if necessary + const reportName = params.reportName || "Report"; + + // Refresh the URL with the new reportId and reportName + if (params.reportId && params.reportName) { + router.push(`/reports/${value}/${reportName}/e2e`); + } + } + }; + + const defaultValue = `id: ${data.indexData[selectReportContext].reportId} - ${data.indexData[selectReportContext].reportDate.split(" - ")[0]}`; + return ( + + ({ + value: report.reportId, + label: `id: ${report.reportId} - ${report.reportDate.split(" - ")[0]}`, + }))} + defaultValue={defaultValue.toString()} + disabled={isSelectorDisabled} + /> + + ); +}; + +export default SelectPreviousReport; diff --git a/next-js/app/components/shared/StatusCount.tsx b/next-js/app/components/shared/StatusCount.tsx new file mode 100644 index 0000000..52f3ae4 --- /dev/null +++ b/next-js/app/components/shared/StatusCount.tsx @@ -0,0 +1,32 @@ +import React from "react"; +import { Text, Badge, Group, Paper } from "@mantine/core"; +import { CheckboxIcon, CrossCircledIcon } from "@radix-ui/react-icons"; + +function StatusCount({ passCount, failCount }: { passCount: number; failCount: number }) { + return ( + + + + + + Pass: + + + {passCount} + + + + + + Fail: + + + {failCount} + + + + + ); +} + +export default StatusCount; diff --git a/next-js/app/components/shared/TicketsCovered.tsx b/next-js/app/components/shared/TicketsCovered.tsx new file mode 100644 index 0000000..5f0221a --- /dev/null +++ b/next-js/app/components/shared/TicketsCovered.tsx @@ -0,0 +1,115 @@ +import { Box, Title, Accordion, Group, Flex, Text, Table } from "@mantine/core"; +import useSWR from "swr"; +import { ApiReportResponse } from "@/app/utils/interfaces"; +import Loading from "./Loading"; + +const fetcher = (url: string) => fetch(url).then((res) => res.json()); + +const TicketsCovered = () => { + const { data, error, isLoading } = useSWR("/api/reports", fetcher); + + if (error) return Failed to load; + if (isLoading) return ; + + if (!data || !Array.isArray(data.reports) || data.reports.length === 0) { + return No report data available; + } + + const { ticketsJira, specs } = data.reports[0] || {}; + + const ticketsJiraArray = () => { + if (!ticketsJira) { + return []; + } + let tickets = []; + if (typeof ticketsJira === "string") { + try { + tickets = JSON.parse(ticketsJira); + } catch (error) { + return []; + } + } + return tickets; + }; + + + const rows = () => { + // Count the number of times each ticket appears in the specs + const ticketCounts: { [key: string]: number } = {}; + + specs.forEach((spec) => { + if (spec.specJiraTicket) { + // Divide the tickets string and remove whitespace + const tickets = spec.specJiraTicket.split(",").map(t => t.trim()); + + // Count each ticket + tickets.forEach((ticket) => { + if (ticket) { + ticketCounts[ticket] = (ticketCounts[ticket] || 0) + 1; + } + }); + } + }) + + return Object.entries(ticketCounts).map(([ticket, count], index) => ( + + + + {ticket} + + + {count} + + )); + }; + + return ( + <> + + + + + + + + Tickets Covered: + + Report id: {"No report id"} + + + + + + + {ticketsJiraArray().length === 0 ? ( + + No tickets in the database. + + ) : ( + + + + Ticket + Total + + + {rows()} + + )} + + + + + + + > + ); +}; + +export default TicketsCovered; diff --git a/next-js/app/components/shared/VideoPlayer.tsx b/next-js/app/components/shared/VideoPlayer.tsx new file mode 100644 index 0000000..a3eef04 --- /dev/null +++ b/next-js/app/components/shared/VideoPlayer.tsx @@ -0,0 +1,66 @@ +import React, { useEffect, useRef, useState } from "react"; +import { Box, Title, Accordion, Group, Flex } from "@mantine/core"; + +interface VideoPlayerProps { + src?: string; + alt?: string; +} + +const VideoPlayer: React.FC = ({ src, alt }) => { + // State to track the active accordion item + const [activeItem, setActiveItem] = useState(null); + + // Reference to the video element + const videoRef = useRef(null); + + // Effect to play the video when the accordion opens + useEffect(() => { + if (activeItem === 'ChangesDetected' && videoRef.current) { + videoRef.current.play(); + } + }, [activeItem]); + + return ( + <> + + + + + + + + Automated Test Showcase + + + + + + + + Your browser does not support the video tag. + + + + + + + + > + ); +}; + +export default VideoPlayer; + diff --git a/next-js/app/db/actions/connectToDatabase.ts b/next-js/app/db/actions/connectToDatabase.ts new file mode 100644 index 0000000..97f201a --- /dev/null +++ b/next-js/app/db/actions/connectToDatabase.ts @@ -0,0 +1,152 @@ +import { client } from "../prisma"; +import { ReportIndexData, Report, HistoryJsonTypes } from "../../utils/interfaces"; + +export async function createReportIndexData(reportIndexData: ReportIndexData[]) { + for (const item of reportIndexData) { + // Check if a report with the same reportId already exists + const existingReport = await client.indexReports.findFirst({ + where: { + reportId: item.reportId, + }, + }); + if (!existingReport) { + try { + const data = await client.indexReports.create({ + data: { + reportId: item.reportId, + reportDate: item.reportDate, + tests: { + create: item.tests.map((test) => ({ + dirName: test.dirName, + title: test.title, + testId: test.testId, + testPath: test.testPath, + dateTest: item.reportDate, + typeOfTest: test.typeOfTest, + passCount: test.passCount, + failCount: test.failCount, + totalTests: test.totalTests, + })), + }, + }, + }); + + // eslint-disable-next-line no-console + console.log("Data inserted successfully - createReportIndexData - MongoDB"); + return data; + } catch (e) { + console.error("Error inserting data", item); + console.error(e); + } + } + } +} + +export async function createReportDescribeData(reportDescribeData: Report[]) { + for (const item of reportDescribeData) { + const existingReport = await client.reportDescribe.findFirst({ + where: { + testDescribeId: item.id, + }, + }); + + if (!existingReport) { + try { + const reportDescribe = await client.reportDescribe.create({ + data: { + testDescribe: item.title, + reportId: item.reportId, + testDescribeId: item.id, + testDescribePath: item.titlePath, + typeOfTest: item.typeOfTest, + testDescribeDate: item.date, + ticketsJira: item.tickets, + failCount: item.failCount, + passCount: item.passCount, + specs: { + create: item.specs.map((spec) => ({ + specId: spec.testId, + specTitle: spec.title, + specUrl: spec.websiteUrl, + specReporter: spec.reporter, + specScreenshotPath: spec.screenshotPath, + specStatus: spec.status, + specJiraRef: spec.jiraRef, + specJiraTicket: spec.jiraTicket, + specMessage: spec.message, + })), + }, + }, + }); + + // eslint-disable-next-line no-console + console.log("Data inserted successfully - createReportDescribeData - MongoDB"); + return reportDescribe; + } catch (e) { + console.error("Error inserting data", item); + console.error(e); + } + } + } +} +export async function createHistoryData(data: HistoryJsonTypes) { + // Check if a history with the given reportTestId already exists + const existingTestHistory = await client.reportHistory.findFirst({ + where: { + reportId: data.reportId, // Using reportTestId as the unique identifier + }, + }); + // If it doesn't exist, create a new history along with its related tests + if (!existingTestHistory) { + try { + const createdTestHistoryData = await client.reportHistory.create({ + data: { + reportId: data.reportId, + reportTestId: data.id, + titleReport: data.title, + titlePath: data.titlePath, + dateHistory: data.date, + // Ensure changesDetected is defined and is an array before mapping over it + changesDetected: data.changesDetected?.map((changeDetected) => ({ + testDetails: { + create: { + testId: changeDetected.testDetails.testId, + title: changeDetected.testDetails.title, + reported: changeDetected?.testDetails.reporter, + newTest: changeDetected.testDetails.newTest, + status: changeDetected.testDetails.status, + // Check if previous has any keys, if not, set it to undefined + previous: Object.keys(changeDetected?.testDetails.previous || {}).length + ? { + create: { + statusChange: changeDetected.testDetails.previous?.statusChange, + messageChange: changeDetected.testDetails.previous?.messageChange, + reported: changeDetected.testDetails.previous?.reporter, + }, + } + : undefined, + // No need to check actual since it has data + actual: { + create: { + statusChange: changeDetected.testDetails.actual?.statusChange, + messageChange: changeDetected.testDetails.actual?.messageChange, + reporter: changeDetected.testDetails.actual?.reporter, + }, + }, + }, + }, + })), + }, + }); + // eslint-disable-next-line no-console + console.log("Data inserted successfully - createHistoryData - MongoDB"); + return createdTestHistoryData; + } catch (e) { + console.error("Error inserting data - createHistoryData:", data); + console.error(e); + } + } else { + // console.log(`Entry with id ${data.id} already exists. Skipping insertion.`); + } +} + diff --git a/next-js/app/db/actions/getHistoryReportAction.ts b/next-js/app/db/actions/getHistoryReportAction.ts new file mode 100644 index 0000000..b5ca08f --- /dev/null +++ b/next-js/app/db/actions/getHistoryReportAction.ts @@ -0,0 +1,19 @@ +import { client } from "../prisma"; + +export async function getHistoryReport(reportName: string) { + try { + const data = await client.reportHistory.findMany({ + where: { + titleReport: reportName, + }, + orderBy: { + createdAt: "desc", + }, + }); + + return data; + } catch (e) { + console.error(e, "Error getting history report"); + throw e; + } +} diff --git a/next-js/app/db/actions/getReportAction.ts b/next-js/app/db/actions/getReportAction.ts new file mode 100644 index 0000000..295d401 --- /dev/null +++ b/next-js/app/db/actions/getReportAction.ts @@ -0,0 +1,21 @@ +"use server"; + +import { client } from "../prisma"; +import { ApiReportResponse } from "../../utils/interfaces"; + +export async function getReportAction(): Promise { + const data = await client.reportDescribe.findMany({ + include: { + specs: true, + }, + orderBy: { + testDescribeDate: "desc", + }, + }); + + return data.map((report) => ({ + reports: [], + ...report, + testDescribeDate: report.testDescribeDate || "", + })); +} diff --git a/next-js/app/db/actions/getReportIndexAction.ts b/next-js/app/db/actions/getReportIndexAction.ts new file mode 100644 index 0000000..3f1224b --- /dev/null +++ b/next-js/app/db/actions/getReportIndexAction.ts @@ -0,0 +1,21 @@ +"use server"; + +import { client } from "../prisma"; +import { ApiResponse } from "../../utils/interfaces"; + +export async function getIndexReport(): Promise { + const data = await client.indexReports.findMany({ + include: { + tests: true, + }, + orderBy: { + reportDate: "desc", + }, + }); + + return data.map((report) => ({ + indexData: [], + ...report, + reportDate: report.reportDate || "", + })); +} diff --git a/next-js/app/db/prisma.ts b/next-js/app/db/prisma.ts new file mode 100644 index 0000000..2c9fb33 --- /dev/null +++ b/next-js/app/db/prisma.ts @@ -0,0 +1,7 @@ +import { PrismaClient } from "@prisma/client"; + +const globalForPrisma = globalThis as unknown as { client: PrismaClient }; + +export const client = globalForPrisma.client || new PrismaClient(); + +if (process.env.NODE_ENV !== "production") globalForPrisma.client = client; diff --git a/next-js/app/db/scripts/populateDatabase.ts b/next-js/app/db/scripts/populateDatabase.ts new file mode 100644 index 0000000..b8cc6f2 --- /dev/null +++ b/next-js/app/db/scripts/populateDatabase.ts @@ -0,0 +1,81 @@ +import fs from "fs"; +import path from "path"; +import { client } from "../prisma"; +import { createHistoryData, createReportDescribeData, createReportIndexData } from "../actions/connectToDatabase"; +import { ReportIndexData } from "../../utils/interfaces"; + +// Reads JSON files from each directory and creates data for the database + +// Gets the list of directory names within the 'public/data' directory +const reportDirPath = path.join(process.cwd(), "public", "data"); +const getDirectories = () => { + return fs + .readdirSync(reportDirPath, { withFileTypes: true }) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => dirent.name); +}; + +// Populates with Report Index data +export async function populateReportIndexDataBase() { + const file = await fs.readFileSync(process.cwd() + "/public/data/jsonReportIndex.json", "utf8"); + const reportIndexData: ReportIndexData[] = JSON.parse(file); + await createReportIndexData(reportIndexData); +} + +// Populates with Report Describe data +export async function populateReportDescribeDatabase() { + const directories = getDirectories(); + + for (const dir of directories) { + const dataDirectory = path.join(reportDirPath, dir); + const fileNames = fs.readdirSync(dataDirectory); + + for (const fileName of fileNames) { + const filePath = path.join(dataDirectory, fileName); + if (fs.statSync(filePath).isFile()) { + const fileContents = fs.readFileSync(filePath, "utf8"); + const jsonData = JSON.parse(fileContents); + await createReportDescribeData(jsonData); + } + } + } +} + +// Populates with History data +export async function populateHistoryReportDatabase() { + const directories = getDirectories(); + + for (const dir of directories) { + const dataDirectory = path.join(reportDirPath, dir, "history"); + + // Ensure the 'history' directory exists + if (!fs.existsSync(dataDirectory)) { + fs.mkdirSync(dataDirectory, { recursive: true }); + } + + // Proceed with file reading only if the directory exists or was created successfully + const fileNames = fs.existsSync(dataDirectory) ? fs.readdirSync(dataDirectory) : []; + + for (const fileName of fileNames) { + const filePath = path.join(dataDirectory, fileName); + const fileContents = fs.readFileSync(filePath, "utf8"); + const jsonData = JSON.parse(fileContents); + + // Call the function that handles database insertion here + await createHistoryData(jsonData); + } + } +} + +export const populateDatabase = async () => { + try { + await Promise.all([populateHistoryReportDatabase(), populateReportDescribeDatabase(), populateReportIndexDataBase()]); + // eslint-disable-next-line no-console + console.log("Database has been populated - populateDatabase()."); + } catch (e) { + // eslint-disable-next-line no-console + console.error("An error occurred while populating the database:", e); + } finally { + await client.$disconnect(); + } +}; diff --git a/next-js/app/globals.css b/next-js/app/globals.css new file mode 100644 index 0000000..29462cb --- /dev/null +++ b/next-js/app/globals.css @@ -0,0 +1,67 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +.flex-grow { + flex: 1; +} + +/* styles/globals.css */ +body { + background-image: url("/estimate-page-background.svg"); + background-repeat: no-repeat; + background-attachment: fixed; + background-size: cover; +} + +.main-container { + display: flex; + flex-direction: column; + min-height: calc(100vh - 78px); +} + +.text { + @media (max-width: em(576px)) { + font-size: small; + } +} + +.table-responsive { + overflow-x: auto; /* Enable horizontal scrolling */ +} + +/* Make scrollbar visible */ +.table-responsive::-webkit-scrollbar { + -webkit-appearance: none; + height: 8px; /* height of the horizontal scrollbar */ +} + +.table-responsive::-webkit-scrollbar-thumb { + border-radius: 4px; + background-color: rgba(0, 0, 0, 0.5); /* color and opacity of the scrollbar */ + box-shadow: 0 0 1px rgba(255, 255, 255, 0.5); /* creates a subtle inset effect on the scrollbar */ +} + +.title-report { + color: var(--mantine-color-blue-0); +} + +.description-report { + color: var(--mantine-color-white); +} + + +.button-disabled { + &:disabled, + &[data-disabled] { + border-color: light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)) !important; + background-color: transparent !important; + } +} + +.text-report { + @media (max-width: em(576px)) { + font-size: small; + } +} + diff --git a/next-js/app/home/Header.tsx b/next-js/app/home/Header.tsx new file mode 100644 index 0000000..f8c9392 --- /dev/null +++ b/next-js/app/home/Header.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import { Flex } from "@mantine/core"; +import PageHeader from "@/app/components/shared/PageHeader"; + +export const Header = () => { + return ( + + + + ) +} + +export default Header; diff --git a/next-js/app/home/HomePage.tsx b/next-js/app/home/HomePage.tsx new file mode 100644 index 0000000..bc9dbd5 --- /dev/null +++ b/next-js/app/home/HomePage.tsx @@ -0,0 +1,107 @@ +"use client"; + +import React, { useEffect } from "react"; +import { useRouter } from "next/navigation"; +import useSWR from "swr"; +import { Container, Text, Card, Badge, Divider, Button, Flex, SimpleGrid } from "@mantine/core"; +import Header from "./Header"; +import { ApiResponse, TestReport } from "../utils/interfaces"; +import ChangesDetected from "../components/shared/ChangesDetected"; +import Loading from "../components/shared/Loading"; +import { useAppContext } from "@/context"; + +type TestBadgeProps = { + count: number; + label: string; + color: string; +}; + +const TestBadge: React.FC = ({ count, label, color }) => ( + + {label}: {count} + +); + +const fetcher = (url: string) => fetch(url).then((res) => res.json()); + +const HomePage = () => { + const { data, error, isLoading } = useSWR("/api/index", fetcher); + const { selectReportContext, setSelectReportContext, reportIdContext } = useAppContext(); + + const router = useRouter(); + + useEffect(() => {}, [data, selectReportContext, setSelectReportContext, reportIdContext]); + + if (error) return Failed to load; + if (isLoading) return ; + if (!data || !Array.isArray(data.indexData) || data.indexData.length === 0) { + return No report data available; + } + + const indexdData = data?.indexData.filter((report) => report?.reportId === reportIdContext); + + const { reportId, reportDate, tests } = indexdData[0] || {}; + + return ( + <> + + + {indexdData ? ( + <> + + + + Test Report ID: + + + {reportId} + + + + + + Generated: + + + {reportDate} + + + + + + + + {tests?.map((test: TestReport, index: number) => ( + + + + {test.testPath} + + + + + + + + + + + + router.push(`/reports/${reportId}/${test.title}/e2e`)}> + View Details + + + + + ))} + + > + ) : ( + No test reports found for the selected ID. + )} + + > + ); +}; + +export default HomePage; diff --git a/next-js/app/layout.tsx b/next-js/app/layout.tsx new file mode 100644 index 0000000..796f467 --- /dev/null +++ b/next-js/app/layout.tsx @@ -0,0 +1,51 @@ +import React from "react"; +import { metadata } from "./utils/metada"; +import { ColorSchemeScript, BackgroundImage } from "@mantine/core"; +import { Poppins } from "next/font/google"; +import "./globals.css"; +import { MantineProvider } from "@mantine/core"; +import "@mantine/core/styles.css"; +import { theme } from "../theme"; +import Footer from "./components/layout/Footer"; +import Navbar from "./components/layout/Navbar"; +import { Analytics } from "@vercel/analytics/react"; +import { AppWrapper } from "../context/index"; + +const poppins = Poppins({ + subsets: ["devanagari", "latin", "latin-ext"], + weight: ["200", "300", "400", "500", "600", "700", "800"], + style: ["normal", "italic"], +}); + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + + + {String(metadata.title) || "DEMO REPORT"} + + + + + + + + + + + {children} + + + + + + + + ); +} diff --git a/next-js/app/page.tsx b/next-js/app/page.tsx new file mode 100644 index 0000000..9fd2bf0 --- /dev/null +++ b/next-js/app/page.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import HomePage from "./home/HomePage"; +import { populateDatabase } from "./db/scripts/populateDatabase"; +import { Box } from "@mantine/core"; + + +export default async function Home() { + // Populate the database with data from Json files in the 'public/data' directory + populateDatabase(); + + return ( + + + + ); +} diff --git a/next-js/app/reports/[reportId]/[reportName]/e2e/Header.tsx b/next-js/app/reports/[reportId]/[reportName]/e2e/Header.tsx new file mode 100644 index 0000000..8061cc1 --- /dev/null +++ b/next-js/app/reports/[reportId]/[reportName]/e2e/Header.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import { Flex } from "@mantine/core"; +import PageHeader from "@/app/components/shared/PageHeader"; + + +export const HeaderReport = ({ title }: { title?: string }) => { + const titleNoCamelCase = title ? title.split(/(?<=[a-z])(?=[A-Z])/g).join(" ") : "Default Title"; + + return ( + + + + ); +}; + +export default HeaderReport; diff --git a/next-js/app/reports/[reportId]/[reportName]/e2e/Report.module.css b/next-js/app/reports/[reportId]/[reportName]/e2e/Report.module.css new file mode 100644 index 0000000..d932397 --- /dev/null +++ b/next-js/app/reports/[reportId]/[reportName]/e2e/Report.module.css @@ -0,0 +1,13 @@ +.title { + color: var(--mantine-color-blue-0); +} + +.description { + color: var(--mantine-color-white); +} + +.text { + @media (max-width: em(576px)) { + font-size: small; + } +} diff --git a/next-js/app/reports/[reportId]/[reportName]/e2e/Report.tsx b/next-js/app/reports/[reportId]/[reportName]/e2e/Report.tsx new file mode 100644 index 0000000..16b26d0 --- /dev/null +++ b/next-js/app/reports/[reportId]/[reportName]/e2e/Report.tsx @@ -0,0 +1,323 @@ +'use client' + +import React, { useEffect, useState, useMemo, Fragment } from 'react' +import useSWR from 'swr' +import Link from 'next/link' +import { + Text, + Badge, + Table, + Divider, + Box, + TextInput, + Flex, + Grid, + ScrollArea, + Paper, +} from '@mantine/core' +import { + ApiReportResponse, + SpecDescribe, + ApiResponse, +} from '@/app/utils/interfaces' +import classes from './Report.module.css' +import ChangesDetected from '@/app/components/shared/ChangesDetected' +import ImageAccordionModal from '@/app/components/shared/ImageAccordionModal' +import { useParams } from 'next/navigation' +import Loading from '@/app/components/shared/Loading' +import StatusCount from '@/app/components/shared/StatusCount' +import VideoPlayer from '@/app/components/shared/VideoPlayer' +import { useAppContext } from '@/context' +import TicketsCovered from '@/app/components/shared/TicketsCovered' + +const fetcher = (url: string) => fetch(url).then((res) => res.json()) + +const Report = () => { + const { data, error, isLoading } = useSWR( + '/api/reports', + fetcher + ) + const { data: indexData, isLoading: isIndexLoading } = useSWR( + '/api/index', + fetcher + ) + const params = useParams<{ reportId: string; reportName: string }>() + const { reportIdContext, selectReportContext } = useAppContext() + + const [search, setSearch] = useState('') + const [filteredTests, setFilteredTests] = useState([]) + + const reportIdParam = params.reportId + const reportNameParam = params.reportName + + const reportData = useMemo(() => { + return data?.reports.filter( + (report) => + report?.testDescribeId === `${reportNameParam}-${reportIdParam}` + ) + }, [data, reportNameParam, reportIdParam]) + + useEffect(() => { + // Refresh the context when changing reportIdParam or receiving new data + }, [data, params, reportIdContext, selectReportContext]) + + useEffect(() => { + if (reportData && reportData[0]?.specs) { + if (search) { + setFilteredTests( + reportData[0].specs.filter( + (test) => + test.specTitle.toLowerCase().includes(search.toLowerCase()) || + test.specId.toLowerCase().includes(search.toLowerCase()) || + test.specStatus.toLowerCase().includes(search.toLowerCase()) || + test.specMessage.toLowerCase().includes(search.toLowerCase()) || + test.specJiraTicket + .toLowerCase() + .includes(search.toLowerCase()) || + test.specReporter.toLowerCase().includes(search.toLowerCase()) + ) + ) + } else { + setFilteredTests(reportData[0]?.specs) + } + } + }, [search, reportData]) + + if (error) return Failed to load + if (isLoading) return + if (isIndexLoading) return + + if (reportIdParam) { + const findReportId = indexData?.indexData?.some( + (report) => report.reportId === reportIdParam + ) + if (!findReportId) return Report not found + } else { + return false + } + + const { + testDescribe, + testDescribeId, + testDescribePath, + testDescribeDate, + passCount, + failCount, + ticketsJira, + } = reportData?.[0] || {} + + const ticketsJiraArray = () => { + if (!ticketsJira) { + return No tickets in the database. + } + let tickets = [] + if (typeof ticketsJira === 'string') { + try { + tickets = JSON.parse(ticketsJira) + } catch (error) { + console.error('Parsing error:', error) + return Invalid ticket format. + } + } + if (tickets.length === 0) { + return No tickets in the database. + } + + return tickets.map((ticket: string, index: number) => ( + + + {ticket} + + {index < tickets.length - 1 ? | : null} + + )) + } + + return ( + <> + + + + + + Test name + + + {testDescribe} + + + + + Test id + + + {testDescribeId} + + + + + Test path + + + {testDescribePath} + + + + + + + API endpoint + + + {/* TODO: Refactor this to use the correct API endpoint */} + Add an API endpoint to the report + + + + + + Tickets covered: + + + + {ticketsJiraArray()} + + + + + + Generated + + + {testDescribeDate} + + + + + + + {/* */} + {/* {selectReportContext === 0 && } */} + + {/* TODO: Add logic to show the video player only if the has video */} + + + + + + + + + Total: + {' '} + {filteredTests.length.toString().padStart(3, '0')} + + setSearch(event.currentTarget.value)} + mb="md" + style={{ width: '16rem' }} + /> + + + + + + + + Id + Status + Test Name + Reported + Jira Ref + Screenshot + Description + + + + {filteredTests?.map((item) => ( + + {item.specId} + + + {item.specStatus} + + + {item.specTitle} + {item.specReporter} + + {item.specJiraTicket + ? item.specJiraTicket + .split(',') + .map((ticket, index) => ( + + {index > 0 && ' '} + + {ticket.trim()} + + + )) + : ''} + + + + + + {item.specMessage} + + ))} + + + + + + + > + ) +} + +export default Report diff --git a/next-js/app/reports/[reportId]/[reportName]/e2e/history/Header.tsx b/next-js/app/reports/[reportId]/[reportName]/e2e/history/Header.tsx new file mode 100644 index 0000000..dac3d0a --- /dev/null +++ b/next-js/app/reports/[reportId]/[reportName]/e2e/history/Header.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import { Flex } from "@mantine/core"; +import PageHeader from "@/app/components/shared/PageHeader"; + + +export const HeaderReport = ({ slogan, title }: { title?: string, slogan?: string }) => { + const titleNoCamelCase = title ? title.split(/(?<=[a-z])(?=[A-Z])/g).join(" ") : "Default Title"; + + return ( + + + + ); +}; + +export default HeaderReport; diff --git a/next-js/app/reports/[reportId]/[reportName]/e2e/history/History.tsx b/next-js/app/reports/[reportId]/[reportName]/e2e/history/History.tsx new file mode 100644 index 0000000..3561c90 --- /dev/null +++ b/next-js/app/reports/[reportId]/[reportName]/e2e/history/History.tsx @@ -0,0 +1,143 @@ +"use client"; + +import React, { useState } from "react"; +import { Text, Card, Group, Badge, Flex, Stack } from "@mantine/core"; +import Loading from "@/app/components/shared/Loading"; +import Header from "./Header"; +import { ApiHistoryResponse, HistoryReport } from "@/app/utils/interfaces"; +import HistorySearch from "@/app/reports/[reportId]/[reportName]/e2e/history/HistorySearch"; +import { useParams } from "next/navigation"; +import { useEffect } from "react"; + +interface HistoryPageProps { + data?: ApiHistoryResponse[]; +} + +const HistoryPage: React.FC = ({ data = [] }) => { + const [filteredReports, setFilteredReports] = useState([]); + + + const [isLoading, setIsLoading] = useState(false); + + const param = useParams(); + const reportName = param.reportName; + + useEffect(() => { + setIsLoading(true); + if (!data) { + setFilteredReports(data); + } + setIsLoading(false); + }, [data]); + + if (isLoading) return ; + + const HistoryItem = ({ report }: { report: HistoryReport }) => { + return ( + + + {report.reportTestId} + + + + Date: {report.dateHistory} + + {report.changesDetected?.map((change, index) => { + // const { create } = change.testDetails; + return ( + + {change?.testDetails?.create?.newTest ? ( + + + {change?.testDetails?.create?.status} + + + New test reported by {change?.testDetails?.create?.reported || "unknown"} for {change?.testDetails?.create?.title} + + + ) : ( + + + {change?.testDetails?.create?.status} + + + Reported by: {change?.testDetails?.create?.reported} - {change?.testDetails?.create?.title}:{" "} + {change?.testDetails?.create?.previous?.create?.statusChange && ( + <> + Status changed from{" "} + + {change?.testDetails?.create?.previous.create.statusChange} + {" "} + to {change?.testDetails?.create?.actual?.create?.statusChange} + > + )} + {change?.testDetails?.create?.previous?.create?.messageChange && ( + <> + + Description changed from{" "} + + {change?.testDetails?.create?.previous?.create?.messageChange} + {" "} + to {change?.testDetails?.create?.actual?.create?.messageChange}{" "} + + > + )} + {change?.testDetails?.create?.previous?.create?.reporter && ( + + Reporter changed from{" "} + + {change?.testDetails?.create?.previous?.create?.reporter} + {" "} + to {change?.testDetails?.create?.actual?.create?.reporter}. + + )} + + + )} + + ); + })} + + ); + }; + + return ( + <> + + + + {data.length === 0 ? ( + No history found + ) : ( + <> + + + {filteredReports.map((report, index) => ( + + ))} + > + )} + + > + ); +}; + +export default HistoryPage; diff --git a/next-js/app/reports/[reportId]/[reportName]/e2e/history/HistorySearch.tsx b/next-js/app/reports/[reportId]/[reportName]/e2e/history/HistorySearch.tsx new file mode 100644 index 0000000..2b8d973 --- /dev/null +++ b/next-js/app/reports/[reportId]/[reportName]/e2e/history/HistorySearch.tsx @@ -0,0 +1,61 @@ +import React, { useState, useEffect } from "react"; +import { TextInput, Flex } from "@mantine/core"; +import { HistoryReport } from "@/app/utils/interfaces"; + +interface Props { + reports: HistoryReport[]; + setFilteredReports: (historyData: HistoryReport[]) => void; +} + +const HistorySearch: React.FC = ({ reports, setFilteredReports }) => { + const [searchTerm, setSearchTerm] = useState(""); + + useEffect(() => { + const lowerSearchTerm = searchTerm.toLowerCase(); + + const filteredReports = reports + .map((report) => { + // Filter the keys below from the history report + const changesDetected = report?.changesDetected?.filter((change) => { + const create = change?.testDetails?.create; + + if (create) { + return ( + report.reportId?.toLowerCase().includes(lowerSearchTerm) || + report.dateHistory?.toLowerCase().includes(lowerSearchTerm) || + create.status?.toLowerCase().includes(lowerSearchTerm) || + create.reported?.toLowerCase().includes(lowerSearchTerm) || + create.title?.toLowerCase().includes(lowerSearchTerm) || + // More Options to filter + // create.previous?.create?.statusChange?.toLowerCase().includes(lowerSearchTerm) || + // create.previous?.create?.messageChange?.toLowerCase().includes(lowerSearchTerm) || + // create.previous?.create?.reporter?.toLowerCase().includes(lowerSearchTerm) || + create.actual?.create?.statusChange?.toLowerCase().includes(lowerSearchTerm) || + // create.actual?.create?.messageChange?.toLowerCase().includes(lowerSearchTerm) || + create.actual?.create?.reporter?.toLowerCase().includes(lowerSearchTerm) + ); + } + return false; + }); + + // If changes were found, return a new object containing the filtered changes + if (changesDetected && changesDetected.length > 0) { + return { ...report, changesDetected }; + } + return undefined; + }) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .filter((report): report is any => report !== undefined); + + setFilteredReports(filteredReports); + }, [searchTerm, reports, setFilteredReports]); + + return ( + + setSearchTerm(event.currentTarget.value)} style={{ width: "16rem" }} /> + + ); +}; + +export default HistorySearch; diff --git a/next-js/app/reports/[reportId]/[reportName]/e2e/history/page.tsx b/next-js/app/reports/[reportId]/[reportName]/e2e/history/page.tsx new file mode 100644 index 0000000..57e94f1 --- /dev/null +++ b/next-js/app/reports/[reportId]/[reportName]/e2e/history/page.tsx @@ -0,0 +1,40 @@ +import History from './History' +import { Container } from '@mantine/core' +import { getHistoryReport } from '@/app/db/actions/getHistoryReportAction' +import { ApiHistoryResponse } from '@/app/utils/interfaces' + +export function generateStaticParams() { + return [ + { reportId: '001', reportName: 'MiniMarket', e2e: true, history: true }, + { reportId: '001', reportName: 'GloballinkGo', e2e: true, history: true }, + ] +} + +export default async function Page(context: { + params: { reportId: string; reportName: string } +}) { + const reportName = context.params.reportName + + const data: ApiHistoryResponse[] | undefined = ( + await getHistoryReport(reportName) + ).map( + (item) => + ({ + id: item.id, + reportId: item?.reportId, + reportTestId: item?.reportTestId, + titleReport: item?.titleReport, + titlePath: item?.titlePath, + dateHistory: item?.dateHistory, + changesDetected: item?.changesDetected, + createdAt: item?.createdAt, + updatedAt: item?.updatedAt, + } as ApiHistoryResponse) + ) + + return ( + + + + ) +} diff --git a/next-js/app/reports/[reportId]/[reportName]/e2e/page.tsx b/next-js/app/reports/[reportId]/[reportName]/e2e/page.tsx new file mode 100644 index 0000000..13eeb00 --- /dev/null +++ b/next-js/app/reports/[reportId]/[reportName]/e2e/page.tsx @@ -0,0 +1,23 @@ +import Report from "./Report"; +import Header from "./Header"; +import { Container } from "@mantine/core"; + +export function generateStaticParams() { + return [ + { reportId: "001", reportName: "MiniMarket", e2e: true, history: true }, + { reportId: "001", reportName: "GloballinkGo", e2e: true, history: true }, + ]; +} + +export default function Page({ params }: { params: { reportId: string; reportName: string } }) { + const { reportName } = params; + return ( + + + + + + + ); +} +1; diff --git a/next-js/app/utils/interfaces.ts b/next-js/app/utils/interfaces.ts new file mode 100644 index 0000000..08dde64 --- /dev/null +++ b/next-js/app/utils/interfaces.ts @@ -0,0 +1,215 @@ +export interface Spec { + testId: string; + title: string; + websiteUrl: string; + reporter: string; + screenshotPath: string; + status: "Pass" | "Fail"; + jiraRef: string; + jiraTicket: string; + message: string; +} + +export interface Report { + title: string; + reportId: string; + id: string; + titlePath: string; + typeOfTest: string; + tickets?: string; + date: string; + passCount: number; + failCount: number; + specs: Spec[]; +} + +export interface TestReport { + dirName: string; + title: string; + testId: string; + testPath: string; + dateTest: string | undefined; + typeOfTest: string; + passCount: number; + failCount: number; + totalTests: number; +} + +export interface ReportIndexData { + reportId: string; + reportDate: string; + tests: TestReport[]; +} + +export interface ApiResponse { + indexData: ReportIndexData[]; +} + +export interface ReportDataInterface { + reportIndexData: Change[]; +} + +export interface ReportDataInterface { + reportIndexData: Change[]; +} + +export interface ImageAccordionModalProps { + imageUrl: string; + message: string; + title: string; +} + +export interface Change { + testDetails: { + testId: string; + reporter: string; + title: string; + newTest: boolean; + status: string; + previous?: { + statusChange?: string; + reporter?: string; + messageChange?: string; + }; + actual: { + statusChange?: string | undefined; + reporter?: string | undefined; + messageChange?: string | undefined; + }; + }; +} + +export interface ChangesDetectedInterface { + changesDetected: Change[]; +} + +export interface StatusMessageProps { + currentReportId: string; +} + +export interface TestStatus { + statusChange?: string | null; + messageChange?: string | null; + reporter?: string | null; +} + +export interface TestDetails { + testId?: string; + title?: string; + newTest?: boolean; + reporter?: string; + status?: string; + previous?: TestStatus; + actual?: TestStatus; +} + +export interface ChangesTestDetails { + testDetails: TestDetails; +} + +export interface HistoryJsonTypes { + reportId: string; + id: string; + title: string; + titlePath: string; + date: string; + // changesDetected: ChangesTestDetails[]; + changesDetected: Change[]; +} +export interface ChangesDetectedProps { + reportId: string; + reportTestId: string; + titleReport: string; + titlePath: string; + dateHistory: string; + // changesDetected: ChangesTestDetails[]; + changesDetected: Change[]; +} + +export interface ApiHistorytResponse { + data: ChangesDetectedProps[] +} + +export interface ReportDescribeTypes { + reportId: string; + testDescribeTitle: string; + testDescribeId: string; + typeOfTest: string; + testDescribeDate: string; + testDescribePath: string; + ticketsJira?: string; + testDescribe: string; + passCount: number; + failCount: number; + specs: SpecDescribe[]; +} + +export interface ApiReportResponse { + reports: ReportDescribeTypes[]; +} + +export interface SpecDescribe { + specId: string; + specTitle: string; + specUrl: string; + specReporter: string; + specScreenshotPath: string; + specStatus: "Pass" | "Fail"; + specJiraRef: string; + specJiraTicket: string; + specMessage: string; +} + +// HistoryPage +export interface NestedCreateDetails { + statusChange?: string; + messageChange?: string; + reporter?: string; +} + +export interface CreateDetails { + status: "Pass" | "Fail"; + reported: string; + title: string; + newTest: boolean; + actual?: NestedCreatePreviousActual; + previous?: NestedCreatePreviousActual; +} + +export interface NestedCreatePreviousActual { + create?: NestedCreateDetails; +} + +export interface TestDetails { + create?: CreateDetails; +} + +export interface ChangeHistory { + testDetails?: TestDetails; +} + +export interface HistoryReport { + id?: string; + reportId?: string; + reportTestId?: string; + titleReport?: string; + titlePath?: string; + dateHistory?: string | null; + createdAt?: Date; + updatedAt?: Date; + changesDetected?: ChangeHistory[]; +} + +export interface ApiHistoryResponse { + data?: HistoryReport[] | undefined; +} + +export interface ScriptConfigTypes { + id: string; + reportIdScript: string; + scriptTag: string; +} + +export interface ScriptApiResponse { + data: ScriptConfigTypes[]; +} \ No newline at end of file diff --git a/next-js/app/utils/metada.ts b/next-js/app/utils/metada.ts new file mode 100644 index 0000000..e8b3dac --- /dev/null +++ b/next-js/app/utils/metada.ts @@ -0,0 +1,7 @@ +import type { Metadata } from "next"; + +export const metadata: Metadata = { + title: "TBardini - Demo Test Report", + description: "Explore comprehensive end-to-end test results for a Next.js application, using Playwright. This report highlights the robust testing strategies and practices I employ to ensure high-quality software products.", +}; + diff --git a/next-js/context/index.tsx b/next-js/context/index.tsx new file mode 100644 index 0000000..f56bcc2 --- /dev/null +++ b/next-js/context/index.tsx @@ -0,0 +1,59 @@ +"use client"; + +import React, { createContext, useState, useContext, useEffect } from "react"; +import useSWR from "swr"; +import { ApiResponse } from "../app/utils/interfaces"; +import Loading from "@/app/components/shared/Loading"; + +interface ContextType { + reportIdContext: string; + setReportIdContext: (id: string) => void; + selectReportContext: number; + setSelectReportContext: (item: number) => void; +} + +const AppContext = createContext(undefined); + +const fetcher = (url: string) => fetch(url).then((res) => res.json()); + +export function AppWrapper({ children }: { children: React.ReactNode }) { + const { data, error, isLoading } = useSWR("/api/index", fetcher); + + const [reportIdContext, setReportIdContext] = useState(""); + const [selectReportContext, setSelectReportContext] = useState(0); + + useEffect(() => { + if (data?.indexData[0]?.reportId) { + setReportIdContext(data.indexData[0].reportId); + } + }, [data]); + + if (error) { + return Failed to load; + } + + if (isLoading) { + return ; + } + + return ( + + {children} + + ); +} + +export function useAppContext() { + const context = useContext(AppContext); + if (context === undefined) { + throw new Error("useAppContext must be used within a AppWrapper"); + } + return context; +} diff --git a/next-js/next.config.mjs b/next-js/next.config.mjs new file mode 100644 index 0000000..4cb30e9 --- /dev/null +++ b/next-js/next.config.mjs @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + +}; + +export default nextConfig; diff --git a/next-js/package.json b/next-js/package.json new file mode 100644 index 0000000..1e7a9af --- /dev/null +++ b/next-js/package.json @@ -0,0 +1,52 @@ +{ + "name": "custom-e2e-report", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "NODE_ENV=development next dev", + "build": "NODE_ENV=production next build", + "start": "NODE_ENV=production next start", + "lint": "next lint", + "postinstall": "prisma generate", + "vercel-build": "pnpx prisma generate && pnpx prisma migrate && next build" + }, + "dependencies": { + "@mantine/core": "^7.6.1", + "@mantine/form": "^6.0.21", + "@mantine/hooks": "^7.6.1", + "@prisma/client": "^5.12.1", + "@radix-ui/react-icons": "^1.3.0", + "@tabler/icons-react": "^2.47.0", + "@tiptap/extension-link": "^2.2.4", + "@tiptap/react": "^2.2.4", + "@tiptap/starter-kit": "^2.2.4", + "@vercel/analytics": "^1.2.2", + "add": "^2.0.6", + "dayjs": "^1.11.10", + "embla-carousel-react": "^8.0.0", + "next": "14.1.2", + "postcss-import": "^16.1.0", + "punycode": "^2.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "recharts": "^2.12.2", + "sharp": "^0.33.3", + "swr": "^2.2.5" + }, + "devDependencies": { + "@next/eslint-plugin-next": "^14.1.3", + "@types/node": "^20.11.30", + "@types/react": "^18.2.63", + "@types/react-dom": "^18.2.20", + "autoprefixer": "^10.4.18", + "eslint": "^8.57.0", + "eslint-config-next": "14.1.1", + "postcss": "^8.4.35", + "postcss-preset-mantine": "^1.13.0", + "postcss-simple-vars": "^7.0.1", + "prisma": "^5.12.1", + "tailwindcss": "^3.4.1", + "ts-node": "^10.9.1", + "typescript": "^5.4.3" + } +} diff --git a/next-js/pnpm-lock.yaml b/next-js/pnpm-lock.yaml new file mode 100644 index 0000000..4e13a3e --- /dev/null +++ b/next-js/pnpm-lock.yaml @@ -0,0 +1,4432 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@mantine/core': + specifier: ^7.6.1 + version: 7.8.1(@mantine/hooks@7.8.1)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0) + '@mantine/form': + specifier: ^6.0.21 + version: 6.0.21(react@18.2.0) + '@mantine/hooks': + specifier: ^7.6.1 + version: 7.8.1(react@18.2.0) + '@prisma/client': + specifier: ^5.12.1 + version: 5.13.0(prisma@5.13.0) + '@radix-ui/react-icons': + specifier: ^1.3.0 + version: 1.3.0(react@18.2.0) + '@tabler/icons-react': + specifier: ^2.47.0 + version: 2.47.0(react@18.2.0) + '@tiptap/extension-link': + specifier: ^2.2.4 + version: 2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0) + '@tiptap/react': + specifier: ^2.2.4 + version: 2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0)(react-dom@18.2.0)(react@18.2.0) + '@tiptap/starter-kit': + specifier: ^2.2.4 + version: 2.3.0(@tiptap/pm@2.3.0) + '@vercel/analytics': + specifier: ^1.2.2 + version: 1.2.2(next@14.1.2)(react@18.2.0) + add: + specifier: ^2.0.6 + version: 2.0.6 + dayjs: + specifier: ^1.11.10 + version: 1.11.10 + embla-carousel-react: + specifier: ^8.0.0 + version: 8.0.2(react@18.2.0) + next: + specifier: 14.1.2 + version: 14.1.2(react-dom@18.2.0)(react@18.2.0) + postcss-import: + specifier: ^16.1.0 + version: 16.1.0(postcss@8.4.38) + punycode: + specifier: ^2.3.1 + version: 2.3.1 + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + recharts: + specifier: ^2.12.2 + version: 2.12.6(react-dom@18.2.0)(react@18.2.0) + sharp: + specifier: ^0.33.3 + version: 0.33.3 + swr: + specifier: ^2.2.5 + version: 2.2.5(react@18.2.0) + +devDependencies: + '@next/eslint-plugin-next': + specifier: ^14.1.3 + version: 14.2.2 + '@types/node': + specifier: ^20.11.30 + version: 20.12.7 + '@types/react': + specifier: ^18.2.63 + version: 18.2.79 + '@types/react-dom': + specifier: ^18.2.20 + version: 18.2.25 + autoprefixer: + specifier: ^10.4.18 + version: 10.4.19(postcss@8.4.38) + eslint: + specifier: ^8.57.0 + version: 8.57.0 + eslint-config-next: + specifier: 14.1.1 + version: 14.1.1(eslint@8.57.0)(typescript@5.4.5) + postcss: + specifier: ^8.4.35 + version: 8.4.38 + postcss-preset-mantine: + specifier: ^1.13.0 + version: 1.14.4(postcss@8.4.38) + postcss-simple-vars: + specifier: ^7.0.1 + version: 7.0.1(postcss@8.4.38) + prisma: + specifier: ^5.12.1 + version: 5.13.0 + tailwindcss: + specifier: ^3.4.1 + version: 3.4.3(ts-node@10.9.2) + ts-node: + specifier: ^10.9.1 + version: 10.9.2(@types/node@20.12.7)(typescript@5.4.5) + typescript: + specifier: ^5.4.3 + version: 5.4.5 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + dev: true + + /@babel/runtime@7.24.4: + resolution: {integrity: sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 + + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + + /@emnapi/runtime@1.1.1: + resolution: {integrity: sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: false + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.57.0: + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@floating-ui/core@1.6.0: + resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==} + dependencies: + '@floating-ui/utils': 0.2.1 + dev: false + + /@floating-ui/dom@1.6.3: + resolution: {integrity: sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==} + dependencies: + '@floating-ui/core': 1.6.0 + '@floating-ui/utils': 0.2.1 + dev: false + + /@floating-ui/react-dom@2.0.8(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/dom': 1.6.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@floating-ui/react@0.26.12(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-D09o62HrWdIkstF2kGekIKAC0/N/Dl6wo3CQsnLcOmO3LkW6Ik8uIb3kw8JYkwxNCcg+uJ2bpWUiIijTBep05w==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) + '@floating-ui/utils': 0.2.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tabbable: 6.2.0 + dev: false + + /@floating-ui/utils@0.2.1: + resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} + dev: false + + /@humanwhocodes/config-array@0.11.14: + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.3: + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + dev: true + + /@img/sharp-darwin-arm64@0.33.3: + resolution: {integrity: sha512-FaNiGX1MrOuJ3hxuNzWgsT/mg5OHG/Izh59WW2mk1UwYHUwtfbhk5QNKYZgxf0pLOhx9ctGiGa2OykD71vOnSw==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.2 + dev: false + optional: true + + /@img/sharp-darwin-x64@0.33.3: + resolution: {integrity: sha512-2QeSl7QDK9ru//YBT4sQkoq7L0EAJZA3rtV+v9p8xTKl4U1bUqTIaCnoC7Ctx2kCjQgwFXDasOtPTCT8eCTXvw==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.2 + dev: false + optional: true + + /@img/sharp-libvips-darwin-arm64@1.0.2: + resolution: {integrity: sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==} + engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-darwin-x64@1.0.2: + resolution: {integrity: sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==} + engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-arm64@1.0.2: + resolution: {integrity: sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==} + engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-arm@1.0.2: + resolution: {integrity: sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==} + engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-s390x@1.0.2: + resolution: {integrity: sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==} + engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-x64@1.0.2: + resolution: {integrity: sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==} + engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linuxmusl-arm64@1.0.2: + resolution: {integrity: sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==} + engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linuxmusl-x64@1.0.2: + resolution: {integrity: sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==} + engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-linux-arm64@0.33.3: + resolution: {integrity: sha512-Zf+sF1jHZJKA6Gor9hoYG2ljr4wo9cY4twaxgFDvlG0Xz9V7sinsPp8pFd1XtlhTzYo0IhDbl3rK7P6MzHpnYA==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.2 + dev: false + optional: true + + /@img/sharp-linux-arm@0.33.3: + resolution: {integrity: sha512-Q7Ee3fFSC9P7vUSqVEF0zccJsZ8GiiCJYGWDdhEjdlOeS9/jdkyJ6sUSPj+bL8VuOYFSbofrW0t/86ceVhx32w==} + engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.2 + dev: false + optional: true + + /@img/sharp-linux-s390x@0.33.3: + resolution: {integrity: sha512-vFk441DKRFepjhTEH20oBlFrHcLjPfI8B0pMIxGm3+yilKyYeHEVvrZhYFdqIseSclIqbQ3SnZMwEMWonY5XFA==} + engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.2 + dev: false + optional: true + + /@img/sharp-linux-x64@0.33.3: + resolution: {integrity: sha512-Q4I++herIJxJi+qmbySd072oDPRkCg/SClLEIDh5IL9h1zjhqjv82H0Seupd+q2m0yOfD+/fJnjSoDFtKiHu2g==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.2 + dev: false + optional: true + + /@img/sharp-linuxmusl-arm64@0.33.3: + resolution: {integrity: sha512-qnDccehRDXadhM9PM5hLvcPRYqyFCBN31kq+ErBSZtZlsAc1U4Z85xf/RXv1qolkdu+ibw64fUDaRdktxTNP9A==} + engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.2 + dev: false + optional: true + + /@img/sharp-linuxmusl-x64@0.33.3: + resolution: {integrity: sha512-Jhchim8kHWIU/GZ+9poHMWRcefeaxFIs9EBqf9KtcC14Ojk6qua7ghKiPs0sbeLbLj/2IGBtDcxHyjCdYWkk2w==} + engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.2 + dev: false + optional: true + + /@img/sharp-wasm32@0.33.3: + resolution: {integrity: sha512-68zivsdJ0koE96stdUfM+gmyaK/NcoSZK5dV5CAjES0FUXS9lchYt8LAB5rTbM7nlWtxaU/2GON0HVN6/ZYJAQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [wasm32] + requiresBuild: true + dependencies: + '@emnapi/runtime': 1.1.1 + dev: false + optional: true + + /@img/sharp-win32-ia32@0.33.3: + resolution: {integrity: sha512-CyimAduT2whQD8ER4Ux7exKrtfoaUiVr7HG0zZvO0XTFn2idUWljjxv58GxNTkFb8/J9Ub9AqITGkJD6ZginxQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-win32-x64@0.33.3: + resolution: {integrity: sha512-viT4fUIDKnli3IfOephGnolMzhz5VaTvDRkYqtZxOMIoMQ4MrAziO7pT1nVnOt2FAm7qW5aa+CCc13aEY6Le0g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: true + + /@jridgewell/gen-mapping@0.3.5: + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + dev: true + + /@jridgewell/resolve-uri@3.1.2: + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.2.1: + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.25: + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@mantine/core@7.8.1(@mantine/hooks@7.8.1)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-dttbP2BhBzFJYBqgAQedJRca5+MXlJbppRhRsufnFeO7YC/UpZutOoHQ9dxGEQnhAWJ/d+wuRvYWG/gXex+wYQ==} + peerDependencies: + '@mantine/hooks': 7.8.1 + react: ^18.2.0 + react-dom: ^18.2.0 + dependencies: + '@floating-ui/react': 0.26.12(react-dom@18.2.0)(react@18.2.0) + '@mantine/hooks': 7.8.1(react@18.2.0) + clsx: 2.1.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-number-format: 5.3.4(react-dom@18.2.0)(react@18.2.0) + react-remove-scroll: 2.5.9(@types/react@18.2.79)(react@18.2.0) + react-textarea-autosize: 8.5.3(@types/react@18.2.79)(react@18.2.0) + type-fest: 4.17.0 + transitivePeerDependencies: + - '@types/react' + dev: false + + /@mantine/form@6.0.21(react@18.2.0): + resolution: {integrity: sha512-d4tlxyZic7MSDnaPx/WliCX1sRFDkUd2nxx4MxxO2T4OSek0YDqTlSBCxeoveu60P+vrQQN5rbbsVsaOJBe4SQ==} + peerDependencies: + react: '>=16.8.0' + dependencies: + fast-deep-equal: 3.1.3 + klona: 2.0.6 + react: 18.2.0 + dev: false + + /@mantine/hooks@7.8.1(react@18.2.0): + resolution: {integrity: sha512-zCLqnxTUR2N6Awbt4rv/26UKTc75dXTVmCPsWUb6wdQExuC28fucG6kMoNYHVmOBDq9f3KP9zWOGDelHM2ogZA==} + peerDependencies: + react: ^18.2.0 + dependencies: + react: 18.2.0 + dev: false + + /@next/env@14.1.2: + resolution: {integrity: sha512-U0iEG+JF86j6qyu330sfPgsMmDVH8vWVmzZadl+an5EU3o5HqdNytOpM+HsFpl58PmhGBTKx3UmM9c+eoLK0mA==} + dev: false + + /@next/eslint-plugin-next@14.1.1: + resolution: {integrity: sha512-NP1WoGFnFLpqqCWgGFjnn/sTwUExdPyjeFKRdQP1X/bL/tjAQ/TXDmYqw6vzGaP5NaZ2u6xzg+N/0nd7fOPOGQ==} + dependencies: + glob: 10.3.10 + dev: true + + /@next/eslint-plugin-next@14.2.2: + resolution: {integrity: sha512-q+Ec2648JtBpKiu/FSJm8HAsFXlNvioHeBCbTP12T1SGcHYwhqHULSfQgFkPgHDu3kzNp2Kem4J54bK4rPQ5SQ==} + dependencies: + glob: 10.3.10 + dev: true + + /@next/swc-darwin-arm64@14.1.2: + resolution: {integrity: sha512-E4/clgk0ZrYMo9eMRwP/4IO/cvXF1yEYSnGcdGfH+NYTR8bNFy76TSlc1Vb2rK3oaQY4BVHRpx8f/sMN/D5gNw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@next/swc-darwin-x64@14.1.2: + resolution: {integrity: sha512-j8mEOI+ZM0tU9B/L/OGa6F7d9FXYMkog5OWWuhTWzz3iZ91UKIGGpD/ojTNKuejainDMgbqOBTNnLg0jZywM/g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-arm64-gnu@14.1.2: + resolution: {integrity: sha512-qpRrd5hl6BFTWiFLgHtJmqqQGRMs+ol0MN9pEp0SYoLs3j8OTErPiDMhbKWjMWHGdc2E3kg4RRBV3cSTZiePiQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-arm64-musl@14.1.2: + resolution: {integrity: sha512-HAhvVXAv+wnbj0wztT0YnpgJVoHtw1Mv4Y1R/JJcg5yXSU8FsP2uEGUwjQaqPoD76YSZjuKl32YbJlmPgQbLFw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-x64-gnu@14.1.2: + resolution: {integrity: sha512-PCWC312woXLWOXiedi1E+fEw6B/ECP1fMiK1nSoGS2E43o56Z8kq4WeJLbJoufFQGVj5ZOKU3jIVyV//3CI4wQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-x64-musl@14.1.2: + resolution: {integrity: sha512-KQSKzdWPNrYZjeTPCsepEpagOzU8Nf3Zzu53X1cLsSY6QlOIkYcSgEihRjsMKyeQW4aSvc+nN5pIpC2pLWNSMA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-arm64-msvc@14.1.2: + resolution: {integrity: sha512-3b0PouKd09Ulm2T1tjaRnwQj9+UwSsMO680d/sD4XAlm29KkNmVLAEIwWTfb3L+E11Qyw+jdcN3HtbDCg5+vYA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-ia32-msvc@14.1.2: + resolution: {integrity: sha512-CC1gaJY4h+wg6d5r2biggGM6nCFXh/6WEim2VOQI0WrA6easCQi2P2hzWyrU6moQ0g1GOiWzesGc6nn0a92Kgg==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-x64-msvc@14.1.2: + resolution: {integrity: sha512-pfASwanOd+yP3D80O63DuQffrBySZPuB7wRN0IGSRq/0rDm9p/MvvnLzzgP2kSiLOUklOrFYVax7P6AEzjGykQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + dev: true + + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: true + optional: true + + /@popperjs/core@2.11.8: + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + dev: false + + /@prisma/client@5.13.0(prisma@5.13.0): + resolution: {integrity: sha512-uYdfpPncbZ/syJyiYBwGZS8Gt1PTNoErNYMuqHDa2r30rNSFtgTA/LXsSk55R7pdRTMi5pHkeP9B14K6nHmwkg==} + engines: {node: '>=16.13'} + requiresBuild: true + peerDependencies: + prisma: '*' + peerDependenciesMeta: + prisma: + optional: true + dependencies: + prisma: 5.13.0 + dev: false + + /@prisma/debug@5.13.0: + resolution: {integrity: sha512-699iqlEvzyCj9ETrXhs8o8wQc/eVW+FigSsHpiskSFydhjVuwTJEfj/nIYqTaWFYuxiWQRfm3r01meuW97SZaQ==} + + /@prisma/engines-version@5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b: + resolution: {integrity: sha512-AyUuhahTINGn8auyqYdmxsN+qn0mw3eg+uhkp8zwknXYIqoT3bChG4RqNY/nfDkPvzWAPBa9mrDyBeOnWSgO6A==} + + /@prisma/engines@5.13.0: + resolution: {integrity: sha512-hIFLm4H1boj6CBZx55P4xKby9jgDTeDG0Jj3iXtwaaHmlD5JmiDkZhh8+DYWkTGchu+rRF36AVROLnk0oaqhHw==} + requiresBuild: true + dependencies: + '@prisma/debug': 5.13.0 + '@prisma/engines-version': 5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b + '@prisma/fetch-engine': 5.13.0 + '@prisma/get-platform': 5.13.0 + + /@prisma/fetch-engine@5.13.0: + resolution: {integrity: sha512-Yh4W+t6YKyqgcSEB3odBXt7QyVSm0OQlBSldQF2SNXtmOgMX8D7PF/fvH6E6qBCpjB/yeJLy/FfwfFijoHI6sA==} + dependencies: + '@prisma/debug': 5.13.0 + '@prisma/engines-version': 5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b + '@prisma/get-platform': 5.13.0 + + /@prisma/get-platform@5.13.0: + resolution: {integrity: sha512-B/WrQwYTzwr7qCLifQzYOmQhZcFmIFhR81xC45gweInSUn2hTEbfKUPd2keAog+y5WI5xLAFNJ3wkXplvSVkSw==} + dependencies: + '@prisma/debug': 5.13.0 + + /@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 + + /@remirror/core-constants@2.0.2: + resolution: {integrity: sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==} + dev: false + + /@rushstack/eslint-patch@1.10.2: + resolution: {integrity: sha512-hw437iINopmQuxWPSUEvqE56NCPsiU8N4AYtfHmJFckclktzK9YQJieD3XkDCDH4OjL+C7zgPUh73R/nrcHrqw==} + dev: true + + /@swc/helpers@0.5.2: + resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + dependencies: + tslib: 2.6.2 + dev: false + + /@tabler/icons-react@2.47.0(react@18.2.0): + resolution: {integrity: sha512-iqly2FvCF/qUbgmvS8E40rVeYY7laltc5GUjRxQj59DuX0x/6CpKHTXt86YlI2whg4czvd/c8Ce8YR08uEku0g==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 + dependencies: + '@tabler/icons': 2.47.0 + prop-types: 15.8.1 + react: 18.2.0 + dev: false + + /@tabler/icons@2.47.0: + resolution: {integrity: sha512-4w5evLh+7FUUiA1GucvGj2ReX2TvOjEr4ejXdwL/bsjoSkof6r1gQmzqI+VHrE2CpJpB3al7bCTulOkFa/RcyA==} + dev: false + + /@tiptap/core@2.3.0(@tiptap/pm@2.3.0): + resolution: {integrity: sha512-Gk2JN3i5CMkYGmsbyFI7cBUftWa+F7QYmeCLTWfbuy+hCM2OBsnYVKxhggFPGXRL5KLBEgBWeCeWMHfIw3B2MA==} + peerDependencies: + '@tiptap/pm': ^2.0.0 + dependencies: + '@tiptap/pm': 2.3.0 + dev: false + + /@tiptap/extension-blockquote@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-Cztt77t7f+f0fuPy+FWUL8rKTIpcdsVT0z0zYQFFafvGaom0ZALQSOdTR/q+Kle9I4DaCMO3/Q0mwax/D4k4+A==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/extension-bold@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-SzkbJibHXFNU7TRaAebTtwbXUEhGZ8+MhlBn12aQ4QhdjNtFpQwKXQPyYeDyZGcyiOFgtFTb+WIfCGm8ZX0Fpw==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/extension-bubble-menu@2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0): + resolution: {integrity: sha512-dqyfQ8idTlhapvt0fxCGvkyjw92pBEwPqmkJ01h3EE8wTh53j0ytOHyMSf1KBuzardxpd8Yya3zlrAcR0Z3DlQ==} + peerDependencies: + '@tiptap/core': ^2.0.0 + '@tiptap/pm': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + '@tiptap/pm': 2.3.0 + tippy.js: 6.3.7 + dev: false + + /@tiptap/extension-bullet-list@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-4nU4vJ5FjRDLqHm085vYAkuo68UK84Wl6CDSjm7sPVcu0FvQX02Okqt65azoSYQeS1SSSd5qq9YZuGWcYdp4Cw==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/extension-code-block@2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0): + resolution: {integrity: sha512-+Ne6PRBwQt70Pp8aW2PewaEy4bHrNYn4N+y8MObsFtqLutXBz4nXnsXWiNYFQZwzlUY+CHG4XS73mx8oMOFfDw==} + peerDependencies: + '@tiptap/core': ^2.0.0 + '@tiptap/pm': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + '@tiptap/pm': 2.3.0 + dev: false + + /@tiptap/extension-code@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-O2FZmosiIRoVbW82fZy8xW4h4gb2xAzxWzHEcsHPlwCbE3vYvcBMmbkQ5p+33eRtuRQInzl3Q/cwupv9ctIepQ==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/extension-document@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-WC55SMrtlsNOnHXpzbXDzJOp7eKmZV0rXooKmvCDqoiLO/DKpyQXyF+0UHfcRPmUAi2GWFPaer7+p1H9xzcjXg==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/extension-dropcursor@2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0): + resolution: {integrity: sha512-WWxxGQPWdbzxyYP6jtBYSq4wMRhINhI0wBC8pgkxTVwCIWftMuYj++FP4LLIpuWgj78PWApuoM0QQxk4Lj7FOw==} + peerDependencies: + '@tiptap/core': ^2.0.0 + '@tiptap/pm': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + '@tiptap/pm': 2.3.0 + dev: false + + /@tiptap/extension-floating-menu@2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0): + resolution: {integrity: sha512-bNY43/yU/+wGfmk2eDV7EPDAN/akbC+YnSKTA5VPJADzscvlrL2HlQrxbd/STIdlwKqdPU5MokcvCChhfZ4f6w==} + peerDependencies: + '@tiptap/core': ^2.0.0 + '@tiptap/pm': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + '@tiptap/pm': 2.3.0 + tippy.js: 6.3.7 + dev: false + + /@tiptap/extension-gapcursor@2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0): + resolution: {integrity: sha512-OxcXcfD0uzNcXdXu2ZpXFAtXIsgK2MBHvFUs0t0gxtcL/t43pTOQBLy+29Ei30BxpwLghtX8jQ6IDzMiybq/sA==} + peerDependencies: + '@tiptap/core': ^2.0.0 + '@tiptap/pm': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + '@tiptap/pm': 2.3.0 + dev: false + + /@tiptap/extension-hard-break@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-9pXi69SzLabbjY5KZ54UKzu7HAHTla9aYZKH56VatOAiJOPKJppFbU2/NfJwGzDrEtfOiDqr3dYbUDF3RuCFoQ==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/extension-heading@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-YcZoUYfqb0nohoPgem4f8mjn5OqDomFrbJiC9VRHUOCIuEu+aJEYwp8mmdkLnS3f+LRCZ6G76cJJ50lkzSAZRw==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/extension-history@2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0): + resolution: {integrity: sha512-EF5Oq9fe/VBzU1Lsow2ubOlx1e1r4OQT1WUPGsRnL7pr94GH1Skpk7/hs9COJ9K6kP3Ebt42XjP0JEQodR58YA==} + peerDependencies: + '@tiptap/core': ^2.0.0 + '@tiptap/pm': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + '@tiptap/pm': 2.3.0 + dev: false + + /@tiptap/extension-horizontal-rule@2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0): + resolution: {integrity: sha512-4DB8GU3uuDzzyqUmONIb3CHXcQ6Nuy4mHHkFSmUyEjg1i5eMQU5H7S6mNvZbltcJB2ImgCSwSMlj1kVN3MLIPg==} + peerDependencies: + '@tiptap/core': ^2.0.0 + '@tiptap/pm': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + '@tiptap/pm': 2.3.0 + dev: false + + /@tiptap/extension-italic@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-jdFjLjdt5JtPlGMpoS6TEq5rznjbAYVlPwcw5VkYENVIYIGIR1ylIw2JwK1nUEsQ+OgYwVxHLejcUXWG1dCi2g==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/extension-link@2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0): + resolution: {integrity: sha512-CnJAlV0ZOdEhKmDfYKuHJVG8g79iCFQ85cX/CROTWyuMfXz9uhj2rLpZ6nfidVbonqxAhQp7NAIr2y+Fj5/53A==} + peerDependencies: + '@tiptap/core': ^2.0.0 + '@tiptap/pm': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + '@tiptap/pm': 2.3.0 + linkifyjs: 4.1.3 + dev: false + + /@tiptap/extension-list-item@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-mHU+IuRa56OT6YCtxf5Z7OSUrbWdKhGCEX7RTrteDVs5oMB6W3oF9j88M5qQmZ1WDcxvQhAOoXctnMt6eX9zcA==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/extension-ordered-list@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-gkf0tltXjlUj0cqyfDV2r7xy9YPKtcVSWwlCPun6OOi0KzKFiAMqQpA9hy2W6gJ+KCp8+KNRMClZOfH4TnnBfg==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/extension-paragraph@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-peCpA7DFqkd0cHb+cHv4YHNoMsXG8tKFNJlCHpLmsZWl2hWmpKgKmUrXAUfzjcFSvkZxn0xYc5oWbqUgg+2LzA==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/extension-strike@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-gOW4ALeH8gkJiUGGXVy/AOd5lAPTX0bzoOW1+sCLcTA7t8dluBW7M2ngNYxTEtlKqyv7aLfrgsYSiqucmmfSLw==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/extension-text@2.3.0(@tiptap/core@2.3.0): + resolution: {integrity: sha512-zkudl0TyKRy/8vHtyo5dMzjBRD0HEUnsS8YOsjR4xwQq5EYUXleRgM1s6lb6Yms2sLUAZRWdDddoQ686iq4zQg==} + peerDependencies: + '@tiptap/core': ^2.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + dev: false + + /@tiptap/pm@2.3.0: + resolution: {integrity: sha512-4WYqShZBwDyReKvapC0nmeYdOtZbZ31y4MjolpKQaSD4I7kg/oZspC+byUGdvIRsNpRN7i2X0IyvdISKk8gw5Q==} + dependencies: + prosemirror-changeset: 2.2.1 + prosemirror-collab: 1.3.1 + prosemirror-commands: 1.5.2 + prosemirror-dropcursor: 1.8.1 + prosemirror-gapcursor: 1.3.2 + prosemirror-history: 1.4.0 + prosemirror-inputrules: 1.4.0 + prosemirror-keymap: 1.2.2 + prosemirror-markdown: 1.12.0 + prosemirror-menu: 1.2.4 + prosemirror-model: 1.20.0 + prosemirror-schema-basic: 1.2.2 + prosemirror-schema-list: 1.3.0 + prosemirror-state: 1.4.3 + prosemirror-tables: 1.3.7 + prosemirror-trailing-node: 2.0.8(prosemirror-model@1.20.0)(prosemirror-state@1.4.3)(prosemirror-view@1.33.5) + prosemirror-transform: 1.8.0 + prosemirror-view: 1.33.5 + dev: false + + /@tiptap/react@2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-ThgFJQTWYKRClTV2Zg0wBRqfy0EGz3U4NOey7jwncUjSjx5+o9nXbfQAYWDKQFfWyE+wnrBTYfddEP9pHNX5cQ==} + peerDependencies: + '@tiptap/core': ^2.0.0 + '@tiptap/pm': ^2.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + '@tiptap/extension-bubble-menu': 2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0) + '@tiptap/extension-floating-menu': 2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0) + '@tiptap/pm': 2.3.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@tiptap/starter-kit@2.3.0(@tiptap/pm@2.3.0): + resolution: {integrity: sha512-TjvCd/hzEnuEYOdr5uQqcfHOMuj7JRoZBPdheupwl3SbuYiCxtcqYyAE5qoGXWwuVe9xVGerOLVPkDUgmyrH6A==} + dependencies: + '@tiptap/core': 2.3.0(@tiptap/pm@2.3.0) + '@tiptap/extension-blockquote': 2.3.0(@tiptap/core@2.3.0) + '@tiptap/extension-bold': 2.3.0(@tiptap/core@2.3.0) + '@tiptap/extension-bullet-list': 2.3.0(@tiptap/core@2.3.0) + '@tiptap/extension-code': 2.3.0(@tiptap/core@2.3.0) + '@tiptap/extension-code-block': 2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0) + '@tiptap/extension-document': 2.3.0(@tiptap/core@2.3.0) + '@tiptap/extension-dropcursor': 2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0) + '@tiptap/extension-gapcursor': 2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0) + '@tiptap/extension-hard-break': 2.3.0(@tiptap/core@2.3.0) + '@tiptap/extension-heading': 2.3.0(@tiptap/core@2.3.0) + '@tiptap/extension-history': 2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0) + '@tiptap/extension-horizontal-rule': 2.3.0(@tiptap/core@2.3.0)(@tiptap/pm@2.3.0) + '@tiptap/extension-italic': 2.3.0(@tiptap/core@2.3.0) + '@tiptap/extension-list-item': 2.3.0(@tiptap/core@2.3.0) + '@tiptap/extension-ordered-list': 2.3.0(@tiptap/core@2.3.0) + '@tiptap/extension-paragraph': 2.3.0(@tiptap/core@2.3.0) + '@tiptap/extension-strike': 2.3.0(@tiptap/core@2.3.0) + '@tiptap/extension-text': 2.3.0(@tiptap/core@2.3.0) + transitivePeerDependencies: + - '@tiptap/pm' + dev: false + + /@tsconfig/node10@1.0.11: + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + + /@types/d3-array@3.2.1: + resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} + dev: false + + /@types/d3-color@3.1.3: + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + dev: false + + /@types/d3-ease@3.0.2: + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + dev: false + + /@types/d3-interpolate@3.0.4: + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + dependencies: + '@types/d3-color': 3.1.3 + dev: false + + /@types/d3-path@3.1.0: + resolution: {integrity: sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==} + dev: false + + /@types/d3-scale@4.0.8: + resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==} + dependencies: + '@types/d3-time': 3.0.3 + dev: false + + /@types/d3-shape@3.1.6: + resolution: {integrity: sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==} + dependencies: + '@types/d3-path': 3.1.0 + dev: false + + /@types/d3-time@3.0.3: + resolution: {integrity: sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==} + dev: false + + /@types/d3-timer@3.0.2: + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + dev: false + + /@types/json5@0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + + /@types/node@20.12.7: + resolution: {integrity: sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/prop-types@15.7.12: + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + + /@types/react-dom@18.2.25: + resolution: {integrity: sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==} + dependencies: + '@types/react': 18.2.79 + dev: true + + /@types/react@18.2.79: + resolution: {integrity: sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==} + dependencies: + '@types/prop-types': 15.7.12 + csstype: 3.1.3 + + /@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.4 + eslint: 8.57.0 + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.21.0: + resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + dev: true + + /@typescript-eslint/types@6.21.0: + resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.21.0(typescript@5.4.5): + resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.6.0 + ts-api-utils: 1.3.0(typescript@5.4.5) + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/visitor-keys@6.21.0: + resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.21.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /@vercel/analytics@1.2.2(next@14.1.2)(react@18.2.0): + resolution: {integrity: sha512-X0rctVWkQV1e5Y300ehVNqpOfSOufo7ieA5PIdna8yX/U7Vjz0GFsGf4qvAhxV02uQ2CVt7GYcrFfddXXK2Y4A==} + peerDependencies: + next: '>= 13' + react: ^18 || ^19 + peerDependenciesMeta: + next: + optional: true + react: + optional: true + dependencies: + next: 14.1.2(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + server-only: 0.0.1 + dev: false + + /acorn-jsx@5.3.2(acorn@8.11.3): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.11.3 + dev: true + + /acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /add@2.0.6: + resolution: {integrity: sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q==} + dev: false + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: true + + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + /aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: true + + /array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + is-array-buffer: 3.0.4 + dev: true + + /array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + is-string: 1.0.7 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.toreversed@1.1.2: + resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.tosorted@1.1.3: + resolution: {integrity: sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 + dev: true + + /arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 + dev: true + + /ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + dev: true + + /autoprefixer@10.4.19(postcss@8.4.38): + resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.23.0 + caniuse-lite: 1.0.30001612 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: true + + /available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + dependencies: + possible-typed-array-names: 1.0.0 + dev: true + + /axe-core@4.7.0: + resolution: {integrity: sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==} + engines: {node: '>=4'} + dev: true + + /axobject-query@3.2.1: + resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} + dependencies: + dequal: 2.0.3 + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001612 + electron-to-chromium: 1.4.747 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.23.0) + dev: true + + /busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + dev: false + + /call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: true + + /caniuse-lite@1.0.30001612: + resolution: {integrity: sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==} + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + dev: false + + /clsx@2.1.0: + resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==} + engines: {node: '>=6'} + dev: false + + /clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + dev: false + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + + /color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + dev: false + + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + + /crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + dev: false + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + /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 + + /damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + dev: true + + /data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + dev: true + + /data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + dev: true + + /data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + dev: true + + /dayjs@1.11.10: + resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} + dev: false + + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + dev: false + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + dev: true + + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + dev: true + + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: true + + /detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + dev: false + + /detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + dev: false + + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: true + + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dev: true + + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dependencies: + '@babel/runtime': 7.24.4 + csstype: 3.1.3 + dev: false + + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true + + /electron-to-chromium@1.4.747: + resolution: {integrity: sha512-+FnSWZIAvFHbsNVmUxhEqWiaOiPMcfum1GQzlWCg/wLigVtshOsjXHyEFfmt6cFK6+HkS3QOJBv6/3OPumbBfw==} + dev: true + + /embla-carousel-react@8.0.2(react@18.2.0): + resolution: {integrity: sha512-RHe1GKLulOW8EDN+cJgbFbVVfRXcaLT2/89dyVw3ONGgVpZjD19wB87I1LUZ1aCzOSrTccx0PFSQanK4OOfGPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.1 || ^18.0.0 + dependencies: + embla-carousel: 8.0.2 + embla-carousel-reactive-utils: 8.0.2(embla-carousel@8.0.2) + react: 18.2.0 + dev: false + + /embla-carousel-reactive-utils@8.0.2(embla-carousel@8.0.2): + resolution: {integrity: sha512-nLZqDkQdO0hvOP49/dUwjkkepMnUXgIzhyRuDjwGqswpB4Ibnc5M+w7rSQQAM+uMj0cPaXnYOTlv8XD7I/zVNw==} + peerDependencies: + embla-carousel: 8.0.2 + dependencies: + embla-carousel: 8.0.2 + dev: false + + /embla-carousel@8.0.2: + resolution: {integrity: sha512-bogsDO8xosuh/l3PxIvA5AMl3+BnRVAse9sDW/60amzj4MbGS5re4WH5eVEXiuH8G1/3G7QUAX2QNr3Yx8z5rA==} + dev: false + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true + + /enhanced-resolve@5.16.0: + resolution: {integrity: sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==} + engines: {node: '>=10.13.0'} + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + dev: true + + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + dev: false + + /es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.3 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-data-view: 1.0.1 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.6 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.15 + dev: true + + /es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + dev: true + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + dev: true + + /es-iterator-helpers@1.0.18: + resolution: {integrity: sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.3 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + globalthis: 1.0.3 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + internal-slot: 1.0.7 + iterator.prototype: 1.1.2 + safe-array-concat: 1.1.2 + dev: true + + /es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + dev: true + + /es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + dev: true + + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + dependencies: + hasown: 2.0.2 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + /eslint-config-next@14.1.1(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-OLyw2oHzwE0M0EODGYMbjksDQKSshQWBzYY+Nkoxoe3+Q5G0lpb9EkekyDk7Foz9BMfotbYShJrgYoBEAVqU4Q==} + peerDependencies: + eslint: ^7.23.0 || ^8.0.0 + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@next/eslint-plugin-next': 14.1.1 + '@rushstack/eslint-patch': 1.10.2 + '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.5) + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) + eslint-plugin-react: 7.34.1(eslint@8.57.0) + eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) + typescript: 5.4.5 + transitivePeerDependencies: + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + dependencies: + debug: 3.2.7 + is-core-module: 2.13.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): + resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + dependencies: + debug: 4.3.4 + enhanced-resolve: 5.16.0 + eslint: 8.57.0 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + fast-glob: 3.3.2 + get-tsconfig: 4.7.3 + is-core-module: 2.13.1 + is-glob: 4.0.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-node + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.5) + debug: 3.2.7 + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.5) + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.5 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + hasown: 2.0.2 + is-core-module: 2.13.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.0 + semver: 6.3.1 + tsconfig-paths: 3.15.0 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-jsx-a11y@6.8.0(eslint@8.57.0): + resolution: {integrity: sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + '@babel/runtime': 7.24.4 + aria-query: 5.3.0 + array-includes: 3.1.8 + array.prototype.flatmap: 1.3.2 + ast-types-flow: 0.0.8 + axe-core: 4.7.0 + axobject-query: 3.2.1 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + es-iterator-helpers: 1.0.18 + eslint: 8.57.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.2 + object.entries: 1.1.8 + object.fromentries: 2.0.8 + dev: true + + /eslint-plugin-react-hooks@4.6.0(eslint@8.57.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.57.0 + dev: true + + /eslint-plugin-react@7.34.1(eslint@8.57.0): + resolution: {integrity: sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.8 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.2 + array.prototype.toreversed: 1.1.2 + array.prototype.tosorted: 1.1.3 + doctrine: 2.1.0 + es-iterator-helpers: 1.0.18 + eslint: 8.57.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.8 + object.fromentries: 2.0.8 + object.hasown: 1.1.4 + object.values: 1.2.0 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.11 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) + eslint-visitor-keys: 3.4.3 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + dev: false + + /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.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.2.0 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + dev: true + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + dev: true + + /fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + dev: true + + /get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + dev: false + + /get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + dev: true + + /get-tsconfig@4.7.3: + resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.4 + minipass: 7.0.4 + path-scurry: 1.10.2 + dev: true + + /glob@10.3.12: + resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.4 + minipass: 7.0.4 + path-scurry: 1.10.2 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.4 + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + dependencies: + es-define-property: 1.0.0 + dev: true + + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + + /ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.0.6 + dev: true + + /internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + dev: false + + /invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + dev: true + + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false + + /is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.3.0 + dev: true + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.2 + + /is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + dependencies: + is-typed-array: 1.1.13 + dev: true + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + dependencies: + call-bind: 1.0.7 + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + dev: true + + /is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + dev: true + + /is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + dev: true + + /is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + dev: true + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.15 + dev: true + + /is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.7 + dev: true + + /is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + dev: true + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + dependencies: + define-properties: 1.2.1 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + reflect.getprototypeof: 1.0.6 + set-function-name: 2.0.2 + dev: true + + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: true + + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + + /jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.8 + array.prototype.flat: 1.3.2 + object.assign: 4.1.5 + object.values: 1.2.0 + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + dev: false + + /language-subtag-registry@0.3.22: + resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} + dev: true + + /language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + dependencies: + language-subtag-registry: 0.3.22 + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: true + + /lilconfig@3.1.1: + resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} + engines: {node: '>=14'} + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + dependencies: + uc.micro: 2.1.0 + dev: false + + /linkifyjs@4.1.3: + resolution: {integrity: sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==} + dev: false + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + + /lru-cache@10.2.0: + resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} + engines: {node: 14 || >=16.14} + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + dev: false + + /mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + dev: false + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimatch@9.0.4: + resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: true + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /next@14.1.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-p4RfNmopqkzRP1uUyBJnHii+qMg71f2udWhTTZopBB8b3T5QXNzn7yO+LCYHPWZG2kAvEn4l4neyJHqkXvo2wg==} + engines: {node: '>=18.17.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + sass: + optional: true + dependencies: + '@next/env': 14.1.2 + '@swc/helpers': 0.5.2 + busboy: 1.6.0 + caniuse-lite: 1.0.30001612 + graceful-fs: 4.2.11 + postcss: 8.4.31 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + styled-jsx: 5.1.1(react@18.2.0) + optionalDependencies: + '@next/swc-darwin-arm64': 14.1.2 + '@next/swc-darwin-x64': 14.1.2 + '@next/swc-linux-arm64-gnu': 14.1.2 + '@next/swc-linux-arm64-musl': 14.1.2 + '@next/swc-linux-x64-gnu': 14.1.2 + '@next/swc-linux-x64-musl': 14.1.2 + '@next/swc-win32-arm64-msvc': 14.1.2 + '@next/swc-win32-ia32-msvc': 14.1.2 + '@next/swc-win32-x64-msvc': 14.1.2 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: false + + /node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: true + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: true + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.entries@1.1.8: + resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + dev: true + + /object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + dev: true + + /object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + dev: true + + /object.hasown@1.1.4: + resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + dev: true + + /object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + dev: false + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + /path-scurry@1.10.2: + resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.2.0 + minipass: 7.0.4 + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: true + + /possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + dev: true + + /postcss-import@15.1.0(postcss@8.4.38): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + dev: true + + /postcss-import@16.1.0(postcss@8.4.38): + resolution: {integrity: sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg==} + engines: {node: '>=18.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + dev: false + + /postcss-js@4.0.1(postcss@8.4.38): + 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.38 + dev: true + + /postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2): + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 3.1.1 + postcss: 8.4.38 + ts-node: 10.9.2(@types/node@20.12.7)(typescript@5.4.5) + yaml: 2.4.1 + dev: true + + /postcss-mixins@9.0.4(postcss@8.4.38): + resolution: {integrity: sha512-XVq5jwQJDRu5M1XGkdpgASqLk37OqkH4JCFDXl/Dn7janOJjCTEKL+36cnRVy7bMtoBzALfO7bV7nTIsFnUWLA==} + engines: {node: '>=14.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + fast-glob: 3.3.2 + postcss: 8.4.38 + postcss-js: 4.0.1(postcss@8.4.38) + postcss-simple-vars: 7.0.1(postcss@8.4.38) + sugarss: 4.0.1(postcss@8.4.38) + dev: true + + /postcss-nested@6.0.1(postcss@8.4.38): + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.0.16 + dev: true + + /postcss-preset-mantine@1.14.4(postcss@8.4.38): + resolution: {integrity: sha512-T1K3MVhU1hA9mJWfqoGvMcK5WKcHpVi4JUX6AYTbESvp78WneB/KFONUi+eXDG9Lpw62W/KNxEYl1ic3Dpm88w==} + peerDependencies: + postcss: '>=8.0.0' + dependencies: + postcss: 8.4.38 + postcss-mixins: 9.0.4(postcss@8.4.38) + postcss-nested: 6.0.1(postcss@8.4.38) + dev: true + + /postcss-selector-parser@6.0.16: + resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-simple-vars@7.0.1(postcss@8.4.38): + resolution: {integrity: sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A==} + engines: {node: '>=14.0'} + peerDependencies: + postcss: ^8.2.1 + dependencies: + postcss: 8.4.38 + dev: true + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.2.0 + dev: false + + /postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.2.0 + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prisma@5.13.0: + resolution: {integrity: sha512-kGtcJaElNRAdAGsCNykFSZ7dBKpL14Cbs+VaQ8cECxQlRPDjBlMHNFYeYt0SKovAVy2Y65JXQwB3A5+zIQwnTg==} + engines: {node: '>=16.13'} + hasBin: true + requiresBuild: true + dependencies: + '@prisma/engines': 5.13.0 + + /prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + /prosemirror-changeset@2.2.1: + resolution: {integrity: sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==} + dependencies: + prosemirror-transform: 1.8.0 + dev: false + + /prosemirror-collab@1.3.1: + resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==} + dependencies: + prosemirror-state: 1.4.3 + dev: false + + /prosemirror-commands@1.5.2: + resolution: {integrity: sha512-hgLcPaakxH8tu6YvVAaILV2tXYsW3rAdDR8WNkeKGcgeMVQg3/TMhPdVoh7iAmfgVjZGtcOSjKiQaoeKjzd2mQ==} + dependencies: + prosemirror-model: 1.20.0 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.8.0 + dev: false + + /prosemirror-dropcursor@1.8.1: + resolution: {integrity: sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==} + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.8.0 + prosemirror-view: 1.33.5 + dev: false + + /prosemirror-gapcursor@1.3.2: + resolution: {integrity: sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==} + dependencies: + prosemirror-keymap: 1.2.2 + prosemirror-model: 1.20.0 + prosemirror-state: 1.4.3 + prosemirror-view: 1.33.5 + dev: false + + /prosemirror-history@1.4.0: + resolution: {integrity: sha512-UUiGzDVcqo1lovOPdi9YxxUps3oBFWAIYkXLu3Ot+JPv1qzVogRbcizxK3LhHmtaUxclohgiOVesRw5QSlMnbQ==} + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.8.0 + prosemirror-view: 1.33.5 + rope-sequence: 1.3.4 + dev: false + + /prosemirror-inputrules@1.4.0: + resolution: {integrity: sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==} + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.8.0 + dev: false + + /prosemirror-keymap@1.2.2: + resolution: {integrity: sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==} + dependencies: + prosemirror-state: 1.4.3 + w3c-keyname: 2.2.8 + dev: false + + /prosemirror-markdown@1.12.0: + resolution: {integrity: sha512-6F5HS8Z0HDYiS2VQDZzfZP6A0s/I0gbkJy8NCzzDMtcsz3qrfqyroMMeoSjAmOhDITyon11NbXSzztfKi+frSQ==} + dependencies: + markdown-it: 14.1.0 + prosemirror-model: 1.20.0 + dev: false + + /prosemirror-menu@1.2.4: + resolution: {integrity: sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==} + dependencies: + crelt: 1.0.6 + prosemirror-commands: 1.5.2 + prosemirror-history: 1.4.0 + prosemirror-state: 1.4.3 + dev: false + + /prosemirror-model@1.20.0: + resolution: {integrity: sha512-q7AY7vMjKYqDCeoedgUiAgrLabliXxndJuuFmcmc2+YU1SblvnOiG2WEACF2lwAZsMlfLpiAilA3L+TWlDqIsQ==} + dependencies: + orderedmap: 2.1.1 + dev: false + + /prosemirror-schema-basic@1.2.2: + resolution: {integrity: sha512-/dT4JFEGyO7QnNTe9UaKUhjDXbTNkiWTq/N4VpKaF79bBjSExVV2NXmJpcM7z/gD7mbqNjxbmWW5nf1iNSSGnw==} + dependencies: + prosemirror-model: 1.20.0 + dev: false + + /prosemirror-schema-list@1.3.0: + resolution: {integrity: sha512-Hz/7gM4skaaYfRPNgr421CU4GSwotmEwBVvJh5ltGiffUJwm7C8GfN/Bc6DR1EKEp5pDKhODmdXXyi9uIsZl5A==} + dependencies: + prosemirror-model: 1.20.0 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.8.0 + dev: false + + /prosemirror-state@1.4.3: + resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==} + dependencies: + prosemirror-model: 1.20.0 + prosemirror-transform: 1.8.0 + prosemirror-view: 1.33.5 + dev: false + + /prosemirror-tables@1.3.7: + resolution: {integrity: sha512-oEwX1wrziuxMtwFvdDWSFHVUWrFJWt929kVVfHvtTi8yvw+5ppxjXZkMG/fuTdFo+3DXyIPSKfid+Be1npKXDA==} + dependencies: + prosemirror-keymap: 1.2.2 + prosemirror-model: 1.20.0 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.8.0 + prosemirror-view: 1.33.5 + dev: false + + /prosemirror-trailing-node@2.0.8(prosemirror-model@1.20.0)(prosemirror-state@1.4.3)(prosemirror-view@1.33.5): + resolution: {integrity: sha512-ujRYhSuhQb1Jsarh1IHqb2KoSnRiD7wAMDGucP35DN7j5af6X7B18PfdPIrbwsPTqIAj0fyOvxbuPsWhNvylmA==} + peerDependencies: + prosemirror-model: ^1.19.0 + prosemirror-state: ^1.4.2 + prosemirror-view: ^1.31.2 + dependencies: + '@remirror/core-constants': 2.0.2 + escape-string-regexp: 4.0.0 + prosemirror-model: 1.20.0 + prosemirror-state: 1.4.3 + prosemirror-view: 1.33.5 + dev: false + + /prosemirror-transform@1.8.0: + resolution: {integrity: sha512-BaSBsIMv52F1BVVMvOmp1yzD3u65uC3HTzCBQV1WDPqJRQ2LuHKcyfn0jwqodo8sR9vVzMzZyI+Dal5W9E6a9A==} + dependencies: + prosemirror-model: 1.20.0 + dev: false + + /prosemirror-view@1.33.5: + resolution: {integrity: sha512-AbYYLgg2h5CLARLcTtbNrMARlMwV51jTrezcJkV0NS9J4vi28+rhJ45iIWVSjCcRY209BoySDuJ58b8wIFqdmQ==} + dependencies: + prosemirror-model: 1.20.0 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.8.0 + dev: false + + /punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + dev: false + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /react-dom@18.2.0(react@18.2.0): + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + dev: false + + /react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + /react-number-format@5.3.4(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-2hHN5mbLuCDUx19bv0Q8wet67QqYK6xmtLQeY5xx+h7UXiMmRtaCwqko4mMPoKXLc6xAzwRrutg8XbTRlsfjRg==} + peerDependencies: + react: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react-remove-scroll-bar@2.3.6(@types/react@18.2.79)(react@18.2.0): + resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==} + 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.2.79 + react: 18.2.0 + react-style-singleton: 2.2.1(@types/react@18.2.79)(react@18.2.0) + tslib: 2.6.2 + dev: false + + /react-remove-scroll@2.5.9(@types/react@18.2.79)(react@18.2.0): + resolution: {integrity: sha512-bvHCLBrFfM2OgcrpPY2YW84sPdS2o2HKWJUf1xGyGLnSoEnOTOBpahIarjRuYtN0ryahCeP242yf+5TrBX/pZA==} + 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.2.79 + react: 18.2.0 + react-remove-scroll-bar: 2.3.6(@types/react@18.2.79)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.2.79)(react@18.2.0) + tslib: 2.6.2 + use-callback-ref: 1.3.2(@types/react@18.2.79)(react@18.2.0) + use-sidecar: 1.1.2(@types/react@18.2.79)(react@18.2.0) + dev: false + + /react-smooth@4.0.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.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: 4.4.5(react-dom@18.2.0)(react@18.2.0) + dev: false + + /react-style-singleton@2.2.1(@types/react@18.2.79)(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.2.79 + get-nonce: 1.0.1 + invariant: 2.2.4 + react: 18.2.0 + tslib: 2.6.2 + dev: false + + /react-textarea-autosize@8.5.3(@types/react@18.2.79)(react@18.2.0): + resolution: {integrity: sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.24.4 + react: 18.2.0 + use-composed-ref: 1.3.0(react@18.2.0) + use-latest: 1.2.1(@types/react@18.2.79)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + + /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + dependencies: + '@babel/runtime': 7.24.4 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: false + + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /recharts-scale@0.4.5: + resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + dependencies: + decimal.js-light: 2.5.1 + dev: false + + /recharts@2.12.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-D+7j9WI+D0NHauah3fKHuNNcRK8bOypPW7os1DERinogGBGaHI7i6tQKJ0aUF3JXyBZ63dyfKIW2WTOPJDxJ8w==} + engines: {node: '>=14'} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + clsx: 2.1.1 + eventemitter3: 4.0.7 + lodash: 4.17.21 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-is: 16.13.1 + react-smooth: 4.0.1(react-dom@18.2.0)(react@18.2.0) + recharts-scale: 0.4.5 + tiny-invariant: 1.3.3 + victory-vendor: 36.9.2 + dev: false + + /reflect.getprototypeof@1.0.6: + resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + globalthis: 1.0.3 + which-builtin-type: 1.1.3 + dev: true + + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + /regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + /resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rope-sequence@1.3.4: + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + dev: false + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + + /safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-regex: 1.1.4 + dev: true + + /scheduler@0.23.0: + resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + + /server-only@0.0.1: + resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + dev: false + + /set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + dev: true + + /set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + dev: true + + /sharp@0.33.3: + resolution: {integrity: sha512-vHUeXJU1UvlO/BNwTpT0x/r53WkLUVxrmb5JTgW92fdFCFk0ispLMAeu/jPO2vjkXM1fYUi3K7/qcLF47pwM1A==} + engines: {libvips: '>=8.15.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0} + requiresBuild: true + dependencies: + color: 4.2.3 + detect-libc: 2.0.3 + semver: 7.6.0 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.3 + '@img/sharp-darwin-x64': 0.33.3 + '@img/sharp-libvips-darwin-arm64': 1.0.2 + '@img/sharp-libvips-darwin-x64': 1.0.2 + '@img/sharp-libvips-linux-arm': 1.0.2 + '@img/sharp-libvips-linux-arm64': 1.0.2 + '@img/sharp-libvips-linux-s390x': 1.0.2 + '@img/sharp-libvips-linux-x64': 1.0.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.2 + '@img/sharp-libvips-linuxmusl-x64': 1.0.2 + '@img/sharp-linux-arm': 0.33.3 + '@img/sharp-linux-arm64': 0.33.3 + '@img/sharp-linux-s390x': 0.33.3 + '@img/sharp-linux-x64': 0.33.3 + '@img/sharp-linuxmusl-arm64': 0.33.3 + '@img/sharp-linuxmusl-x64': 0.33.3 + '@img/sharp-wasm32': 0.33.3 + '@img/sharp-win32-ia32': 0.33.3 + '@img/sharp-win32-x64': 0.33.3 + dev: false + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 + dev: true + + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: false + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + /streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + dev: false + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + dev: true + + /string.prototype.matchall@4.0.11: + resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.7 + regexp.prototype.flags: 1.5.2 + set-function-name: 2.0.2 + side-channel: 1.0.6 + dev: true + + /string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + dev: true + + /string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + dev: true + + /string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /styled-jsx@5.1.1(react@18.2.0): + resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + dependencies: + client-only: 0.0.1 + react: 18.2.0 + dev: false + + /sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.3.12 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + dev: true + + /sugarss@4.0.1(postcss@8.4.38): + resolution: {integrity: sha512-WCjS5NfuVJjkQzK10s8WOBY+hhDxxNt/N6ZaGwxFZ+wN3/lKKFSaaKUNecULcTTvE4urLcKaZFQD8vO0mOZujw==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + dependencies: + postcss: 8.4.38 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + /swr@2.2.5(react@18.2.0): + resolution: {integrity: sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + dependencies: + client-only: 0.0.1 + react: 18.2.0 + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false + + /tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + dev: false + + /tailwindcss@3.4.3(ts-node@10.9.2): + resolution: {integrity: sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.0 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.38 + postcss-import: 15.1.0(postcss@8.4.38) + postcss-js: 4.0.1(postcss@8.4.38) + postcss-load-config: 4.0.2(postcss@8.4.38)(ts-node@10.9.2) + postcss-nested: 6.0.1(postcss@8.4.38) + postcss-selector-parser: 6.0.16 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + dev: true + + /tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: true + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: true + + /tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + dev: false + + /tippy.js@6.3.7: + resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==} + dependencies: + '@popperjs/core': 2.11.8 + 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 + dev: true + + /ts-api-utils@1.3.0(typescript@5.4.5): + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.4.5 + dev: true + + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: true + + /ts-node@10.9.2(@types/node@20.12.7)(typescript@5.4.5): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.12.7 + acorn: 8.11.3 + acorn-walk: 8.3.2 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.4.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest@4.17.0: + resolution: {integrity: sha512-9flrz1zkfLRH3jO3bLflmTxryzKMxVa7841VeMgBaNQGY6vH4RCcpN/sQLB7mQQYh1GZ5utT2deypMuCy4yicw==} + engines: {node: '>=16'} + dev: false + + /typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 + dev: true + + /typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + dev: true + + /typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + dev: true + + /typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 + dev: true + + /typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + dev: false + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.7 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /update-browserslist-db@1.0.13(browserslist@4.23.0): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.23.0 + escalade: 3.1.2 + picocolors: 1.0.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + + /use-callback-ref@1.3.2(@types/react@18.2.79)(react@18.2.0): + resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==} + 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.2.79 + react: 18.2.0 + tslib: 2.6.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.2.79)(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.2.79 + react: 18.2.0 + dev: false + + /use-latest@1.2.1(@types/react@18.2.79)(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.2.79 + react: 18.2.0 + use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.79)(react@18.2.0) + dev: false + + /use-sidecar@1.1.2(@types/react@18.2.79)(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.2.79 + detect-node-es: 1.1.0 + react: 18.2.0 + tslib: 2.6.2 + dev: false + + /use-sync-external-store@1.2.0(react@18.2.0): + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + + /victory-vendor@36.9.2: + resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + dependencies: + '@types/d3-array': 3.2.1 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.8 + '@types/d3-shape': 3.1.6 + '@types/d3-time': 3.0.3 + '@types/d3-timer': 3.0.2 + 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 + + /w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + dev: false + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + dependencies: + function.prototype.name: 1.1.6 + has-tostringtag: 1.0.2 + is-async-function: 2.0.0 + is-date-object: 1.0.5 + is-finalizationregistry: 1.0.2 + is-generator-function: 1.0.10 + is-regex: 1.1.4 + is-weakref: 1.0.2 + isarray: 2.0.5 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.2 + which-typed-array: 1.1.15 + dev: true + + /which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.3 + dev: true + + /which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.2 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + /yaml@2.4.1: + resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==} + engines: {node: '>= 14'} + hasBin: true + dev: true + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/next-js/postcss.config.js b/next-js/postcss.config.js new file mode 100644 index 0000000..502e0ce --- /dev/null +++ b/next-js/postcss.config.js @@ -0,0 +1,19 @@ +module.exports = { + plugins: { + 'postcss-preset-mantine': {}, + 'postcss-simple-vars': { + variables: { + 'mantine-breakpoint-xs': '36em', + + 'mantine-breakpoint-sm': '48em', + 'mantine-breakpoint-md': '62em', + 'mantine-breakpoint-lg': '75em', + 'mantine-breakpoint-xl': '88em', + }, + }, + 'postcss-import': {}, + 'tailwindcss/nesting': {}, + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/next-js/prisma/schema.prisma b/next-js/prisma/schema.prisma new file mode 100644 index 0000000..23ceb67 --- /dev/null +++ b/next-js/prisma/schema.prisma @@ -0,0 +1,94 @@ +// ref: https://www.prisma.io/docs/orm/prisma-schema/data-model/relations +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mongodb" + url = env("DATABASE_URL") +} + +model IndexReports { + id String @id @default(auto()) @map("_id") @db.ObjectId + reportId String @unique + reportDate String + tests IndexTests[] // One to one relationship + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // reportId String @map("reportIdIndex") // rename the field reportId, but this keeps the same name in the database + // @@map("index") // Rename the model to index +} + +model IndexTests { + id String @id @default(auto()) @map("_id") @db.ObjectId + dirName String? + title String? + testId String? + testPath String? + dateTest String? + typeOfTest String? + passCount Int? + failCount Int? + totalTests Int? + + indexReports IndexReports? @relation(fields: [indexReportId], references: [id]) + indexReportId String? @db.ObjectId +} + +model ReportDescribe { + id String @id @default(auto()) @map("_id") @db.ObjectId + reportId String + testDescribeId String @unique + testDescribe String + testDescribePath String? + testDescribeDate String? + typeOfTest String + ticketsJira String? + passCount Int + failCount Int + specs ReportDescribeTests[] // One to many relationship + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ReportDescribeTests { + id String @id @default(auto()) @map("_id") @db.ObjectId + specId String + specTitle String + specUrl String? + specReporter String? + specScreenshotPath String? + specStatus String + specJiraRef String? + specJiraTicket String? + specMessage String? + + reportDescribe ReportDescribe? @relation(fields: [reportDescribeId], references: [id]) + reportDescribeId String? @db.ObjectId + + createdAt DateTime? @default(now()) + updatedAt DateTime? @updatedAt +} + +model ReportHistory { + id String @id @default(auto()) @map("_id") @db.ObjectId + reportId String + reportTestId String + titleReport String + titlePath String + dateHistory String? + changesDetected Json? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +// model GlgoScript { +// id String @id @default(auto()) @map("_id") @db.ObjectId + +// script String? + +// createdAt DateTime @default(now()) +// } diff --git a/next-js/public/animations/tb-logo-glasses.gif b/next-js/public/animations/tb-logo-glasses.gif new file mode 100644 index 0000000..5a3e63d Binary files /dev/null and b/next-js/public/animations/tb-logo-glasses.gif differ diff --git a/next-js/public/animations/tb-logo-pieces.gif b/next-js/public/animations/tb-logo-pieces.gif new file mode 100644 index 0000000..2a5fd8b Binary files /dev/null and b/next-js/public/animations/tb-logo-pieces.gif differ diff --git a/next-js/public/data/GloballinkGo/GloballinkGo-001.json b/next-js/public/data/GloballinkGo/GloballinkGo-001.json new file mode 100644 index 0000000..dd1c9f1 --- /dev/null +++ b/next-js/public/data/GloballinkGo/GloballinkGo-001.json @@ -0,0 +1,235 @@ +[ + { + "title": "GloballinkGo", + "reportId": "001", + "id": "GlobalLink Go-001", + "titlePath": "globallinkgo.com > WordsServedEstimatePage", + "typeOfTest": "e2e", + "date": "05/01/2024 - 04:38 PM", + "tickets": "[\"TICKET-2658\", \"TICKET-2663\", \"TICKET-489\"]", + "passCount": 8, + "failCount": 12, + "specs": [ + { + "testId": "001", + "title": "www.tbardini.com", + "websiteUrl": "www.tbardini.com", + "reporter": "Thiago", + "screenshotPath": "image001_Pass.png", + "status": "Pass", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "", + "message": "Test passed successfully" + }, + { + "testId": "002", + "title": "mantine.dev", + "websiteUrl": "mantine.dev", + "reporter": "Thiago", + "screenshotPath": "image002_Pass.png", + "status": "Pass", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "", + "message": "Test passed successfully" + }, + { + "testId": "003", + "title": "forbes.com", + "websiteUrl": "forbes.com", + "reporter": "Thiago", + "screenshotPath": "image003_ZeroWords_Fail.png", + "status": "Fail", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "TICKET-2663", + "message": "API/QuickQuote - Zero words analyzed despite 'OK' (200) response" + }, + { + "testId": "004", + "title": "techcrunch.com", + "websiteUrl": "techcrunch.com", + "reporter": "Thiago", + "screenshotPath": "image004_Pass.png", + "status": "Pass", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "", + "message": "Test passed successfully" + }, + { + "testId": "005", + "title": "theguardian.com", + "websiteUrl": "theguardian.com", + "reporter": "Thiago", + "screenshotPath": "image005_Pass.png", + "status": "Pass", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "", + "message": "Test passed successfully" + }, + { + "testId": "006", + "title": "wired.com", + "websiteUrl": "wired.com", + "reporter": "Thiago", + "screenshotPath": "image006_FailedTo_Fail.png", + "status": "Fail", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "No Jira Ref", + "message": "API/QuickQuote (200) has no spider jobs - Failed to connect to the website." + }, + { + "testId": "007", + "title": "foodnetwork.com", + "websiteUrl": "foodnetwork.com", + "reporter": "Thiago", + "screenshotPath": "image007_Cancelled.png", + "status": "Fail", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "TICKET-2658", + "message": "API/QuickQuote - Cancelled - Getting 404 status" + }, + { + "testId": "008", + "title": "smittenkitchen.com", + "websiteUrl": "smittenkitchen.com", + "reporter": "Thiago", + "screenshotPath": "image008_Pass.png", + "status": "Pass", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "", + "message": "Test passed successfully" + }, + { + "testId": "009", + "title": "thepioneerwoman.com", + "websiteUrl": "thepioneerwoman.com", + "reporter": "Thiago", + "screenshotPath": "image009_ZeroWords_Fail.png", + "status": "Fail", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "TICKET-2663", + "message": "API/QuickQuote - Zero words analyzed despite 'OK' (200) response" + }, + { + "testId": "010", + "title": "food52.com", + "websiteUrl": "food52.com", + "reporter": "Thiago", + "screenshotPath": "image010_ZeroWords_Fail.png", + "status": "Fail", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "TICKET-2663", + "message": "API/QuickQuote - Zero words analyzed despite 'OK' (200) response" + }, + { + "testId": "011", + "title": "thekitchn.com", + "websiteUrl": "thekitchn.com", + "reporter": "Thiago", + "screenshotPath": "image011_ZeroWords_Fail.png", + "status": "Fail", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "TICKET-2663", + "message": "API/QuickQuote - Zero words analyzed despite 'OK' (200) response" + }, + { + "testId": "012", + "title": "cooksillustrated.com", + "websiteUrl": "cooksillustrated.com", + "reporter": "Thiago", + "screenshotPath": "image012_ZeroWords_Fail.png", + "status": "Fail", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "TICKET-2663", + "message": "API/QuickQuote - Zero words analyzed despite 'OK' (200) response" + }, + { + "testId": "013", + "title": "delish.com", + "websiteUrl": "delish.com", + "reporter": "Thiago", + "screenshotPath": "image013_ZeroWords_Fail.png", + "status": "Fail", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "TICKET-2663", + "message": "API/QuickQuote - Zero words analyzed despite 'OK' (200) response" + }, + { + "testId": "014", + "title": "cookstr.com", + "websiteUrl": "cookstr.com", + "reporter": "Thiago", + "screenshotPath": "image014_Cancelled.png", + "status": "Fail", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "TICKET-2658", + "message": "API/QuickQuote - Cancelled - Getting 404 status" + }, + { + "testId": "015", + "title": "simplyrecipes.com", + "websiteUrl": "simplyrecipes.com", + "reporter": "Thiago", + "screenshotPath": "image015_Pass.png", + "status": "Pass", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "", + "message": "Test passed successfully" + }, + { + "testId": "016", + "title": "www.kfc.com", + "websiteUrl": "www.kfc.com", + "reporter": "Thiago", + "screenshotPath": "image016_Cancelled.png", + "status": "Fail", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "TICKET-2658", + "message": "API/QuickQuote - Cancelled - Getting 404 status" + }, + { + "testId": "017", + "title": "www.lowes.com", + "websiteUrl": "www.lowes.com", + "reporter": "Thiago", + "screenshotPath": "image017_Pass.png", + "status": "Pass", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "", + "message": "Test passed successfully" + }, + { + "testId": "018", + "title": "www.pizzahut.com", + "websiteUrl": "www.pizzahut.com", + "reporter": "Thiago", + "screenshotPath": "image018_Cancelled.png", + "status": "Fail", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "TICKET-2658", + "message": "API/QuickQuote - Cancelled - Getting 404 status" + }, + { + "testId": "019", + "title": "www.homedepot.com", + "websiteUrl": "www.homedepot.com", + "reporter": "Thiago", + "screenshotPath": "image019_Cancelled.png", + "status": "Fail", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "TICKET-2658", + "message": "API/QuickQuote - Cancelled - Getting 404 status" + }, + { + "testId": "020", + "title": "openai.com", + "websiteUrl": "openai.com", + "reporter": "Thiago", + "screenshotPath": "image020_Pass.png", + "status": "Pass", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "", + "message": "Test passed successfully" + } + ] + } +] \ No newline at end of file diff --git a/next-js/public/data/MiniMarket/MiniMarket-001.json b/next-js/public/data/MiniMarket/MiniMarket-001.json new file mode 100644 index 0000000..7a42531 --- /dev/null +++ b/next-js/public/data/MiniMarket/MiniMarket-001.json @@ -0,0 +1,26 @@ +[ + { + "title": "MiniMarket", + "reportId": "001", + "id": "Mini Market-001", + "titlePath": "mini-market-tbardini.vercel.app > Add Products to Cart", + "typeOfTest": "e2e", + "date": "05/01/2024 - 04:38 PM", + "tickets": "[\"TICKET-001\"]", + "passCount": 1, + "failCount": 0, + "specs": [ + { + "testId": "001", + "title": "Add Products to Cart", + "websiteUrl": "mini-market-tbardini.vercel.app", + "reporter": "Thiago", + "screenshotPath": "mini-market-Pass.png", + "status": "Pass", + "jiraRef": "https://github.com/thiagobardini", + "jiraTicket": "", + "message": "Test passed successfully" + } + ] + } +] \ No newline at end of file diff --git a/next-js/public/data/jsonReportIndex.json b/next-js/public/data/jsonReportIndex.json new file mode 100644 index 0000000..5102238 --- /dev/null +++ b/next-js/public/data/jsonReportIndex.json @@ -0,0 +1,34 @@ +[ + { + "reportId": "001", + "reportDate": "05/01/2024 - 04:38 PM", + "tests": [ + { + "dirName": "GloballinkGo", + "title": "GloballinkGo", + "testId": "GlobalLink Go-001", + "testPath": "globallinkgo.com > WordsServedEstimatePage", + "date": "05/01/2024 - 04:38 PM", + "typeOfTest": "e2e", + "passCount": 8, + "failCount": 12, + "totalTests": 20 + }, + { + "dirName": "MiniMarket", + "title": "MiniMarket", + "testId": "Mini Market-001", + "testPath": "mini-market-tbardini.vercel.app > Add Products to Cart", + "date": "05/01/2024 - 04:38 PM", + "typeOfTest": "e2e", + "passCount": 1, + "failCount": 0, + "totalTests": 1 + }, + { + "directory": "WordsServedEstimatePage", + "message": "No test reports found" + } + ] + } +] \ No newline at end of file diff --git a/next-js/public/data/reportId.json b/next-js/public/data/reportId.json new file mode 100644 index 0000000..a4a6166 --- /dev/null +++ b/next-js/public/data/reportId.json @@ -0,0 +1,3 @@ +{ + "reportId": "002" +} \ No newline at end of file diff --git a/next-js/public/estimate-page-background.svg b/next-js/public/estimate-page-background.svg new file mode 100644 index 0000000..dc059ae --- /dev/null +++ b/next-js/public/estimate-page-background.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/next-js/public/images/TBardini-dot-dark.png b/next-js/public/images/TBardini-dot-dark.png new file mode 100644 index 0000000..5e93d18 Binary files /dev/null and b/next-js/public/images/TBardini-dot-dark.png differ diff --git a/next-js/public/images/TBardini-dot-light.png b/next-js/public/images/TBardini-dot-light.png new file mode 100644 index 0000000..726a007 Binary files /dev/null and b/next-js/public/images/TBardini-dot-light.png differ diff --git a/next-js/public/images/tbtag.png b/next-js/public/images/tbtag.png new file mode 100644 index 0000000..b393f5d Binary files /dev/null and b/next-js/public/images/tbtag.png differ diff --git a/next-js/public/test-report/image001_Pass.png b/next-js/public/test-report/image001_Pass.png new file mode 100644 index 0000000..8c4791a Binary files /dev/null and b/next-js/public/test-report/image001_Pass.png differ diff --git a/next-js/public/test-report/image002_Pass.png b/next-js/public/test-report/image002_Pass.png new file mode 100644 index 0000000..76ceda5 Binary files /dev/null and b/next-js/public/test-report/image002_Pass.png differ diff --git a/next-js/public/test-report/image003_ZeroWords_Fail.png b/next-js/public/test-report/image003_ZeroWords_Fail.png new file mode 100644 index 0000000..a2301fc Binary files /dev/null and b/next-js/public/test-report/image003_ZeroWords_Fail.png differ diff --git a/next-js/public/test-report/image004_Pass.png b/next-js/public/test-report/image004_Pass.png new file mode 100644 index 0000000..866c57b Binary files /dev/null and b/next-js/public/test-report/image004_Pass.png differ diff --git a/next-js/public/test-report/image005_Pass.png b/next-js/public/test-report/image005_Pass.png new file mode 100644 index 0000000..a2afaac Binary files /dev/null and b/next-js/public/test-report/image005_Pass.png differ diff --git a/next-js/public/test-report/image006_FailedTo_Fail.png b/next-js/public/test-report/image006_FailedTo_Fail.png new file mode 100644 index 0000000..7e72dfc Binary files /dev/null and b/next-js/public/test-report/image006_FailedTo_Fail.png differ diff --git a/next-js/public/test-report/image007_Cancelled.png b/next-js/public/test-report/image007_Cancelled.png new file mode 100644 index 0000000..e81b69d Binary files /dev/null and b/next-js/public/test-report/image007_Cancelled.png differ diff --git a/next-js/public/test-report/image008_Pass.png b/next-js/public/test-report/image008_Pass.png new file mode 100644 index 0000000..8614dc3 Binary files /dev/null and b/next-js/public/test-report/image008_Pass.png differ diff --git a/next-js/public/test-report/image009_ZeroWords_Fail.png b/next-js/public/test-report/image009_ZeroWords_Fail.png new file mode 100644 index 0000000..21f9583 Binary files /dev/null and b/next-js/public/test-report/image009_ZeroWords_Fail.png differ diff --git a/next-js/public/test-report/image010_ZeroWords_Fail.png b/next-js/public/test-report/image010_ZeroWords_Fail.png new file mode 100644 index 0000000..729f513 Binary files /dev/null and b/next-js/public/test-report/image010_ZeroWords_Fail.png differ diff --git a/next-js/public/test-report/image011_ZeroWords_Fail.png b/next-js/public/test-report/image011_ZeroWords_Fail.png new file mode 100644 index 0000000..5463bde Binary files /dev/null and b/next-js/public/test-report/image011_ZeroWords_Fail.png differ diff --git a/next-js/public/test-report/image012_ZeroWords_Fail.png b/next-js/public/test-report/image012_ZeroWords_Fail.png new file mode 100644 index 0000000..3880777 Binary files /dev/null and b/next-js/public/test-report/image012_ZeroWords_Fail.png differ diff --git a/next-js/public/test-report/image013_ZeroWords_Fail.png b/next-js/public/test-report/image013_ZeroWords_Fail.png new file mode 100644 index 0000000..34386e4 Binary files /dev/null and b/next-js/public/test-report/image013_ZeroWords_Fail.png differ diff --git a/next-js/public/test-report/image014_Cancelled.png b/next-js/public/test-report/image014_Cancelled.png new file mode 100644 index 0000000..5f52636 Binary files /dev/null and b/next-js/public/test-report/image014_Cancelled.png differ diff --git a/next-js/public/test-report/image015_Pass.png b/next-js/public/test-report/image015_Pass.png new file mode 100644 index 0000000..48008bb Binary files /dev/null and b/next-js/public/test-report/image015_Pass.png differ diff --git a/next-js/public/test-report/image016_Cancelled.png b/next-js/public/test-report/image016_Cancelled.png new file mode 100644 index 0000000..8f415df Binary files /dev/null and b/next-js/public/test-report/image016_Cancelled.png differ diff --git a/next-js/public/test-report/image017_Pass.png b/next-js/public/test-report/image017_Pass.png new file mode 100644 index 0000000..b01476d Binary files /dev/null and b/next-js/public/test-report/image017_Pass.png differ diff --git a/next-js/public/test-report/image018_Cancelled.png b/next-js/public/test-report/image018_Cancelled.png new file mode 100644 index 0000000..dbe0e69 Binary files /dev/null and b/next-js/public/test-report/image018_Cancelled.png differ diff --git a/next-js/public/test-report/image019_Cancelled.png b/next-js/public/test-report/image019_Cancelled.png new file mode 100644 index 0000000..67c36ed Binary files /dev/null and b/next-js/public/test-report/image019_Cancelled.png differ diff --git a/next-js/public/test-report/image020_Pass.png b/next-js/public/test-report/image020_Pass.png new file mode 100644 index 0000000..13fba4a Binary files /dev/null and b/next-js/public/test-report/image020_Pass.png differ diff --git a/next-js/public/test-report/mini-market-Pass.png b/next-js/public/test-report/mini-market-Pass.png new file mode 100644 index 0000000..2ba1140 Binary files /dev/null and b/next-js/public/test-report/mini-market-Pass.png differ diff --git a/next-js/public/videos/GloballinkGo.webm b/next-js/public/videos/GloballinkGo.webm new file mode 100644 index 0000000..43868f9 Binary files /dev/null and b/next-js/public/videos/GloballinkGo.webm differ diff --git a/next-js/tailwind.config.ts b/next-js/tailwind.config.ts new file mode 100644 index 0000000..7e4bd91 --- /dev/null +++ b/next-js/tailwind.config.ts @@ -0,0 +1,20 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: [ + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + "./app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + backgroundImage: { + "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", + "gradient-conic": + "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", + }, + }, + }, + plugins: [], +}; +export default config; diff --git a/next-js/theme.ts b/next-js/theme.ts new file mode 100644 index 0000000..591b6d8 --- /dev/null +++ b/next-js/theme.ts @@ -0,0 +1,90 @@ +"use client"; + +import { MantineTheme, MantineThemeOverride, createTheme } from "@mantine/core"; + +type MantineThemes = MantineTheme & MantineThemeOverride; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const theme = createTheme({ + fontFamily: '__Poppins_732638, __Poppins_Fallback_732638, sans-serif', + breakpoints: { + lg: "75em", + md: "65em", + sm: "48em", + xl: "88em", + xs: "36em" + }, + colors: { + text1: ['#FFFFFF', '#000000', '#0d0d0d', '#0071DB', '#FF6C2E', '#18AFD1', '#00B7DF', '#2C49AC', '#E9476E', '#0d0d0d'], + text2: ['#FFC266', '#162B75', '#071D49', '#D9D9D9', '#AE3EB6', '#00807B', '#C24E00', '#764E9A', '#3A8131', '#546D9E'], + text3: ['#BB446E', '#FF9416', '#6A68DF', '#2C78C9', '#677C13', '#833A1A', '#BA4597', '#B94A13', '#9E004D', '#996900'], + text4: ['#00328D', '#102A65', '#687967'], + background1: ['#FFFFFF', '#000000', '#FAFBFF', '#0d0d0d', '#EFF4FB', '#0071DB', '#1F3689', '#0086F6', '#1890FF'], + background2: ['#FFA216', '#D5D9F0', '#FFDD86', '#F9FBFE', '#FAFAFA', '#FFC69F', '#F0F4FD', '#F0F4F9', '#E9F1FD', '#DC4147'], + background3: ['#B9DDFF', '#DEEDFF', '#E0F0FF'], + dot1: ['#146FAF', '#FFA216', '#1890FF', '#EE5CE7'], + border1: ['#0d0d0d', '#808080', '#B3B3CC', '#E3E3E3', '#CBD1F9', '#FFB700'], + button1: ['#0d0d0d', '#BB3EAB', '#85D3FF','#FFAF51', '#DBEEFF', '#005B46', '#9E004D', '#FFB169', '#0076B8', '#0074E0'], + button2:['#CA4B0C'], + icon1: [], + divider1: ['#CFDAF4', '#0d0d0d', '#91B3FA'], + articlesList1: ['#F9EAFA', '#E6F4F3', '#E6F1FF', '#FAEDE7', '#FFF6E8', '#F3F2F8'], + }, + components: { + Text: { + styles: (theme: MantineThemes) => ({ + root: { + color: theme.colors.text1[2], + } + }) + }, + Title: { + styles: (theme: MantineThemes) => ({ + root: { + color: theme.colors.text1[2], + } + }) + }, + Button: { + styles: (theme: MantineThemes, {variant = ''}) => ({ + root: { + transform: 'none', + '&:active': { + transform: 'none', + }, + height: "3rem", + borderRadius: 14, + color: variant !== 'outline' + ? theme.colors.text1[0] + : theme.colors.text1[2], + backgroundColor: variant !== 'outline' + ? theme.colors.button1[0] + : 'transparent', + '&:hover': { + backgroundColor: theme.colors.button1[0], + }, + borderWidth: variant !== 'outline' + ? 'none' + : '2px', + borderColor: variant !== 'outline' + ? 'transparent' + : theme.colors.button1[0] + }, + input: { + transform: 'none', + '&:active': { + transform: 'none', + }, + } + }) + }, + Card: { + styles: () => ({ + root: { + borderRadius: 20 + } + }) + } + }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any +} as any); diff --git a/next-js/tsconfig.json b/next-js/tsconfig.json new file mode 100644 index 0000000..09347c5 --- /dev/null +++ b/next-js/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "app/getreport/report.jslib/getLatestReports.js", "app/reports/[reportId]/[reportName]/TestReport.tsx", "app/home/HomePage.tsx", "app/components/shared/MissingTests.tsx", "app/tests/[slug]/page.jsx", "app/tests/[slug]/[history]/page.jsx", "app/reports/test/page.jsx", "app/api/reports/route.ts", "app/api/index/route.ts", ".examples/SearchInput.tsx", ".examples/ReportTable.tsx"], + "exclude": ["node_modules"] +} diff --git a/next-js/turbo.json b/next-js/turbo.json new file mode 100644 index 0000000..ffa5d0b --- /dev/null +++ b/next-js/turbo.json @@ -0,0 +1,7 @@ +{ + "env": { + "global": { + "NODE_ENV": "process.env.NODE_ENV" + } + } +} diff --git a/next-js/vercel.json b/next-js/vercel.json new file mode 100644 index 0000000..1db5d80 --- /dev/null +++ b/next-js/vercel.json @@ -0,0 +1,5 @@ +{ + "rewrites": [ + { "source": "/(.*)", "destination": "/" } + ] +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..83e97b3 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "custom-report-playwright", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test:ui": "npx playwright test ./tests/* --ui", + "test:custom-report": "npx playwright test --grep @smoke", + "generate:custom-test-report": "node ./script/generateJsonCustomReport.js && node ./script/generateJsonCustomReportViewHistory.js && node ./script/generateJsonCustomReportIndex.js && node ./script/updateReportId.js", + "playground:custom-test-report": "node ./script/generateJsonCustomReport.js && node ./script/generateJsonCustomReportViewHistory.js && node ./script/generateJsonCustomReportIndex.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@playwright/test": "^1.43.1", + "@types/node": "^20.12.7" + } +} diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..7805811 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,97 @@ +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: "./tests", + /* 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"], + ["json", { outputFile: "./test-results/test-results.json" }], // Built-in json reporter + ], + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + // video: { + // mode: "on", + // size: { width: 640, height: 480 } + // }, + + /* 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"] }, + testMatch: "**/*.desktop.spec.ts", // Only run desktop tests + snapshotDir: `__screenshots__/`, // Save screenshots to this directory + }, + { + name: "firefox", + use: { ...devices["Desktop Firefox"] }, + testMatch: "**/*.desktop.spec.ts", // Only run desktop tests + snapshotDir: `__screenshots__/`, // Save screenshots to this directory + }, + { + name: "webkit", + use: { ...devices["Desktop Safari"] }, + testMatch: "**/*.desktop.spec.ts", // Only run desktop tests + snapshotDir: `__screenshots__/`, // Save screenshots to this directory + }, + { + name: "IOS", + use: { ...devices["iPhone SE"] }, + testMatch: "**/*.mobile.spec.ts", // Only run mobile tests + snapshotDir: `__screenshots__/`, // Save screenshots to this directory + }, + { + name: "Android", + use: { ...devices["Galaxy S III"] }, + testMatch: "**/*.mobile.spec.ts", // Only run mobile tests + snapshotDir: `__screenshots__/`, // Save screenshots to this directory + }, + /* 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/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..0194e1e --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,57 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +devDependencies: + '@playwright/test': + specifier: ^1.43.1 + version: 1.43.1 + '@types/node': + specifier: ^20.12.7 + version: 20.12.7 + +packages: + + /@playwright/test@1.43.1: + resolution: {integrity: sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA==} + engines: {node: '>=16'} + hasBin: true + dependencies: + playwright: 1.43.1 + dev: true + + /@types/node@20.12.7: + resolution: {integrity: sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==} + dependencies: + undici-types: 5.26.5 + dev: true + + /fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /playwright-core@1.43.1: + resolution: {integrity: sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==} + engines: {node: '>=16'} + hasBin: true + dev: true + + /playwright@1.43.1: + resolution: {integrity: sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==} + engines: {node: '>=16'} + hasBin: true + dependencies: + playwright-core: 1.43.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true diff --git a/script/generateJsonCustomReport.js b/script/generateJsonCustomReport.js new file mode 100644 index 0000000..ead0d30 --- /dev/null +++ b/script/generateJsonCustomReport.js @@ -0,0 +1,150 @@ +"--no-ignore"; +const fs = require("fs"); +const path = require("path"); +const jsonData = require("../test-results/test-results.json"); +const reportIdData = require("../next-js/public/data/reportId.json"); + +let idCounter = 1; +// Returns the current date and time in the format "MM/DD/YY - HH:MM" +function getFormattedDateTime() { + const date = new Date(); + const day = date.getDate().toString().padStart(2, "0"); + const month = (date.getMonth() + 1).toString().padStart(2, "0"); + const year = date.getFullYear(); + let hours = date.getHours(); + const minutes = date.getMinutes().toString().padStart(2, "0"); + const ampm = hours >= 12 ? "PM" : "AM"; + hours = hours % 12; + hours = hours ? hours : 12; + const formattedDateTime = `${month}/${day}/${year} - ${hours.toString().padStart(2, "0")}:${minutes} ${ampm}`; + return formattedDateTime; +} + +const dateTimeStamp = getFormattedDateTime(); + +// Function to sanitize and normalize titles for folder names +function sanitizeTitle(title) { + return ( + title + // Remove the suffix "@smoke" + .replace(/@smoke$/, "") + // Remove special characters, including "|", ">", and spaces + .replace(/[\|\s>]+/g, " ") + // Split the string into words, then capitalize the first letter of each word + // Join them back without spaces + .trim() // Garantee that extra spaces at the beginning and end are removed + .split(" ") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join("") + ); +} + +jsonData.suites.forEach((suite) => { + suite.suites.forEach((subSuite) => { + // Check if there are any tests with annotations + const hasAnnotations = subSuite.specs.some((spec) => spec.tests.some((test) => test.annotations && test.annotations.length)); + + if (!hasAnnotations) return; // Skip tests without info.annotations + + // Process suites with annotations + const sanitizedTitle = sanitizeTitle(subSuite.title); + const folderName = path.join(__dirname, "..", `next-js/public/data`, sanitizedTitle); + if (!fs.existsSync(folderName)) { + fs.mkdirSync(folderName, { recursive: true }); + } + + let specsArray = []; + + subSuite.specs.forEach((spec) => { + spec.tests.forEach((test) => { + const testAnnotations = test.annotations; + let passCount = 0; + let failCount = 0; + + // The second annotation contains the required information + if (testAnnotations && testAnnotations.length > 1) { + const annotationData = JSON.parse(testAnnotations[1].description); + const annotationDataProject = JSON.parse(testAnnotations[0].description); + + // // Regex to extract the Jira ticket number from the URL + // const jiraUrl = annotationData.jiraRef; + // const jiraTicketRegex = /browse\/(MOXIE-\d+)$/; + // const match = jiraUrl.match(jiraTicketRegex); + // const jiraTicket = match ? match[1] : ""; + + const specObject = { + testId: testAnnotations[1].type, + title: spec.title, + websiteUrl: annotationData.websiteUrl, + reporter: annotationData.reporter, + screenshotPath: annotationData.screenshotPath, + status: test.results[0].status === "passed" ? "Pass" : "Fail", + jiraRef: annotationData.jiraRef, + jiraTicket: annotationData.jiraTicket, + message: + test.results[0].status === "passed" + ? "Test passed successfully" + : test.results[0].error && test.results[0].error.message + ? test.results[0].error.message.replace(/Error:\s+/g, "") + : "Error message not available.", + }; + + specsArray.push(specObject); + + const customData = { + title: sanitizeTitle(subSuite.title), + reportId: annotationDataProject.testReportId, + id: testAnnotations[0].type, + titlePath: annotationDataProject.testPath, + typeOfTest: annotationDataProject.typeOfTest, + date: dateTimeStamp, + tickets: annotationDataProject.tickets, + passCount: passCount, + failCount: failCount, + specs: specsArray, + }; + + customData.specs.forEach((spec) => { + if (spec.status === "Pass") { + passCount++; + } else if (spec.status === "Fail") { + failCount++; + } + }); + + customData.passCount = passCount; + customData.failCount = failCount; + + const currentReportId = reportIdData.reportId; + + // Write the data to a JSON file + const dataToWrite = [customData]; + + const fileName = path.join(folderName, `${sanitizeTitle(subSuite.title).replace(/ /g, "-")}-${currentReportId}.json`); + fs.writeFileSync(fileName, JSON.stringify(dataToWrite, null, 2)); + idCounter++; + } + + const files = fs + .readdirSync(folderName) + .map((fileName) => { + const filePath = path.join(folderName, fileName); + return { + name: fileName, + time: fs.statSync(filePath).mtime.getTime(), + }; + }) + .sort((a, b) => b.time - a.time); // Sort by most recent + + // Keep only the 4 most recent files + if (files.length > 5) { + files.slice(5).forEach((file) => { + fs.unlinkSync(path.join(folderName, file.name)); // Delete the older files + }); + } + }); + }); + }); +}); +// eslint-disable-next-line no-console +console.log("JSON reports generated successfully - generateJsonCustomReport.js."); diff --git a/script/generateJsonCustomReportIndex.js b/script/generateJsonCustomReportIndex.js new file mode 100644 index 0000000..1f8b8cd --- /dev/null +++ b/script/generateJsonCustomReportIndex.js @@ -0,0 +1,78 @@ +"--no-ignore"; +const fs = require("fs"); +const path = require("path"); +const reportId = require("../next-js/public/data/reportId.json").reportId; + +function getFormattedDateTime() { + const date = new Date(); + const day = date.getDate().toString().padStart(2, "0"); + const month = (date.getMonth() + 1).toString().padStart(2, "0"); + const year = date.getFullYear(); + let hours = date.getHours(); + const minutes = date.getMinutes().toString().padStart(2, "0"); + const ampm = hours >= 12 ? "PM" : "AM"; + hours = hours % 12; + hours = hours ? hours : 12; + const formattedDateTime = `${month}/${day}/${year} - ${hours.toString().padStart(2, "0")}:${minutes} ${ampm}`; + return formattedDateTime; +} + +function generateJsonCustomReportIndex() { + const reportDirPath = path.join(__dirname, "..", "next-js/public/data"); + const dateTimeStamp = getFormattedDateTime(); + + let directories = fs + .readdirSync(reportDirPath, { withFileTypes: true }) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => dirent.name); + + let reports = directories.map((dir) => { + const jsonFiles = fs + .readdirSync(path.join(reportDirPath, dir)) + .filter((file) => file.endsWith(".json")) + .map((file) => path.join(reportDirPath, dir, file)) + .sort((a, b) => fs.statSync(b).mtime - fs.statSync(a).mtime); + + if (jsonFiles.length > 0) { + const latestReport = JSON.parse(fs.readFileSync(jsonFiles[0], "utf8")); + const passCount = latestReport[0].passCount || 0; + const failCount = latestReport[0].failCount || 0; + const testCount = passCount + failCount; + const testPath = latestReport[0].titlePath; + const testId = latestReport[0].id; + const typeOfTest = latestReport[0].typeOfTest; + const date = latestReport[0].date; + const title = latestReport[0].title; + + return { + dirName: dir, + title, + testId, + testPath, + date, + typeOfTest, + passCount, + failCount, + totalTests: testCount, + }; + } else { + return { directory: dir, message: "No test reports found" }; + } + }); + + const reportJSONContent = { + reportId, + reportDate: dateTimeStamp, + tests: reports, + }; + + // Write the data to a JSON file + const dataToWrite = [reportJSONContent]; + + fs.writeFileSync(path.join(__dirname, "..", `next-js/public/data`, "jsonReportIndex.json"), JSON.stringify(dataToWrite, null, 2), "utf8"); +} + +generateJsonCustomReportIndex(); + +// eslint-disable-next-line no-console +console.log("JSON Index report generated successfully - generateJSONCustomReportIndex.js."); diff --git a/script/generateJsonCustomReportViewHistory.js b/script/generateJsonCustomReportViewHistory.js new file mode 100644 index 0000000..55813e2 --- /dev/null +++ b/script/generateJsonCustomReportViewHistory.js @@ -0,0 +1,135 @@ +"--no-ignore"; +const fs = require("fs"); +const path = require("path"); +const reportId = require("../next-js/public/data/reportId.json").reportId; + +let projectTitle; + +function compareSpecs(oldSpecs, newSpecs, projectDetails) { + projectTitle = projectDetails.title; + const changesDetected = []; + if (!Array.isArray(newSpecs)) { + console.error("newSpecs is not an array:", newSpecs); + return; // Return undefined + } + newSpecs.forEach((newSpec) => { + const oldSpec = oldSpecs.find((old) => old.testId === newSpec.testId); + let changeDetails = { testDetails: { previous: {}, actual: {} } }; + + changeDetails.testDetails = { + testId: newSpec.testId, + title: newSpec.title, + reporter: newSpec.reporter, + newTest: false, + status: newSpec.status, + }; + if (oldSpec) { + changeDetails.testDetails.previous = {}; + changeDetails.testDetails.actual = {}; + if (oldSpec.status !== newSpec.status) { + changeDetails.testDetails.previous.statusChange = oldSpec.status; + changeDetails.testDetails.actual.statusChange = newSpec.status; + } + if (oldSpec.reporter !== newSpec.reporter) { + changeDetails.testDetails.previous.reporterChange = oldSpec.reporter; + changeDetails.testDetails.actual.reporterChange = newSpec.reporter; + } + if (oldSpec.message !== newSpec.message) { + changeDetails.testDetails.previous.messageChange = oldSpec.message; + changeDetails.testDetails.actual.messageChange = newSpec.message; + } + // If there are any changes, add the changeDetails to the changesDetected array + if (Object.keys(changeDetails.testDetails.previous).length > 0) { + changesDetected.push(changeDetails); + } + } else { + // If there is no oldSpec, this means it is a new test + changesDetected.push({ + testDetails: { + reportId: reportId, + testId: newSpec.testId, + title: newSpec.title, + newTest: true, + status: newSpec.status, + previous: {}, + actual: { + statusChange: newSpec.status, + reporter: newSpec.reporter, + messageChange: newSpec.message, + }, + }, + }); + } + }); + + return { + reportId: reportId, + id: projectDetails.id, + title: projectDetails.title, + titlePath: projectDetails.titlePath, + date: projectDetails.date, + changesDetected, + }; +} + +function getJSONFilesFromDir(directory) { + return fs + .readdirSync(directory) + .filter((file) => file.endsWith(".json")) + .map((file) => { + return { + path: path.join(directory, file), + mtime: fs.statSync(path.join(directory, file)).mtime, + }; + }) + .sort((a, b) => b.mtime - a.mtime) // Orden by the most recent file + .map((file) => file.path); // Return only the path +} + +function loadJSONFile(filePath) { + return JSON.parse(fs.readFileSync(filePath, "utf8")); +} + +/* +The function scans a directory for test result subdirectories, +compares the latest two test spec files within each, +and records any changes in a new JSON file stored in a history subfolder. +*/ +const testResultsDir = path.join(__dirname, "..", "./next-js/public/data"); + +fs.readdirSync(testResultsDir).forEach((folder) => { + const subDirPath = path.join(testResultsDir, folder); + if (fs.statSync(subDirPath).isDirectory()) { + const jsonFiles = getJSONFilesFromDir(subDirPath); + if (jsonFiles.length >= 2) { + const latestFilePath = jsonFiles[0]; + const secondLatestFilePath = jsonFiles[1]; + + const latestData = loadJSONFile(latestFilePath); + const secondLatestData = loadJSONFile(secondLatestFilePath); + + const differences = compareSpecs(secondLatestData[0].specs, latestData[0].specs, latestData[0]); + if (differences.changesDetected && differences.changesDetected.length > 0) { + const historyFolderPath = path.join(subDirPath, "history"); + if (!fs.existsSync(historyFolderPath)) { + fs.mkdirSync(historyFolderPath); + } + + const differencesFileName = `${projectTitle}-changes-${reportId}.json`; + const differencesFilePath = path.join(historyFolderPath, differencesFileName); + + // Write the data to a JSON file + const dataToWrite = differences; + console.log("dataToWrite", dataToWrite); + console.log("differencesFilePath", differencesFilePath); + + fs.writeFileSync(differencesFilePath, JSON.stringify(dataToWrite, null, 2)); + // eslint-disable-next-line no-console + console.log(`Differences detected in ${folder}. File created: ${differencesFileName}`); + } + } + } +}); + +// eslint-disable-next-line no-console +console.log("JSON reports generated successfully - generateJsonCustomReportViewHistory.js."); diff --git a/script/updateReportId.js b/script/updateReportId.js new file mode 100644 index 0000000..173f096 --- /dev/null +++ b/script/updateReportId.js @@ -0,0 +1,32 @@ +"--no-ignore"; +const fs = require("fs"); +const path = require("path"); + +// Path to the reportId.json file +const reportIdPath = path.join(__dirname, "..", "next-js/public/data/reportId.json"); + +// This function reads the reportId.json, increments the reportId, and writes it back to the file. +function updateReportId() { + // Check if the reportId.json file exists + if (!fs.existsSync(reportIdPath)) { + throw new Error("reportId.json file not found."); + } + + // Read the file content + const content = fs.readFileSync(reportIdPath, "utf8"); + const reportIdObj = JSON.parse(content); + + // Increment the reportId if it is a valid number + if (reportIdObj.reportId && !isNaN(reportIdObj.reportId)) { + // Convert reportId to an integer, increment it, and pad it with leading zeros + reportIdObj.reportId = (parseInt(reportIdObj.reportId, 10) + 1).toString().padStart(3, "0"); + } else { + throw new Error("Invalid or missing reportId in the JSON file."); + } + + // Write the updated reportId back to the reportId.json file + fs.writeFileSync(reportIdPath, JSON.stringify(reportIdObj, null, 2), "utf8"); +} + +// Call the function to update the reportId +updateReportId(); diff --git a/tests/apps/globallinkgoMarketing/desktop/e2e/GlobalLinkGo.e2e.desktop.spec.ts b/tests/apps/globallinkgoMarketing/desktop/e2e/GlobalLinkGo.e2e.desktop.spec.ts new file mode 100644 index 0000000..02e330e --- /dev/null +++ b/tests/apps/globallinkgoMarketing/desktop/e2e/GlobalLinkGo.e2e.desktop.spec.ts @@ -0,0 +1,222 @@ +import { test, expect, TestInfo } from "@playwright/test"; +import { goToPort } from "../../../../utils/generalTestUtils"; +import configConstants from "../../../../data/configConstants.json"; +import reportId from "../../../../../next-js/public/data/reportId.json"; + +import { customReportTestInfoCondition, customReportTestInfoTestDescribe, customReportTestInfoConditionPass } from "../../../../utils/customReport"; + +const websites = [ + { id: "001", url: "www.tbardini.com", email: "contact@bookshop.org", reporter: "Thiago" }, + { id: "002", url: "mantine.dev", email: "contact@mantine.com", reporter: "Thiago" }, + { id: "003", url: "forbes.com", email: "contact@forbes.com", reporter: "Thiago" }, + { id: "004", url: "techcrunch.com", email: "contact@techcrunch.com", reporter: "Thiago" }, + { id: "005", url: "theguardian.com", email: "contact@theguardian.com", reporter: "Thiago" }, + { id: "006", url: "wired.com", email: "contact@wired.com", reporter: "Thiago" }, + { id: "007", url: "foodnetwork.com", email: "contact@foodnetwork.com", reporter: "Thiago" }, + { id: "008", url: "smittenkitchen.com", email: "contact@smittenkitchen.com", reporter: "Thiago" }, + { id: "009", url: "thepioneerwoman.com", email: "contact@thepioneerwoman.com", reporter: "Thiago" }, + { id: "010", url: "food52.com", email: "contact@food52.com", reporter: "Thiago" }, + { id: "011", url: "thekitchn.com", email: "contact@thekitchn.com", reporter: "Thiago" }, + { id: "012", url: "cooksillustrated.com", email: "contact@cooksillustrated.com", reporter: "Thiago" }, + { id: "013", url: "delish.com", email: "contact@delish.com", reporter: "Thiago" }, + { id: "014", url: "cookstr.com", email: "contact@cookstr.com", reporter: "Thiago" }, + { id: "015", url: "simplyrecipes.com", email: "contact@simplyrecipes.com", reporter: "Thiago" }, + { id: "016", url: "www.kfc.com", email: "contact@test.com", reporter: "Thiago" }, + { id: "017", url: "www.lowes.com", email: "contact@test.com", reporter: "Thiago" }, + { id: "018", url: "www.pizzahut.com", email: "contact@test.com", reporter: "Thiago" }, + { id: "019", url: "www.homedepot.com", email: "contact@test.com", reporter: "Thiago" }, + { id: "020", url: "openai.com", email: "contact@test.com", reporter: "Thiago" }, +]; + +test.describe("GlobalLink Go @smoke", () => { + // Heavy tests - only run on Chromium + test.setTimeout(130000); + test.skip(({ browserName }) => browserName !== "chromium", "Test only runs on Chromium."); + const testReportId = reportId.reportId; + + // eslint-disable-next-line no-empty-pattern + test.beforeEach(async ({}, testInfo: TestInfo) => { + customReportTestInfoTestDescribe({ + testInfo: testInfo, + testReportId: testReportId, + testReportName: "GlobalLink Go", + testPath: "globallinkgo.com > WordsServedEstimatePage", + typeOfTest: "e2e", + tickets: `["TICKET-2658", "TICKET-2663", "TICKET-489"]`, + }); + }); + + // API Endpoint For Testing + const globalLinkGoPort = configConstants.glgoMarketing; + + // Setup the test environment + goToPort(globalLinkGoPort); + + websites.forEach((data) => { + const { id, url, email } = data; + test(`${url}`, async ({ page }, testInfo) => { + testInfo.duration; + testInfo.snapshotSuffix; + + // Custom report screenshot name & path + let imageName; + const pathScreenshot = `next-test-report/public/test-report`; + + await page.getByRole("button", { name: "Pricing" }).click(); + await page.getByLabel("Your Website *").click(); + await page.getByLabel("Your Website *").fill(url); + await page.getByLabel("Your Email *").click(); + await page.getByLabel("Your Email *").fill(email); + + // Verify if the email and URL were filled before proceeding + const isUrlFilled = await page.getByLabel("Your Website *").evaluate((node: HTMLInputElement) => node.value.length > 0); + const isEmailFilled = await page.getByLabel("Your Email *").evaluate((node: HTMLInputElement) => node.value.length > 0); + + // Encode the URL, e.g., "www.bostonhpa.org%2Fpublic" + const encodedWebsiteUrl = encodeURIComponent(url); + + // Listen for the QuickQuote response + const quickQuoteResponsePromise = page.waitForResponse((response) => response.url().includes(`QuickQuote?domain=${encodedWebsiteUrl}`), { timeout: 130000 }); + + // Listen for the CRM API response + const crmAPIResponsePromise = page.waitForResponse((response) => response.url().includes(`CRM/objects/contacts`), { timeout: 130000 }); + + // Click the "Calculate Now" button if the fields are already filled + if (isUrlFilled && isEmailFilled) { + await page.getByRole("button", { name: "Calculate Now" }).click(); + } else { + // Try to fill the fields if they are not filled + if (!isUrlFilled) { + await page.getByLabel("Your Website *").fill(url); + } + if (!isEmailFilled) { + await page.getByLabel("Your Email *").fill(email); + } + // Click the "Calculate Now" button + await page.getByRole("button", { name: "Calculate Now" }).click(); + } + + // await quickQuoteRequestPromise; + await page.waitForRequest((request) => request.url().includes("QuickQuote?domain="), { timeout: 130000 }); + + // Go to the Words Served Estimate page + await Promise.race([ + page.goto(`${globalLinkGoPort}/estimate?domain=${url}&email=${email}`, { waitUntil: "load", timeout: 130000 }), + page.getByRole("dialog").getByRole("button").click(), + page + .locator('text="Failed to connect to the"') + .waitFor({ state: "visible", timeout: 130000 }) + .then(async () => { + const imageName = `image${id}_Failed.png`; + await customReportTestInfoCondition({ + page: page, + testInfo: testInfo, + data: data, + pathScreenshot: pathScreenshot, + imageName: imageName, + jiraRef: "", + errorMessage: `API/QuickQuote (504) has no spider jobs - Failed to connect to the website.`, + }); + }), + ]); + await quickQuoteResponsePromise; + + const quickQuoteResponse = await quickQuoteResponsePromise; + + const analyzedWordsText = await page.getByTestId("pw-analyzed-words").textContent({ timeout: 130000 }); + const analyzedWords: number | null = analyzedWordsText ? parseInt(analyzedWordsText, 10) : null; + + const statusCode = quickQuoteResponse.status(); + + // Failure scenarios: + + // 1. The status is 'Cancelled' QuickQuote API 404 status + if (!quickQuoteResponse.ok()) { + // Custom report data + imageName = `image${id}_Cancelled.png`; + await customReportTestInfoCondition({ + page: page, + testInfo: testInfo, + data: data, + pathScreenshot: pathScreenshot, + imageName: imageName, + jiraRef: "TICKET-2658", + errorMessage: `API/QuickQuote - Cancelled - Getting ${statusCode} status`, + }); + } + + const crmAPIResponse = await crmAPIResponsePromise; + + // 2. The analyzed words are zero - QuickQuote API 200 status + if ((analyzedWords === null || analyzedWords === 0 || isNaN(analyzedWords)) && quickQuoteResponse.ok()) { + await crmAPIResponsePromise; + + // 2.1) CRM API status is not 200 + if (crmAPIResponse.status() !== 200) { + imageName = `image${id}_ZeroWords_CrmApi_Fail.png`; + await customReportTestInfoCondition({ + page: page, + testInfo: testInfo, + data: data, + pathScreenshot: pathScreenshot, + imageName: imageName, + jiraRef: "TICKET-489,TICKET-2663", + errorMessage: `Two failures: API/QuickQuote - Zero words analyzed despite 'OK' (${statusCode}) response (TICKET-2663) & CRM API - Getting ${crmAPIResponse.status()} status (TICKET-489)`, + }); + } else if (analyzedWords === 0) { + imageName = `image${id}_ZeroWords_Fail.png`; + await customReportTestInfoCondition({ + page: page, + testInfo: testInfo, + data: data, + pathScreenshot: pathScreenshot, + imageName: imageName, + jiraRef: "TICKET-2663", + errorMessage: `API/QuickQuote - Zero words analyzed despite 'OK' (${statusCode}) response`, + }); + } else { + // Analyzedwords is NaN + imageName = `image${id}_FailedTo_Fail.png`; + await customReportTestInfoCondition({ + page: page, + testInfo: testInfo, + data: data, + pathScreenshot: pathScreenshot, + imageName: imageName, + jiraRef: "No Jira Ref", + errorMessage: `API/QuickQuote (${statusCode}) has no spider jobs - Failed to connect to the website.`, + }); + } + } + + await crmAPIResponsePromise; + // 3. Pass and CRM API status is not 200 + if (crmAPIResponse.status() !== 200) { + imageName = `image-${id}_CrmApi_Fail.png`; + await customReportTestInfoCondition({ + page: page, + testInfo: testInfo, + data: data, + pathScreenshot: pathScreenshot, + imageName: imageName, + jiraRef: "TICKET-489", + errorMessage: `Test PASS, but CRM API - Getting ${crmAPIResponse.status()} status (TICKET-489)`, + }); + } else { + // 4. Pass + await expect(async () => { + imageName = `image${id}_Pass.png`; + await customReportTestInfoConditionPass({ + page: page, + testInfo: testInfo, + data: data, + pathScreenshot: pathScreenshot, + imageName: imageName, + jiraRef: "", + errorMessage: "", + }); + }).toPass(); + } + }); + }); +}); diff --git a/tests/apps/globallinkgoMarketing/desktop/e2e/MiniMarket.e2e.desktop.spec.ts b/tests/apps/globallinkgoMarketing/desktop/e2e/MiniMarket.e2e.desktop.spec.ts new file mode 100644 index 0000000..e8cfe94 --- /dev/null +++ b/tests/apps/globallinkgoMarketing/desktop/e2e/MiniMarket.e2e.desktop.spec.ts @@ -0,0 +1,101 @@ +import { test, expect, TestInfo } from "@playwright/test"; +import { goToPort } from "../../../../utils/generalTestUtils"; +import configConstants from "../../../../data/configConstants.json"; +import reportId from "../../../../../next-js/public/data/reportId.json"; + +import { customReportTestInfoCondition, customReportTestInfoTestDescribe, customReportTestInfoConditionPass } from "../../../../utils/customReport"; + +const report = [ + { + id: "001", + url: "mini-market-tbardini.vercel.app", + email: "thiagobardini@icloud.com", + reporter: "Thiago", + }, +]; +test.describe("Mini Market @smoke", () => { + test.setTimeout(130000); + const testReportId = reportId.reportId; + + // eslint-disable-next-line no-empty-pattern + test.beforeEach(async ({}, testInfo: TestInfo) => { + customReportTestInfoTestDescribe({ + testInfo: testInfo, + testReportId: testReportId, + testReportName: "Mini Market", + testPath: "mini-market-tbardini.vercel.app > Stripe Payment", + typeOfTest: "e2e", + tickets: `["TICKET-001"]`, + }); + }); + + // API Endpoint For Testing + const miniMarketUrlPort = configConstants.miniMarketUrl; + + // Setup the test environment + goToPort(miniMarketUrlPort); + report.forEach((data) => { + const { email } = data; + test("Stripe Payment", async ({ page }, testInfo) => { + testInfo.duration; + testInfo.snapshotSuffix; + + // Custom report screenshot name & path + let imageName; + const pathScreenshot = `next-test-report/public/test-report`; + + try { + // Click on the "Start Shopping" button + await page.getByRole("link", { name: "Start shopping" }).click(); + + // Add product to cart + await page.getByRole("button", { name: "$10" }).click(); + // await expect(page.getByText("1", { exact: true })).toBeVisible(); + + await page.getByRole("button", { name: "$10" }).click(); + // await expect(page.getByText("2", { exact: true })).toBeVisible(); + + await page.getByRole("button", { name: "$2.75" }).click(); + // await expect(page.getByText("1", { exact: true })).toBeVisible(); + + await page.getByRole("button", { name: "$5" }).click(); + await page.getByRole("button", { name: "$5" }).click(); + await page.getByRole("button", { name: "$5" }).click(); + // await expect(page.getByText("3", { exact: true })).toBeVisible(); + + await page.getByRole("button", { name: "$3.25" }).click(); + // await expect(page.getByText("1").nth(4)).toBeVisible(); + + // await expect(page.getByRole("link", { name: "Cart (7)" })).toBeVisible(); + // await page.getByRole("link", { name: "Cart (7)" }).click(); + + // await page.getByText("Card number: 4242 4242 4242").click(); + // await page.getByPlaceholder("Email").click(); + // await page.getByPlaceholder("Email").fill(email); + + await expect(async () => { + imageName = `mini-market-Pass.png`; + await customReportTestInfoConditionPass({ + page: page, + testInfo: testInfo, + data: data, + pathScreenshot: pathScreenshot, + imageName: imageName, + jiraRef: "", + errorMessage: "", + }); + }).toPass(); + } catch (error) { + await customReportTestInfoCondition({ + page: page, + testInfo: testInfo, + data: data, + pathScreenshot: pathScreenshot, + imageName: imageName, + jiraRef: "TICKET-001", + errorMessage: error, + }); + } + }); + }); +}); diff --git a/tests/data/configConstants.json b/tests/data/configConstants.json new file mode 100644 index 0000000..ea1921d --- /dev/null +++ b/tests/data/configConstants.json @@ -0,0 +1,4 @@ +{ + "glgoMarketing": "https://www.globallinkgo.com", + "miniMarketUrl": "https://mini-market-tbardini.vercel.app/" +} diff --git a/tests/interfaces/interfaces.ts b/tests/interfaces/interfaces.ts new file mode 100644 index 0000000..ac1e5eb --- /dev/null +++ b/tests/interfaces/interfaces.ts @@ -0,0 +1,25 @@ +import { Page, TestInfo } from "@playwright/test"; + +export interface customReporttDescribeInterface { + testInfo: TestInfo; + testReportId: string; + testReportName: string; + testPath: string; + typeOfTest: string; + tickets: string; +} + +export interface customReportConditionInterface { + page: Page; + testInfo: TestInfo; + pathScreenshot: string; + data: { + id?: string; + url?: string; + email?: string; + reporter?: string; + }; + imageName: string; + jiraRef?: string; + errorMessage?: string; +} diff --git a/tests/utils/customReport.ts b/tests/utils/customReport.ts new file mode 100644 index 0000000..47438a2 --- /dev/null +++ b/tests/utils/customReport.ts @@ -0,0 +1,62 @@ +import { customReportConditionInterface, customReporttDescribeInterface } from "../interfaces/interfaces"; + +export function customReportTestInfoTestDescribe(data: customReporttDescribeInterface) { + const { testInfo, testReportId, testReportName, testPath, typeOfTest, tickets } = data; + const testParams = { + testReportId: testReportId, + testPath: testPath, + typeOfTest: typeOfTest, + tickets: tickets || "", + }; + testInfo.annotations.push({ + type: `${testReportName}-${testReportId}`, + description: JSON.stringify(testParams), + }); +} + +export async function customReportTestInfoCondition(customReportData: customReportConditionInterface) { + const { page, testInfo, data, imageName, jiraRef, errorMessage } = customReportData; + + const pathScreenshot = `next-js/public/test-report`; + + await page.screenshot({ + path: `${pathScreenshot}/${imageName}`, + fullPage: true, + }); + + const htmlCustomReportData = { + websiteId: data.id || "001", + websiteUrl: data.url || "TestQA", + reporter: data.reporter || "Playwright", + jiraRef: `https://github.com/thiagobardini` || "", + jiraTicket: jiraRef, + screenshotPath: imageName, + }; + + const htmlDataReportDataString = JSON.stringify(htmlCustomReportData); + testInfo.annotations.push({ type: data.id ?? "default-type", description: htmlDataReportDataString }); + + throw new Error(errorMessage); +} +export async function customReportTestInfoConditionPass(customReportData: customReportConditionInterface) { + const { page, testInfo, data, imageName, jiraRef } = customReportData; + + const pathScreenshot = `next-js/public/test-report`; + + await page.screenshot({ + path: `${pathScreenshot}/${imageName}`, + fullPage: true, + }); + + const htmlCustomReportData = { + websiteId: data.id || "001", + websiteUrl: data.url || "TestQA", + reporter: data.reporter || "Playwright", + jiraRef: `https://github.com/thiagobardini` || "", + jiraTicket: jiraRef, + screenshotPath: imageName, + }; + + const htmlDataReportDataString = JSON.stringify(htmlCustomReportData); + testInfo.annotations.push({ type: data.id ?? "default-type", description: htmlDataReportDataString }); +} diff --git a/tests/utils/generalTestUtils.ts b/tests/utils/generalTestUtils.ts new file mode 100644 index 0000000..7f3de69 --- /dev/null +++ b/tests/utils/generalTestUtils.ts @@ -0,0 +1,97 @@ +import { test, expect, Page } from "@playwright/test"; + +// Types +type DataObject = { [key: string]: string }; + +// Navigates to the specified port +const goToPort = (port: string) => { + test.beforeEach(async ({ page }) => { + await page.goto(`${port}?testing=groovy`, { waitUntil: "load" }); + // eslint-disable-next-line no-console + page.on("console", (msg) => console.log(msg.text())); + }); +}; + +// Navigates to the specified project +const goToProject = (projectName: string) => { + test.beforeEach("Go to Project", async ({ page }) => { + await page.getByTestId("pw-project-selector").click(); + await page.getByPlaceholder("Search...").fill(projectName); + await page.getByPlaceholder("Search...").press("ArrowDown"); + + // Check if the project with the specified name exists + const projectExists = await page.getByRole("menuitem", { name: projectName, exact: true }).isVisible(); + if (!projectExists) { + // Throw an error if the project is not found + throw new Error(`Project "${projectName}" not found. Provide a valid project name.`); + } + + await page.getByRole("menuitem", { name: projectName }).press("Enter"); + await page.click("body"); + }); +}; + +// Returns the current date and time in the format "MM/DD/YY-HH:MM" +export function getFormattedDateTime(): string { + const date = new Date(); + const day = date.getDate().toString().padStart(2, "0"); + const month = (date.getMonth() + 1).toString().padStart(2, "0"); + const year = date.getFullYear(); + let hours = date.getHours(); + const minutes = date.getMinutes().toString().padStart(2, "0"); + const ampm = hours >= 12 ? "PM" : "AM"; + hours = hours % 12; + hours = hours ? hours : 12; // A hora '0' deve ser '12' + const formattedDateTime = `${month}/${day}/${year} - ${hours.toString().padStart(2, "0")}:${minutes} ${ampm}`; + return formattedDateTime; +} + +// Returns the locator for a button based on the page name +const getLocator = (page: Page, pageName: string) => page.getByRole("button", { name: pageName }); + +// Checks text visibility +async function checkTextVisibility(page: Page, textId: string, dataObject: DataObject, dataKey: string): Promise { + const element = page.locator(`[data-testid="${textId}"]`); + await expect(element).toBeVisible(); + + if (typeof dataObject[dataKey] === "undefined") { + throw new Error(`The key '${dataKey}' is not defined in the data object.`); + } + await expect(element).toHaveText(dataObject[dataKey]); +} + +// Checks tooltip visibility +async function checkTooltipVisibility(page: Page, textId: string, dataObject: DataObject, dataKeyTitle: string, dataKeyTooltip: string): Promise { + const tooltipTrigger = page.locator(`[data-testid="${textId}"]`); + await tooltipTrigger.hover(); + const tooltip = page.locator('[role="tooltip"]').first(); + + if (typeof dataObject[dataKeyTooltip] === "undefined") { + throw new Error(`The key '${dataKeyTooltip}' is not defined in the data object.`); + } + + await expect(tooltip).toHaveText(dataObject[dataKeyTooltip]); + await page.click("body"); +} + +// Checks the visibility of an icon and its associated tooltip +async function checkIconAndTooltipVisibility(page: Page, iconId: string, dataObject: DataObject, dataKey: string): Promise { + const icon = page.locator(`[data-testid="${iconId}"]`); + await icon.hover(); + const tooltip = page.locator('[role="tooltip"]').first(); + + if (typeof dataObject[dataKey] === "undefined") { + throw new Error(`The key '${dataKey}' is not defined in the data object.`); + } + await expect(tooltip).toHaveText(dataObject[dataKey]); + await page.click("body"); +} + +// Clicks a tab or button based on the tab or button name +export async function clickTabOrButton(page: Page, testId: string, dataObject: DataObject, tabButtonName: string): Promise { + const tabButton = page.locator(`[data-testid="${testId}"]`, { hasText: dataObject[tabButtonName] }); + await tabButton.click(); +} + +// Exports +export { goToPort, goToProject, getLocator, checkTextVisibility, checkIconAndTooltipVisibility, checkTooltipVisibility }; diff --git a/tests/utils/screenshotTests.ts b/tests/utils/screenshotTests.ts new file mode 100644 index 0000000..60f4efd --- /dev/null +++ b/tests/utils/screenshotTests.ts @@ -0,0 +1,62 @@ +// screenshotTests.ts +import { expect, Page } from "@playwright/test"; + +interface ScreenshotTestParams { + page: Page; + testId?: string; + screenshotName: string; + fullPage?: boolean; + maskTestIds?: string[]; + maxDiffPixelRatioNum?: number; +} + +// Function to check full page screenshot without mask +export async function checkFullPageScreenshot({ page, screenshotName, fullPage = false }: ScreenshotTestParams): Promise { + await page.waitForLoadState("load"); + await page.waitForTimeout(3000); + + await page.click("body"); + await expect(page).toHaveScreenshot(screenshotName, { fullPage, animations: "disabled", timeout: 10000 }); +} + +// Function to check full page screenshot with mask +export async function checkFullPageScreenshotWithMask({ page, screenshotName, fullPage = false, maskTestIds = [] }: ScreenshotTestParams): Promise { + // Create masks for dynamic elements we want to exclude from the screenshot + const mask = maskTestIds.map((testId) => page.locator(`[data-testid="${testId}"]`)); + + // Wait for each masked element to be visible before taking the screenshot + for (const maskLocator of mask) { + await maskLocator.first().waitFor({ state: "visible", timeout: 10000 }); + } + + await expect(page).toHaveScreenshot(screenshotName, { fullPage, mask, timeout: 10000 }); +} + +// Function to check screenshot of a specific element with no mask +export async function checkElementScreenshot({ page, testId, screenshotName }: ScreenshotTestParams): Promise { + const locator = page.locator(`[data-testid="${testId}"]`); + await locator.waitFor({ state: "visible" }); + await page.waitForTimeout(1000); + await expect(locator).toHaveScreenshot(screenshotName, { timeout: 10000 }); +} + +// Function to check screenshot of specific element with mask +export async function checkElementScreenshotWithMask({ page, testId, screenshotName, maskTestIds = [] }: ScreenshotTestParams): Promise { + const locator = page.locator(`[data-testid="${testId}"]`); + await locator.waitFor({ state: "visible" }); + await page.waitForTimeout(1000); + const mask = maskTestIds.map((testId) => page.locator(`[data-testid="${testId}"]`)); + await expect(locator).toHaveScreenshot(screenshotName, { mask, timeout: 10000 }); +} + +// Function to check screenshot with pixel ratio considerations +export async function checkScreenshotPixelRatio({ page, testId, screenshotName, maxDiffPixelRatioNum }: ScreenshotTestParams): Promise { + const locator = page.locator(`[data-testid="${testId}"]`); + await locator.waitFor({ state: "visible" }); + await page.waitForTimeout(1000); + await expect(locator).toHaveScreenshot(screenshotName, { + maxDiffPixelRatio: maxDiffPixelRatioNum, + // You might want to include other options like maxDiffPixels if needed + timeout: 10000, + }); +}