Release 0.0.2 #17

Open
ksv741 wants to merge 33 commits from krylov into development
43 changed files with 413 additions and 113 deletions
Showing only changes of commit 3516914ad3 - Show all commits

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -38,12 +38,16 @@
"@mui/material": "5.15.19",
"@react-spring/web": "9.7.3",
"classnames": "2.5.1",
"i18next": "23.11.5",
"i18next-browser-languagedetector": "8.0.0",
"i18next-http-backend": "2.5.2",
"notistack": "3.0.1",
"prop-types": "15.8.1",
"react": "18.3.1",
"react-dnd": "16.0.1",
"react-dnd-html5-backend": "16.0.1",
"react-dom": "18.3.1",
"react-i18next": "14.1.2",
"react-router-dom": "6.23.1",
"workbox-core": "7.1.0",
"workbox-precaching": "7.1.0",

View File

@ -0,0 +1,4 @@
{
"title": "Analyze Page",
"toolbar": "Analyze Page Toolbar"
}

View File

@ -0,0 +1,5 @@
{
"title": "Something goes wrong",
"toolbar": "Unhandled error",
"404-toolbar": "Page does not exist"
}

View File

@ -0,0 +1,4 @@
{
"title": "Payments Page",
"toolbar": "Payments Page Toolbar"
}

View File

@ -0,0 +1,4 @@
{
"title": "Schedule Page",
"toolbar": "Schedule Page Toolbar"
}

View File

@ -0,0 +1,5 @@
{
"analyze-page-link": "Analyze",
"payments-page-link": "Payments list",
"schedule-page-link": "Schedule"
}

View File

@ -0,0 +1,3 @@
{
"language": "English"
}

View File

@ -0,0 +1,4 @@
{
"title": "Аналитика",
"toolbar": "Доска аналитики"
}

View File

@ -0,0 +1,5 @@
{
"title": "Что-то пошло не так",
"toolbar": "Произошла непредвиденная ошибка",
"404-toolbar": "Такой страницы не существует"
}

View File

@ -0,0 +1,4 @@
{
"title": "Платежи",
"toolbar": "Доска платежей"
}

View File

@ -0,0 +1,4 @@
{
"title": "График платежей",
"toolbar": "Доска графика платежей"
}

View File

@ -0,0 +1,5 @@
{
"analyze-page-link": "Аналитика",
"payments-page-link": "Список платежей",
"schedule-page-link": "График платежей"
}

View File

@ -0,0 +1,3 @@
{
"language": "Русский"
}

View File

