rearrange files

This commit is contained in:
sup39 2023-02-16 09:54:31 +09:00
parent 9c9d3385c8
commit 8a6878d5ff
26 changed files with 493 additions and 262 deletions

View file

@ -1,10 +0,0 @@
import config from '#config';
export default function Footer() {
const year = new Date().getFullYear();
const {site: {startYear: year0 = year, author} = {}} = config;
return <footer>{author &&
<div>Copyright © {year>year0 ? `${year0}-${year}` : year} {author} All rights reserved.</div>
}</footer>;
}

View file

@ -1,38 +1,4 @@
import Head from 'next/head'; import MDXPageFactory from '@sup39/mdx-page';
import type {AppProps} from 'next/app'; import config from '#config';
import Nav from './Nav';
import Footer from './Footer';
import MetaInfo from './MetaInfo';
import type {HeadingInfo} from '@sup39/rehype-mdx-export-headings';
export type MDXProps = { export default MDXPageFactory(config);
children: JSX.Element
router: AppProps['router'],
meta: Partial<{
title: string
description: string
h1: string
[key: string]: any
}>
headings: HeadingInfo[]
};
export default function MDXRoot({children, router: {pathname}, meta={}, headings}: MDXProps) {
const {title, description} = meta;
const h1 = meta.h1 ?? title;
return <>
<Head>
<title>{title}</title>
{description && <meta name="description" content={description} />}
</Head>
<Nav pathname={pathname} headings={headings} />
<main>
<article>
{h1 ? <h1>{h1}</h1> : <></>}
<MetaInfo data={meta} />
{children}
</article>
</main>
<Footer />
</>;
}

View file

@ -1,12 +0,0 @@
import config from '#config';
export default function MetaInfo({data}: {data: {[_: string]: any}}) {
const {metaFields: fields = []} = config;
return <div>{fields.map(({label, prop}) => {
const val = data[prop];
return val == null ? null : <div key={prop}>
<span>{label}</span>
<span>{val}</span>
</div>;
})}</div>;
}

View file

@ -1,12 +0,0 @@
import Link from 'next/link';
import config from '#config';
export default function NavHeader({onToggleFold}: {onToggleFold?: ()=>void}) {
const {site: {name: siteName = 'supMDX'} = {}} = config;
return <header>
<Link href="/" id="icon-link">
<div style={{fontSize: '1.5em'}}>{siteName}</div>
</Link>
<div className="menu-toggle" onClick={onToggleFold} />
</header>;
}

View file

@ -1,15 +0,0 @@
/** <S $="span" _=".a.b.c" className="extra class-name">...</S> */
export function S<E extends React.ElementType='span'>({
$: TagName = 'span',
_: modifier = '',
className, children, ...props
}: {
$?: string,
_?: string,
} & React.ComponentProps<E>) {
const cls = [
...(className ? [className] : []),
...(modifier.match(/\.(\w+)/g) ?? []),
].map(s => s.slice(1)).join(' ');
return <TagName className={cls} {...props}>{children}</TagName>;
}

View file

@ -0,0 +1,12 @@
import React from 'react';
const hx = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;
export const anchoredHx = Object.fromEntries(hx.map(H => [
H,
({children, id, ...props}: React.ComponentProps<(typeof hx)[number]>) => (
<H {...{id, ...props}}>
{id && <a className="anchor" href={'#'+encodeURIComponent(id)} />}
{children}
</H>
),
]));

View file

@ -0,0 +1,6 @@
export * from './heading';
export * from './tag';
import {anchoredHx} from './heading';
const mdx = {...anchoredHx};
export default mdx;

79
core/components/tag.tsx Normal file
View file

@ -0,0 +1,79 @@
import React from 'react';
export type TagFactory = {
[modifier: string]: TagFactory
(children: React.ReactNode): JSX.Element
(s: TemplateStringsArray, ...argv: any[]): JSX.Element
};
export type TagInfo = {
tagName: string
attrs: Record<string, string>
};
export const AttrProxyHandler: ProxyHandler<TagInfo> = {
get({tagName, attrs}, modifier) {
let extra: TagInfo['attrs'];
if (typeof modifier === 'symbol') {
extra = {};
} else if (modifier.startsWith('#')) {
// #id
extra = {id: modifier.slice(1)};
} else if (modifier.includes('=')) {
// attr=value
const idx = modifier.indexOf('=');
extra = {[modifier.slice(0, idx)]: modifier.slice(idx+1)};
} else {
// .class
extra = {class: attrs.class ? attrs.class+' '+modifier : modifier};
}
// apply
return new Proxy(
Object.assign(()=>{}, {tagName, attrs: {...attrs, ...extra}}),
AttrProxyHandler,
);
},
apply({
tagName,
attrs: {class: className, for: htmlFor, style, ...attrs},
}, thisArg, args) {
const [s, ...argv] = args;
const children = s instanceof Array ?
s[0]+argv.map((e, i) => `${e}${s[i+1]}`).join('') : s;
return React.createElement(
tagName,
{
className,
htmlFor,
...(style == null ? {} : {
style: Object.fromEntries(style.split(';').flatMap(line => {
const idx = line.indexOf(':');
if (idx < 0) return [];
const key0 = line.slice(0, idx);
const value = line.slice(idx+1);
// kebab-case to camelCase
const ktoks = key0.split('-');
const key = ktoks[0] + ktoks.slice(1).map(s => s[0].toUpperCase()+s.slice(1)).join('');
return [[key, value]];
})),
}),
...attrs,
},
children,
);
},
};
export const TagProxyHandler: ProxyHandler<Record<string, TagFactory>> = {
get(self, arg) {
const tagName = arg.toString();
return new Proxy(
Object.assign(()=>{}, {tagName, attrs: {}}),
AttrProxyHandler,
);
},
};
export const T = new Proxy({}, TagProxyHandler);
export const S = T.span;
export const C = T.code;
export const tags = {T, S, C};

