implemented identity, updated session, added 'workings' list

added paginated querying, added 'common'
This commit is contained in:
lnn0q 2025-05-17 00:29:55 +03:00
parent 027deba422
commit 6ada519e06
36 changed files with 1422 additions and 279 deletions

View file

@ -0,0 +1,159 @@
import { Card, Heading, SystemStyleObject } from '@chakra-ui/react';
import EditorJS, { EditorConfig } from '@editorjs/editorjs';
import Header from '@editorjs/header';
import List from '@editorjs/list';
import Quote from '@editorjs/quote';
import { useEffect, useRef } from 'react';
// @ts-ignore
import Link from '@coolbytes/editorjs-link';
export function Editor({ initialData }: { initialData?: EditorConfig['data'] }) {
const ejInstance = useRef<EditorJS | undefined | null>();
const initEditor = () => {
const editor = new EditorJS({
holder: 'editorjs',
onReady: () => {
ejInstance.current = editor;
},
autofocus: true,
data: initialData,
// onChange: async () => {
// const content = await editor.saver.save();
// },
tools: {
header: Header,
list: List,
link: Link,
quote: Quote
}
});
};
useEffect(() => {
if (ejInstance.current === null) {
initEditor();
}
return () => {
ejInstance?.current?.destroy();
ejInstance.current = null;
};
}, []);
return (
<>
<Card.Root
variant={'subtle'}
width="100%"
maxWidth="840px"
minHeight="sm"
justifyContent="center"
alignItems="center"
>
<Card.Header>
<Heading textAlign="center">Editor</Heading>
</Card.Header>
<Card.Body
width="90%"
css={{
'& *': {
fontSize: 'revert',
fontWeight: 'revert'
},
...editorJsStyles
}}
>
<div className="editorjs" id="editorjs"></div>
</Card.Body>
</Card.Root>
</>
);
}
const editorJsStyles:
| SystemStyleObject
| Omit<Array<SystemStyleObject | undefined>, keyof any[]>
| undefined = {
'& .ce-block--selected .ce-block__content, \
& .ce-inline-toolbar, \
& .codex-editor--narrow .ce-toolbox, \
& .ce-conversion-toolbar, \
& .ce-settings, \
& .ce-settings__button, \
& .ce-toolbar__settings-btn, \
& .cdx-button, \
& .ce-popover': {
background: 'colorPalette.500',
border: 'none'
},
'& .ce-inline-tool, \
& .ce-conversion-toolbar__label, \
& .ce-toolbox__button, \
& .cdx-settings-button, \
& .ce-toolbar__plus': {
color: 'colorPalette.500'
},
'& ::selection': {
background: 'colorPalette.500'
},
'& .cdx-settings-button:hover, \
& .ce-settings__button:hover, \
& .ce-toolbox__button--active, \
& .ce-toolbox__button:hover, \
& .cdx-button:hover, \
& .ce-inline-toolbar__dropdown:hover, \
& .ce-inline-tool:hover, \
& .ce-popover__item:hover, \
& .ce-toolbar__settings-btn:hover': {
background: 'colorPalette.500',
color: 'fg'
},
'& .cdx-notify--error': {
background: 'red !important'
},
'& .cdx-notify__cross::after, & .cdx-notify__cross::before': {
background: 'white'
},
'& .ce-toolbar__plus:hover, \
& .ce-popover__container': {
backgroundColor: 'bg.emphasized',
color: 'colorPalette.500',
border: 'none'
},
'& .ce-popover-item:hover:not(.ce-popover-item--no-hover)': {
backgroundColor: 'colorPalette.500',
color: 'fg'
},
'& .cdx-search-field': {
background: 'bg.muted',
color: 'fg',
border: 'none'
},
'& .ce-popover-item__title, \
& .ce-popover-item__icon': {
color: 'fg'
},
'& .ce-popover-item-separator': {
color: 'bg.muted'
},
'& .ce-toolbar__settings-btn': {
color: 'white'
},
'& .ce-toolbar__settings-btn:hover': {
color: 'black'
}
};

View file

@ -0,0 +1,44 @@
import { ButtonGroup, Pagination as ChakraPagination, IconButton } from '@chakra-ui/react';
import { LuChevronLeft, LuChevronRight } from 'react-icons/lu';
export function Pagination({
total,
page,
setPage,
pageSize = 5
}: {
total: number;
page: number;
setPage: (value: number) => void;
pageSize?: number;
}) {
return (
<ChakraPagination.Root
count={total}
page={page}
pageSize={pageSize}
onPageChange={e => setPage(e.page)}
defaultPage={1}
>
<ButtonGroup variant="ghost" size="sm">
<ChakraPagination.PrevTrigger asChild>
<IconButton>
<LuChevronLeft />
</IconButton>
</ChakraPagination.PrevTrigger>
<ChakraPagination.Items
render={page => (
<IconButton variant={{ base: 'ghost', _selected: 'solid' }}>{page.value}</IconButton>
)}
/>
<ChakraPagination.NextTrigger asChild>
<IconButton>
<LuChevronRight />
</IconButton>
</ChakraPagination.NextTrigger>
</ButtonGroup>
</ChakraPagination.Root>
);
}

View file

@ -0,0 +1,14 @@
import { List } from '@chakra-ui/react';
import { WorkingCardProps, WorkingListCard } from './WorkingListCard';
export function WorkingList({ workingsList }: { workingsList: WorkingCardProps[] }) {
return (
<List.Root variant="plain" gap={4}>
{workingsList.map(workingData => (
<List.Item key={workingData.id}>
<WorkingListCard {...workingData} />
</List.Item>
))}
</List.Root>
);
}

View file

@ -0,0 +1,115 @@
import { Badge, BadgeProps, Box, Card, Heading, Icon, Link, Stack, Text } from '@chakra-ui/react';
import { WorkingProgressStatus } from '@generated/types';
import { ReactNode } from 'react';
import { WorkingProgressStatusBadge } from './WorkingProgressStatusBadge';
import { WorkingRatingBadge } from './WorkingRatingBadge';
export interface WorkingCardProps {
id: string;
title: string;
description: string;
author: { id: string; username: string };
progressStatus: WorkingProgressStatus;
updatedAt: string;
chaptersTotal: number;
wordsTotal: number;
ratingTotal: number;
reviewsTotal: number;
}
export function WorkingListCard({
title,
description,
author,
progressStatus,
updatedAt,
chaptersTotal,
wordsTotal,
ratingTotal,
reviewsTotal
}: WorkingCardProps) {
return (
<Card.Root width="100%" variant="elevated">
<Card.Body>
<Stack>
<Stack direction="row" justifyContent="space-between">
<Link>
<Heading size="lg">{title}</Heading>
</Link>
<Stack direction="row" justifyContent="center" alignItems="center">
<WorkingRatingBadge ratingTotal={ratingTotal} />
<IconBadge backgroundColor="orange.400" iconClass="nf nf-oct-comment">
<Text>{reviewsTotal}</Text>
</IconBadge>
<WorkingProgressStatusBadge progressStatus={progressStatus} />
</Stack>
</Stack>
<Stack>
<Stack direction="row">
<Text>by:</Text>
<Link>{author.username}</Link>
</Stack>
<Stack direction="row">
<Box>
<Badge size="md" variant="solid">
<Stack direction="row">
<Text>last updated:</Text>
<Text>{updatedAt}</Text>
</Stack>
</Badge>
</Box>
<Box>
<Badge size="md" variant="solid">
<Stack direction="row">
<Text>chapters:</Text>
<Text>{chaptersTotal}</Text>
</Stack>
</Badge>
</Box>
<Box>
<Badge size="md" variant="solid">
<Stack direction="row">
<Text>words:</Text>
<Text>{wordsTotal}</Text>
</Stack>
</Badge>
</Box>
</Stack>
</Stack>
<Stack>
<Text lineClamp={4}>{description}</Text>
</Stack>
</Stack>
</Card.Body>
</Card.Root>
);
}
export function IconBadge({
children,
iconClass,
backgroundColor,
size = 'lg'
}: {
children?: ReactNode;
iconClass: string;
backgroundColor?: BadgeProps['backgroundColor'];
size?: 'sm' | 'md' | 'lg' | 'xs';
}) {
return (
<Box flex={1} direction="row" justifyContent="center" alignItems="center">
<Badge variant="solid" size={size} backgroundColor={backgroundColor}>
<Stack direction="row">
<Icon fontSize="xl">
<i className={iconClass} />
</Icon>
{children}
</Stack>
</Badge>
</Box>
);
}

View file

@ -0,0 +1,40 @@
import { Button, Icon, Input, InputGroup, Stack } from '@chakra-ui/react';
export function WorkingListSearch({
value,
onChange,
onSubmit
}: {
value: string;
onChange: (value: string) => void;
onSubmit: (value: string) => void;
}) {
return (
<form
onSubmit={event => {
event.preventDefault();
onSubmit(value);
}}
>
<Stack direction="row">
<InputGroup
flex="1"
startElement={
<Icon color="colorPalette.500">
<i className="nf nf-oct-search" />
</Icon>
}
>
<Input
value={value}
onChange={event => onChange(event.currentTarget.value)}
placeholder="Search works..."
/>
</InputGroup>
<Button type="submit" border="none">
Refresh
</Button>
</Stack>
</form>
);
}

