From 6ada519e06553870692459b5aabc610466e72f08 Mon Sep 17 00:00:00 2001 From: lnn0q Date: Sat, 17 May 2025 00:29:55 +0300 Subject: [PATCH] implemented identity, updated session, added 'workings' list added paginated querying, added 'common' --- app/client/components/Editor.tsx | 159 ++++++ app/client/components/Pagination.tsx | 44 ++ app/client/components/WorkingList.tsx | 14 + app/client/components/WorkingListCard.tsx | 115 +++++ app/client/components/WorkingListSearch.tsx | 40 ++ .../components/WorkingProgressStatusBadge.tsx | 37 ++ app/client/components/WorkingRatingBadge.tsx | 28 ++ app/client/components/ui/color-mode.tsx | 1 - app/client/components/ui/provider.tsx | 9 +- app/client/fragments.tsx | 1 + app/client/helpers.tsx | 25 + app/client/hooks/auth.tsx | 3 +- app/client/hooks/identity.tsx | 53 +- app/client/hooks/usePaginatedQuery.tsx | 40 ++ app/client/hooks/useUserOneQuery.tsx | 33 ++ app/client/main.tsx | 33 +- app/client/pages/__root.tsx | 102 ++-- app/client/pages/editor.tsx | 15 + app/client/pages/example.tsx | 46 ++ app/client/pages/index.tsx | 149 ++++-- app/client/pages/profile/$username.tsx | 10 + app/client/routeTree.gen.ts | 102 +++- app/common.ts | 3 + app/server/express/auth/refreshToken.ts | 2 +- app/server/graphql/graphql.ts | 10 +- app/server/graphql/resolvers/auth/login.ts | 1 + .../graphql/resolvers/identityResolver.ts | 14 + .../resolvers/mappers/identityMapper.ts | 12 + .../graphql/resolvers/mappers/userMapper.ts | 11 + .../graphql/resolvers/userOneResolver.ts | 13 + app/server/graphql/schema.graphql | 81 +++ app/server/identity.ts | 3 +- app/server/prisma/schema.prisma | 6 +- index.html | 2 +- package-lock.json | 476 +++++++++++------- package.json | 8 +- 36 files changed, 1422 insertions(+), 279 deletions(-) create mode 100644 app/client/components/Editor.tsx create mode 100644 app/client/components/Pagination.tsx create mode 100644 app/client/components/WorkingList.tsx create mode 100644 app/client/components/WorkingListCard.tsx create mode 100644 app/client/components/WorkingListSearch.tsx create mode 100644 app/client/components/WorkingProgressStatusBadge.tsx create mode 100644 app/client/components/WorkingRatingBadge.tsx create mode 100644 app/client/hooks/usePaginatedQuery.tsx create mode 100644 app/client/hooks/useUserOneQuery.tsx create mode 100644 app/client/pages/editor.tsx create mode 100644 app/client/pages/example.tsx create mode 100644 app/client/pages/profile/$username.tsx create mode 100644 app/common.ts create mode 100644 app/server/graphql/resolvers/identityResolver.ts create mode 100644 app/server/graphql/resolvers/mappers/identityMapper.ts create mode 100644 app/server/graphql/resolvers/mappers/userMapper.ts create mode 100644 app/server/graphql/resolvers/userOneResolver.ts diff --git a/app/client/components/Editor.tsx b/app/client/components/Editor.tsx new file mode 100644 index 0000000..b6fe75c --- /dev/null +++ b/app/client/components/Editor.tsx @@ -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(); + + 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 ( + <> + + + Editor + + +
+
+
+ + ); +} + +const editorJsStyles: + | SystemStyleObject + | Omit, 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' + } +}; diff --git a/app/client/components/Pagination.tsx b/app/client/components/Pagination.tsx new file mode 100644 index 0000000..acb7f51 --- /dev/null +++ b/app/client/components/Pagination.tsx @@ -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 ( + setPage(e.page)} + defaultPage={1} + > + + + + + + + + ( + {page.value} + )} + /> + + + + + + + + + ); +} diff --git a/app/client/components/WorkingList.tsx b/app/client/components/WorkingList.tsx new file mode 100644 index 0000000..8b0bfaa --- /dev/null +++ b/app/client/components/WorkingList.tsx @@ -0,0 +1,14 @@ +import { List } from '@chakra-ui/react'; +import { WorkingCardProps, WorkingListCard } from './WorkingListCard'; + +export function WorkingList({ workingsList }: { workingsList: WorkingCardProps[] }) { + return ( + + {workingsList.map(workingData => ( + + + + ))} + + ); +} diff --git a/app/client/components/WorkingListCard.tsx b/app/client/components/WorkingListCard.tsx new file mode 100644 index 0000000..7f934d1 --- /dev/null +++ b/app/client/components/WorkingListCard.tsx @@ -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 ( + + + + + + {title} + + + + + + {reviewsTotal} + + + + + + + + by: + {author.username} + + + + + + + last updated: + {updatedAt} + + + + + + + chapters: + {chaptersTotal} + + + + + + + words: + {wordsTotal} + + + + + + + + {description} + + + + + ); +} + +export function IconBadge({ + children, + iconClass, + backgroundColor, + size = 'lg' +}: { + children?: ReactNode; + iconClass: string; + backgroundColor?: BadgeProps['backgroundColor']; + size?: 'sm' | 'md' | 'lg' | 'xs'; +}) { + return ( + + + + + + + {children} + + + + ); +} diff --git a/app/client/components/WorkingListSearch.tsx b/app/client/components/WorkingListSearch.tsx new file mode 100644 index 0000000..c30dfdc --- /dev/null +++ b/app/client/components/WorkingListSearch.tsx @@ -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 ( +
{ + event.preventDefault(); + onSubmit(value); + }} + > + + + + + } + > + onChange(event.currentTarget.value)} + placeholder="Search works..." + /> + + + + + ); +} diff --git a/app/client/components/WorkingProgressStatusBadge.tsx b/app/client/components/WorkingProgressStatusBadge.tsx new file mode 100644 index 0000000..016077e --- /dev/null +++ b/app/client/components/WorkingProgressStatusBadge.tsx @@ -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 ( + + + + + + + {toWorkingProgressStatusLabel(progressStatus)} + + + + ); +} + +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); +} diff --git a/app/client/components/WorkingRatingBadge.tsx b/app/client/components/WorkingRatingBadge.tsx new file mode 100644 index 0000000..b32c635 --- /dev/null +++ b/app/client/components/WorkingRatingBadge.tsx @@ -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 ( + + {ratingParams.rating} + + ); +} diff --git a/app/client/components/ui/color-mode.tsx b/app/client/components/ui/color-mode.tsx index b8231ef..60916cb 100644 --- a/app/client/components/ui/color-mode.tsx +++ b/app/client/components/ui/color-mode.tsx @@ -15,7 +15,6 @@ export function ColorModeProvider(props: ColorModeProviderProps) { export function useColorMode() { const { resolvedTheme, setTheme } = useTheme(); - console.log(resolvedTheme); const toggleColorMode = () => { setTheme(resolvedTheme === 'light' ? 'dark' : 'light'); }; diff --git a/app/client/components/ui/provider.tsx b/app/client/components/ui/provider.tsx index 9a5fa9b..ed76f8a 100644 --- a/app/client/components/ui/provider.tsx +++ b/app/client/components/ui/provider.tsx @@ -1,11 +1,16 @@ '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'; export function Provider(props: ColorModeProviderProps) { + const system = createSystem(defaultConfig, { + theme: { + tokens: {} + } + }); return ( - + {props.children} diff --git a/app/client/fragments.tsx b/app/client/fragments.tsx index 0d12ce3..a80c1c0 100644 --- a/app/client/fragments.tsx +++ b/app/client/fragments.tsx @@ -2,6 +2,7 @@ import { gql } from '@apollo/client'; export const sessionFragment = gql` fragment Session on Session { + userId accessToken } `; diff --git a/app/client/helpers.tsx b/app/client/helpers.tsx index 8b13789..d9e8ff2 100644 --- a/app/client/helpers.tsx +++ b/app/client/helpers.tsx @@ -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); +} diff --git a/app/client/hooks/auth.tsx b/app/client/hooks/auth.tsx index fdfe8a5..52aa307 100644 --- a/app/client/hooks/auth.tsx +++ b/app/client/hooks/auth.tsx @@ -31,7 +31,7 @@ export function useAuth(): UseAuthOutput { 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 { ...authContext, @@ -45,7 +45,6 @@ export function useAuth(): UseAuthOutput { } } }); - if (data != null) { authContext.setHeaders(data.login); return data.login; diff --git a/app/client/hooks/identity.tsx b/app/client/hooks/identity.tsx index 59e890f..ba9999a 100644 --- a/app/client/hooks/identity.tsx +++ b/app/client/hooks/identity.tsx @@ -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(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 | undefined + ) => Promise>; +} { + const { data, loading, error, refetch } = useQuery(query, { + skip + }); + return { + data, + loading, + error, + refetch + }; +} + +const query = gql` + query IdentityRoot { + identity { + id + email + isDarkTheme + user { + id + username + createdAt + updatedAt + } + } + } +`; diff --git a/app/client/hooks/usePaginatedQuery.tsx b/app/client/hooks/usePaginatedQuery.tsx new file mode 100644 index 0000000..512e699 --- /dev/null +++ b/app/client/hooks/usePaginatedQuery.tsx @@ -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 { + data?: MaybeMasked | undefined; + loading: boolean; + error: ApolloError | undefined; + refetch: ( + variables?: Partial | undefined + ) => Promise>>; +} + +export function usePaginatedQuery(props: UsePaginatedQueryProps): UsePaginatedQueryReturn { + const { data, loading, error, refetch } = useQuery(props.query, { + variables: { input: props.input } + }); + return { + data, + loading, + error, + refetch + }; +} diff --git a/app/client/hooks/useUserOneQuery.tsx b/app/client/hooks/useUserOneQuery.tsx new file mode 100644 index 0000000..ca9ba64 --- /dev/null +++ b/app/client/hooks/useUserOneQuery.tsx @@ -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 | undefined + ) => Promise>; +} { + const { data, loading, error, refetch } = useQuery(query, { + variables: { id }, + skip + }); + return { + data, + loading, + error, + refetch + }; +} + +const query = gql` + query UserOne($id: ID!) { + userOne(id: $id) { + id + username + createdAt + updatedAt + } + } +`; diff --git a/app/client/main.tsx b/app/client/main.tsx index d2f6fb2..01bd4ee 100644 --- a/app/client/main.tsx +++ b/app/client/main.tsx @@ -1,7 +1,7 @@ import { ApolloClient, ApolloProvider, HttpLink, InMemoryCache, from } from '@apollo/client'; import { loadDevMessages, loadErrorMessages } from '@apollo/client/dev'; 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/nerd-fonts-icons.css'; 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 ReactDOM from 'react-dom/client'; import { AuthProvider, useRestAuth } from './hooks/auth'; +import { IdentityProvider, useIdentityQuery } from './hooks/identity'; // TanStackRouter - Import the generated route tree import { Session } from '@generated/types'; @@ -59,7 +60,11 @@ function App() { return ( - + +
+ +
+
); @@ -113,7 +118,7 @@ function App() { - + @@ -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 ( + +
+ +
+
+ ); + + return ( + + + + ); +} + const rootElement = document.getElementById('root')!; if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement); diff --git a/app/client/pages/__root.tsx b/app/client/pages/__root.tsx index cd1fc55..74c93cd 100644 --- a/app/client/pages/__root.tsx +++ b/app/client/pages/__root.tsx @@ -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 React from 'react'; import { Avatar } from '../components/ui/avatar'; import { Button } from '../components/ui/button'; +import { ColorModeButton } from '../components/ui/color-mode'; import { Tooltip } from '../components/ui/tooltip'; import { useAuth } from '../hooks/auth'; +import { useIdentity } from '../hooks/identity'; import { appName } from '../main'; -import { ColorModeButton } from '../components/ui/color-mode'; const TanStackRouterDevtools = process.env.NODE_ENV === 'production' @@ -26,48 +27,82 @@ interface RouterContext { export const Route = createRootRouteWithContext()({ component: () => { - const { isAuthenticated, logout } = useAuth(); + const { logout } = useAuth(); + const identity = useIdentity(); + return ( - + - - [{appName}] - - - - - - + + + + + + {appName} + + + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + + + + } + endElement={⌘K} + > + + + - {isAuthenticated ? ( + {identity != null ? ( - + - - <> - logout()}> - + + + + - + - Log out + My profile - + + + logout()}> + + + + + + Log out + + + @@ -75,30 +110,13 @@ export const Route = createRootRouteWithContext()({ <> - + - + )} - - {/* {!isAuthenticated ? ( - <> - - - - - - - - ) : ( - - logout()}> - - - - )} */} diff --git a/app/client/pages/editor.tsx b/app/client/pages/editor.tsx new file mode 100644 index 0000000..cc23a73 --- /dev/null +++ b/app/client/pages/editor.tsx @@ -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 ( + + + + ); +} diff --git a/app/client/pages/example.tsx b/app/client/pages/example.tsx new file mode 100644 index 0000000..4e1cb6a --- /dev/null +++ b/app/client/pages/example.tsx @@ -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(getBooksQuery) + + return ( + + Hello world! + This is my template! + + {data && ( + <> + {data.booksMany.map((book: any) => ( + + {book.title} + {book.author} + + ))} + + )} + + + + + + ) +} + +const getBooksQuery = gql` + query BooksMany { + booksMany { + id + author + title + } + } +` diff --git a/app/client/pages/index.tsx b/app/client/pages/index.tsx index 61ec4d1..075b289 100644 --- a/app/client/pages/index.tsx +++ b/app/client/pages/index.tsx @@ -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 { gql, useQuery } from '@apollo/client'; - -import { BooksManyQuery } from '@generated/types'; +import { useState } from 'react'; +import { Pagination } from '../components/Pagination'; +import { WorkingList } from '../components/WorkingList'; +import { WorkingListSearch } from '../components/WorkingListSearch'; +import { usePaginatedQuery } from '../hooks/usePaginatedQuery'; export const Route = createFileRoute('/')({ - component: Dashboard + component: RouteComponent }); -function Dashboard() { - const { data, refetch } = useQuery(getBooksQuery); +function RouteComponent() { + const [workingSearchValue, setWorkingSearchValue] = useState(''); + const [workingsListPage, setWorkingsListPage] = useState(1); + const [workingsListPageSize, setWorkingsListPageSize] = useState(2); + const startIndex = (workingsListPage - 1) * workingsListPageSize; + + const { data } = usePaginatedQuery({ + query, + input: { + filter: { + terms: workingSearchValue, + offset: startIndex, + size: workingsListPageSize + } + } + }); return ( - - Hello world! - This is my template! - - {data && ( + + + { + alert(value); + }} + onChange={value => setWorkingSearchValue(value)} + /> + + {data != null ? ( <> - {data.booksMany.map((book: any) => ( - - {book.title} - {book.author} - - ))} + ({ + ...item, + chaptersTotal: item.chapters.total, + reviewsTotal: item.reviews.total + }))} + /> + + + ) : ( + )} - - - ); } -const getBooksQuery = gql` - query BooksMany { - booksMany { - id - author - title +const query = gql` + query WorkingList($input: WorkingManyInput!) { + workingMany(input: $input) { + items { + id + 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' }] +// }; +// diff --git a/app/client/pages/profile/$username.tsx b/app/client/pages/profile/$username.tsx new file mode 100644 index 0000000..f53d5af --- /dev/null +++ b/app/client/pages/profile/$username.tsx @@ -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
{`Hello, "${username}"!`}
; +} diff --git a/app/client/routeTree.gen.ts b/app/client/routeTree.gen.ts index 95d41eb..5c6aa85 100644 --- a/app/client/routeTree.gen.ts +++ b/app/client/routeTree.gen.ts @@ -15,8 +15,11 @@ import { createFileRoute } from '@tanstack/react-router' import { Route as rootRoute } from './pages/__root' import { Route as RegisterImport } from './pages/register' 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 IndexImport } from './pages/index' +import { Route as ProfileUsernameImport } from './pages/profile/$username' // Create Virtual Routes @@ -42,6 +45,18 @@ const LoginRoute = LoginImport.update({ getParentRoute: () => rootRoute, } 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({ id: '/about', path: '/about', @@ -54,6 +69,12 @@ const IndexRoute = IndexImport.update({ getParentRoute: () => rootRoute, } as any) +const ProfileUsernameRoute = ProfileUsernameImport.update({ + id: '/profile/$username', + path: '/profile/$username', + getParentRoute: () => rootRoute, +} as any) + // Populate the FileRoutesByPath interface declare module '@tanstack/react-router' { @@ -72,6 +93,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AboutImport 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': { id: '/login' path: '/login' @@ -93,6 +128,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BookLazyImport 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 { '/': typeof IndexRoute '/about': typeof AboutRoute + '/editor': typeof EditorRoute + '/example': typeof ExampleRoute '/login': typeof LoginRoute '/register': typeof RegisterRoute '/book': typeof BookLazyRoute + '/profile/$username': typeof ProfileUsernameRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/about': typeof AboutRoute + '/editor': typeof EditorRoute + '/example': typeof ExampleRoute '/login': typeof LoginRoute '/register': typeof RegisterRoute '/book': typeof BookLazyRoute + '/profile/$username': typeof ProfileUsernameRoute } export interface FileRoutesById { __root__: typeof rootRoute '/': typeof IndexRoute '/about': typeof AboutRoute + '/editor': typeof EditorRoute + '/example': typeof ExampleRoute '/login': typeof LoginRoute '/register': typeof RegisterRoute '/book': typeof BookLazyRoute + '/profile/$username': typeof ProfileUsernameRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/about' | '/login' | '/register' | '/book' + fullPaths: + | '/' + | '/about' + | '/editor' + | '/example' + | '/login' + | '/register' + | '/book' + | '/profile/$username' fileRoutesByTo: FileRoutesByTo - to: '/' | '/about' | '/login' | '/register' | '/book' - id: '__root__' | '/' | '/about' | '/login' | '/register' | '/book' + to: + | '/' + | '/about' + | '/editor' + | '/example' + | '/login' + | '/register' + | '/book' + | '/profile/$username' + id: + | '__root__' + | '/' + | '/about' + | '/editor' + | '/example' + | '/login' + | '/register' + | '/book' + | '/profile/$username' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute AboutRoute: typeof AboutRoute + EditorRoute: typeof EditorRoute + ExampleRoute: typeof ExampleRoute LoginRoute: typeof LoginRoute RegisterRoute: typeof RegisterRoute BookLazyRoute: typeof BookLazyRoute + ProfileUsernameRoute: typeof ProfileUsernameRoute } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, AboutRoute: AboutRoute, + EditorRoute: EditorRoute, + ExampleRoute: ExampleRoute, LoginRoute: LoginRoute, RegisterRoute: RegisterRoute, BookLazyRoute: BookLazyRoute, + ProfileUsernameRoute: ProfileUsernameRoute, } export const routeTree = rootRoute @@ -160,9 +242,12 @@ export const routeTree = rootRoute "children": [ "/", "/about", + "/editor", + "/example", "/login", "/register", - "/book" + "/book", + "/profile/$username" ] }, "/": { @@ -171,6 +256,12 @@ export const routeTree = rootRoute "/about": { "filePath": "about.tsx" }, + "/editor": { + "filePath": "editor.tsx" + }, + "/example": { + "filePath": "example.tsx" + }, "/login": { "filePath": "login.tsx" }, @@ -179,6 +270,9 @@ export const routeTree = rootRoute }, "/book": { "filePath": "book.lazy.tsx" + }, + "/profile/$username": { + "filePath": "profile/$username.tsx" } } } diff --git a/app/common.ts b/app/common.ts new file mode 100644 index 0000000..9d8628e --- /dev/null +++ b/app/common.ts @@ -0,0 +1,3 @@ +export function call(fn: () => T) { + return fn(); +} diff --git a/app/server/express/auth/refreshToken.ts b/app/server/express/auth/refreshToken.ts index b790dc8..7fc18bf 100644 --- a/app/server/express/auth/refreshToken.ts +++ b/app/server/express/auth/refreshToken.ts @@ -43,7 +43,7 @@ export async function refreshTokenController(req: Request, res: Response) { expiresIn: config.accessToken.expiresIn }); - const session: Session = { accessToken }; + const session: Session = { userId: user.id, accessToken }; res.json(session); } catch (error) { console.error(error); diff --git a/app/server/graphql/graphql.ts b/app/server/graphql/graphql.ts index 55eee78..d16f7a8 100644 --- a/app/server/graphql/graphql.ts +++ b/app/server/graphql/graphql.ts @@ -2,17 +2,21 @@ import { MutationResolvers, QueryResolvers } from '@generated/types'; /* Query */ -import { BooksManyResolver } from './resolvers/booksManyResolver'; import { BookOneResolver } from './resolvers/bookOneResolver'; +import { BooksManyResolver } from './resolvers/booksManyResolver'; +import { IdentityResolver } from './resolvers/identityResolver'; +import { UserOneResolver } from './resolvers/userOneResolver'; /* Mutation */ -import { SignUpResolver } from './resolvers/auth/signUp'; import { LoginResolver } from './resolvers/auth/login'; import { LogoutResolver } from './resolvers/auth/logout'; +import { SignUpResolver } from './resolvers/auth/signUp'; export const Query: QueryResolvers = { booksMany: BooksManyResolver, - bookOne: BookOneResolver + bookOne: BookOneResolver, + identity: IdentityResolver, + userOne: UserOneResolver }; export const Mutation: MutationResolvers = { diff --git a/app/server/graphql/resolvers/auth/login.ts b/app/server/graphql/resolvers/auth/login.ts index a0e11a7..a0e8d61 100644 --- a/app/server/graphql/resolvers/auth/login.ts +++ b/app/server/graphql/resolvers/auth/login.ts @@ -40,6 +40,7 @@ export async function LoginResolver( }); return { + userId: user.id, accessToken }; } diff --git a/app/server/graphql/resolvers/identityResolver.ts b/app/server/graphql/resolvers/identityResolver.ts new file mode 100644 index 0000000..a6eaf7e --- /dev/null +++ b/app/server/graphql/resolvers/identityResolver.ts @@ -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 { + if (identity == null) throw new Error('Unauthorized.'); + const model = await prisma.user.findUniqueOrThrow({ where: { id: identity.id } }); + return toIdentitySchema(model); +} diff --git a/app/server/graphql/resolvers/mappers/identityMapper.ts b/app/server/graphql/resolvers/mappers/identityMapper.ts new file mode 100644 index 0000000..7d88124 --- /dev/null +++ b/app/server/graphql/resolvers/mappers/identityMapper.ts @@ -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 { + return { + id: model.id, + email: model.email, + isDarkTheme: true, + user: await toUserSchema(model) + }; +} diff --git a/app/server/graphql/resolvers/mappers/userMapper.ts b/app/server/graphql/resolvers/mappers/userMapper.ts new file mode 100644 index 0000000..f416913 --- /dev/null +++ b/app/server/graphql/resolvers/mappers/userMapper.ts @@ -0,0 +1,11 @@ +import { User as UserSchema } from '@generated/types'; +import { User } from '@prisma/client'; + +export async function toUserSchema(model: User): Promise { + return { + id: model.id, + username: model.username, + createdAt: model.createdAt.toISOString(), + updatedAt: model.updatedAt.toISOString() + }; +} diff --git a/app/server/graphql/resolvers/userOneResolver.ts b/app/server/graphql/resolvers/userOneResolver.ts new file mode 100644 index 0000000..e08cc1f --- /dev/null +++ b/app/server/graphql/resolvers/userOneResolver.ts @@ -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 { + const model = await prisma.user.findUniqueOrThrow({ where: { id } }); + return toUserSchema(model); +} diff --git a/app/server/graphql/schema.graphql b/app/server/graphql/schema.graphql index f2200c4..100412f 100644 --- a/app/server/graphql/schema.graphql +++ b/app/server/graphql/schema.graphql @@ -3,6 +3,9 @@ scalar Void type Query { booksMany: [Book!]! bookOne(id: ID!): Book! + identity: Identity! + userOne(id: ID!): User! + workingMany(input: WorkingManyInput!): WorkingManyOutput! } type Mutation { @@ -23,11 +26,89 @@ input LoginInput { } type Session { + userId: ID! accessToken: String! } +type Identity { + id: ID! + email: String! + isDarkTheme: Boolean! + user: User! +} + +type User { + id: ID! + username: String! + createdAt: String! + updatedAt: String! +} + type Book { id: ID! title: 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! +} diff --git a/app/server/identity.ts b/app/server/identity.ts index 14e26bc..61cc911 100644 --- a/app/server/identity.ts +++ b/app/server/identity.ts @@ -1,16 +1,17 @@ import { Session } from '@generated/types'; import 'dotenv/config'; import { Request, Response } from 'express'; +import { GraphQLError } from 'graphql'; import jwt from 'jsonwebtoken'; import { config } from './main'; import { prisma } from './prisma/client'; -import { GraphQLError } from 'graphql'; export interface IdentityContext { req: Request; res: Response; identity?: { id: string; + email: string; username: string; }; } diff --git a/app/server/prisma/schema.prisma b/app/server/prisma/schema.prisma index 7c46089..bb96aeb 100644 --- a/app/server/prisma/schema.prisma +++ b/app/server/prisma/schema.prisma @@ -10,12 +10,14 @@ datasource db { model User { id String @id + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + email String @unique @db.VarChar(255) username String @unique @db.VarChar(255) password String refreshToken String? @unique @db.VarChar(255) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + isDarkMode Boolean @default(true) } diff --git a/index.html b/index.html index e08581c..ab7eb08 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - ln + echoes
diff --git a/package-lock.json b/package-lock.json index ce2e7df..f569929 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,13 +12,18 @@ "@apollo/server": "^4.11.2", "@chakra-ui/react": "^3.15.0", "@chakra-ui/toast": "^7.0.2", + "@coolbytes/editorjs-link": "^1.0.0", "@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", "@hookform/resolvers": "^3.9.1", "@prisma/client": "^6.4.1", "@tanstack/react-router": "^1.85.0", "@tanstack/router-vite-plugin": "^1.84.4", - "@types/cors": "^2.8.17", "apollo-link-logger": "^2.0.1", "bcrypt": "^5.1.1", "cookie-parser": "^1.4.7", @@ -49,6 +54,7 @@ "@tanstack/router-plugin": "^1.84.4", "@types/bcrypt": "^5.0.2", "@types/cookie-parser": "^1.4.8", + "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/jsonwebtoken": "^9.0.9", "@types/node": "^20.9.3", @@ -1807,6 +1813,18 @@ "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": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@deftomat/opinionated/-/opinionated-1.1.3.tgz", @@ -1894,9 +1912,9 @@ } }, "node_modules/@deftomat/opinionated/node_modules/glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", + "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -2257,6 +2275,84 @@ "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": { "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", @@ -2411,9 +2507,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", "cpu": [ "ppc64" ], @@ -2427,9 +2523,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", "cpu": [ "arm" ], @@ -2443,9 +2539,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", "cpu": [ "arm64" ], @@ -2459,9 +2555,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", "cpu": [ "x64" ], @@ -2475,9 +2571,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", "cpu": [ "arm64" ], @@ -2491,9 +2587,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", "cpu": [ "x64" ], @@ -2507,9 +2603,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", "cpu": [ "arm64" ], @@ -2523,9 +2619,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", "cpu": [ "x64" ], @@ -2539,9 +2635,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", "cpu": [ "arm" ], @@ -2555,9 +2651,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", "cpu": [ "arm64" ], @@ -2571,9 +2667,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", "cpu": [ "ia32" ], @@ -2587,9 +2683,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", "cpu": [ "loong64" ], @@ -2603,9 +2699,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", "cpu": [ "mips64el" ], @@ -2619,9 +2715,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", "cpu": [ "ppc64" ], @@ -2635,9 +2731,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", "cpu": [ "riscv64" ], @@ -2651,9 +2747,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", "cpu": [ "s390x" ], @@ -2667,9 +2763,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", "cpu": [ "x64" ], @@ -2683,9 +2779,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", "cpu": [ "arm64" ], @@ -2699,9 +2795,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", "cpu": [ "x64" ], @@ -2715,9 +2811,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", "cpu": [ "arm64" ], @@ -2731,9 +2827,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", "cpu": [ "x64" ], @@ -2747,9 +2843,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", "cpu": [ "x64" ], @@ -2763,9 +2859,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", "cpu": [ "arm64" ], @@ -2779,9 +2875,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", "cpu": [ "ia32" ], @@ -2795,9 +2891,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", "cpu": [ "x64" ], @@ -2896,6 +2992,13 @@ "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": { "version": "1.6.9", "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": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" } }, "node_modules/@graphql-codegen/client-preset": { @@ -3604,13 +3707,13 @@ } }, "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/fetch": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.5.tgz", - "integrity": "sha512-+yFJU3hmXPAHJULwx0VzCIsvr/H0lvbPvbOH3areOH3NAuCxCwaJsQ8w6/MwwMcvEWIynSsmAxoyaH04KeosPg==", + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz", + "integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==", "dev": true, "license": "MIT", "dependencies": { - "@whatwg-node/node-fetch": "^0.7.11", + "@whatwg-node/node-fetch": "^0.7.19", "urlpattern-polyfill": "^10.0.0" }, "engines": { @@ -3618,15 +3721,15 @@ } }, "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.17.tgz", - "integrity": "sha512-Ni8A2H/r6brNf4u8Y7ATxmWUD0xltsQ6a4NnjWSbw4PgaT34CbY+u4QtVsFj9pTC3dBKJADKjac3AieAig+PZA==", + "version": "0.7.20", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz", + "integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==", "dev": true, "license": "MIT", "dependencies": { + "@fastify/busboy": "^3.1.1", "@whatwg-node/disposablestack": "^0.0.6", - "@whatwg-node/promise-helpers": "^1.2.5", - "busboy": "^1.6.0", + "@whatwg-node/promise-helpers": "^1.3.2", "tslib": "^2.6.3" }, "engines": { @@ -3913,13 +4016,13 @@ } }, "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/fetch": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.5.tgz", - "integrity": "sha512-+yFJU3hmXPAHJULwx0VzCIsvr/H0lvbPvbOH3areOH3NAuCxCwaJsQ8w6/MwwMcvEWIynSsmAxoyaH04KeosPg==", + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz", + "integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==", "dev": true, "license": "MIT", "dependencies": { - "@whatwg-node/node-fetch": "^0.7.11", + "@whatwg-node/node-fetch": "^0.7.19", "urlpattern-polyfill": "^10.0.0" }, "engines": { @@ -3927,15 +4030,15 @@ } }, "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.17.tgz", - "integrity": "sha512-Ni8A2H/r6brNf4u8Y7ATxmWUD0xltsQ6a4NnjWSbw4PgaT34CbY+u4QtVsFj9pTC3dBKJADKjac3AieAig+PZA==", + "version": "0.7.20", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz", + "integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==", "dev": true, "license": "MIT", "dependencies": { + "@fastify/busboy": "^3.1.1", "@whatwg-node/disposablestack": "^0.0.6", - "@whatwg-node/promise-helpers": "^1.2.5", - "busboy": "^1.6.0", + "@whatwg-node/promise-helpers": "^1.3.2", "tslib": "^2.6.3" }, "engines": { @@ -4114,13 +4217,13 @@ } }, "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/fetch": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.5.tgz", - "integrity": "sha512-+yFJU3hmXPAHJULwx0VzCIsvr/H0lvbPvbOH3areOH3NAuCxCwaJsQ8w6/MwwMcvEWIynSsmAxoyaH04KeosPg==", + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz", + "integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==", "dev": true, "license": "MIT", "dependencies": { - "@whatwg-node/node-fetch": "^0.7.11", + "@whatwg-node/node-fetch": "^0.7.19", "urlpattern-polyfill": "^10.0.0" }, "engines": { @@ -4128,15 +4231,15 @@ } }, "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.17.tgz", - "integrity": "sha512-Ni8A2H/r6brNf4u8Y7ATxmWUD0xltsQ6a4NnjWSbw4PgaT34CbY+u4QtVsFj9pTC3dBKJADKjac3AieAig+PZA==", + "version": "0.7.20", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz", + "integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==", "dev": true, "license": "MIT", "dependencies": { + "@fastify/busboy": "^3.1.1", "@whatwg-node/disposablestack": "^0.0.6", - "@whatwg-node/promise-helpers": "^1.2.5", - "busboy": "^1.6.0", + "@whatwg-node/promise-helpers": "^1.3.2", "tslib": "^2.6.3" }, "engines": { @@ -4481,13 +4584,13 @@ } }, "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/fetch": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.5.tgz", - "integrity": "sha512-+yFJU3hmXPAHJULwx0VzCIsvr/H0lvbPvbOH3areOH3NAuCxCwaJsQ8w6/MwwMcvEWIynSsmAxoyaH04KeosPg==", + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz", + "integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==", "dev": true, "license": "MIT", "dependencies": { - "@whatwg-node/node-fetch": "^0.7.11", + "@whatwg-node/node-fetch": "^0.7.19", "urlpattern-polyfill": "^10.0.0" }, "engines": { @@ -4495,15 +4598,15 @@ } }, "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.17.tgz", - "integrity": "sha512-Ni8A2H/r6brNf4u8Y7ATxmWUD0xltsQ6a4NnjWSbw4PgaT34CbY+u4QtVsFj9pTC3dBKJADKjac3AieAig+PZA==", + "version": "0.7.20", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz", + "integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==", "dev": true, "license": "MIT", "dependencies": { + "@fastify/busboy": "^3.1.1", "@whatwg-node/disposablestack": "^0.0.6", - "@whatwg-node/promise-helpers": "^1.2.5", - "busboy": "^1.6.0", + "@whatwg-node/promise-helpers": "^1.3.2", "tslib": "^2.6.3" }, "engines": { @@ -4625,13 +4728,13 @@ } }, "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/fetch": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.5.tgz", - "integrity": "sha512-+yFJU3hmXPAHJULwx0VzCIsvr/H0lvbPvbOH3areOH3NAuCxCwaJsQ8w6/MwwMcvEWIynSsmAxoyaH04KeosPg==", + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz", + "integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==", "dev": true, "license": "MIT", "dependencies": { - "@whatwg-node/node-fetch": "^0.7.11", + "@whatwg-node/node-fetch": "^0.7.19", "urlpattern-polyfill": "^10.0.0" }, "engines": { @@ -4639,15 +4742,15 @@ } }, "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.17.tgz", - "integrity": "sha512-Ni8A2H/r6brNf4u8Y7ATxmWUD0xltsQ6a4NnjWSbw4PgaT34CbY+u4QtVsFj9pTC3dBKJADKjac3AieAig+PZA==", + "version": "0.7.20", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz", + "integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==", "dev": true, "license": "MIT", "dependencies": { + "@fastify/busboy": "^3.1.1", "@whatwg-node/disposablestack": "^0.0.6", - "@whatwg-node/promise-helpers": "^1.2.5", - "busboy": "^1.6.0", + "@whatwg-node/promise-helpers": "^1.3.2", "tslib": "^2.6.3" }, "engines": { @@ -5018,9 +5121,9 @@ } }, "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5785,6 +5888,7 @@ "version": "2.8.17", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -6050,9 +6154,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -6199,9 +6303,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -6236,9 +6340,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -6359,9 +6463,9 @@ } }, "node_modules/@whatwg-node/promise-helpers": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.0.tgz", - "integrity": "sha512-486CouizxHXucj8Ky153DDragfkMcHtVEToF5Pn/fInhUUSiCmt9Q4JVBa6UK5q4RammFBtGQ4C9qhGlXU9YbA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz", + "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==", "dev": true, "license": "MIT", "dependencies": { @@ -8680,9 +8784,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -9248,9 +9352,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -9260,31 +9364,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" } }, "node_modules/esbuild-register": { @@ -11834,9 +11938,9 @@ } }, "node_modules/jsonwebtoken/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -12582,9 +12686,9 @@ } }, "node_modules/nodemon/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -14242,9 +14346,9 @@ } }, "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -16293,9 +16397,9 @@ } }, "node_modules/yarn-deduplicate/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" diff --git a/package.json b/package.json index 91281b7..afe8a13 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,13 @@ "@apollo/server": "^4.11.2", "@chakra-ui/react": "^3.15.0", "@chakra-ui/toast": "^7.0.2", + "@coolbytes/editorjs-link": "^1.0.0", "@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", "@hookform/resolvers": "^3.9.1", "@prisma/client": "^6.4.1", @@ -53,8 +59,8 @@ "@tanstack/router-devtools": "^1.85.0", "@tanstack/router-plugin": "^1.84.4", "@types/bcrypt": "^5.0.2", - "@types/cors": "^2.8.17", "@types/cookie-parser": "^1.4.8", + "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/jsonwebtoken": "^9.0.9", "@types/node": "^20.9.3",