@ -1,8 +1,8 @@
import CalendarMonthIcon from '@mui/icons-material/CalendarMonth';
import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted';
import PieChartIcon from '@mui/icons-material/PieChart';
import CircularProgress from '@mui/material/CircularProgress';
import { Suspense } from 'react';
import { useTranslation } from 'react-i18next';
import { createBrowserRouter, Navigate, RouterProvider as RRDRouterProvider } from 'react-router-dom';
import { AnalyzePage, AnalyzePageToolbar } from 'pages/AnalyzePage';
@ -11,6 +11,7 @@ import { PaymentsPage, PaymentsPageToolbar } from 'pages/PaymentsPage';
import { SchedulePage, SchedulePageToolbar } from 'pages/SchedulePage';
import { AsideMenuLayout } from 'widgets/AsideMenuLayout';
import { PageLoader } from 'widgets/PageLoader';
import type { ReactNode } from 'react';
import type { RouteObject } from 'react-router-dom';
@ -23,76 +24,78 @@ type PageListItem = {
Toolbar?: ReactNode;
};
const pages: PageListItem[] = [
{
icon: <PieChartIcon />,
title: 'Аналитика',
path: '/analyze',
Content: (
<Suspense fallback={<CircularProgress />}>
<AnalyzePage />
</Suspense>
),
Toolbar: <AnalyzePageToolbar />,
},
{
icon: <CalendarMonthIcon />,
title: 'График платежей',
path: '/schedule',
Content: (
<Suspense fallback={<CircularProgress />}>
<SchedulePage />
</Suspense>
),
Toolbar: <SchedulePageToolbar />,
},
{
icon: <FormatListBulletedIcon />,
title: 'Список платежей',
path: '/payments',
Content: (
<Suspense fallback={<CircularProgress />}>
<PaymentsPage />
</Suspense>
),
Toolbar: <PaymentsPageToolbar />,
},
];
const RouterProvider = () => {
const { t } = useTranslation('sidebar');
const routes: RouteObject[] = pages.map((page) => ({
path: page.path,
element: (
const pages: PageListItem[] = [
{
icon: <PieChartIcon />,
title: t('analyze-page-link'),
path: '/analyze',
Content: <AnalyzePage />,
Toolbar: <AnalyzePageToolbar />,
},
{
icon: <CalendarMonthIcon />,
title: t('schedule-page-link'),
path: '/schedule',
Content: <SchedulePage />,
Toolbar: <SchedulePageToolbar />,
},
{
icon: <FormatListBulletedIcon />,
title: t('payments-page-link'),
path: '/payments',
Content: <PaymentsPage />,
Toolbar: <PaymentsPageToolbar />,
},
];
const Fallback = (
<AsideMenuLayout
Content={page.Content}
Content={<PageLoader />}
pages={pages}
Toolbar={page.Toolbar}
/>
),
errorElement: (
<AsideMenuLayout
Content={<ErrorPage />}
pages={pages}
Toolbar={<ErrorPageToolbar />}
/>
),
}));
const router = createBrowserRouter([
...routes,
{
path: '/',
element: <Navigate replace to="/analyze" />,
);
const routes: RouteObject[] = pages.map((page) => ({
path: page.path,
element: (
<Suspense fallback={Fallback}>
<AsideMenuLayout
Content={page.Content}
pages={pages}
Toolbar={page.Toolbar}
/>
</Suspense>
),
errorElement: (
<AsideMenuLayout
Content={<ErrorPage type="404" />}
pages={pages}
Toolbar={<ErrorPageToolbar type="404" />}
/>
<Suspense fallback={Fallback}>
<AsideMenuLayout
Content={<ErrorPage />}
pages={pages}
Toolbar={<ErrorPageToolbar />}
/>
</Suspense>
),
},
]);
}));
const RouterProvider = () => (
<RRDRouterProvider router={router} />
);
const router = createBrowserRouter([
...routes,
{
path: '/',
element: <Navigate replace to="/analyze" />,
errorElement: (
<AsideMenuLayout
Content={<ErrorPage type="404" />}
pages={pages}
Toolbar={<ErrorPageToolbar type="404" />}
/>
),
},
]);
return (
<RRDRouterProvider router={router} />
);
};
export default RouterProvider;

View File

@ -0,0 +1 @@
export { default as LangSwitcher } from './ui/LangSwitcher.ui';

View File

@ -0,0 +1,30 @@
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import i18next from 'i18n';
import { LANGUAGE } from 'shared/constants';
import type { FC, ChangeEventHandler } from 'react';
const LangSwitcher: FC = () => {
const { i18n } = useTranslation();
const onChangedHandler = useCallback<ChangeEventHandler<HTMLSelectElement>>(async (e) => {
await i18next.changeLanguage(e.target.value);
}, []);
return (
<select value={i18n.language} onChange={onChangedHandler}>
{Object.entries(LANGUAGE).map(([lang, params]) => (
<option key={lang} value={lang}>
{params.emoji}
{params.name}
</option>
))}
</select>
);
};
export default LangSwitcher;

22
src/i18n.ts Normal file
View File

@ -0,0 +1,22 @@
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';
import type { HttpBackendOptions } from 'i18next-http-backend';
// eslint-disable-next-line @typescript-eslint/no-floating-promises
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init<HttpBackendOptions>({
fallbackLng: 'en',
debug: process.env.NODE_ENV === 'development',
load: 'currentOnly',
backend: {
loadPath: 'locales/{{lng}}/{{ns}}.json',
},
});
export default i18n;

View File

@ -1,3 +1,4 @@
import './i18n';
import 'shared/styles/fonts/fonts.css';
import React from 'react';
import { createRoot } from 'react-dom/client';

View File