View file

@ -0,0 +1,37 @@
import { Badge, Box, Icon, Stack, Text } from '@chakra-ui/react';
import { WorkingProgressStatus } from '@generated/types';
import {
assertUnreachable,
toBgColorByProgressStatus,
toWorkingProgressStatusLabel
} from '../helpers';
export function WorkingProgressStatusBadge({
progressStatus
}: {
progressStatus: WorkingProgressStatus;
}) {
return (
<Box flex={1} justifyContent="center" alignItems="center">
<Badge
variant="solid"
backgroundColor={toBgColorByProgressStatus(progressStatus)}
size={'lg'}
>
<Stack direction="row">
<Icon fontSize="xl">
<i className={`${toWorkingProgressStatusIcon(progressStatus)}`} />
</Icon>
<Text>{toWorkingProgressStatusLabel(progressStatus)}</Text>
</Stack>
</Badge>
</Box>
);
}
function toWorkingProgressStatusIcon(value: WorkingProgressStatus): string {
if (value === WorkingProgressStatus.InProgress) return 'nf nf-fa-clock';
if (value === WorkingProgressStatus.Frozen) return 'nf nf-fa-snowflake';
if (value === WorkingProgressStatus.Finished) return 'nf nf-fa-circle_check';
assertUnreachable(value);
}

View file

@ -0,0 +1,28 @@
import { BadgeProps, Text } from '@chakra-ui/react';
import { IconBadge } from './WorkingListCard';
interface WorkingRatingParams {
rating: string;
color: BadgeProps['backgroundColor'];
iconClass: string;
}
export function WorkingRatingBadge({ ratingTotal }: { ratingTotal: number }) {
const ratingParams: WorkingRatingParams =
ratingTotal >= 0
? {
rating: ratingTotal.toString(),
color: 'green.500',
iconClass: 'nf nf-fa-angle_up'
}
: {
rating: ratingTotal.toString().slice(1),
color: 'red.500',
iconClass: 'nf nf-fa-angle_down'
};
return (
<IconBadge backgroundColor={ratingParams.color} iconClass={ratingParams.iconClass}>
<Text>{ratingParams.rating}</Text>
</IconBadge>
);
}

View file

@ -15,7 +15,6 @@ export function ColorModeProvider(props: ColorModeProviderProps) {
export function useColorMode() { export function useColorMode() {
const { resolvedTheme, setTheme } = useTheme(); const { resolvedTheme, setTheme } = useTheme();
console.log(resolvedTheme);
const toggleColorMode = () => { const toggleColorMode = () => {
setTheme(resolvedTheme === 'light' ? 'dark' : 'light'); setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
}; };

View file

@ -1,11 +1,16 @@
'use client'; 'use client';
import { ChakraProvider, defaultSystem, Theme } from '@chakra-ui/react'; import { ChakraProvider, createSystem, defaultConfig, Theme } from '@chakra-ui/react';
import { ColorModeProvider, type ColorModeProviderProps } from './color-mode'; import { ColorModeProvider, type ColorModeProviderProps } from './color-mode';
export function Provider(props: ColorModeProviderProps) { export function Provider(props: ColorModeProviderProps) {
const system = createSystem(defaultConfig, {
theme: {
tokens: {}
}
});
return ( return (
<ChakraProvider value={defaultSystem}> <ChakraProvider value={system}>
<Theme colorPalette="cyan"> <Theme colorPalette="cyan">
<ColorModeProvider defaultTheme="dark">{props.children}</ColorModeProvider> <ColorModeProvider defaultTheme="dark">{props.children}</ColorModeProvider>
</Theme> </Theme>

View file

@ -2,6 +2,7 @@ import { gql } from '@apollo/client';
export const sessionFragment = gql` export const sessionFragment = gql`
fragment Session on Session { fragment Session on Session {
userId
accessToken accessToken
} }
`; `;

View file

@ -1 +1,26 @@
import { WorkingProgressStatus } from '@generated/types';
export function assertUnreachable(value: never): never {
throw new Error(`Unhandled scenario: ${value}`);
}
export function toBgColorByProgressStatus(value: WorkingProgressStatus): string {
if (value === WorkingProgressStatus.InProgress) return 'yellow.500';
if (value === WorkingProgressStatus.Frozen) return 'blue.500';
if (value === WorkingProgressStatus.Finished) return 'green.500';
assertUnreachable(value);
}
export function toFgColorByProgressStatus(value: WorkingProgressStatus): string {
if (value === WorkingProgressStatus.InProgress) return 'fg.warning';
if (value === WorkingProgressStatus.Frozen) return 'fg.info';
if (value === WorkingProgressStatus.Finished) return 'fg.success';
assertUnreachable(value);
}
export function toWorkingProgressStatusLabel(value: WorkingProgressStatus): string {
if (value === WorkingProgressStatus.InProgress) return 'In progress';
if (value === WorkingProgressStatus.Frozen) return 'Frozen';
if (value === WorkingProgressStatus.Finished) return 'Finished';
assertUnreachable(value);
}

View file

@ -31,7 +31,7 @@ export function useAuth(): UseAuthOutput {
successMsg: 'Logged out successfully.' successMsg: 'Logged out successfully.'
}); });
if (!authContext) throw new Error('useAuth must be used within an AuthProvider'); if (authContext == null) throw new Error('useAuth must be used within an AuthProvider');
return { return {
...authContext, ...authContext,
@ -45,7 +45,6 @@ export function useAuth(): UseAuthOutput {
} }
} }
}); });
if (data != null) { if (data != null) {
authContext.setHeaders(data.login); authContext.setHeaders(data.login);
return data.login; return data.login;

View file

@ -1,3 +1,52 @@
// interface UseIdentityProps {} import { ApolloError, ApolloQueryResult, gql, OperationVariables, useQuery } from '@apollo/client';
import { Identity, IdentityRootQuery } from '@generated/types';
import { createContext, useContext } from 'react';
// export function useIdentity(props: UseIdentityProps) {} export interface IdentityContext {
data: Identity;
refetch: () => void;
}
export const IdentityContext = createContext<IdentityContext | null>(null);
export const IdentityProvider = IdentityContext.Provider;
export function useIdentity(): IdentityContext | null {
const identityContext = useContext(IdentityContext);
if (identityContext == null) return null;
return identityContext;
}
export function useIdentityQuery({ skip }: { skip?: boolean }): {
data: IdentityRootQuery | undefined;
loading: boolean;
error: ApolloError | undefined;
refetch: (
variables?: Partial<OperationVariables> | undefined
) => Promise<ApolloQueryResult<IdentityRootQuery>>;
} {
const { data, loading, error, refetch } = useQuery<IdentityRootQuery>(query, {
skip
});
return {
data,
loading,
error,
refetch
};
}
const query = gql`
query IdentityRoot {
identity {
id
email
isDarkTheme
user {
id
username
createdAt
updatedAt
}
}
}
`;

View file

@ -0,0 +1,40 @@
import {
ApolloError,
ApolloQueryResult,
MaybeMasked,
OperationVariables,
useQuery
} from '@apollo/client';
import { DocumentNode } from 'graphql';
interface UsePaginatedQueryProps {
query: DocumentNode;
input: {
filter: {
terms?: string | null;
offset?: number | null;
size?: number | null;
};
};
}
interface UsePaginatedQueryReturn<T> {
data?: MaybeMasked<T> | undefined;
loading: boolean;
error: ApolloError | undefined;
refetch: (
variables?: Partial<OperationVariables> | undefined
) => Promise<ApolloQueryResult<MaybeMasked<T>>>;
}
export function usePaginatedQuery<T>(props: UsePaginatedQueryProps): UsePaginatedQueryReturn<T> {
const { data, loading, error, refetch } = useQuery<T>(props.query, {
variables: { input: props.input }
});
return {
data,
loading,
error,
refetch
};
}

View file

@ -0,0 +1,33 @@
import { ApolloError, ApolloQueryResult, gql, OperationVariables, useQuery } from '@apollo/client';
import { UserOneQuery } from '@generated/types';
export function useUserOneQuery({ id, skip }: { id?: string; skip?: boolean }): {
data: UserOneQuery | undefined;
loading: boolean;
error: ApolloError | undefined;
refetch: (
variables?: Partial<OperationVariables> | undefined
) => Promise<ApolloQueryResult<UserOneQuery>>;
} {
const { data, loading, error, refetch } = useQuery<UserOneQuery>(query, {
variables: { id },
skip
});
return {
data,
loading,
error,
refetch
};
}
const query = gql`
query UserOne($id: ID!) {
userOne(id: $id) {
id
username
createdAt
updatedAt
}
}
`;

View file

@ -1,7 +1,7 @@
import { ApolloClient, ApolloProvider, HttpLink, InMemoryCache, from } from '@apollo/client'; import { ApolloClient, ApolloProvider, HttpLink, InMemoryCache, from } from '@apollo/client';
import { loadDevMessages, loadErrorMessages } from '@apollo/client/dev'; import { loadDevMessages, loadErrorMessages } from '@apollo/client/dev';
import { onError } from '@apollo/client/link/error'; import { onError } from '@apollo/client/link/error';
import { Skeleton } from '@chakra-ui/react'; import { Box, Center, Spinner } from '@chakra-ui/react';
import '@client/assets/styles/index.css'; import '@client/assets/styles/index.css';
import '@client/assets/styles/nerd-fonts-icons.css'; import '@client/assets/styles/nerd-fonts-icons.css';
import { Provider as ChakraProvider } from '@client/components/ui/provider'; import { Provider as ChakraProvider } from '@client/components/ui/provider';
@ -10,6 +10,7 @@ import { RouterProvider, createRouter } from '@tanstack/react-router';
import { StrictMode, useEffect, useState } from 'react'; import { StrictMode, useEffect, useState } from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import { AuthProvider, useRestAuth } from './hooks/auth'; import { AuthProvider, useRestAuth } from './hooks/auth';
import { IdentityProvider, useIdentityQuery } from './hooks/identity';
// TanStackRouter - Import the generated route tree // TanStackRouter - Import the generated route tree
import { Session } from '@generated/types'; import { Session } from '@generated/types';
@ -59,7 +60,11 @@ function App() {
return ( return (
<StrictMode> <StrictMode>
<ChakraProvider> <ChakraProvider>
<Skeleton /> <Box pos="absolute" inset="0">
<Center h="full">
<Spinner color="colorPalette.500" />
</Center>
</Box>
</ChakraProvider> </ChakraProvider>
</StrictMode> </StrictMode>
); );
@ -113,7 +118,7 @@ function App() {
<ApolloProvider client={client}> <ApolloProvider client={client}>
<ChakraProvider> <ChakraProvider>
<Toaster /> <Toaster />
<RouterProvider router={router} context={{ isAuthenticated: authHeaders != null }} /> <RouterProviderWrapper authHeaders={authHeaders} />
</ChakraProvider> </ChakraProvider>
</ApolloProvider> </ApolloProvider>
</AuthProvider> </AuthProvider>
@ -121,6 +126,28 @@ function App() {
); );
} }
function RouterProviderWrapper({ authHeaders }: { authHeaders: Session | null }) {
const userId = authHeaders?.userId;
const { data, loading, refetch } = useIdentityQuery({ skip: userId == null });
if (loading)
return (
<Box pos="absolute" inset="0">
<Center h="full">
<Spinner color="colorPalette.500" />
</Center>
</Box>
);
return (
<IdentityProvider
value={userId != null && data != null ? { data: data.identity, refetch } : null}
>
<RouterProvider router={router} context={{ isAuthenticated: authHeaders != null }} />
</IdentityProvider>
);
}
const rootElement = document.getElementById('root')!; const rootElement = document.getElementById('root')!;
if (!rootElement.innerHTML) { if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement); const root = ReactDOM.createRoot(rootElement);