25
core/nav/Nav.tsx Normal file
View file

@ -0,0 +1,25 @@
import {useState} from 'react';
import type {NavEntryInfo} from './NavEntry';
import {NavBase, HeadingInfo} from './NavBase';
import {NavHeader, NavHeaderConfig} from './NavHeader';
export type NavConfig = {
site: NavHeaderConfig
nav: NavEntryInfo[]
}
export type NavProps = {
children?: React.ReactNode
headings: HeadingInfo[]
pathname: string
config: NavConfig
};
export function Nav({config, children, ...props}: NavProps) {
const [navFold, setNavFold] = useState(false);
return (
<NavBase className={navFold ? 'open' : ''} entries={config.nav} {...props}>
<NavHeader config={config.site} onToggleFold={()=>setNavFold(e=>!e)} />
{children}
</NavBase>
);
}

30
core/nav/NavBase.tsx Normal file
View file

@ -0,0 +1,30 @@
import {NavEntry, NavEntryInfo} from './NavEntry';
export type HeadingInfo = {
tagName: string;
label: string;
id: string;
};
export type NavBaseProps = {
children?: React.ReactNode
headings: HeadingInfo[]
pathname: string
entries: NavEntryInfo[]
className?: string
}
export function NavBase({
children, headings, pathname, entries, className,
}: NavBaseProps) {
const headingsJSX = <ul className="">{headings.map(({label, id}) => <li key={id}>
<a href={'#'+id}>{label}</a>
</li>)}</ul>;
return <nav className={className}>
{children}
<div className="nav-root">
{entries.map(entry => <NavEntry key={entry.link} entry={entry} dir={'/'} here={pathname} />)}
{headingsJSX}
</div>
</nav>;
}

View file