@ -1,10 +1,16 @@
import { useTranslation } from 'react-i18next';
import type { DetailedHTMLProps, FC, HTMLAttributes } from 'react';
type AnalyzePageProps = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
const AnalyzePage: FC<AnalyzePageProps> = (props) => (
<div {...props}>
Analyze page
</div>
);
const AnalyzePage: FC<AnalyzePageProps> = (props) => {
const { t } = useTranslation('analyze-page');
return (
<div {...props}>
{t('title')}
</div>
);
};
export default AnalyzePage;

View File

@ -1,15 +1,20 @@
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import { useTranslation } from 'react-i18next';
import type { ToolbarProps } from '@mui/material/Toolbar/Toolbar';
import type { FC } from 'react';
type AnalyzePageToolbarProps = ToolbarProps;
const AnalyzePageToolbar: FC<AnalyzePageToolbarProps> = (props) => (
<Toolbar {...props}>
<Typography noWrap component="div" variant="h6">
Analyze Page Toolbar
</Typography>
</Toolbar>
);
const AnalyzePageToolbar: FC<AnalyzePageToolbarProps> = (props) => {
const { t } = useTranslation('analyze-page');
return (
<Toolbar {...props}>
<Typography noWrap component="div" variant="h6">
{t('toolbar')}
</Typography>
</Toolbar>
);
};
export default AnalyzePageToolbar;

View File