View file

@ -1,12 +1,13 @@
import { Separator, Icon, Menu, Stack, Text } from '@chakra-ui/react'; import { Icon, Input, InputGroup, Kbd, Menu, Separator, Stack, Text } from '@chakra-ui/react';
import { createRootRouteWithContext, Link, Outlet } from '@tanstack/react-router'; import { createRootRouteWithContext, Link, Outlet } from '@tanstack/react-router';
import React from 'react'; import React from 'react';
import { Avatar } from '../components/ui/avatar'; import { Avatar } from '../components/ui/avatar';
import { Button } from '../components/ui/button'; import { Button } from '../components/ui/button';
import { ColorModeButton } from '../components/ui/color-mode';
import { Tooltip } from '../components/ui/tooltip'; import { Tooltip } from '../components/ui/tooltip';
import { useAuth } from '../hooks/auth'; import { useAuth } from '../hooks/auth';
import { useIdentity } from '../hooks/identity';
import { appName } from '../main'; import { appName } from '../main';
import { ColorModeButton } from '../components/ui/color-mode';
const TanStackRouterDevtools = const TanStackRouterDevtools =
process.env.NODE_ENV === 'production' process.env.NODE_ENV === 'production'
@ -26,48 +27,82 @@ interface RouterContext {
export const Route = createRootRouteWithContext<RouterContext>()({ export const Route = createRootRouteWithContext<RouterContext>()({
component: () => { component: () => {
const { isAuthenticated, logout } = useAuth(); const { logout } = useAuth();
const identity = useIdentity();
return ( return (
<Stack padding={3}> <Stack padding={3}>
<Stack flexDirection="row" justifyContent="space-between" alignItems="center"> <Stack flexDirection="row" justifyContent="space-between" alignItems="center">
<Stack flexDirection="row" alignItems="center" gap={4}> <Stack flexDirection="row" justifyContent="center" alignItems="center" gap={4}>
<Link to="/"> <Link to="/">
<Text color="colorPalette.500" fontSize="xl"> <Stack direction="row" justifyContent="center" alignItems="center" gap={1}>
[{appName}] <Icon fontSize={24} color="colorPalette.500">
</Text> <i className="nf nf-md-bird" />
</Link> </Icon>
<Link to="/about"> <Text fontSize="lg" fontWeight="bold">
<Icon color="colorPalette.500" fontSize="md"> {appName}
<i className="nf nf-oct-question" /> </Text>
</Icon> </Stack>
</Link> </Link>
{/* <Stack hideBelow="md"> */}
{/* <Link to="/about"> */}
{/* <Icon color="colorPalette.500" fontSize="md"> */}
{/* <i className="nf nf-oct-question" /> */}
{/* </Icon> */}
{/* </Link> */}
{/* </Stack> */}
</Stack> </Stack>
<Stack flexDirection="row" alignItems="center" gap={4}> <Stack flexDirection="row" alignItems="center" gap={4}>
<InputGroup
hideBelow="md"
flex={1}
alignItems="center"
startElement={
<Icon color="colorPalette.500">
<i className="nf nf-oct-search" />
</Icon>
}
endElement={<Kbd>⌘K</Kbd>}
>
<Input height={8} placeholder="Search works..." />
</InputGroup>
<ColorModeButton /> <ColorModeButton />
{isAuthenticated ? ( {identity != null ? (
<Menu.Root> <Menu.Root>
<Menu.Trigger> <Menu.Trigger css={{ '&:hover': { cursor: 'pointer' } }}>
<Avatar <Avatar
// src={'https://avatars.githubusercontent.com/u/40203084?v=4'} size="sm"
fallback={'A'} // src="https://avatars.githubusercontent.com/u/40203084?v=4"
/> />
</Menu.Trigger> </Menu.Trigger>
<Menu.Positioner mt={2}> <Menu.Positioner mt={2}>
<Menu.Content> <Menu.Content backgroundColor={'bg.subtle'}>
<> <Link to={`/profile/${identity.data.user.username}`}>
<Menu.Item value="logout" onClick={async () => logout()}> <Menu.Item value="/">
<Tooltip content="Log out"> <Tooltip content="My profile">
<Stack direction="row"> <Stack direction="row">
<Icon color="colorPalette.500" fontSize="xl"> <Icon color="colorPalette.500" fontSize="xl">
<i className="nf nf-fa-sign_out" /> <i className="nf nf-fa-user" />
</Icon> </Icon>
Log out My profile
</Stack> </Stack>
</Tooltip> </Tooltip>
</Menu.Item> </Menu.Item>
</> </Link>
<Menu.Item value="logout" onClick={async () => logout()}>
<Tooltip content="Log out">
<Stack direction="row">
<Icon color="colorPalette.500" fontSize="xl">
<i className="nf nf-fa-sign_out" />
</Icon>
Log out
</Stack>
</Tooltip>
</Menu.Item>
</Menu.Content> </Menu.Content>
</Menu.Positioner> </Menu.Positioner>
</Menu.Root> </Menu.Root>
@ -75,30 +110,13 @@ export const Route = createRootRouteWithContext<RouterContext>()({
<> <>
<Separator orientation="vertical" height={5} /> <Separator orientation="vertical" height={5} />
<Link to="/login"> <Link to="/login">
<Button>Sign in</Button> <Button height={8}>Sign in</Button>
</Link> </Link>
<Link to="/register"> <Link to="/register">
<Button>Sign up</Button> <Button height={8}>Sign up</Button>
</Link> </Link>
</> </>
)} )}
{/* {!isAuthenticated ? (
<>
<Link to="/login">
<Button>Sign in</Button>
</Link>
<Link to="/register">
<Button>Sign up</Button>
</Link>
</>
) : (
<Tooltip content="Log out">
<Icon color="colorPalette.500" fontSize="xl" onClick={async () => logout()}>
<i className="nf nf-fa-sign_out" />
</Icon>
</Tooltip>
)} */}
</Stack> </Stack>
</Stack> </Stack>
<Separator style={{ padding: '4px' }} /> <Separator style={{ padding: '4px' }} />

View file

@ -0,0 +1,15 @@
import { Stack } from '@chakra-ui/react';
import { createFileRoute } from '@tanstack/react-router';
import { Editor } from '../components/Editor';
export const Route = createFileRoute('/editor')({
component: RouteComponent
});
function RouteComponent() {
return (
<Stack justifyContent="center" alignItems="center">
<Editor />
</Stack>
);
}

View file

@ -0,0 +1,46 @@
import { Box, Button, Stack, Text } from '@chakra-ui/react'
import { createFileRoute } from '@tanstack/react-router'
import { gql, useQuery } from '@apollo/client'
import { BooksManyQuery } from '@generated/types'
export const Route = createFileRoute('/example')({
component: Dashboard,
})
function Dashboard() {
const { data, refetch } = useQuery<BooksManyQuery>(getBooksQuery)
return (
<Stack>
<Text>Hello world!</Text>
<Text>This is my template!</Text>
<Stack>
{data && (
<>
{data.booksMany.map((book: any) => (
<Stack key={book.id}>
<Text>{book.title}</Text>
<Text>{book.author}</Text>
</Stack>
))}
</>
)}
<Box>
<Button onClick={async () => refetch()}>Refetch</Button>
</Box>
</Stack>
</Stack>
)
}
const getBooksQuery = gql`
query BooksMany {
booksMany {
id
author
title
}
}
`

View file

@ -1,46 +1,139 @@
import { Box, Button, Stack, Text } from '@chakra-ui/react'; import { gql } from '@apollo/client';
import { Skeleton, Stack } from '@chakra-ui/react';
import { WorkingListQuery } from '@generated/types';
import { createFileRoute } from '@tanstack/react-router'; import { createFileRoute } from '@tanstack/react-router';
import { useState } from 'react';
import { gql, useQuery } from '@apollo/client'; import { Pagination } from '../components/Pagination';
import { WorkingList } from '../components/WorkingList';
import { BooksManyQuery } from '@generated/types'; import { WorkingListSearch } from '../components/WorkingListSearch';
import { usePaginatedQuery } from '../hooks/usePaginatedQuery';
export const Route = createFileRoute('/')({ export const Route = createFileRoute('/')({
component: Dashboard component: RouteComponent
}); });
function Dashboard() { function RouteComponent() {
const { data, refetch } = useQuery<BooksManyQuery>(getBooksQuery); const [workingSearchValue, setWorkingSearchValue] = useState<string>('');
const [workingsListPage, setWorkingsListPage] = useState<number>(1);
const [workingsListPageSize, setWorkingsListPageSize] = useState<number>(2);
const startIndex = (workingsListPage - 1) * workingsListPageSize;
const { data } = usePaginatedQuery<WorkingListQuery>({
query,
input: {
filter: {
terms: workingSearchValue,
offset: startIndex,
size: workingsListPageSize
}
}
});
return ( return (
<Stack> <Stack justifyContent="center" alignItems="center">
<Text>Hello world!</Text> <Stack width="90%">
<Text>This is my template!</Text> <WorkingListSearch
<Stack> value={workingSearchValue}
{data && ( onSubmit={value => {
alert(value);
}}
onChange={value => setWorkingSearchValue(value)}
/>
{data != null ? (
<> <>
{data.booksMany.map((book: any) => ( <WorkingList
<Stack key={book.id}> workingsList={data.workingMany.items.map(item => ({
<Text>{book.title}</Text> ...item,
<Text>{book.author}</Text> chaptersTotal: item.chapters.total,
</Stack> reviewsTotal: item.reviews.total
))} }))}
/>
<Pagination
total={data.workingMany.total}
page={workingsListPage}
setPage={setWorkingsListPage}
pageSize={workingsListPageSize}
/>
</> </>
) : (
<Skeleton />
)} )}
<Box>
<Button onClick={async () => refetch()}>Refetch</Button>
</Box>
</Stack> </Stack>
</Stack> </Stack>
); );
} }
const getBooksQuery = gql` const query = gql`
query BooksMany { query WorkingList($input: WorkingManyInput!) {
booksMany { workingMany(input: $input) {
id items {
author id
title title
description
author {
id
username
}
progressStatus
updatedAt
chapters {
total
}
wordsTotal
ratingTotal
reviews {
total
}
}
total
} }
} }
`; `;
// const workingData1: WorkingCardProps = {
// id: '1',
// title: 'Worth the Candle',
// description: `A teenager struggling after the death of his best friend finds himself in a fantasy
// world - one which seems to be an amalgamation of every Dungeons and Dragons campaign
// they ever played together. Now he's stuck trying to find the answers to why he's there
// and what this world is trying to say. The most terrifying answer might be that this
// world is an expression of the person he was back on Earth.`,
// author: {
// id: '1',
// createdAt: '12.8.2025',
// updatedAt: '4.9.2025',
// username: 'AlexanderWhales'
// },
// progressStatus: WorkingProgressStatus.Finished,
// updatedAt: '20.10.2025',
// chaptersTotal: 10,
// wordsTotal: 34000,
// ratingTotal: 10,
// reviewsTotal: 3
// };
//
// const workingData2: WorkingCardProps = {
// id: '2',
// title: 'Test',
// description: `Description.`,
// author: {
// id: '1',
// createdAt: '12.8.2025',
// updatedAt: '4.9.2025',
// username: 'JohnSmith'
// },
// progressStatus: WorkingProgressStatus.InProgress,
// updatedAt: '20.10.2025',
// chaptersTotal: 10,
// wordsTotal: 1200,
// ratingTotal: -10,
// reviewsTotal: 0
// };
//
// const mockWorkingData = {
// total: 4,
// items: [workingData1, workingData2, { ...workingData1, id: '3' }, { ...workingData2, id: '4' }]
// };
//

View file

@ -0,0 +1,10 @@
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/profile/$username')({
component: RouteComponent
});
function RouteComponent() {
const { username } = Route.useParams();
return <div>{`Hello, "${username}"!`}</div>;
}

View file

@ -15,8 +15,11 @@ import { createFileRoute } from '@tanstack/react-router'
import { Route as rootRoute } from './pages/__root' import { Route as rootRoute } from './pages/__root'
import { Route as RegisterImport } from './pages/register' import { Route as RegisterImport } from './pages/register'
import { Route as LoginImport } from './pages/login' import { Route as LoginImport } from './pages/login'
import { Route as ExampleImport } from './pages/example'
import { Route as EditorImport } from './pages/editor'
import { Route as AboutImport } from './pages/about' import { Route as AboutImport } from './pages/about'
import { Route as IndexImport } from './pages/index' import { Route as IndexImport } from './pages/index'
import { Route as ProfileUsernameImport } from './pages/profile/$username'
// Create Virtual Routes // Create Virtual Routes
@ -42,6 +45,18 @@ const LoginRoute = LoginImport.update({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,
} as any) } as any)
const ExampleRoute = ExampleImport.update({
id: '/example',
path: '/example',
getParentRoute: () => rootRoute,
} as any)
const EditorRoute = EditorImport.update({
id: '/editor',
path: '/editor',
getParentRoute: () => rootRoute,
} as any)
const AboutRoute = AboutImport.update({ const AboutRoute = AboutImport.update({
id: '/about', id: '/about',
path: '/about', path: '/about',
@ -54,6 +69,12 @@ const IndexRoute = IndexImport.update({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,
} as any) } as any)
const ProfileUsernameRoute = ProfileUsernameImport.update({
id: '/profile/$username',
path: '/profile/$username',
getParentRoute: () => rootRoute,
} as any)
// Populate the FileRoutesByPath interface // Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' { declare module '@tanstack/react-router' {
@ -72,6 +93,20 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AboutImport preLoaderRoute: typeof AboutImport
parentRoute: typeof rootRoute parentRoute: typeof rootRoute
} }
'/editor': {
id: '/editor'
path: '/editor'
fullPath: '/editor'
preLoaderRoute: typeof EditorImport
parentRoute: typeof rootRoute
}
'/example': {
id: '/example'
path: '/example'
fullPath: '/example'
preLoaderRoute: typeof ExampleImport
parentRoute: typeof rootRoute
}
'/login': { '/login': {
id: '/login' id: '/login'
path: '/login' path: '/login'
@ -93,6 +128,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof BookLazyImport preLoaderRoute: typeof BookLazyImport
parentRoute: typeof rootRoute parentRoute: typeof rootRoute
} }
'/profile/$username': {
id: '/profile/$username'
path: '/profile/$username'
fullPath: '/profile/$username'
preLoaderRoute: typeof ProfileUsernameImport
parentRoute: typeof rootRoute
}
} }
} }
@ -101,51 +143,91 @@ declare module '@tanstack/react-router' {
export interface FileRoutesByFullPath { export interface FileRoutesByFullPath {
'/': typeof IndexRoute '/': typeof IndexRoute
'/about': typeof AboutRoute '/about': typeof AboutRoute
'/editor': typeof EditorRoute
'/example': typeof ExampleRoute
'/login': typeof LoginRoute '/login': typeof LoginRoute
'/register': typeof RegisterRoute '/register': typeof RegisterRoute
'/book': typeof BookLazyRoute '/book': typeof BookLazyRoute
'/profile/$username': typeof ProfileUsernameRoute
} }
export interface FileRoutesByTo { export interface FileRoutesByTo {
'/': typeof IndexRoute '/': typeof IndexRoute
'/about': typeof AboutRoute '/about': typeof AboutRoute
'/editor': typeof EditorRoute
'/example': typeof ExampleRoute
'/login': typeof LoginRoute '/login': typeof LoginRoute
'/register': typeof RegisterRoute '/register': typeof RegisterRoute
'/book': typeof BookLazyRoute '/book': typeof BookLazyRoute
'/profile/$username': typeof ProfileUsernameRoute
} }
export interface FileRoutesById { export interface FileRoutesById {
__root__: typeof rootRoute __root__: typeof rootRoute
'/': typeof IndexRoute '/': typeof IndexRoute
'/about': typeof AboutRoute '/about': typeof AboutRoute
'/editor': typeof EditorRoute
'/example': typeof ExampleRoute
'/login': typeof LoginRoute '/login': typeof LoginRoute
'/register': typeof RegisterRoute '/register': typeof RegisterRoute
'/book': typeof BookLazyRoute '/book': typeof BookLazyRoute
'/profile/$username': typeof ProfileUsernameRoute
} }
export interface FileRouteTypes { export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/about' | '/login' | '/register' | '/book' fullPaths:
| '/'
| '/about'
| '/editor'
| '/example'
| '/login'
| '/register'
| '/book'
| '/profile/$username'
fileRoutesByTo: FileRoutesByTo fileRoutesByTo: FileRoutesByTo
to: '/' | '/about' | '/login' | '/register' | '/book' to:
id: '__root__' | '/' | '/about' | '/login' | '/register' | '/book' | '/'
| '/about'
| '/editor'
| '/example'
| '/login'
| '/register'
| '/book'
| '/profile/$username'
id:
| '__root__'
| '/'
| '/about'
| '/editor'
| '/example'
| '/login'
| '/register'
| '/book'
| '/profile/$username'
fileRoutesById: FileRoutesById fileRoutesById: FileRoutesById
} }
export interface RootRouteChildren { export interface RootRouteChildren {
IndexRoute: typeof IndexRoute IndexRoute: typeof IndexRoute
AboutRoute: typeof AboutRoute AboutRoute: typeof AboutRoute
EditorRoute: typeof EditorRoute
ExampleRoute: typeof ExampleRoute
LoginRoute: typeof LoginRoute LoginRoute: typeof LoginRoute
RegisterRoute: typeof RegisterRoute RegisterRoute: typeof RegisterRoute
BookLazyRoute: typeof BookLazyRoute BookLazyRoute: typeof BookLazyRoute
ProfileUsernameRoute: typeof ProfileUsernameRoute
} }
const rootRouteChildren: RootRouteChildren = { const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute, IndexRoute: IndexRoute,
AboutRoute: AboutRoute, AboutRoute: AboutRoute,
EditorRoute: EditorRoute,
ExampleRoute: ExampleRoute,
LoginRoute: LoginRoute, LoginRoute: LoginRoute,
RegisterRoute: RegisterRoute, RegisterRoute: RegisterRoute,
BookLazyRoute: BookLazyRoute, BookLazyRoute: BookLazyRoute,
ProfileUsernameRoute: ProfileUsernameRoute,
} }
export const routeTree = rootRoute export const routeTree = rootRoute
@ -160,9 +242,12 @@ export const routeTree = rootRoute
"children": [ "children": [
"/", "/",
"/about", "/about",
"/editor",
"/example",
"/login", "/login",
"/register", "/register",
"/book" "/book",
"/profile/$username"
] ]
}, },
"/": { "/": {
@ -171,6 +256,12 @@ export const routeTree = rootRoute
"/about": { "/about": {
"filePath": "about.tsx" "filePath": "about.tsx"
}, },
"/editor": {
"filePath": "editor.tsx"
},
"/example": {
"filePath": "example.tsx"
},
"/login": { "/login": {
"filePath": "login.tsx" "filePath": "login.tsx"
}, },
@ -179,6 +270,9 @@ export const routeTree = rootRoute
}, },
"/book": { "/book": {
"filePath": "book.lazy.tsx" "filePath": "book.lazy.tsx"
},
"/profile/$username": {
"filePath": "profile/$username.tsx"
} }
} }
} }