@ -1,17 +1,13 @@
import {useState} from 'react'; import {useState} from 'react';
import Link from 'next/link'; import Link from 'next/link';
import NavHeader from './NavHeader';
import type {HeadingInfo} from '@sup39/rehype-mdx-export-headings';
import config from '#config';
export type NavEntryInfo = { export type NavEntryInfo = {
name: string label: string
link: string link: string
children?: NavEntryInfo[]|null children?: NavEntryInfo[]
}; };
export function NavEntry<Body, >({ export function NavEntry<Body, >({
entry: {name, link, children}, dir, here, children: childrenJSX, entry: {label, link, children}, dir, here, children: childrenJSX,
}: {entry: NavEntryInfo, dir: string, here: string, children?: Body}) { }: {entry: NavEntryInfo, dir: string, here: string, children?: Body}) {
const href = dir+link; const href = dir+link;
const isHere = href.replace(/\/$/, '')===here; // remove trailing slash const isHere = href.replace(/\/$/, '')===here; // remove trailing slash
@ -21,7 +17,7 @@ export function NavEntry<Body, >({
const entryCls = 'nav-entry'+(isHere ? ' nav-here' : ''); const entryCls = 'nav-entry'+(isHere ? ' nav-here' : '');
return children?.length ? <div className={'nav-dir'+(toggle ? ' nav-fold-open' : '')}><> return children?.length ? <div className={'nav-dir'+(toggle ? ' nav-fold-open' : '')}><>
<div className={entryCls}> <div className={entryCls}>
<Link href={href}>{name}</Link> <Link href={href}>{label}</Link>
<svg viewBox="0 0 8 8" onClick={()=>setToggle(e=>!e)}><polyline points="6 3 4 5 2 3"></polyline></svg> <svg viewBox="0 0 8 8" onClick={()=>setToggle(e=>!e)}><polyline points="6 3 4 5 2 3"></polyline></svg>
</div> </div>
{isHere ? childrenJSX : <></>} {isHere ? childrenJSX : <></>}
@ -29,27 +25,6 @@ export function NavEntry<Body, >({
children.map(entry => <NavEntry key={entry.link} entry={entry} dir={href} here={here}>{childrenJSX}</NavEntry>) children.map(entry => <NavEntry key={entry.link} entry={entry} dir={href} here={here}>{childrenJSX}</NavEntry>)
}</div> }</div>
</></div> : <div className={entryCls}> </></div> : <div className={entryCls}>
<Link href={href}>{name}</Link> <Link href={href}>{label}</Link>
</div>; </div>;
} }
export default function Nav({children, headings, pathname}: {
children?: JSX.Element
headings: HeadingInfo[]
pathname: string
}) {
const [navFold, setNavFold] = useState(false);
const headingsJSX = <ul className=''>{headings.map(({label, id}) => <li key={id}>
<a href={'#'+id}>{label}</a>
</li>)}</ul>;
return <nav className={navFold ? 'open' : ''}>
<NavHeader onToggleFold={()=>setNavFold(e=>!e)} />
{children}
<div className='nav-root'>
{config.nav.map(entry => <NavEntry key={entry.link} entry={entry} dir={'/'} here={pathname} />)}
{headingsJSX}
</div>
</nav>;
}

36
core/nav/NavHeader.tsx Normal file
View file

@ -0,0 +1,36 @@
import Link from 'next/link';
export type NavHeaderConfig = {
title: string
subtitle?: string
icon?: string | {
href: string
alt?: string
size?: number
width?: number
height?: number
}
};
export function NavHeader({config, onToggleFold}: {
config: NavHeaderConfig
onToggleFold?: ()=>void
}) {
const {title, subtitle, icon: icon0} = config;
const icon = typeof icon0 === 'string' ? {href: icon0} : icon0;
return <header>
<Link href="/" id="icon-link">
{icon?.href && <img className="icon"
src={icon?.href}
alt={icon?.alt ?? 'icon'}
width={icon?.width ?? icon?.size ?? 96}
height={icon?.height ?? icon?.size ?? 96}
/>}
<div className="icon-text">
<div>{title}</div>
<div>{subtitle}</div>
</div>
</Link>
<div className="menu-toggle" onClick={onToggleFold} />
</header>;
}

7
core/nav/index.tsx Normal file
View file

@ -0,0 +1,7 @@
export * from './Nav';
export * from './NavBase';
export * from './NavEntry';
export * from './NavHeader';
import {Nav} from './Nav';
export default Nav;

16
core/page/Footer.tsx Normal file
View file

@ -0,0 +1,16 @@
import React from 'react';
export type FooterConfig = {
site: {
startYear?: number
author: string
}
};
export function Footer({config}: {config: FooterConfig}) {
const year = new Date().getFullYear();
const {site: {startYear: year0 = year, author}} = config;
const yearStr = year>year0 ? `${year0}-${year}` : year;
return <footer>{
author && <div>Copyright © {yearStr} {author} All rights reserved.</div>
}</footer>;
}

83
core/page/MDXPage.tsx Normal file
View file

@ -0,0 +1,83 @@
import Head from 'next/head';
import {Nav, NavHeaderConfig} from '@sup39/mdx-nav';
import {Footer} from './Footer';
import {MetaInfo} from './MetaInfo';
import {I18N, LocaleInfo, translate} from './i18n';
import {I18NNavEntryInfo, translateNav} from './i18n-nav';
type HeadingInfo = {
tagName: string;
label: string;
id: string;
};
export type MDXProps = {
children: JSX.Element
router: {pathname: string} & LocaleInfo
meta: Partial<{
title: I18N<string>
description: I18N<string>
h1: I18N<string>
[key: string]: any
}>
headings: HeadingInfo[]
config: MDXConfig
};
export type MDXConfig = {
site: {
startYear?: number
author: string
title: I18N<string>
subtitle?: I18N<string>
} & Omit<NavHeaderConfig, 'title'|'subtitle'>
metaFields?: {label: I18N<string>, attr: string}[]
nav: I18NNavEntryInfo[]
}
export function MDXPage({
children, router, meta: meta18={}, headings, config,
}: MDXProps) {
const meta = Object.fromEntries(Object.entries(meta18).map(([k, v]) => [
k, translate(v, router),
]));
const {title, description} = meta;
const h1 = meta.h1 ?? title;
const metaFields = config.metaFields?.map(({label, attr}) => ({
label: translate(label, router),
attr,
})) ?? [];
const {pathname} = router;
const navConfig = {
site: {
...config.site,
title: translate(config.site.title, router),
subtitle: translate(config.site.subtitle, router),
},
nav: translateNav(config.nav, router),
};
return <>
<Head>
{title && <title>{title}</title>}
{description && <meta name="description" content={description} />}
</Head>
<Nav config={navConfig} pathname={pathname} headings={headings} />
<main>
<article>
{h1 ? <h1>{h1}</h1> : <></>}
<MetaInfo fields={metaFields} data={meta} />
{children}
</article>
</main>
<Footer config={config} />
</>;
}
export default function MDXPageFactory(config: MDXConfig) {
return function Page({children, ...props}: Omit<MDXProps, 'config'>) {
return <MDXPage config={config} {...props}>{children}</MDXPage>;
};
}

19
core/page/MetaInfo.tsx Normal file
View file

@ -0,0 +1,19 @@
import React from 'react';
export type MetaInfoProps = {
data: Record<string, any>
fields: {
label: string
attr: string
}[]
};
export function MetaInfo({fields, data}: MetaInfoProps) {
return <div>{fields.map(({label, attr}) => {
const val = data[attr];
return val == null ? null : <div key={attr}>
<span>{label}</span>
<span>{val}</span>
</div>;
})}</div>;
}

19
core/page/i18n-nav.ts Normal file
View file

@ -0,0 +1,19 @@
import type {NavEntryInfo} from '@sup39/mdx-nav';
import {I18N, translate} from './i18n';
export type I18NNavEntryInfo = {
label: I18N<string>
link: string
children?: I18NNavEntryInfo[]
};
export function translateNav(
nav18: I18NNavEntryInfo[],
localeInfo: Parameters<typeof translate>[1],
): NavEntryInfo[] {
return nav18.map(e => ({
label: translate(e.label, localeInfo),
link: e.link,
...(e.children ? {children: translateNav(e.children, localeInfo)} : {}),
}));
}

29
core/page/i18n.ts Normal file
View file

@ -0,0 +1,29 @@
export type NonRecordType = boolean|number|bigint|string|symbol|any[];
export type I18N<T> = (T & NonRecordType) | Record<string, T>;
export type LocaleInfo = {
locale?: string
defaultLocale?: string
};
export function translate<T>(
data: I18N<T>,
{locale, defaultLocale=''}: LocaleInfo,
): T;
export function translate<T>(
data: I18N<T>|null,
{locale, defaultLocale=''}: LocaleInfo,
): null;
export function translate<T>(
data: I18N<T>|undefined,
{locale, defaultLocale=''}: LocaleInfo,
): undefined;
export function translate<T, U extends null|undefined>(
data: I18N<T>|U,
{locale, defaultLocale=''}: LocaleInfo,
): T|U {
if (data == null) return data;
if (typeof data !== 'object') return data;
if (data instanceof Array) return data;
return (locale == null ? undefined : data[locale]) ??
data[defaultLocale] ?? Object.values(data)[0];
}

8
core/page/index.tsx Normal file
View file

@ -0,0 +1,8 @@
export * from './Footer';
export * from './MDXPage';
export * from './MetaInfo';
export * from './i18n';
export * from './i18n-nav';
import MDXPageFactory from './MDXPage';
export default MDXPageFactory;

View file

@ -20,7 +20,8 @@
"*.tsx" "*.tsx"
], ],
"rules": { "rules": {
"no-undef": "off" "no-undef": "off",
"@next/next/no-img-element": "off"
} }
}, },
{ {
@ -41,12 +42,12 @@
"node_modules" "node_modules"
], ],
"dependencies": { "dependencies": {
"@mdx-js/loader": "^2.1.5", "@mdx-js/loader": "^2.3.0",
"@mdx-js/react": "^2.1.5", "@mdx-js/react": "^2.3.0",
"@next/mdx": "^13.0.2", "@next/mdx": "13.0.7",
"@sup39/rehype-mdx-component-wrapper": "^0.1.0", "@sup39/rehype-mdx-component-wrapper": "^0.1.0",
"@sup39/rehype-mdx-export-headings": "^0.1.1", "@sup39/rehype-mdx-export-headings": "^0.1.1",
"next": "^13.0.2", "next": "13.0.7",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"rehype-katex": "^6.0.2", "rehype-katex": "^6.0.2",
@ -58,7 +59,7 @@
"yaml-loader": "^0.8.0" "yaml-loader": "^0.8.0"
}, },
"devDependencies": { "devDependencies": {
"@sup39/eslint-config-typescript": "^0.1.2", "@sup39/eslint-config-typescript": "^0.1.4",
"@types/node": "18.11.9", "@types/node": "18.11.9",
"@types/react": "18.0.24", "@types/react": "18.0.24",
"eslint": "^8.26.0", "eslint": "^8.26.0",

View file

@ -1,20 +1,11 @@
import React from 'react'; import React from 'react';
import type {AppProps} from 'next/app'; import type {AppProps} from 'next/app';
import {MDXProvider} from '@mdx-js/react'; import {MDXProvider} from '@mdx-js/react';
import {S} from '@/mdx'; import mdx from '@sup39/mdx-components';
import '../styles/index.sass'; import '../styles/index.sass';
// add anchor to all headings having id
const hx = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;
const extendedHx = Object.fromEntries(hx.map(H => [H,
({children, id, ...props}: React.ComponentProps<(typeof hx)[number]>) => <H id={id} {...props}>
{id && <a className="anchor" href={'#'+encodeURIComponent(id)} />}
{children}
</H>,
]));
export default function App({Component, pageProps, router}: AppProps) { export default function App({Component, pageProps, router}: AppProps) {
return <MDXProvider components={{S, ...extendedHx}}> return <MDXProvider components={{...mdx}}>
<Component router={router} {...pageProps} /> <Component router={router} {...pageProps} />
</MDXProvider>; </MDXProvider>;
} }

View file

@ -3,15 +3,3 @@ title: supMDX
description: You can write any meta data you want description: You can write any meta data you want
author: me author: me
--- ---
## h2
### Tables
|a |b |
|--|--|
|cc|dd|
|ee|ff|
### Katex
$$
x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}
$$

11
supMDX-env.d.ts vendored
View file

@ -1,13 +1,6 @@
declare module '#config' { declare module '#config' {
import type {NavEntry} from '@/Nav'; import type {MDXConfig} from '@sup39/mdx-page';
type Config = { type Config = MDXConfig & {
site: {
startYear: number
author: string
name: string
}
metaFields?: {label: string, prop: string}[]
nav: NavEntry[]
}; };
const config: Config; const config: Config;
export default config; export default config;

View file

@ -1,9 +1,11 @@
site: site:
startYear: 2022 startYear: 2023
author: <Edit author name in supMDX.yml> author: <Edit author name in supMDX.yml>
name: <Site Name> title: <Site Name>
subtitle: <Subtitle>
icon:
metaFields: metaFields:
- {prop: author, label: 'Author: '} - {attr: author, label: 'Author: '}
- {prop: createdAt, label: 'Created at '} - {attr: createdAt, label: 'Created at '}
- {prop: updatedAt, label: 'Updated at '} - {attr: updatedAt, label: 'Updated at '}
nav: [] nav: []

View file

@ -2,6 +2,7 @@
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@sup39/mdx-*": ["core/*"],
"@/*": ["components/*"], "@/*": ["components/*"],
"#/*": ["pages/*"], "#/*": ["pages/*"],
"#config": ["supMDX.yml"], "#config": ["supMDX.yml"],

200
yarn.lock
View file

@ -72,10 +72,10 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@mdx-js/loader@^2.1.5": "@mdx-js/loader@^2.3.0":
version "2.1.5" version "2.3.0"
resolved "https://registry.yarnpkg.com/@mdx-js/loader/-/loader-2.1.5.tgz#4e764c65333530fd786dfe4f36e918dfeac0200b" resolved "https://registry.yarnpkg.com/@mdx-js/loader/-/loader-2.3.0.tgz#56a6b07eb0027b6407e953a97c52bd8619601161"
integrity sha512-oXjfTa/iAcMmW8DdQ+hQFodPCLqw5VKT2yoZkLwrZfPVVpUgKrI+5/ZePYq328xxtAUStZmR3ed0PWJrwd5Pkg== integrity sha512-IqsscXh7Q3Rzb+f5DXYk0HU71PK+WuFsEhf+mSV3fOhpLcEpgsHvTQ2h0T6TlZ5gHOaBeFjkXwB52by7ypMyNg==
dependencies: dependencies:
"@mdx-js/mdx" "^2.0.0" "@mdx-js/mdx" "^2.0.0"
source-map "^0.7.0" source-map "^0.7.0"
@ -103,18 +103,18 @@
unist-util-visit "^4.0.0" unist-util-visit "^4.0.0"
vfile "^5.0.0" vfile "^5.0.0"
"@mdx-js/react@^2.1.5": "@mdx-js/react@^2.3.0":
version "2.1.5" version "2.3.0"
resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-2.1.5.tgz#8225a867dae6f845ae5b0ec15bb454c23be3f576" resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-2.3.0.tgz#4208bd6d70f0d0831def28ef28c26149b03180b3"
integrity sha512-3Az1I6SAWA9R38rYjz5rXBrGKeZhq96CSSyQtqY+maPj8stBsoUH5pNcmIixuGkufYsh8F5+ka2CVPo2fycWZw== integrity sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==
dependencies: dependencies:
"@types/mdx" "^2.0.0" "@types/mdx" "^2.0.0"
"@types/react" ">=16" "@types/react" ">=16"
"@next/env@13.0.2": "@next/env@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.0.2.tgz#5fbd7b4146175ae406edfb4a38b62de8c880c09d" resolved "https://registry.yarnpkg.com/@next/env/-/env-13.0.7.tgz#7b6ccd9006d3fb57c369e3fb62b28e15324141e9"
integrity sha512-Qb6WPuRriGIQ19qd6NBxpcrFOfj8ziN7l9eZUfwff5gl4zLXluqtuZPddYZM/oWjN53ZYcuRXzL+oowKyJeYtA== integrity sha512-ZBclBRB7DbkSswXgbJ+muF5RxfgmAuQKAWL8tcm86aZmoiL1ZainxQK0hMcMYdh+IYG8UObAKV2wKB5O+6P4ng==
"@next/eslint-plugin-next@13.0.2": "@next/eslint-plugin-next@13.0.2":
version "13.0.2" version "13.0.2"
@ -123,77 +123,77 @@
dependencies: dependencies:
glob "7.1.7" glob "7.1.7"
"@next/mdx@^13.0.2": "@next/mdx@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/mdx/-/mdx-13.0.2.tgz#60febdab4ccd4ece87ba99694befe146f0e1307e" resolved "https://registry.yarnpkg.com/@next/mdx/-/mdx-13.0.7.tgz#34275121ac690c11746780345fc96b456c9d9e72"
integrity sha512-udtSlWoOLoHdJvtuGyj3ctXW4fogsQDWU2YXp4Sx4JdsS7VKvJkhTU0Df47nAJsQKCJhQ92h+j7rA+1Dulxxqg== integrity sha512-ekuxCOO7j/Un7qxIYtuBAQN+bduDAtYDEMEf0OqBQ3ZDWJpQYrJ1kGr0uk1WjW5Z1OH7hV8ZlGwuBvhfjaao8Q==
dependencies: dependencies:
source-map "^0.7.0" source-map "^0.7.0"
"@next/swc-android-arm-eabi@13.0.2": "@next/swc-android-arm-eabi@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.2.tgz#66669b8aab5062f554b8e9905d855679aabf0342" resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.7.tgz#ddbf3d092d22f17238aa34072f5dcb8129d8b23e"
integrity sha512-X54UQCTFyOGnJP//Z71dPPlp4BCYcQL2ncikKXQcPzVpqPs4C3m+tKC8ivBNH6edAXkppwsLRz1/yQwgSZ9Swg== integrity sha512-QTEamOK/LCwBf05GZ261rULMbZEpE3TYdjHlXfznV+nXwTztzkBNFXwP67gv2wW44BROzgi/vrR9H8oP+J5jxg==
"@next/swc-android-arm64@13.0.2": "@next/swc-android-arm64@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.0.2.tgz#c0641d30525e0fb22bf1e2baf6c461d2d9e52f1a" resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.0.7.tgz#96f150232eb66da377226f21a371d30389371ed5"
integrity sha512-1P00Kv8uKaLubqo7JzPrTqgFAzSOmfb8iwqJrOb9in5IvTRtNGlkR4hU0sXzqbQNM/+SaYxze6Z5ry1IDyb/cQ== integrity sha512-wcy2H0Tl9ME8vKy2GnJZ7Mybwys+43F/Eh2Pvph7mSDpMbYBJ6iA0zeY62iYYXxlZhnAID3+h79FUqUEakkClw==
"@next/swc-darwin-arm64@13.0.2": "@next/swc-darwin-arm64@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.2.tgz#d7e01a33393e83456dbfdc41446bb8a923968ff7" resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.7.tgz#34e80a22573b5321ade8417dfb814cf6e1fd9997"
integrity sha512-1zGIOkInkOLRv0QQGZ+3wffYsyKI4vIy62LYTvDWUn7TAYqnmXwougp9NSLqDeagLwgsv2URrykyAFixA/YqxA== integrity sha512-F/mU7csN1/J2cqXJPMgTQ6MwAbc1pJ6sp6W+X0z5JEY4IFDzxKd3wRc3pCiNF7j8xW381JlNpWxhjCctnNmfaw==
"@next/swc-darwin-x64@13.0.2": "@next/swc-darwin-x64@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.2.tgz#d4a3fbe51edf871a675d89a7afdc78174d1e5841" resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.7.tgz#ecec57211bf54a15872bb44e5ea70c99c2efe785"
integrity sha512-ECDAjoMP1Y90cARaelS6X+k6BQx+MikAYJ8f/eaJrLur44NIOYc9HA/dgcTp5jenguY4yT8V+HCquLjAVle6fA== integrity sha512-636AuRQynCPnIPRVzcCk5B7OMq9XjaYam2T0HeWUCE6y7EqEO3kxiuZ4QmN81T7A6Ydb+JnivYrLelHXmgdj6A==
"@next/swc-freebsd-x64@13.0.2": "@next/swc-freebsd-x64@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.2.tgz#1b54c3f38d3b36f86663a8dfcc81ea05e01f5172" resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.7.tgz#b4a8a49c3c3d200c9d6c43193b82ee39c6eb1d59"
integrity sha512-2DcL/ofQdBnQX3IoI9sjlIAyLCD1oZoUBuhrhWbejvBQjutWrI0JTEv9uG69WcxWhVMm3BCsjv8GK2/68OKp7A== integrity sha512-92XAMzNgQazowZ9t7uZmHRA5VdBl/SwEdrf5UybdfRovsxB4r3+yJWEvFaqYpSEp0gwndbwLokJdpz7OwFdL3Q==
"@next/swc-linux-arm-gnueabihf@13.0.2": "@next/swc-linux-arm-gnueabihf@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.2.tgz#18495cff32c0b2182cfbf677614219d7072214da" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.7.tgz#6f550d348c6ece2b25426a53c5be49a3a8fc54a3"
integrity sha512-Y3OQF1CSBSWW2vGkmvOIuOUNqOq8qX7f1ZpcKUVWP3/Uq++DZmVi9d18lgnSe1I3QFqc+nXWyun9ljsN83j0sw== integrity sha512-3r1CWl5P6I5n5Yxip8EXv/Rfu2Cp6wVmIOpvmczyUR82j+bcMkwPAcUjNkG/vMCagS4xV7NElrcdGb39iFmfLg==
"@next/swc-linux-arm64-gnu@13.0.2": "@next/swc-linux-arm64-gnu@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.2.tgz#5fb2563651166c3c6f32bf9e2f9cc6a16a9ef783" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.7.tgz#20bd7f25a3af0edb4d3506c005f54212eb9a855b"
integrity sha512-mNyzwsFF6kwZYEjnGicx9ksDZYEZvyzEc1BtCu8vdZi/v8UeixQwCiAT6FyYX9uxMPEkzk8qiU0t0u9gvltsKw== integrity sha512-RXo8tt6ppiwyS6hpDw3JdAjKcdVewsefxnxk9xOH4mRhMyq9V2lQx0e24X/dRiZqkx3jnWReR2WRrUlgN1UkSQ==
"@next/swc-linux-arm64-musl@13.0.2": "@next/swc-linux-arm64-musl@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.2.tgz#b9f33c5e17cfe04aa5769b717284cf80865761e6" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.7.tgz#f421bedcf2e1ad1ad7c90af1102df83634e92b6a"
integrity sha512-M6SdYjWgRrY3tJBxz0663zCRPTu5BRONmxlftKWWHv9LjAJ59neTLaGj4rp0A08DkJglZIoCkLOzLrzST6TGag== integrity sha512-RWpnW+bmfXyxyY7iARbueYDGuIF+BEp3etLeYh/RUNHb9PhOHLDgJOG8haGSykud3a6CcyBI8hEjqOhoObaDpw==
"@next/swc-linux-x64-gnu@13.0.2": "@next/swc-linux-x64-gnu@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.2.tgz#29efcc2fd0122689d7e06c5b6b0883fe495db2bf" resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.7.tgz#76cb25d3c00041dabc02e0b3ddd10f9325eb3f60"
integrity sha512-pi63RoxvG4ES1KS06Zpm0MATVIXTs/TIbLbdckeLoM40u1d3mQl/+hSSrLRSxzc2OtyL8fh92sM4gkJrQXAMAw== integrity sha512-/ygUIiMMTYnbKlFs5Ba9J5k/tNxFWy8eI1bBF8UuMTvV8QJHl/aLDiA5dwsei2kk99/cu3eay62JnJXkSk3RSQ==
"@next/swc-linux-x64-musl@13.0.2": "@next/swc-linux-x64-musl@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.2.tgz#d68fdf6eefc57813fa91559c7089b49d6131ecab" resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.7.tgz#4e49b54b3578f7c4753dd7ac9c5e683914427884"
integrity sha512-9Pv91gfYnDONgjtRm78n64b/c54+azeHtlnqBLTnIFWSMBDRl1/WDkhKWIj3fBGPLimtK7Tko3ULR3og9RRUPw== integrity sha512-dLzr6AL77USJN0ejgx5AS8O8SbFlbYTzs0XwAWag4oQpUG2p3ARvxwQgYQ0Z+6EP0zIRZ/XfLkN/mhsyi3m4PA==
"@next/swc-win32-arm64-msvc@13.0.2": "@next/swc-win32-arm64-msvc@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.2.tgz#acdcb3023045f60cca510f659a2349895e6405bd" resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.7.tgz#98f622f9d0e34746e1ec7f25ce436a809a42313d"
integrity sha512-Nvewe6YZaizAkGHHprbMkYqQulBjZCHKBGKeFPwoPtOA+a2Qi4pZzc/qXFyC5/2A6Z0mr2U1zg9rd04WBYMwBw== integrity sha512-+vFIVa82AwqFkpFClKT+n73fGxrhAZ2u1u3mDYEBdxO6c9U4Pj3S5tZFsGFK9kLT/bFvf/eeVOICSLCC7MSgJQ==
"@next/swc-win32-ia32-msvc@13.0.2": "@next/swc-win32-ia32-msvc@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.2.tgz#a78ee9211471768febac9df915c2a8dbbcd05e41" resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.7.tgz#f27f99aeec4207be7688a417f5934ea4868dadfc"
integrity sha512-ZUBYGZw5G3QrqDpRq1EWi3aHmvPZM8ijK5TFL6UbH16cYQ0JpANmuG2P66KB93Qe/lWWzbeAZk/tj1XqwoCuPA== integrity sha512-RNLXIhp+assD39dQY9oHhDxw+/qSJRARKhOFsHfOtf8yEfCHqcKkn3X/L+ih60ntaEqK294y1WkMk6ylotsxwA==
"@next/swc-win32-x64-msvc@13.0.2": "@next/swc-win32-x64-msvc@13.0.7":
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.2.tgz#e86c2de910cd68a17974db5556d4588737412c68" resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.7.tgz#7aaa6cee723cde844e891e895e5561a60d9fa7f3"
integrity sha512-fA9uW1dm7C0mEYGcKlbmLcVm2sKcye+1kPxh2cM4jVR+kQQMtHWsjIzeSpe2grQLSDan06z4n6hbr8b1c3hA8w== integrity sha512-kvdnlLcrnEq72ZP0lqe2Z5NqvB9N5uSCvtXJ0PhKvNncWWd0fEG9Ec9erXgwCmVlM2ytw41k9/uuQ+SVw4Pihw==
"@nodelib/fs.scandir@2.1.5": "@nodelib/fs.scandir@2.1.5":
version "2.1.5" version "2.1.5"
@ -233,17 +233,17 @@
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728"
integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==
"@sup39/eslint-config-basic@latest": "@sup39/eslint-config-basic@^0.1.5":
version "0.1.5" version "0.1.5"
resolved "https://registry.yarnpkg.com/@sup39/eslint-config-basic/-/eslint-config-basic-0.1.5.tgz#73520b79d5fe2efafdc615207689c64b7307542c" resolved "https://registry.yarnpkg.com/@sup39/eslint-config-basic/-/eslint-config-basic-0.1.5.tgz#73520b79d5fe2efafdc615207689c64b7307542c"
integrity sha512-fAYz/rIGkAUzBhQqPKo9w442HRGop71HBa4wnQHeLdySE3LxMyiNtuz5a3TaNrfWm3x+er2TCGq3if/FzRp+5g== integrity sha512-fAYz/rIGkAUzBhQqPKo9w442HRGop71HBa4wnQHeLdySE3LxMyiNtuz5a3TaNrfWm3x+er2TCGq3if/FzRp+5g==
"@sup39/eslint-config-typescript@^0.1.2": "@sup39/eslint-config-typescript@^0.1.4":
version "0.1.2" version "0.1.4"
resolved "https://registry.yarnpkg.com/@sup39/eslint-config-typescript/-/eslint-config-typescript-0.1.2.tgz#ef51e08eabe5a0ac46a2509b91b89835f17cc786" resolved "https://registry.yarnpkg.com/@sup39/eslint-config-typescript/-/eslint-config-typescript-0.1.4.tgz#d5d3c1ac20e160b1f042910e535587e00ce8c871"
integrity sha512-c/PGuvT0hyvAgjKXelCLJGR7JXUw9I6zh+dhBSwukaW6p2nhBMAfLjXvuATL4Xo8op/281/8uCRddwPl25FtMA== integrity sha512-0hjNZGUgkrFkeifSMECE0NG65MLEMAgVnpiM6IL+yX4gLjz431DHDY/a//2ptfqWaP30JasFRYVuWp8EIDw+dQ==
dependencies: dependencies:
"@sup39/eslint-config-basic" latest "@sup39/eslint-config-basic" "^0.1.5"
"@typescript-eslint/eslint-plugin" "^5" "@typescript-eslint/eslint-plugin" "^5"
"@typescript-eslint/parser" "^5" "@typescript-eslint/parser" "^5"
@ -257,10 +257,10 @@
resolved "https://registry.yarnpkg.com/@sup39/rehype-mdx-export-headings/-/rehype-mdx-export-headings-0.1.1.tgz#852b22df675ee6904215da200a3cc18ffe153509" resolved "https://registry.yarnpkg.com/@sup39/rehype-mdx-export-headings/-/rehype-mdx-export-headings-0.1.1.tgz#852b22df675ee6904215da200a3cc18ffe153509"
integrity sha512-erq58RWz+KYUOp/75K0kbsKwI4M66LovC44BYpzvaSCyDNEM1Y3j5tgxcAt8VZLrJuSpfzgpVPPnj6fWhTpmtA== integrity sha512-erq58RWz+KYUOp/75K0kbsKwI4M66LovC44BYpzvaSCyDNEM1Y3j5tgxcAt8VZLrJuSpfzgpVPPnj6fWhTpmtA==
"@swc/helpers@0.4.11": "@swc/helpers@0.4.14":
version "0.4.11" version "0.4.14"
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.11.tgz#db23a376761b3d31c26502122f349a21b592c8de" resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74"
integrity sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw== integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==
dependencies: dependencies:
tslib "^2.4.0" tslib "^2.4.0"
@ -2621,31 +2621,30 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
next@^13.0.2: next@13.0.7:
version "13.0.2" version "13.0.7"
resolved "https://registry.yarnpkg.com/next/-/next-13.0.2.tgz#b8c8642c70f736ed91105645391d335fc51c8f62" resolved "https://registry.yarnpkg.com/next/-/next-13.0.7.tgz#f07a0cc3afefdb86fb6668048e910a2193e3c1e2"
integrity sha512-uQ5z5e4D9mOe8+upy6bQdYYjo/kk1v3jMW87kTy2TgAyAsEO+CkwRnMgyZ4JoHEnhPZLHwh7dk0XymRNLe1gFw== integrity sha512-YfTifqX9vfHm+rSU/H/3xvzOHDkYuMuh4wsvTjiqj9h7qHEF7KHB66X4qrH96Po+ohdid4JY8YVGPziDwdXL0A==
dependencies: dependencies:
"@next/env" "13.0.2" "@next/env" "13.0.7"
"@swc/helpers" "0.4.11" "@swc/helpers" "0.4.14"
caniuse-lite "^1.0.30001406" caniuse-lite "^1.0.30001406"
postcss "8.4.14" postcss "8.4.14"
styled-jsx "5.1.0" styled-jsx "5.1.0"
use-sync-external-store "1.2.0"
optionalDependencies: optionalDependencies:
"@next/swc-android-arm-eabi" "13.0.2" "@next/swc-android-arm-eabi" "13.0.7"
"@next/swc-android-arm64" "13.0.2" "@next/swc-android-arm64" "13.0.7"
"@next/swc-darwin-arm64" "13.0.2" "@next/swc-darwin-arm64" "13.0.7"
"@next/swc-darwin-x64" "13.0.2" "@next/swc-darwin-x64" "13.0.7"
"@next/swc-freebsd-x64" "13.0.2" "@next/swc-freebsd-x64" "13.0.7"
"@next/swc-linux-arm-gnueabihf" "13.0.2" "@next/swc-linux-arm-gnueabihf" "13.0.7"
"@next/swc-linux-arm64-gnu" "13.0.2" "@next/swc-linux-arm64-gnu" "13.0.7"
"@next/swc-linux-arm64-musl" "13.0.2" "@next/swc-linux-arm64-musl" "13.0.7"
"@next/swc-linux-x64-gnu" "13.0.2" "@next/swc-linux-x64-gnu" "13.0.7"
"@next/swc-linux-x64-musl" "13.0.2" "@next/swc-linux-x64-musl" "13.0.7"
"@next/swc-win32-arm64-msvc" "13.0.2" "@next/swc-win32-arm64-msvc" "13.0.7"
"@next/swc-win32-ia32-msvc" "13.0.2" "@next/swc-win32-ia32-msvc" "13.0.7"
"@next/swc-win32-x64-msvc" "13.0.2" "@next/swc-win32-x64-msvc" "13.0.7"
normalize-path@^3.0.0, normalize-path@~3.0.0: normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0" version "3.0.0"
@ -3443,11 +3442,6 @@ uri-js@^4.2.2:
dependencies: dependencies:
punycode "^2.1.0" punycode "^2.1.0"
use-sync-external-store@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
uvu@^0.5.0, uvu@^0.5.6: uvu@^0.5.0, uvu@^0.5.6:
version "0.5.6" version "0.5.6"
resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df" resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df"