@ -1,5 +1,6 @@
import { Grid } from '@mui/material';
import Typography from '@mui/material/Typography';
import { useTranslation } from 'react-i18next';
import _404Image from 'public/assets/images/404.png';
import OopsImage from 'public/assets/images/oops.png';
@ -16,6 +17,8 @@ const ErrorPage: FC<ErrorPageProps> = (props) => {
...restProps
} = props;
const { t } = useTranslation('error-page');
if (type === 'ErrorBoundary') {
return (
<Grid
@ -28,7 +31,7 @@ const ErrorPage: FC<ErrorPageProps> = (props) => {
>
<Grid item>
<Typography variant="h5">
Что-то пошло не так
{t('title')}
</Typography>
</Grid>

View File

@ -1,5 +1,6 @@
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import { useTranslation } from 'react-i18next';
import type { ToolbarProps } from '@mui/material/Toolbar/Toolbar';
import type { FC } from 'react';
@ -8,17 +9,19 @@ export type ErrorPageToolbarProps = ToolbarProps & {
type?: '404' | 'ErrorBoundary';
};
const TITLE: Record<NonNullable<ErrorPageToolbarProps['type']>, string> = {
404: 'Страницы не существует',
ErrorBoundary: 'Непредвиденная ошибка',
};
const ErrorPageToolbar: FC<ErrorPageToolbarProps> = (props) => {
const {
type = 'ErrorBoundary',
...restProps
} = props;
const { t } = useTranslation('error-page');
const TITLE: Record<NonNullable<ErrorPageToolbarProps['type']>, string> = {
404: t('404-toolbar'),
ErrorBoundary: t('toolbar'),
};
return (
<Toolbar {...restProps}>
<Typography noWrap component="div" variant="h6">

View File

@ -1,10 +1,16 @@
import { useTranslation } from 'react-i18next';
import type { DetailedHTMLProps, FC, HTMLAttributes } from 'react';
export type PaymentsPageProps = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
const PaymentsPage: FC<PaymentsPageProps> = (props) => (
<div {...props}>
Payments Page
</div>
);
const PaymentsPage: FC<PaymentsPageProps> = (props) => {
const { t } = useTranslation('payments-page');
return (
<div {...props}>
{t('title')}
</div>
);
};
export default PaymentsPage;

View File

@ -1,16 +1,21 @@
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import { useTranslation } from 'react-i18next';
import type { ToolbarProps } from '@mui/material/Toolbar/Toolbar';
import type { FC } from 'react';
export type PaymentsPageToolbarProps = ToolbarProps;
const PaymentsPageToolbar: FC<PaymentsPageToolbarProps> = (props) => (
<Toolbar {...props}>
<Typography noWrap component="div" variant="h6">
Payments Page Toolbar
</Typography>
</Toolbar>
);
const PaymentsPageToolbar: FC<PaymentsPageToolbarProps> = (props) => {
const { t } = useTranslation('payments-page');
return (
<Toolbar {...props}>
<Typography noWrap component="div" variant="h6">
{t('toolbar')}
</Typography>
</Toolbar>
);
};
export default PaymentsPageToolbar;

View File

@ -1,10 +1,16 @@
import { useTranslation } from 'react-i18next';
import type { DetailedHTMLProps, FC, HTMLAttributes } from 'react';
export type SchedulePageProps = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
const SchedulePage: FC<SchedulePageProps> = (props) => (
<div {...props}>
Schedule Page
</div>
);
const SchedulePage: FC<SchedulePageProps> = (props) => {
const { t } = useTranslation('schedule-page');
return (
<div {...props}>
{t('title')}
</div>
);
};
export default SchedulePage;

View File

@ -1,16 +1,21 @@
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import { useTranslation } from 'react-i18next';
import type { ToolbarProps } from '@mui/material/Toolbar/Toolbar';
import type { FC } from 'react';
type SchedulePageToolbarProps = ToolbarProps;
const SchedulePageToolbar: FC<SchedulePageToolbarProps> = (props) => (
<Toolbar {...props}>
<Typography noWrap component="div" variant="h6">
Schedule Page Toolbar
</Typography>
</Toolbar>
);
const SchedulePageToolbar: FC<SchedulePageToolbarProps> = (props) => {
const { t } = useTranslation('schedule-page');
return (
<Toolbar {...props}>
<Typography noWrap component="div" variant="h6">
{t('toolbar')}
</Typography>
</Toolbar>
);
};
export default SchedulePageToolbar;

View File

@ -1,5 +1,5 @@
export { DAYS_IN_WEEK, MONTHS, WEEK_DAYS } from './date';
export { MONTHS_LABEL, WEEK_DAYS_LABEL } from './locale';
export { MONTHS_LABEL, WEEK_DAYS_LABEL, LANGUAGE } from './locale';
export type {
MonthsKeys,
MonthsValues,

View File

@ -136,3 +136,14 @@ export const WEEK_DAYS_LABEL: Record<WeekDaysValues, WeekDaysLabelLocale> = {
},
},
};
export const LANGUAGE = {
ru: {
name: 'Русский',
emoji: '🇷🇺',
},
en: {
name: 'English',
emoji: '🇬🇧',
},
};

View File

@ -7,6 +7,8 @@ import { animated, useTransition } from '@react-spring/web';
import { useContext } from 'react';
import { useLocation } from 'react-router-dom';
import { LangSwitcher } from 'features/LangSwitcher';
import { DesktopAppBar, MobileAppBar } from './components/AppBar';
import { AsideMenuContext, AsideMenuProvider } from './components/AsideMenuProvider';
import { DesktopDrawer } from './components/DesktopDrawer';
@ -15,6 +17,7 @@ import { MobileDrawer } from './components/MobileDrawer';
import type { MenuItemProps } from './components/MenuItem/ui/MenuItem.ui';
import type { BoxProps } from '@mui/material/Box/Box';
// eslint-disable-next-line import/max-dependencies
import type { FC, ReactNode } from 'react';
export const DESKTOP_DRAWER_WIDTH = 250;
@ -32,7 +35,6 @@ const AsideMenuLayout: FC<AsideMenuLayoutProps> = (props) => {
Toolbar,
...restProps
} = props;
const { desktopDrawerOpen, setMobileDrawerOpen } = useContext(AsideMenuContext);
const theme = useTheme();
const isMobileView = useMediaQuery(theme.breakpoints.down('sm'));
@ -49,7 +51,7 @@ const AsideMenuLayout: FC<AsideMenuLayoutProps> = (props) => {
const AnimatedContent = (
<>
{transitions((styles) => (
<animated.div style={styles}>
<animated.div style={{ ...styles, height: '100%' }}>
{Content}
</animated.div>
))}
@ -85,6 +87,8 @@ const AsideMenuLayout: FC<AsideMenuLayoutProps> = (props) => {
</MobileDrawer>
{AnimatedToolBar}
<LangSwitcher />
</MobileAppBar>
)
}
@ -113,14 +117,16 @@ const AsideMenuLayout: FC<AsideMenuLayoutProps> = (props) => {
</List>
</DesktopDrawer>
<Box sx={{ flexGrow: 1 }}>
<Box sx={{ flexGrow: 1, height: '100vh' }}>
{ Boolean(Toolbar) && (
<DesktopAppBar position="static">
{AnimatedToolBar}
<LangSwitcher />
</DesktopAppBar>
)}
<Box component="main" sx={{ p: 3 }}>
<Box component="main" sx={{ p: 3, height: '100%' }}>
{AnimatedContent}
</Box>
</Box>

View File

@ -0,0 +1 @@
export { default as PageLoader } from './ui/PageLoader.ui';

View File

@ -0,0 +1,20 @@
import CircularProgress from '@mui/material/CircularProgress';
import { styled } from '@mui/material/styles';
const CenteredDiv = styled('div')(
() => ({
width: '100%',
height: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}),
);
const PageLoader = () => (
<CenteredDiv>
<CircularProgress />
</CenteredDiv>
);
export default PageLoader;

114
yarn.lock
View File

@ -2671,7 +2671,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/runtime@npm:^7.11.2":
"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.23.2":
version: 7.24.7
resolution: "@babel/runtime@npm:7.24.7"
dependencies:
@ -7769,6 +7769,15 @@ __metadata:
languageName: node
linkType: hard
"cross-fetch@npm:4.0.0":
version: 4.0.0
resolution: "cross-fetch@npm:4.0.0"
dependencies:
node-fetch: "npm:^2.6.12"
checksum: 10c0/386727dc4c6b044746086aced959ff21101abb85c43df5e1d151547ccb6f338f86dec3f28b9dbddfa8ff5b9ec8662ed2263ad4607a93b2dc354fb7fe3bbb898a
languageName: node
linkType: hard
"cross-spawn@npm:7.0.3, cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3":
version: 7.0.3
resolution: "cross-spawn@npm:7.0.3"
@ -10579,6 +10588,15 @@ __metadata:
languageName: node
linkType: hard
"html-parse-stringify@npm:^3.0.1":
version: 3.0.1
resolution: "html-parse-stringify@npm:3.0.1"
dependencies:
void-elements: "npm:3.1.0"
checksum: 10c0/159292753d48b84d216d61121054ae5a33466b3db5b446e2ffc093ac077a411a99ce6cbe0d18e55b87cf25fa3c5a86c4d8b130b9719ec9b66623259000c72c15
languageName: node
linkType: hard
"html-webpack-plugin@npm:5.6.0":
version: 5.6.0
resolution: "html-webpack-plugin@npm:5.6.0"
@ -10751,6 +10769,33 @@ __metadata:
languageName: node
linkType: hard
"i18next-browser-languagedetector@npm:8.0.0":
version: 8.0.0
resolution: "i18next-browser-languagedetector@npm:8.0.0"
dependencies:
"@babel/runtime": "npm:^7.23.2"
checksum: 10c0/08a7c747ec18a0743b54390a0b42836d814b15fb49c92b90c259298e6a6e7e001ff9df99d0fd308283a34fceea53b4d9053aa1695a37f696bc04160017bbb524
languageName: node
linkType: hard
"i18next-http-backend@npm:2.5.2":
version: 2.5.2
resolution: "i18next-http-backend@npm:2.5.2"
dependencies:
cross-fetch: "npm:4.0.0"
checksum: 10c0/57cfbeeea40a751134e1521fac89b14b780c9ad87b74a4f7d7168a88a8b603efed9f5df455dddbb408f7e268eae5aa0a6888e47b9f10cb9482d812d4d6997ffa
languageName: node
linkType: hard
"i18next@npm:23.11.5":
version: 23.11.5
resolution: "i18next@npm:23.11.5"
dependencies:
"@babel/runtime": "npm:^7.23.2"
checksum: 10c0/b0bec64250a3e529d4c51e2fc511406a85c5dde3d005d3aabe919551ca31dfc0a8f5490bf6e44649822e895a1fa91a58092d112367669cd11b2eb89e6ba90d1a
languageName: node
linkType: hard
"iconv-lite@npm:0.4.24":
version: 0.4.24
resolution: "iconv-lite@npm:0.4.24"
@ -13039,6 +13084,9 @@ __metadata:
eslint-config-ksv741: "npm:0.0.3"
eslint-import-resolver-webpack: "npm:0.13.8"
husky: "npm:9.0.11"
i18next: "npm:23.11.5"
i18next-browser-languagedetector: "npm:8.0.0"
i18next-http-backend: "npm:2.5.2"
identity-obj-proxy: "npm:3.0.0"
jest: "npm:29.7.0"
jest-environment-jsdom: "npm:29.7.0"
@ -13050,6 +13098,7 @@ __metadata:
react-dnd: "npm:16.0.1"
react-dnd-html5-backend: "npm:16.0.1"
react-dom: "npm:18.3.1"
react-i18next: "npm:14.1.2"
react-router-dom: "npm:6.23.1"
standard-version: "npm:9.5.0"
ts-jest: "npm:29.1.4"
@ -13156,6 +13205,20 @@ __metadata:
languageName: node
linkType: hard
"node-fetch@npm:^2.6.12":
version: 2.7.0
resolution: "node-fetch@npm:2.7.0"
dependencies:
whatwg-url: "npm:^5.0.0"
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8
languageName: node
linkType: hard
"node-forge@npm:^1":
version: 1.3.1
resolution: "node-forge@npm:1.3.1"
@ -14857,6 +14920,24 @@ __metadata:
languageName: node
linkType: hard
"react-i18next@npm:14.1.2":
version: 14.1.2
resolution: "react-i18next@npm:14.1.2"
dependencies:
"@babel/runtime": "npm:^7.23.9"
html-parse-stringify: "npm:^3.0.1"
peerDependencies:
i18next: ">= 23.2.3"
react: ">= 16.8.0"
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
checksum: 10c0/cb8a83b3696639f083dc9f770d9d9e0681c0fe56f6b5fe24cd6facce08d363c37bd3440078e9d63abacabd7037b783e6b4e4d0c935de9c8dda7820bd4ef7e329
languageName: node
linkType: hard
"react-is@npm:^16.13.1, react-is@npm:^16.7.0":
version: 16.13.1
resolution: "react-is@npm:16.13.1"
@ -16587,6 +16668,13 @@ __metadata:
languageName: node
linkType: hard
"tr46@npm:~0.0.3":
version: 0.0.3
resolution: "tr46@npm:0.0.3"
checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11
languageName: node
linkType: hard
"trim-newlines@npm:^3.0.0":
version: 3.0.1
resolution: "trim-newlines@npm:3.0.1"
@ -17134,6 +17222,13 @@ __metadata:
languageName: node
linkType: hard
"void-elements@npm:3.1.0":
version: 3.1.0
resolution: "void-elements@npm:3.1.0"
checksum: 10c0/0b8686f9f9aa44012e9bd5eabf287ae0cde409b9a2854c5a2335cb83920c957668ac5876e3f0d158dd424744ac411a7270e64128556b451ed3bec875ef18534d
languageName: node
linkType: hard
"w3c-xmlserializer@npm:^4.0.0":
version: 4.0.0
resolution: "w3c-xmlserializer@npm:4.0.0"
@ -17171,6 +17266,13 @@ __metadata:
languageName: node
linkType: hard
"webidl-conversions@npm:^3.0.0":
version: 3.0.1
resolution: "webidl-conversions@npm:3.0.1"
checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db
languageName: node
linkType: hard
"webidl-conversions@npm:^4.0.2":
version: 4.0.2
resolution: "webidl-conversions@npm:4.0.2"
@ -17415,6 +17517,16 @@ __metadata:
languageName: node
linkType: hard
"whatwg-url@npm:^5.0.0":
version: 5.0.0
resolution: "whatwg-url@npm:5.0.0"
dependencies:
tr46: "npm:~0.0.3"
webidl-conversions: "npm:^3.0.0"
checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5
languageName: node
linkType: hard
"whatwg-url@npm:^7.0.0":
version: 7.1.0
resolution: "whatwg-url@npm:7.1.0"