3
app/common.ts Normal file
View file

@ -0,0 +1,3 @@
export function call<T>(fn: () => T) {
return fn();
}

View file

@ -43,7 +43,7 @@ export async function refreshTokenController(req: Request, res: Response) {
expiresIn: config.accessToken.expiresIn expiresIn: config.accessToken.expiresIn
}); });
const session: Session = { accessToken }; const session: Session = { userId: user.id, accessToken };
res.json(session); res.json(session);
} catch (error) { } catch (error) {
console.error(error); console.error(error);

View file

@ -2,17 +2,21 @@
import { MutationResolvers, QueryResolvers } from '@generated/types'; import { MutationResolvers, QueryResolvers } from '@generated/types';
/* Query */ /* Query */
import { BooksManyResolver } from './resolvers/booksManyResolver';
import { BookOneResolver } from './resolvers/bookOneResolver'; import { BookOneResolver } from './resolvers/bookOneResolver';
import { BooksManyResolver } from './resolvers/booksManyResolver';
import { IdentityResolver } from './resolvers/identityResolver';
import { UserOneResolver } from './resolvers/userOneResolver';
/* Mutation */ /* Mutation */
import { SignUpResolver } from './resolvers/auth/signUp';
import { LoginResolver } from './resolvers/auth/login'; import { LoginResolver } from './resolvers/auth/login';
import { LogoutResolver } from './resolvers/auth/logout'; import { LogoutResolver } from './resolvers/auth/logout';
import { SignUpResolver } from './resolvers/auth/signUp';
export const Query: QueryResolvers = { export const Query: QueryResolvers = {
booksMany: BooksManyResolver, booksMany: BooksManyResolver,
bookOne: BookOneResolver bookOne: BookOneResolver,
identity: IdentityResolver,
userOne: UserOneResolver
}; };
export const Mutation: MutationResolvers = { export const Mutation: MutationResolvers = {

View file

@ -40,6 +40,7 @@ export async function LoginResolver(
}); });
return { return {
userId: user.id,
accessToken accessToken
}; };
} }

View file

@ -0,0 +1,14 @@
import { IdentityContext } from '@app/server/identity';
import { prisma } from '@app/server/prisma/client';
import { Identity } from '@generated/types';
import { toIdentitySchema } from './mappers/identityMapper';
export async function IdentityResolver(
_: any,
__: any,
{ identity }: IdentityContext
): Promise<Identity> {
if (identity == null) throw new Error('Unauthorized.');
const model = await prisma.user.findUniqueOrThrow({ where: { id: identity.id } });
return toIdentitySchema(model);
}

View file

@ -0,0 +1,12 @@
import { Identity as IdentitySchema } from '@generated/types';
import { User } from '@prisma/client';
import { toUserSchema } from './userMapper';
export async function toIdentitySchema(model: User): Promise<IdentitySchema> {
return {
id: model.id,
email: model.email,
isDarkTheme: true,
user: await toUserSchema(model)
};
}

View file

@ -0,0 +1,11 @@
import { User as UserSchema } from '@generated/types';
import { User } from '@prisma/client';
export async function toUserSchema(model: User): Promise<UserSchema> {
return {
id: model.id,
username: model.username,
createdAt: model.createdAt.toISOString(),
updatedAt: model.updatedAt.toISOString()
};
}

View file

@ -0,0 +1,13 @@
import { IdentityContext } from '@app/server/identity';
import { prisma } from '@app/server/prisma/client';
import { QueryUserOneArgs, User } from '@generated/types';
import { toUserSchema } from './mappers/userMapper';
export async function UserOneResolver(
_: any,
{ id }: QueryUserOneArgs,
{ identity }: IdentityContext
): Promise<User> {
const model = await prisma.user.findUniqueOrThrow({ where: { id } });
return toUserSchema(model);
}

View file

@ -3,6 +3,9 @@ scalar Void
type Query { type Query {
booksMany: [Book!]! booksMany: [Book!]!
bookOne(id: ID!): Book! bookOne(id: ID!): Book!
identity: Identity!
userOne(id: ID!): User!
workingMany(input: WorkingManyInput!): WorkingManyOutput!
} }
type Mutation { type Mutation {
@ -23,11 +26,89 @@ input LoginInput {
} }
type Session { type Session {
userId: ID!
accessToken: String! accessToken: String!
} }
type Identity {
id: ID!
email: String!
isDarkTheme: Boolean!
user: User!
}
type User {
id: ID!
username: String!
createdAt: String!
updatedAt: String!
}
type Book { type Book {
id: ID! id: ID!
title: String! title: String!
author: String! author: String!
} }
input Working
input PaginationFilterInput {
terms: String
offset: Int
size: Int
}
input WorkingManyInput {
filter: PaginationFilterInput!
}
enum WorkingProgressStatus {
inProgress
frozen
finished
}
type WorkingManyOutput {
items: [Working!]!
total: Int!
}
type Working {
id: ID!
title: String!
description: String!
author: User!
createdAt: String!
updatedAt: String!
progressStatus: WorkingProgressStatus!
wordsTotal: Int!
ratingTotal: Int!
chapters: ChapterManyOutput!
reviews: ReviewManyOutput!
}
type ChapterManyOutput {
items: [Chapter!]!
total: Int!
}
type Chapter {
id: ID!
title: String!
createdAt: String!
updatedAt: String!
foreword: String!
footnote: String!
}
type ReviewManyOutput {
items: [Review!]!
total: Int!
}
type Review {
id: ID!
author: User!
text: String!
ratingTotal: Int!
}

View file

@ -1,16 +1,17 @@
import { Session } from '@generated/types'; import { Session } from '@generated/types';
import 'dotenv/config'; import 'dotenv/config';
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { GraphQLError } from 'graphql';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { config } from './main'; import { config } from './main';
import { prisma } from './prisma/client'; import { prisma } from './prisma/client';
import { GraphQLError } from 'graphql';
export interface IdentityContext { export interface IdentityContext {
req: Request; req: Request;
res: Response; res: Response;
identity?: { identity?: {
id: string; id: string;
email: string;
username: string; username: string;
}; };
} }

View file

@ -10,12 +10,14 @@ datasource db {
model User { model User {
id String @id id String @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String @unique @db.VarChar(255) email String @unique @db.VarChar(255)
username String @unique @db.VarChar(255) username String @unique @db.VarChar(255)
password String password String
refreshToken String? @unique @db.VarChar(255) refreshToken String? @unique @db.VarChar(255)
createdAt DateTime @default(now()) isDarkMode Boolean @default(true)
updatedAt DateTime @updatedAt
} }

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/ln-logo.svg" /> <link rel="icon" type="image/svg+xml" href="/ln-logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ln</title> <title>echoes</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

476
package-lock.json generated
View file

@ -12,13 +12,18 @@
"@apollo/server": "^4.11.2", "@apollo/server": "^4.11.2",
"@chakra-ui/react": "^3.15.0", "@chakra-ui/react": "^3.15.0",
"@chakra-ui/toast": "^7.0.2", "@chakra-ui/toast": "^7.0.2",
"@coolbytes/editorjs-link": "^1.0.0",
"@deftomat/opinionated": "^1.1.3", "@deftomat/opinionated": "^1.1.3",
"@editorjs/editorjs": "^2.30.8",
"@editorjs/header": "^2.8.8",
"@editorjs/link": "^2.6.2",
"@editorjs/list": "^2.0.8",
"@editorjs/quote": "^2.7.6",
"@emotion/react": "^11.13.5", "@emotion/react": "^11.13.5",
"@hookform/resolvers": "^3.9.1", "@hookform/resolvers": "^3.9.1",
"@prisma/client": "^6.4.1", "@prisma/client": "^6.4.1",
"@tanstack/react-router": "^1.85.0", "@tanstack/react-router": "^1.85.0",
"@tanstack/router-vite-plugin": "^1.84.4", "@tanstack/router-vite-plugin": "^1.84.4",
"@types/cors": "^2.8.17",
"apollo-link-logger": "^2.0.1", "apollo-link-logger": "^2.0.1",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"cookie-parser": "^1.4.7", "cookie-parser": "^1.4.7",
@ -49,6 +54,7 @@
"@tanstack/router-plugin": "^1.84.4", "@tanstack/router-plugin": "^1.84.4",
"@types/bcrypt": "^5.0.2", "@types/bcrypt": "^5.0.2",
"@types/cookie-parser": "^1.4.8", "@types/cookie-parser": "^1.4.8",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.9", "@types/jsonwebtoken": "^9.0.9",
"@types/node": "^20.9.3", "@types/node": "^20.9.3",
@ -1807,6 +1813,18 @@
"sisteransi": "^1.0.5" "sisteransi": "^1.0.5"
} }
}, },
"node_modules/@codexteam/icons": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.5.tgz",
"integrity": "sha512-s6H2KXhLz2rgbMZSkRm8dsMJvyUNZsEjxobBEg9ztdrb1B2H3pEzY6iTwI4XUPJWJ3c3qRKwV4TrO3J5jUdoQA==",
"license": "MIT"
},
"node_modules/@coolbytes/editorjs-link": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@coolbytes/editorjs-link/-/editorjs-link-1.0.0.tgz",
"integrity": "sha512-OiZL+l2TC+Urt9jn6hULG/yUMfB1F+M4GUoZv8naCinLs4frB50QpkWp8oNOuiJUBZW4la5uquQQpcYYxfd/Eg==",
"license": "MIT"
},
"node_modules/@deftomat/opinionated": { "node_modules/@deftomat/opinionated": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/@deftomat/opinionated/-/opinionated-1.1.3.tgz", "resolved": "https://registry.npmjs.org/@deftomat/opinionated/-/opinionated-1.1.3.tgz",
@ -1894,9 +1912,9 @@
} }
}, },
"node_modules/@deftomat/opinionated/node_modules/glob": { "node_modules/@deftomat/opinionated/node_modules/glob": {
"version": "11.0.1", "version": "11.0.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz",
"integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"foreground-child": "^3.1.0", "foreground-child": "^3.1.0",
@ -2257,6 +2275,84 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/@editorjs/dom": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/@editorjs/dom/-/dom-0.0.5.tgz",
"integrity": "sha512-SZ78Gwpkp3EUhjBIp0lSojeQ35V9acF8SubJsMeOH/vlOUE40GOnvvwWZnF05lO7bIB0dOHhhJy4N7IIAWxP2w==",
"license": "MIT",
"dependencies": {
"@editorjs/helpers": "^0.0.4"
}
},
"node_modules/@editorjs/editorjs": {
"version": "2.30.8",
"resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.30.8.tgz",
"integrity": "sha512-ClFuxI1qZTfXPJTacQfsJtOUP6bKoIe6BQNdAvGsDTDVwMnZEzoaSOwvUpdZEE56xppVfQueNK/1MElV9SJKHg==",
"license": "Apache-2.0"
},
"node_modules/@editorjs/header": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/@editorjs/header/-/header-2.8.8.tgz",
"integrity": "sha512-bsMSs34u2hoi0UBuRoc5EGWXIFzJiwYgkFUYQGVm63y5FU+s8zPBmVx5Ip2sw1xgs0fqfDROqmteMvvmbCy62w==",
"license": "MIT",
"dependencies": {
"@codexteam/icons": "^0.0.5",
"@editorjs/editorjs": "^2.29.1"
}
},
"node_modules/@editorjs/helpers": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/@editorjs/helpers/-/helpers-0.0.4.tgz",
"integrity": "sha512-ieg3dzo2m1/ELze/RMNADiAiC5amXxIlVXoJ5vvXITOu/p/dPsrF+Oi3h5gBYvtGk9vg5LJUSG5YWU0tBUO1tw==",
"license": "MIT"
},
"node_modules/@editorjs/link": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/@editorjs/link/-/link-2.6.2.tgz",
"integrity": "sha512-3cPx6M4ZvwDDvsi0E0fvMR3rvveAV/C0GRo1JLeZJ9cG9QgyoNolj4eu5Eqx3/r1XTC/he54qYEIZ/Dc4Lr4Ow==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.10.2",
"@codexteam/icons": "^0.0.4"
}
},
"node_modules/@editorjs/link/node_modules/@codexteam/icons": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.4.tgz",
"integrity": "sha512-V8N/TY2TGyas4wLrPIFq7bcow68b3gu8DfDt1+rrHPtXxcexadKauRJL6eQgfG7Z0LCrN4boLRawR4S9gjIh/Q==",
"license": "MIT"
},
"node_modules/@editorjs/list": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/@editorjs/list/-/list-2.0.8.tgz",
"integrity": "sha512-/EJNvpeJYa1YDtYa85ug9R3sbDyI5ZlMHQ2bIh5S9iPa19qXe45jk6kgcew+jMNQq3j4NS4Q6YeeFbU1QkNMWw==",
"license": "MIT",
"dependencies": {
"@codexteam/icons": "^0.3.2"
}
},
"node_modules/@editorjs/list/node_modules/@codexteam/icons": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.3.3.tgz",
"integrity": "sha512-cp7mkZPgmBuSxigTm3Vb+DtVHYeX7qXfQd7o05vcLD8Ag5WvRlol2QSn5P10k0CDAJwmkH9nQGQLBycErS9lsQ==",
"license": "MIT"
},
"node_modules/@editorjs/quote": {
"version": "2.7.6",
"resolved": "https://registry.npmjs.org/@editorjs/quote/-/quote-2.7.6.tgz",
"integrity": "sha512-D01KUMSDj2r+6Z+xjDkQqI+y6URpeHCvj0+P4pah+GtkG040lWjFb2H4pgHFXuol2cbfyAoraYSw85fuPheCvw==",
"license": "MIT",
"dependencies": {
"@codexteam/icons": "^0.3.2",
"@editorjs/dom": "^0.0.5"
}
},
"node_modules/@editorjs/quote/node_modules/@codexteam/icons": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.3.3.tgz",
"integrity": "sha512-cp7mkZPgmBuSxigTm3Vb+DtVHYeX7qXfQd7o05vcLD8Ag5WvRlol2QSn5P10k0CDAJwmkH9nQGQLBycErS9lsQ==",
"license": "MIT"
},
"node_modules/@emotion/babel-plugin": { "node_modules/@emotion/babel-plugin": {
"version": "11.13.5", "version": "11.13.5",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
@ -2411,9 +2507,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz",
"integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@ -2427,9 +2523,9 @@
} }
}, },
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz",
"integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -2443,9 +2539,9 @@
} }
}, },
"node_modules/@esbuild/android-arm64": { "node_modules/@esbuild/android-arm64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz",
"integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2459,9 +2555,9 @@
} }
}, },
"node_modules/@esbuild/android-x64": { "node_modules/@esbuild/android-x64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz",
"integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2475,9 +2571,9 @@
} }
}, },
"node_modules/@esbuild/darwin-arm64": { "node_modules/@esbuild/darwin-arm64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz",
"integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2491,9 +2587,9 @@
} }
}, },
"node_modules/@esbuild/darwin-x64": { "node_modules/@esbuild/darwin-x64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz",
"integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2507,9 +2603,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-arm64": { "node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz",
"integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2523,9 +2619,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-x64": { "node_modules/@esbuild/freebsd-x64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz",
"integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2539,9 +2635,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm": { "node_modules/@esbuild/linux-arm": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz",
"integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -2555,9 +2651,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm64": { "node_modules/@esbuild/linux-arm64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz",
"integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2571,9 +2667,9 @@
} }
}, },
"node_modules/@esbuild/linux-ia32": { "node_modules/@esbuild/linux-ia32": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz",
"integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -2587,9 +2683,9 @@
} }
}, },
"node_modules/@esbuild/linux-loong64": { "node_modules/@esbuild/linux-loong64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz",
"integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@ -2603,9 +2699,9 @@
} }
}, },
"node_modules/@esbuild/linux-mips64el": { "node_modules/@esbuild/linux-mips64el": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz",
"integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==",
"cpu": [ "cpu": [
"mips64el" "mips64el"
], ],
@ -2619,9 +2715,9 @@
} }
}, },
"node_modules/@esbuild/linux-ppc64": { "node_modules/@esbuild/linux-ppc64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz",
"integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@ -2635,9 +2731,9 @@
} }
}, },
"node_modules/@esbuild/linux-riscv64": { "node_modules/@esbuild/linux-riscv64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz",
"integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@ -2651,9 +2747,9 @@
} }
}, },
"node_modules/@esbuild/linux-s390x": { "node_modules/@esbuild/linux-s390x": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz",
"integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@ -2667,9 +2763,9 @@
} }
}, },
"node_modules/@esbuild/linux-x64": { "node_modules/@esbuild/linux-x64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz",
"integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2683,9 +2779,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-arm64": { "node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz",
"integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2699,9 +2795,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-x64": { "node_modules/@esbuild/netbsd-x64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz",
"integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2715,9 +2811,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-arm64": { "node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz",
"integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2731,9 +2827,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-x64": { "node_modules/@esbuild/openbsd-x64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz",
"integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2747,9 +2843,9 @@
} }
}, },
"node_modules/@esbuild/sunos-x64": { "node_modules/@esbuild/sunos-x64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz",
"integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2763,9 +2859,9 @@
} }
}, },
"node_modules/@esbuild/win32-arm64": { "node_modules/@esbuild/win32-arm64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz",
"integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2779,9 +2875,9 @@
} }
}, },
"node_modules/@esbuild/win32-ia32": { "node_modules/@esbuild/win32-ia32": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz",
"integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -2795,9 +2891,9 @@
} }
}, },
"node_modules/@esbuild/win32-x64": { "node_modules/@esbuild/win32-x64": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz",
"integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2896,6 +2992,13 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
} }
}, },
"node_modules/@fastify/busboy": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz",
"integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==",
"dev": true,
"license": "MIT"
},
"node_modules/@floating-ui/core": { "node_modules/@floating-ui/core": {
"version": "1.6.9", "version": "1.6.9",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz",
@ -3052,16 +3155,16 @@
} }
}, },
"node_modules/@graphql-codegen/cli/node_modules/yaml": { "node_modules/@graphql-codegen/cli/node_modules/yaml": {
"version": "2.7.1", "version": "2.8.0",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"yaml": "bin.mjs" "yaml": "bin.mjs"
}, },
"engines": { "engines": {
"node": ">= 14" "node": ">= 14.6"
} }
}, },
"node_modules/@graphql-codegen/client-preset": { "node_modules/@graphql-codegen/client-preset": {
@ -3604,13 +3707,13 @@
} }
}, },
"node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/fetch": { "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/fetch": {
"version": "0.10.5", "version": "0.10.7",
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.5.tgz", "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz",
"integrity": "sha512-+yFJU3hmXPAHJULwx0VzCIsvr/H0lvbPvbOH3areOH3NAuCxCwaJsQ8w6/MwwMcvEWIynSsmAxoyaH04KeosPg==", "integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@whatwg-node/node-fetch": "^0.7.11", "@whatwg-node/node-fetch": "^0.7.19",
"urlpattern-polyfill": "^10.0.0" "urlpattern-polyfill": "^10.0.0"
}, },
"engines": { "engines": {
@ -3618,15 +3721,15 @@
} }
}, },
"node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/node-fetch": { "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/node-fetch": {
"version": "0.7.17", "version": "0.7.20",
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.17.tgz", "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz",
"integrity": "sha512-Ni8A2H/r6brNf4u8Y7ATxmWUD0xltsQ6a4NnjWSbw4PgaT34CbY+u4QtVsFj9pTC3dBKJADKjac3AieAig+PZA==", "integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fastify/busboy": "^3.1.1",
"@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/disposablestack": "^0.0.6",
"@whatwg-node/promise-helpers": "^1.2.5", "@whatwg-node/promise-helpers": "^1.3.2",
"busboy": "^1.6.0",
"tslib": "^2.6.3" "tslib": "^2.6.3"
}, },
"engines": { "engines": {
@ -3913,13 +4016,13 @@
} }
}, },
"node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/fetch": { "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/fetch": {
"version": "0.10.5", "version": "0.10.7",
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.5.tgz", "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz",
"integrity": "sha512-+yFJU3hmXPAHJULwx0VzCIsvr/H0lvbPvbOH3areOH3NAuCxCwaJsQ8w6/MwwMcvEWIynSsmAxoyaH04KeosPg==", "integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@whatwg-node/node-fetch": "^0.7.11", "@whatwg-node/node-fetch": "^0.7.19",
"urlpattern-polyfill": "^10.0.0" "urlpattern-polyfill": "^10.0.0"
}, },
"engines": { "engines": {
@ -3927,15 +4030,15 @@
} }
}, },
"node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/node-fetch": { "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/node-fetch": {
"version": "0.7.17", "version": "0.7.20",
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.17.tgz", "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz",
"integrity": "sha512-Ni8A2H/r6brNf4u8Y7ATxmWUD0xltsQ6a4NnjWSbw4PgaT34CbY+u4QtVsFj9pTC3dBKJADKjac3AieAig+PZA==", "integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fastify/busboy": "^3.1.1",
"@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/disposablestack": "^0.0.6",
"@whatwg-node/promise-helpers": "^1.2.5", "@whatwg-node/promise-helpers": "^1.3.2",
"busboy": "^1.6.0",
"tslib": "^2.6.3" "tslib": "^2.6.3"
}, },
"engines": { "engines": {
@ -4114,13 +4217,13 @@
} }
}, },
"node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/fetch": { "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/fetch": {
"version": "0.10.5", "version": "0.10.7",
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.5.tgz", "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz",
"integrity": "sha512-+yFJU3hmXPAHJULwx0VzCIsvr/H0lvbPvbOH3areOH3NAuCxCwaJsQ8w6/MwwMcvEWIynSsmAxoyaH04KeosPg==", "integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@whatwg-node/node-fetch": "^0.7.11", "@whatwg-node/node-fetch": "^0.7.19",
"urlpattern-polyfill": "^10.0.0" "urlpattern-polyfill": "^10.0.0"
}, },
"engines": { "engines": {
@ -4128,15 +4231,15 @@
} }
}, },
"node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/node-fetch": { "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/node-fetch": {
"version": "0.7.17", "version": "0.7.20",
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.17.tgz", "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz",
"integrity": "sha512-Ni8A2H/r6brNf4u8Y7ATxmWUD0xltsQ6a4NnjWSbw4PgaT34CbY+u4QtVsFj9pTC3dBKJADKjac3AieAig+PZA==", "integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fastify/busboy": "^3.1.1",
"@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/disposablestack": "^0.0.6",
"@whatwg-node/promise-helpers": "^1.2.5", "@whatwg-node/promise-helpers": "^1.3.2",
"busboy": "^1.6.0",
"tslib": "^2.6.3" "tslib": "^2.6.3"
}, },
"engines": { "engines": {
@ -4481,13 +4584,13 @@
} }
}, },
"node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/fetch": { "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/fetch": {
"version": "0.10.5", "version": "0.10.7",
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.5.tgz", "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz",
"integrity": "sha512-+yFJU3hmXPAHJULwx0VzCIsvr/H0lvbPvbOH3areOH3NAuCxCwaJsQ8w6/MwwMcvEWIynSsmAxoyaH04KeosPg==", "integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@whatwg-node/node-fetch": "^0.7.11", "@whatwg-node/node-fetch": "^0.7.19",
"urlpattern-polyfill": "^10.0.0" "urlpattern-polyfill": "^10.0.0"
}, },
"engines": { "engines": {
@ -4495,15 +4598,15 @@
} }
}, },
"node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/node-fetch": { "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/node-fetch": {
"version": "0.7.17", "version": "0.7.20",
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.17.tgz", "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz",
"integrity": "sha512-Ni8A2H/r6brNf4u8Y7ATxmWUD0xltsQ6a4NnjWSbw4PgaT34CbY+u4QtVsFj9pTC3dBKJADKjac3AieAig+PZA==", "integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fastify/busboy": "^3.1.1",
"@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/disposablestack": "^0.0.6",
"@whatwg-node/promise-helpers": "^1.2.5", "@whatwg-node/promise-helpers": "^1.3.2",
"busboy": "^1.6.0",
"tslib": "^2.6.3" "tslib": "^2.6.3"
}, },
"engines": { "engines": {
@ -4625,13 +4728,13 @@
} }
}, },
"node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/fetch": { "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/fetch": {
"version": "0.10.5", "version": "0.10.7",
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.5.tgz", "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz",
"integrity": "sha512-+yFJU3hmXPAHJULwx0VzCIsvr/H0lvbPvbOH3areOH3NAuCxCwaJsQ8w6/MwwMcvEWIynSsmAxoyaH04KeosPg==", "integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@whatwg-node/node-fetch": "^0.7.11", "@whatwg-node/node-fetch": "^0.7.19",
"urlpattern-polyfill": "^10.0.0" "urlpattern-polyfill": "^10.0.0"
}, },
"engines": { "engines": {
@ -4639,15 +4742,15 @@
} }
}, },
"node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/node-fetch": { "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/node-fetch": {
"version": "0.7.17", "version": "0.7.20",
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.17.tgz", "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz",
"integrity": "sha512-Ni8A2H/r6brNf4u8Y7ATxmWUD0xltsQ6a4NnjWSbw4PgaT34CbY+u4QtVsFj9pTC3dBKJADKjac3AieAig+PZA==", "integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fastify/busboy": "^3.1.1",
"@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/disposablestack": "^0.0.6",
"@whatwg-node/promise-helpers": "^1.2.5", "@whatwg-node/promise-helpers": "^1.3.2",
"busboy": "^1.6.0",
"tslib": "^2.6.3" "tslib": "^2.6.3"
}, },
"engines": { "engines": {
@ -5018,9 +5121,9 @@
} }
}, },
"node_modules/@mapbox/node-pre-gyp/node_modules/semver": { "node_modules/@mapbox/node-pre-gyp/node_modules/semver": {
"version": "7.7.1", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -5785,6 +5888,7 @@
"version": "2.8.17", "version": "2.8.17",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
"integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
@ -6050,9 +6154,9 @@
} }
}, },
"node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": {
"version": "7.7.1", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -6199,9 +6303,9 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
"version": "7.7.1", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -6236,9 +6340,9 @@
} }
}, },
"node_modules/@typescript-eslint/utils/node_modules/semver": { "node_modules/@typescript-eslint/utils/node_modules/semver": {
"version": "7.7.1", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -6359,9 +6463,9 @@
} }
}, },
"node_modules/@whatwg-node/promise-helpers": { "node_modules/@whatwg-node/promise-helpers": {
"version": "1.3.0", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.0.tgz", "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz",
"integrity": "sha512-486CouizxHXucj8Ky153DDragfkMcHtVEToF5Pn/fInhUUSiCmt9Q4JVBa6UK5q4RammFBtGQ4C9qhGlXU9YbA==", "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -8680,9 +8784,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/debug": { "node_modules/debug": {
"version": "4.4.0", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
@ -9248,9 +9352,9 @@
} }
}, },
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.25.2", "version": "0.25.4",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz",
"integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
@ -9260,31 +9364,31 @@
"node": ">=18" "node": ">=18"
}, },
"optionalDependencies": { "optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.2", "@esbuild/aix-ppc64": "0.25.4",
"@esbuild/android-arm": "0.25.2", "@esbuild/android-arm": "0.25.4",
"@esbuild/android-arm64": "0.25.2", "@esbuild/android-arm64": "0.25.4",
"@esbuild/android-x64": "0.25.2", "@esbuild/android-x64": "0.25.4",
"@esbuild/darwin-arm64": "0.25.2", "@esbuild/darwin-arm64": "0.25.4",
"@esbuild/darwin-x64": "0.25.2", "@esbuild/darwin-x64": "0.25.4",
"@esbuild/freebsd-arm64": "0.25.2", "@esbuild/freebsd-arm64": "0.25.4",
"@esbuild/freebsd-x64": "0.25.2", "@esbuild/freebsd-x64": "0.25.4",
"@esbuild/linux-arm": "0.25.2", "@esbuild/linux-arm": "0.25.4",
"@esbuild/linux-arm64": "0.25.2", "@esbuild/linux-arm64": "0.25.4",
"@esbuild/linux-ia32": "0.25.2", "@esbuild/linux-ia32": "0.25.4",
"@esbuild/linux-loong64": "0.25.2", "@esbuild/linux-loong64": "0.25.4",
"@esbuild/linux-mips64el": "0.25.2", "@esbuild/linux-mips64el": "0.25.4",
"@esbuild/linux-ppc64": "0.25.2", "@esbuild/linux-ppc64": "0.25.4",
"@esbuild/linux-riscv64": "0.25.2", "@esbuild/linux-riscv64": "0.25.4",
"@esbuild/linux-s390x": "0.25.2", "@esbuild/linux-s390x": "0.25.4",
"@esbuild/linux-x64": "0.25.2", "@esbuild/linux-x64": "0.25.4",
"@esbuild/netbsd-arm64": "0.25.2", "@esbuild/netbsd-arm64": "0.25.4",
"@esbuild/netbsd-x64": "0.25.2", "@esbuild/netbsd-x64": "0.25.4",
"@esbuild/openbsd-arm64": "0.25.2", "@esbuild/openbsd-arm64": "0.25.4",
"@esbuild/openbsd-x64": "0.25.2", "@esbuild/openbsd-x64": "0.25.4",
"@esbuild/sunos-x64": "0.25.2", "@esbuild/sunos-x64": "0.25.4",
"@esbuild/win32-arm64": "0.25.2", "@esbuild/win32-arm64": "0.25.4",
"@esbuild/win32-ia32": "0.25.2", "@esbuild/win32-ia32": "0.25.4",
"@esbuild/win32-x64": "0.25.2" "@esbuild/win32-x64": "0.25.4"
} }
}, },
"node_modules/esbuild-register": { "node_modules/esbuild-register": {
@ -11834,9 +11938,9 @@
} }
}, },
"node_modules/jsonwebtoken/node_modules/semver": { "node_modules/jsonwebtoken/node_modules/semver": {
"version": "7.7.1", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -12582,9 +12686,9 @@
} }
}, },
"node_modules/nodemon/node_modules/semver": { "node_modules/nodemon/node_modules/semver": {
"version": "7.7.1", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"bin": { "bin": {
@ -14242,9 +14346,9 @@
} }
}, },
"node_modules/simple-update-notifier/node_modules/semver": { "node_modules/simple-update-notifier/node_modules/semver": {
"version": "7.7.1", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"bin": { "bin": {
@ -16293,9 +16397,9 @@
} }
}, },
"node_modules/yarn-deduplicate/node_modules/semver": { "node_modules/yarn-deduplicate/node_modules/semver": {
"version": "7.7.1", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"

View file

@ -18,7 +18,13 @@
"@apollo/server": "^4.11.2", "@apollo/server": "^4.11.2",
"@chakra-ui/react": "^3.15.0", "@chakra-ui/react": "^3.15.0",
"@chakra-ui/toast": "^7.0.2", "@chakra-ui/toast": "^7.0.2",
"@coolbytes/editorjs-link": "^1.0.0",
"@deftomat/opinionated": "^1.1.3", "@deftomat/opinionated": "^1.1.3",
"@editorjs/editorjs": "^2.30.8",
"@editorjs/header": "^2.8.8",
"@editorjs/link": "^2.6.2",
"@editorjs/list": "^2.0.8",
"@editorjs/quote": "^2.7.6",
"@emotion/react": "^11.13.5", "@emotion/react": "^11.13.5",
"@hookform/resolvers": "^3.9.1", "@hookform/resolvers": "^3.9.1",
"@prisma/client": "^6.4.1", "@prisma/client": "^6.4.1",
@ -53,8 +59,8 @@
"@tanstack/router-devtools": "^1.85.0", "@tanstack/router-devtools": "^1.85.0",
"@tanstack/router-plugin": "^1.84.4", "@tanstack/router-plugin": "^1.84.4",
"@types/bcrypt": "^5.0.2", "@types/bcrypt": "^5.0.2",
"@types/cors": "^2.8.17",
"@types/cookie-parser": "^1.4.8", "@types/cookie-parser": "^1.4.8",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.9", "@types/jsonwebtoken": "^9.0.9",
"@types/node": "^20.9.3", "@types/node": "^20.9.3",