;
- }
->;
-
-function A2ZTypography({
- sx,
- children,
+const A2ZTypography = ({
+ text,
+ variant,
component,
- ...props
-}: QSTypographyProps
) {
+ noWrap,
+ props,
+}: {
+ text: string;
+ variant?:
+ | 'body1'
+ | 'body2'
+ | 'caption'
+ | 'subtitle1'
+ | 'subtitle2'
+ | 'h1'
+ | 'h2'
+ | 'h3'
+ | 'h4'
+ | 'h5'
+ | 'h6';
+ component?: 'div' | 'span' | 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
+ noWrap?: boolean;
+ props?: Record;
+}) => {
return (
- {children ? children : null}
+ {text}
);
-}
-
-export function A2ZTypographyTooltip({
- sx,
- children,
- component,
- tooltipProps,
- ...props
-}: QSTypographyProps
) {
- return (
-
-
- {children ? children : null}
-
-
- );
-}
+};
export default A2ZTypography;
diff --git a/client/src/shared/components/molecules/load-more-data/index.tsx b/client/src/shared/components/molecules/load-more-data/index.tsx
deleted file mode 100644
index cccfd6d14..000000000
--- a/client/src/shared/components/molecules/load-more-data/index.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-interface PaginationState {
- results: T[];
- page: number;
- totalDocs: number;
-}
-
-interface LoadMoreDataBtnProps {
- state: PaginationState | null;
- fetchDataFun: (params: Record) => void;
- additionalParam?: Record;
-}
-
-const LoadMoreDataBtn = ({
- state,
- fetchDataFun,
- additionalParam = {},
-}: LoadMoreDataBtnProps) => {
- if (state && state.results && state.totalDocs > state.results.length) {
- return (
-
- );
- }
-};
-
-export default LoadMoreDataBtn;
diff --git a/client/src/shared/components/molecules/navbar/components/subscriberModal.tsx b/client/src/shared/components/molecules/navbar/components/subscriberModal.tsx
deleted file mode 100644
index 42376b4cc..000000000
--- a/client/src/shared/components/molecules/navbar/components/subscriberModal.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-/** @jsxImportSource @emotion/react */
-import { css } from '@emotion/react';
-import { Box } from '@mui/material';
-import { Dispatch, SetStateAction, useState } from 'react';
-import InputBox from '../../../atoms/input-box';
-import CAModal from '../../../atoms/modal';
-import { useNotifications } from '../../../../hooks/use-notification';
-import { emailRegex } from '../../../../utils/regex';
-import { subscribeUser } from '../requests';
-
-const SubscribeModal = ({
- showSubscribeModal,
- setShowSubscribeModal,
-}: {
- showSubscribeModal: boolean;
- setShowSubscribeModal: Dispatch>;
-}) => {
- const { addNotification } = useNotifications();
- const [email, setEmail] = useState('');
-
- const handleSubscribe = async () => {
- if (!email.trim().length) {
- addNotification({
- message: 'Email is required',
- type: 'error',
- });
- return;
- }
-
- if (!emailRegex.test(email)) {
- addNotification({
- message: 'Please enter a valid email',
- type: 'error',
- });
- return;
- }
-
- const response = await subscribeUser(email);
- addNotification({
- message: response.message,
- type: response.status === 200 ? 'success' : 'error',
- });
- setShowSubscribeModal(false);
- setEmail('');
- };
-
- return (
- {
- setShowSubscribeModal(false);
- setEmail('');
- }}
- >
-
- Subscribe to Newsletter
-
- Stay updated with our latest articles, projects, and announcements.
-
-
-
-
-
-
-
- );
-};
-
-export default SubscribeModal;
diff --git a/client/src/shared/components/molecules/navbar/components/userNavigationPanel.tsx b/client/src/shared/components/molecules/navbar/components/userNavigationPanel.tsx
deleted file mode 100644
index 952bc7b49..000000000
--- a/client/src/shared/components/molecules/navbar/components/userNavigationPanel.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Link } from 'react-router-dom';
-import AnimationWrapper from '../../../atoms/page-animation';
-import { removeFromSession } from '../../../../utils/session';
-import { useAtom } from 'jotai';
-import { UserAtom } from '../../../../states/user';
-import { emptyUserState } from '../../../../states/emptyStates/user';
-
-const UserNavigationPanel = () => {
- const [user, setUser] = useAtom(UserAtom);
- const signOutUser = () => {
- removeFromSession('user');
- setUser(emptyUserState);
- };
-
- return (
-
-
-
-
-
Write
-
-
-
- Profile
-
-
-
- Dashboard
-
-
-
- Settings
-
-
-
-
-
-
-
- );
-};
-
-export default UserNavigationPanel;
diff --git a/client/src/shared/components/molecules/navbar/index.tsx b/client/src/shared/components/molecules/navbar/index.tsx
deleted file mode 100644
index e4af7023a..000000000
--- a/client/src/shared/components/molecules/navbar/index.tsx
+++ /dev/null
@@ -1,366 +0,0 @@
-import { useEffect, useRef, useState } from 'react';
-import { Link, Outlet, useNavigate } from 'react-router-dom';
-import { useAtom } from 'jotai';
-import { styled, alpha } from '@mui/material/styles';
-import AppBar from '@mui/material/AppBar';
-import Box from '@mui/material/Box';
-import Toolbar from '@mui/material/Toolbar';
-import IconButton from '@mui/material/IconButton';
-import InputBase from '@mui/material/InputBase';
-import Badge from '@mui/material/Badge';
-import MailIcon from '@mui/icons-material/Mail';
-import SearchIcon from '@mui/icons-material/Search';
-import AccountCircle from '@mui/icons-material/AccountCircle';
-import CreateIcon from '@mui/icons-material/Create';
-import NotificationsIcon from '@mui/icons-material/Notifications';
-import Button from '@mui/material/Button';
-
-import A2ZTypography from '../../atoms/typography';
-import UserNavigationPanel from './components/userNavigationPanel';
-import SubscribeModal from './components/subscriberModal';
-import ThemeToggle from '../theme-toggler';
-import { UserAtom } from '../../../states/user';
-import { checkNewNotifications } from './requests';
-import { useDevice } from '../../../hooks/use-device';
-
-const Search = styled('div')(({ theme }) => ({
- position: 'relative',
- borderRadius: theme.shape.borderRadius,
- backgroundColor: alpha(theme.palette.common.white, 0.15),
- '&:hover': {
- backgroundColor: alpha(theme.palette.common.white, 0.25),
- },
- marginRight: theme.spacing(2),
- marginLeft: 0,
- width: '100%',
- [theme.breakpoints.up('sm')]: {
- marginLeft: theme.spacing(3),
- width: 'auto',
- },
-}));
-
-const SearchIconWrapper = styled('div')(({ theme }) => ({
- padding: theme.spacing(0, 2),
- height: '100%',
- position: 'absolute',
- pointerEvents: 'none',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
-}));
-
-const StyledInputBase = styled(InputBase)(({ theme }) => ({
- color: 'inherit',
- '& .MuiInputBase-input': {
- padding: theme.spacing(1, 1, 1, 0),
- // vertical padding + font size from searchIcon
- paddingLeft: `calc(1em + ${theme.spacing(4)})`,
- transition: theme.transitions.create('width'),
- width: '100%',
- [theme.breakpoints.up('md')]: {
- width: '20ch',
- },
- },
-}));
-
-const Navbar = () => {
- const [user, setUser] = useAtom(UserAtom);
- const [searchBoxVisibility, setSearchBoxVisibility] = useState(false);
- const [userNavPanel, setUserNavPanel] = useState(false);
- const [showSubscribeModal, setShowSubscribeModal] = useState(false);
- const searchRef = useRef(null);
- const { isDesktop } = useDevice();
-
- const navigate = useNavigate();
-
- useEffect(() => {
- const fetchNotifications = async () => {
- if (user?.access_token) {
- const response = await checkNewNotifications();
- if (response.status === 200) {
- setUser(prev => ({
- ...prev,
- ...(response as { data: { new_notification_available: boolean } })
- .data,
- }));
- }
- }
- };
- fetchNotifications();
- }, [user?.access_token, setUser]);
-
- const handleUserNavPanel = () => {
- setUserNavPanel(currentVal => !currentVal);
- };
-
- const handleSearch = (e: React.KeyboardEvent) => {
- const query = e.currentTarget.value;
-
- if (e.keyCode === 13 && query.length) {
- navigate(`/search/${query}`);
- }
- };
-
- useEffect(() => {
- const handleKeyDown = (event: KeyboardEvent) => {
- if (
- (event.metaKey && event.key === 'k') ||
- (event.ctrlKey && event.key === 'k')
- ) {
- event.preventDefault();
- event.stopPropagation();
-
- if (isDesktop) {
- setTimeout(() => {
- if (searchRef.current) {
- searchRef.current.focus();
- searchRef.current.select();
- }
- }, 10);
- } else {
- setSearchBoxVisibility(true);
- setTimeout(() => {
- if (searchRef.current) {
- searchRef.current.focus();
- }
- }, 100);
- }
- }
- };
-
- document.addEventListener('keydown', handleKeyDown, true);
- return () => {
- document.removeEventListener('keydown', handleKeyDown, true);
- };
- }, [isDesktop]);
-
- const handleBlur = () => {
- setTimeout(() => {
- setUserNavPanel(false);
- }, 200);
- };
-
- return (
- <>
-
-
-
-
-
-
-
-
-
- Code A2Z
-
-
-
-
-
-
-
- setSearchBoxVisibility(true)}
- onBlur={() => {
- if (window.innerWidth < 1024) {
- setSearchBoxVisibility(false);
- }
- }}
- />
-
-
-
-
- setSearchBoxVisibility(prev => !prev)}
- aria-label="open search"
- >
-
-
-
-
-
-
-
-
-
-
- }
- sx={{ display: { xs: 'none', md: 'inline-flex' } }}
- component={Link}
- to="/editor"
- color="inherit"
- variant="text"
- >
- Write
-
-
-
-
-
-
- {user?.access_token ? (
- <>
-
-
-
-
-
-
-
-
-
-
- {userNavPanel ? : null}
-
- >
- ) : (
- <>
- setShowSubscribeModal(true)}
- >
-
-
-
-
- >
- )}
-
-
-
-
- ({
- display: { xs: searchBoxVisibility ? 'block' : 'none', lg: 'none' },
- position: 'absolute',
- left: 0,
- top: '100%',
- mt: '0.5rem',
- width: '100%',
- bgcolor: theme.palette.background.default,
- borderBottom: `1px solid ${theme.palette.divider}`,
- py: 2,
- px: '5vw',
- zIndex: 10,
- })}
- >
-
-
-
-
-
- setSearchBoxVisibility(true)}
- onBlur={() => {
- if (window.innerWidth < 1024) {
- setTimeout(() => setSearchBoxVisibility(false), 100);
- }
- }}
- sx={{
- width: '100%',
- bgcolor: 'background.paper',
- p: 1,
- borderRadius: 9999,
- }}
- />
-
-
-
-
-
-
-
-
- >
- );
-};
-
-export default Navbar;
diff --git a/client/src/shared/components/molecules/navbar/requests/index.ts b/client/src/shared/components/molecules/navbar/requests/index.ts
deleted file mode 100644
index edf0ffdd5..000000000
--- a/client/src/shared/components/molecules/navbar/requests/index.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { get, post } from '../../../../../infra/rest';
-import {
- CheckNewNotificationsResponse,
- SubscribeUserPayload,
- SubscribeUserResponse,
-} from '../typings';
-
-export const checkNewNotifications = async () => {
- return get(
- `/api/notifications/new`,
- true
- );
-};
-
-export const subscribeUser = async (email: string) => {
- return post(
- `/api/subscriber/subscribe`,
- false,
- {
- email,
- }
- );
-};
diff --git a/client/src/shared/components/molecules/navbar/typings/index.ts b/client/src/shared/components/molecules/navbar/typings/index.ts
deleted file mode 100644
index 2d947eb5b..000000000
--- a/client/src/shared/components/molecules/navbar/typings/index.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-export interface SubscribeUserPayload {
- email: string;
-}
-
-export interface SubscribeUserResponse {
- status: number;
- message: string;
-}
-
-export interface CheckNewNotificationsResponse {
- status: number;
- data: {
- new_notification_available: boolean;
- };
-}
diff --git a/client/src/shared/components/molecules/notification/index.tsx b/client/src/shared/components/molecules/notification/index.tsx
new file mode 100644
index 000000000..27bef694f
--- /dev/null
+++ b/client/src/shared/components/molecules/notification/index.tsx
@@ -0,0 +1,65 @@
+import { useAtom } from 'jotai';
+import {
+ NotificationSystemAtom,
+ NotificationType,
+} from '../../../states/notification';
+import { Snackbar, Alert, Slide, SlideProps } from '@mui/material';
+import { SyntheticEvent } from 'react';
+
+function SlideTransition(props: SlideProps) {
+ return ; // slide in from right side
+}
+
+function A2ZNotifications() {
+ const [notifications, setNotifications] = useAtom(NotificationSystemAtom);
+
+ const handleClose = (
+ id: string,
+ event?: SyntheticEvent | Event,
+ reason?: string
+ ) => {
+ if (reason === 'clickaway') return;
+ setNotifications(prev =>
+ prev.map(n => (n.id === id ? { ...n, open: false } : n))
+ );
+ };
+
+ return (
+ <>
+ {notifications.map(
+ ({
+ id,
+ message,
+ type = NotificationType.INFO,
+ open,
+ autoHideDuration,
+ }) => (
+ handleClose(id, event, reason)}
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
+ TransitionComponent={SlideTransition}
+ sx={{ zIndex: 1400 }}
+ >
+ handleClose(id, event)}
+ severity={type}
+ variant="outlined"
+ sx={{
+ width: '100%',
+ borderRadius: '10px',
+ boxShadow: theme => theme.shadows[6],
+ }}
+ >
+ {message}
+
+
+ )
+ )}
+ >
+ );
+}
+
+export default A2ZNotifications;
diff --git a/client/src/shared/components/molecules/notifications/index.tsx b/client/src/shared/components/molecules/notifications/index.tsx
deleted file mode 100644
index c0fe4d0c3..000000000
--- a/client/src/shared/components/molecules/notifications/index.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-/** @jsxImportSource @emotion/react */
-import { css } from '@emotion/react';
-import { useAtomValue } from 'jotai';
-import { notificationsAtom } from '../../../states/notification';
-import { NotificationType } from '../../../../infra/rest/typings/notification';
-import { Alert, Slide, IconButton, Collapse } from '@mui/material';
-import { Close as CloseIcon } from '@mui/icons-material';
-import { useState } from 'react';
-
-function CANotifications() {
- const notifications = useAtomValue(notificationsAtom);
- const [dismissedNotifications, setDismissedNotifications] = useState<
- Set
- >(new Set());
-
- const handleDismiss = (id: string) => {
- setDismissedNotifications(prev => new Set([...prev, id]));
- };
-
- return (
-
- {notifications.map(
- ({ id, message, type = NotificationType.INFO, open }) => {
- const isDismissed = dismissedNotifications.has(id);
-
- return (
-
-
- handleDismiss(id)}
- sx={{
- color: 'inherit',
- '&:hover': {
- backgroundColor: 'rgba(0, 0, 0, 0.04)',
- },
- }}
- >
-
-
- }
- >
- {message}
-
-
-
- );
- }
- )}
-
- );
-}
-
-export default CANotifications;
diff --git a/client/src/shared/components/molecules/page-navigation/index.tsx b/client/src/shared/components/molecules/page-navigation/index.tsx
index 08bcab89c..b27c026ad 100644
--- a/client/src/shared/components/molecules/page-navigation/index.tsx
+++ b/client/src/shared/components/molecules/page-navigation/index.tsx
@@ -1,11 +1,14 @@
-import { useEffect, useState } from 'react';
-import { activeTabLineRef, activeTabRef } from './refs';
+import { useState } from 'react';
+import { Box, Tabs, Tab, useTheme } from '@mui/material';
interface InPageNavigationProps {
routes: string[];
defaultHidden?: string[];
defaultActiveIndex?: number;
children: React.ReactNode;
+ variant?: 'scrollable' | 'fullWidth' | 'standard';
+ scrollButtons?: 'auto' | true | false;
+ tabProps?: React.ComponentProps;
}
const InPageNavigation = ({
@@ -13,70 +16,58 @@ const InPageNavigation = ({
defaultHidden = [],
defaultActiveIndex = 0,
children,
+ variant = 'standard',
+ scrollButtons = 'auto',
+ tabProps,
}: InPageNavigationProps) => {
- const [inPageNavIndex, setInPageNavIndex] = useState(defaultActiveIndex);
+ const [value, setValue] = useState(defaultActiveIndex);
+ const theme = useTheme();
- const [isResizeEventAdded, setIsResizeEventAdded] = useState(false);
- const [width, setWidth] = useState(window.innerWidth);
-
- const changePageState = (btn: EventTarget, i: number) => {
- const { offsetWidth, offsetLeft } = btn as HTMLElement;
- if (activeTabLineRef && activeTabLineRef.current) {
- activeTabLineRef.current.style.width = offsetWidth + 'px';
- activeTabLineRef.current.style.left = offsetLeft + 'px';
- setInPageNavIndex(i);
- }
+ const handleChange = (_: React.SyntheticEvent, newValue: number) => {
+ setValue(newValue);
};
- useEffect(() => {
- if (
- width > 766 &&
- inPageNavIndex !== defaultActiveIndex &&
- activeTabRef &&
- activeTabRef.current
- ) {
- changePageState(activeTabRef.current, defaultActiveIndex);
- }
-
- if (!isResizeEventAdded) {
- window.addEventListener('resize', () => {
- if (!isResizeEventAdded) {
- setIsResizeEventAdded(true);
- }
-
- setWidth(window.innerWidth);
- });
- }
- }, [width, defaultActiveIndex, inPageNavIndex, isResizeEventAdded]);
-
return (
<>
-
- {routes.map((route, i) => {
- return (
-
- );
- })}
-
-
-
+ />
+ ))}
+
+
- {Array.isArray(children) ? children[inPageNavIndex] : children}
+ {/* Tab content */}
+ {Array.isArray(children) ? children[value] : children}
>
);
};
diff --git a/client/src/shared/components/molecules/page-navigation/refs.ts b/client/src/shared/components/molecules/page-navigation/refs.ts
deleted file mode 100644
index eb08e85ea..000000000
--- a/client/src/shared/components/molecules/page-navigation/refs.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { createRef, RefObject } from 'react';
-
-export const activeTabLineRef: RefObject = createRef();
-export const activeTabRef: RefObject = createRef();
diff --git a/client/src/shared/components/molecules/project-card/index.tsx b/client/src/shared/components/molecules/project-card/index.tsx
deleted file mode 100644
index defa41bc5..000000000
--- a/client/src/shared/components/molecules/project-card/index.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import { Link } from 'react-router-dom';
-import { getDay } from '../../../utils/date';
-import { Project } from '../../../typings';
-
-interface ProjectCardProps {
- project: Project;
- author: {
- fullname: string;
- profile_img: string;
- username: string;
- };
-}
-
-const ProjectPostCard = ({ project, author }: ProjectCardProps) => {
- const {
- publishedAt,
- tags,
- title,
- des,
- banner,
- activity: { total_likes },
- project_id: id,
- } = project;
- const { fullname, profile_img, username } = author;
-
- return (
-
-
-
-

-
- {fullname} @{username}
-
-
{getDay(publishedAt)}
-
-
-
{title}
-
- {des}
-
-
-
- {tags[0]}
-
-
- {total_likes}
-
-
-
-
-

-
-
- );
-};
-
-export default ProjectPostCard;
diff --git a/client/src/shared/components/molecules/sidebar/index.tsx b/client/src/shared/components/molecules/sidebar/index.tsx
deleted file mode 100644
index 49a4d3849..000000000
--- a/client/src/shared/components/molecules/sidebar/index.tsx
+++ /dev/null
@@ -1,141 +0,0 @@
-import { useAtomValue } from 'jotai';
-import { useEffect, useRef, useState } from 'react';
-import { Navigate, NavLink, Outlet } from 'react-router-dom';
-import { UserAtom } from '../../../states/user';
-
-const Sidebar = () => {
- const user = useAtomValue(UserAtom);
- const page = location.pathname.split('/')[2];
-
- const [pageState, setPageState] = useState(page.replace('-', ' '));
- const [showSideNav, setShowSideNav] = useState(false);
-
- const activeTabLine = useRef(null);
- const sideBarIconTab = useRef(null);
- const pageStateTab = useRef(null);
-
- const changePageState = (
- e: React.MouseEvent
- ) => {
- const { offsetWidth, offsetLeft } = e.currentTarget;
- if (!activeTabLine?.current) return;
- activeTabLine.current.style.width = offsetWidth + 'px';
- activeTabLine.current.style.left = offsetLeft + 'px';
-
- if (e.target === sideBarIconTab.current) {
- setShowSideNav(true);
- } else {
- setShowSideNav(false);
- }
- };
-
- useEffect(() => {
- setShowSideNav(false);
- if (pageStateTab.current) {
- pageStateTab.current.click();
- }
- }, [pageState]);
-
- if (!user || !user?.access_token) {
- return ;
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- Dashboard
-
-
-
-
setPageState(e.currentTarget.innerText)}
- className="sidebar-link"
- >
-
- Projects
-
-
setPageState(e.currentTarget.innerText)}
- className="sidebar-link"
- >
-
-
- {user?.new_notification_available ? (
-
- ) : (
- ''
- )}
-
- Notification
-
-
setPageState(e.currentTarget.innerText)}
- className="sidebar-link"
- >
-
- Write
-
-
-
- Settings
-
-
-
-
setPageState(e.currentTarget.innerText)}
- className="sidebar-link"
- >
-
- Edit Profile
-
-
setPageState(e.currentTarget.innerText)}
- className="sidebar-link"
- >
-
- Change Password
-
-
-
-
-
-
-
-
- );
-};
-
-export default Sidebar;
diff --git a/client/src/shared/components/molecules/theme-toggler/index.tsx b/client/src/shared/components/molecules/theme-toggler/index.tsx
deleted file mode 100644
index 1c040c250..000000000
--- a/client/src/shared/components/molecules/theme-toggler/index.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import MenuItem from '@mui/material/MenuItem';
-import FormControl from '@mui/material/FormControl';
-import Select, { SelectChangeEvent } from '@mui/material/Select';
-import LightModeIcon from '@mui/icons-material/LightMode';
-import DarkModeIcon from '@mui/icons-material/DarkMode';
-import ComputerIcon from '@mui/icons-material/Computer';
-import { useTheme } from '../../../hooks/use-theme';
-import { Theme } from '../../../states/theme';
-
-const ThemeToggle = () => {
- const { theme, setTheme } = useTheme();
-
- const handleChange = (event: SelectChangeEvent) => {
- setTheme(event.target.value as Theme);
- };
-
- return (
-
-
-
- );
-};
-
-export default ThemeToggle;
diff --git a/client/src/shared/theme/MuiThemeProvider.tsx b/client/src/shared/components/molecules/theme/index.tsx
similarity index 66%
rename from client/src/shared/theme/MuiThemeProvider.tsx
rename to client/src/shared/components/molecules/theme/index.tsx
index 8270a3424..059aa6ef4 100644
--- a/client/src/shared/theme/MuiThemeProvider.tsx
+++ b/client/src/shared/components/molecules/theme/index.tsx
@@ -1,15 +1,14 @@
import React from 'react';
-import { ThemeProvider, CssBaseline } from '@mui/material';
import { useAtom } from 'jotai';
-import { resolvedThemeAtom } from '../states/theme';
+import { ThemeProvider, CssBaseline } from '@mui/material';
+import { ResolvedThemeAtom, THEME } from '../../../states/theme';
import getMuiTheme from './mui';
const MuiThemeProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
- const [resolved] = useAtom(resolvedThemeAtom);
-
- const theme = getMuiTheme(resolved === 'dark' ? 'dark' : 'light');
+ const [resolved] = useAtom(ResolvedThemeAtom);
+ const theme = getMuiTheme(resolved === THEME.DARK ? THEME.DARK : THEME.LIGHT);
return (
diff --git a/client/src/shared/theme/mui.ts b/client/src/shared/components/molecules/theme/mui.ts
similarity index 54%
rename from client/src/shared/theme/mui.ts
rename to client/src/shared/components/molecules/theme/mui.ts
index 8dfad9176..12414fa68 100644
--- a/client/src/shared/theme/mui.ts
+++ b/client/src/shared/components/molecules/theme/mui.ts
@@ -1,39 +1,41 @@
import { createTheme, type ThemeOptions } from '@mui/material/styles';
const LIGHT_MODE_THEME = {
- primary: { main: '#2563eb', contrastText: '#ffffff' },
- secondary: { main: '#7c3aed', contrastText: '#ffffff' },
+ primary: { main: '#2563eb', contrastText: '#ffffff' }, // refined blue accent
+ secondary: { main: '#6366f1', contrastText: '#ffffff' }, // indigo tone for secondary actions
error: { main: '#dc2626' },
warning: { main: '#f59e0b' },
- info: { main: '#0284c7' },
+ info: { main: '#0ea5e9' },
success: { main: '#16a34a' },
background: {
- default: '#f8fafc',
- paper: '#ffffff',
+ default: '#f9fafb', // very light neutral gray
+ paper: '#ffffff', // clean white surfaces
},
text: {
- primary: '#111827',
- secondary: '#4b5563',
+ primary: '#111827', // dark gray for readability
+ secondary: '#6b7280', // muted gray for subtext
},
+ divider: '#e5e7eb',
};
const DARK_MODE_THEME = {
- primary: { main: '#3b82f6', contrastText: '#ffffff' },
- secondary: { main: '#8b5cf6', contrastText: '#ffffff' },
+ primary: { main: '#3b82f6', contrastText: '#ffffff' }, // same hue for continuity
+ secondary: { main: '#818cf8', contrastText: '#ffffff' }, // soft indigo accent
error: { main: '#f87171' },
warning: { main: '#fbbf24' },
info: { main: '#38bdf8' },
success: { main: '#22c55e' },
background: {
- default: '#18181b',
- paper: '#1f1f23',
+ default: '#0f1115', // deep gray with blue undertone (not pure black)
+ paper: '#181b20', // slightly lifted surface tone
},
text: {
- primary: '#f1f5f9',
- secondary: '#9ca3af',
+ primary: '#f3f4f6', // clean white-gray text
+ secondary: '#9ca3af', // soft muted gray for paragraphs
},
+ divider: '#27272a',
};
export const getMuiTheme = (mode: 'light' | 'dark') => {
diff --git a/client/src/shared/components/molecules/user-card/index.tsx b/client/src/shared/components/molecules/user-card/index.tsx
deleted file mode 100644
index 0bf01a02a..000000000
--- a/client/src/shared/components/molecules/user-card/index.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Link } from 'react-router-dom';
-
-const UserCard = ({
- user,
-}: {
- user: {
- personal_info: {
- fullname: string;
- username: string;
- profile_img: string;
- };
- };
-}) => {
- const {
- personal_info: { fullname, username, profile_img },
- } = user;
-
- return (
-
-
-
-
-
{fullname}
-
@{username}
-
-
- );
-};
-
-export default UserCard;
diff --git a/client/src/shared/components/organisms/comments-wrapper/components/comment-card.tsx b/client/src/shared/components/organisms/comments-wrapper/components/comment-card.tsx
new file mode 100644
index 000000000..05f4f81fa
--- /dev/null
+++ b/client/src/shared/components/organisms/comments-wrapper/components/comment-card.tsx
@@ -0,0 +1,411 @@
+import { useState } from 'react';
+import { useAtom, useAtomValue, useSetAtom } from 'jotai';
+import {
+ Avatar,
+ Box,
+ Button,
+ Divider,
+ IconButton,
+ Typography,
+ useTheme,
+} from '@mui/material';
+import ChatBubbleOutlineIcon from '@mui/icons-material/ChatBubbleOutline';
+import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
+import {
+ AllCommentsAtom,
+ Comments,
+ TotalParentCommentsLoadedAtom,
+} from '../states';
+import { UserAtom } from '../../../../../infra/states/user';
+import { SelectedProjectAtom } from '../../../../../modules/project/states';
+import { useNotifications } from '../../../../hooks/use-notification';
+import {
+ deleteComment,
+ getReplies,
+} from '../../../../../infra/rest/apis/comment';
+import CommentField from './comment-field';
+import { GetRepliesResponse } from '../../../../../infra/rest/apis/comment/typing';
+import { useAuth } from '../../../../hooks/use-auth';
+import { getDay } from '../../../../utils/date';
+
+const CommentCard = ({
+ index,
+ leftVal,
+ comment,
+}: {
+ index: number;
+ leftVal: number;
+ comment: Comments;
+}) => {
+ const user = useAtomValue(UserAtom);
+ const [selectedProject, setSelectedProject] = useAtom(SelectedProjectAtom);
+ const setTotalParentCommentsLoaded = useSetAtom(
+ TotalParentCommentsLoadedAtom
+ );
+ const [comments, setComments] = useAtom(AllCommentsAtom);
+ const { addNotification } = useNotifications();
+ const { isAuthenticated } = useAuth();
+ const theme = useTheme();
+
+ const [isReplying, setReplying] = useState(false);
+
+ if (!selectedProject || !comments) return null;
+
+ const getParentIndex = (): number | undefined => {
+ let startingPoint = index - 1;
+
+ try {
+ while (
+ startingPoint >= 0 &&
+ comment.children_level !== undefined &&
+ comments[startingPoint] &&
+ (comments[startingPoint]?.children_level ?? 0) >= comment.children_level
+ ) {
+ startingPoint--;
+ }
+ } catch {
+ return undefined;
+ }
+
+ return startingPoint >= 0 ? startingPoint : undefined;
+ };
+
+ const removeCommentsCards = (startingPoint: number, isDelete = false) => {
+ const newCommentsArr = [...comments];
+
+ if (newCommentsArr[startingPoint]) {
+ while (
+ comment.children_level !== undefined &&
+ newCommentsArr[startingPoint] &&
+ (newCommentsArr[startingPoint]?.children_level ?? 0) >
+ comment.children_level
+ ) {
+ newCommentsArr.splice(startingPoint, 1);
+ if (!newCommentsArr[startingPoint]) {
+ break;
+ }
+ }
+ }
+
+ if (isDelete) {
+ const parentIndex = getParentIndex();
+
+ if (parentIndex !== undefined && newCommentsArr[parentIndex]) {
+ const parentComment = newCommentsArr[parentIndex];
+ if (parentComment) {
+ parentComment.children_comment_ids =
+ parentComment.children_comment_ids.filter(
+ child => child !== comment._id
+ );
+
+ if (!parentComment.children_comment_ids.length) {
+ (parentComment as Comments).is_reply_loaded = false;
+ }
+ }
+ }
+
+ newCommentsArr.splice(index, 1);
+ }
+
+ if (comment.children_level === 0 && isDelete) {
+ setTotalParentCommentsLoaded(prevVal => prevVal - 1);
+ }
+
+ setComments(newCommentsArr);
+ setSelectedProject({
+ ...selectedProject,
+ activity: {
+ ...selectedProject.activity,
+ total_parent_comments:
+ selectedProject.activity.total_parent_comments -
+ (comment.children_level === 0 && isDelete ? 1 : 0),
+ },
+ });
+ };
+
+ const loadReplies = async ({
+ skip = 0,
+ currentIndex = index,
+ }: {
+ skip?: number;
+ currentIndex?: number;
+ }) => {
+ if (!comments[currentIndex]?.children_comment_ids.length) return;
+ hideReplies();
+
+ try {
+ const response = await getReplies({
+ comment_id: comments[currentIndex]._id,
+ skip,
+ });
+
+ if (!response.data?.length) return;
+ const transformed = response.data.map(comment => ({
+ ...comment,
+ children_level: 0,
+ })) as (GetRepliesResponse & { children_level: number })[];
+
+ const newCommentsArr = [...comments];
+ (newCommentsArr[currentIndex] as Comments).is_reply_loaded = true;
+
+ for (let i = 0; i < response.data?.length; i++) {
+ transformed[i].children_level =
+ (comments[currentIndex]?.children_level || 0) + 1;
+ newCommentsArr.splice(currentIndex + 1 + i + skip, 0, {
+ project_id: comments[currentIndex].project_id,
+ updatedAt: comments[currentIndex].updatedAt,
+ ...transformed[i],
+ });
+ }
+ setComments(newCommentsArr);
+ } catch (err) {
+ console.log(err);
+ }
+ };
+
+ const handleDeleteComment = async (
+ e: React.MouseEvent
+ ) => {
+ const target = e.target as HTMLButtonElement;
+ target.setAttribute('disabled', 'true');
+
+ try {
+ await deleteComment(comment._id);
+
+ target.removeAttribute('disabled');
+ removeCommentsCards(index + 1, true);
+ } catch (err) {
+ target.removeAttribute('disabled');
+ console.log(err);
+ }
+ };
+
+ const hideReplies = () => {
+ (comment as Comments).is_reply_loaded = false;
+ removeCommentsCards(index + 1);
+ };
+
+ const handleReplyClick = () => {
+ if (!isAuthenticated()) {
+ return addNotification({
+ message: 'Please login to reply',
+ type: 'error',
+ });
+ }
+ setReplying(prevVal => !prevVal);
+ };
+
+ const LoadMoreRepliesButton = () => {
+ const parentIndex = getParentIndex();
+
+ const btn = (
+
+ );
+
+ if (comments[index + 1]) {
+ if (
+ (comments[index + 1]?.children_level || 0) <
+ (comments[index]?.children_level || 0)
+ ) {
+ if (
+ parentIndex !== undefined &&
+ index - parentIndex <
+ (comments[parentIndex]?.children_comment_ids.length || 0)
+ ) {
+ return btn;
+ }
+ }
+ } else {
+ if (
+ parentIndex !== undefined &&
+ index - parentIndex <
+ (comments[parentIndex]?.children_comment_ids.length || 0)
+ ) {
+ return btn;
+ }
+ }
+
+ return null;
+ };
+
+ return (
+
+
+ {/* User info */}
+
+
+
+ {comment.personal_info.fullname} @{comment.personal_info.username}
+
+
+ {getDay(comment.updatedAt)}
+
+
+
+ {/* Comment text */}
+
+ {comment.comment}
+
+
+ {/* Actions */}
+
+ {comment.is_reply_loaded ? (
+ }
+ sx={{
+ color: theme.palette.text.secondary,
+ textTransform: 'none',
+ borderRadius: 1,
+ px: 1.5,
+ '&:hover': {
+ bgcolor:
+ theme.palette.mode === 'dark'
+ ? 'rgba(255,255,255,0.05)'
+ : theme.palette.grey[100],
+ },
+ }}
+ >
+ Hide Reply
+
+ ) : (
+
+ )}
+
+
+
+ {(user?.personal_info.username === comment.personal_info.username ||
+ user?.personal_info.username ===
+ selectedProject?.user_id.personal_info.username) && (
+
+
+
+ )}
+
+
+ {/* Reply field */}
+ {isReplying && (
+
+
+
+ )}
+
+
+ {/* Load more replies button (if any) */}
+
+
+
+ );
+};
+
+export default CommentCard;
diff --git a/client/src/shared/components/organisms/comments-wrapper/components/comment-field.tsx b/client/src/shared/components/organisms/comments-wrapper/components/comment-field.tsx
new file mode 100644
index 000000000..262cc42b0
--- /dev/null
+++ b/client/src/shared/components/organisms/comments-wrapper/components/comment-field.tsx
@@ -0,0 +1,154 @@
+import { useAtom, useAtomValue, useSetAtom } from 'jotai';
+import { useState } from 'react';
+import { Box, Button, TextField } from '@mui/material';
+import {
+ AllCommentsAtom,
+ Comments,
+ TotalParentCommentsLoadedAtom,
+} from '../states';
+import { useNotifications } from '../../../../hooks/use-notification';
+import { UserAtom } from '../../../../../infra/states/user';
+import { useAuth } from '../../../../hooks/use-auth';
+import { SelectedProjectAtom } from '../../../../../modules/project/states';
+import { addComment } from '../../../../../infra/rest/apis/comment';
+
+const CommentField = ({
+ action,
+ index = undefined,
+ replyingTo = undefined,
+ setReplying,
+}: {
+ action: string;
+ index?: number;
+ replyingTo?: string;
+ setReplying: (value: boolean) => void;
+}) => {
+ const user = useAtomValue(UserAtom);
+ const [selectedProject, setSelectedProject] = useAtom(SelectedProjectAtom);
+ const setTotalParentCommentsLoaded = useSetAtom(
+ TotalParentCommentsLoadedAtom
+ );
+ const [comments, setComments] = useAtom(AllCommentsAtom);
+ const { addNotification } = useNotifications();
+ const { isAuthenticated } = useAuth();
+
+ const [comment, setComment] = useState('');
+
+ const handleComment = async () => {
+ if (!user || !isAuthenticated()) {
+ return addNotification({
+ message: 'Please login to comment',
+ type: 'error',
+ });
+ }
+ if (!comment.length) {
+ return addNotification({
+ message: 'Write something to leave a comment...',
+ type: 'error',
+ });
+ }
+ if (!selectedProject || !selectedProject._id) {
+ return addNotification({
+ message: 'Project not found',
+ type: 'error',
+ });
+ }
+
+ try {
+ const response = await addComment({
+ project_id: selectedProject._id,
+ comment,
+ notification_id: selectedProject.user_id._id,
+ replying_to: replyingTo,
+ });
+
+ if (response.data && response.data._id) {
+ setComment('');
+
+ // Add user info and required fields to the response
+ const commentWithUser: Comments = {
+ ...response.data,
+ project_id: selectedProject._id,
+ personal_info: user.personal_info,
+ is_reply: !!replyingTo,
+ parent_comment_id: replyingTo || null,
+ updatedAt: response.data.createdAt,
+ children_level: 0,
+ };
+
+ const currentComments = comments || [];
+ let newCommentArr: Comments[];
+
+ if (replyingTo && index !== undefined) {
+ // Handle reply
+ const parentComment = currentComments[index];
+ if (parentComment) {
+ parentComment.children_comment_ids.push(response.data._id);
+ commentWithUser.children_level =
+ (parentComment.children_level || 0) + 1;
+ (parentComment as Comments).is_reply_loaded = true;
+
+ newCommentArr = [...currentComments];
+ newCommentArr.splice(index + 1, 0, commentWithUser);
+ } else {
+ newCommentArr = currentComments;
+ }
+ setReplying(false);
+ } else {
+ // Handle new comment
+ newCommentArr = [commentWithUser, ...currentComments];
+ }
+
+ setComments(newCommentArr);
+ setSelectedProject({
+ ...selectedProject,
+ activity: {
+ ...selectedProject.activity,
+ total_comments: (selectedProject.activity.total_comments || 0) + 1,
+ total_parent_comments:
+ (selectedProject.activity.total_parent_comments || 0) +
+ (replyingTo ? 0 : 1),
+ },
+ });
+
+ setTotalParentCommentsLoaded(prevVal => prevVal + (replyingTo ? 0 : 1));
+ }
+ } catch (error) {
+ addNotification({
+ message: 'Failed to post comment',
+ type: 'error',
+ });
+ console.error('Comment error:', error);
+ }
+ };
+
+ return (
+
+ setComment(e.target.value)}
+ fullWidth
+ />
+
+
+ );
+};
+
+export default CommentField;
diff --git a/client/src/shared/components/organisms/comments-wrapper/hooks/index.ts b/client/src/shared/components/organisms/comments-wrapper/hooks/index.ts
new file mode 100644
index 000000000..5caf07bde
--- /dev/null
+++ b/client/src/shared/components/organisms/comments-wrapper/hooks/index.ts
@@ -0,0 +1,54 @@
+import { useAtom, useAtomValue } from 'jotai';
+import { getComments } from '../../../../../infra/rest/apis/comment';
+import { GetCommentsResponse } from '../../../../../infra/rest/apis/comment/typing';
+import { AllCommentsAtom, TotalParentCommentsLoadedAtom } from '../states';
+import { SelectedProjectAtom } from '../../../../../modules/project/states';
+
+const useCommentsWrapper = () => {
+ const [totalParentCommentsLoaded, setTotalParentCommentsLoaded] = useAtom(
+ TotalParentCommentsLoadedAtom
+ );
+ const [allComments, setAllComments] = useAtom(AllCommentsAtom);
+ const selectedProject = useAtomValue(SelectedProjectAtom);
+
+ const fetchComments = async ({ project_id }: { project_id: string }) => {
+ try {
+ const response = await getComments({
+ project_id,
+ skip: totalParentCommentsLoaded,
+ });
+ if (response.data) {
+ const transformed = response.data.map(comment => ({
+ ...comment,
+ children_level: 0,
+ })) as (GetCommentsResponse & { children_level: number })[];
+ setTotalParentCommentsLoaded(transformed.length);
+
+ if (transformed.length > 0) {
+ if (!allComments) {
+ setAllComments(transformed);
+ } else {
+ setAllComments([...allComments, ...transformed]);
+ }
+ }
+ }
+ } catch (err) {
+ console.error(err);
+ setAllComments(null);
+ }
+ };
+
+ const loadMoreComments = async () => {
+ if (!selectedProject?._id) return;
+ await fetchComments({
+ project_id: selectedProject._id,
+ });
+ };
+
+ return {
+ fetchComments,
+ loadMoreComments,
+ };
+};
+
+export default useCommentsWrapper;
diff --git a/client/src/shared/components/organisms/comments-wrapper/index.tsx b/client/src/shared/components/organisms/comments-wrapper/index.tsx
new file mode 100644
index 000000000..99bf33d06
--- /dev/null
+++ b/client/src/shared/components/organisms/comments-wrapper/index.tsx
@@ -0,0 +1,149 @@
+import { useAtom, useAtomValue } from 'jotai';
+import {
+ Box,
+ Typography,
+ Divider,
+ IconButton,
+ Button,
+ useTheme,
+ Slide,
+} from '@mui/material';
+import CloseIcon from '@mui/icons-material/Close';
+import NoDataMessage from '../../atoms/no-data-msg';
+import { SelectedProjectAtom } from '../../../../modules/project/states';
+import CommentField from './components/comment-field';
+import CommentCard from './components/comment-card';
+import {
+ AllCommentsAtom,
+ Comments,
+ CommentsWrapperAtom,
+ TotalParentCommentsLoadedAtom,
+} from './states';
+import useCommentsWrapper from './hooks';
+
+const CommentsWrapper = () => {
+ const selectedProject = useAtomValue(SelectedProjectAtom);
+ const [commentsWrapper, setCommentsWrapper] = useAtom(CommentsWrapperAtom);
+ const totalParentCommentsLoaded = useAtomValue(TotalParentCommentsLoadedAtom);
+ const comments = useAtomValue(AllCommentsAtom);
+ const { loadMoreComments } = useCommentsWrapper();
+ const theme = useTheme();
+
+ if (!selectedProject) return null;
+
+ return (
+
+
+ {/* Header */}
+
+
+ Comments
+
+
+ {selectedProject.title}
+
+
+ setCommentsWrapper(prev => !prev)}
+ sx={{
+ position: 'absolute',
+ top: 0,
+ right: 0,
+ bgcolor:
+ theme.palette.mode === 'dark'
+ ? '#09090b'
+ : theme.palette.grey[100],
+ '&:hover': {
+ bgcolor:
+ theme.palette.mode === 'dark'
+ ? '#1c1c1e'
+ : theme.palette.grey[200],
+ },
+ }}
+ >
+
+
+
+
+
+
+ {/* Comment input */}
+ {}} />
+
+ {/* Comments list */}
+
+ {comments && comments.length > 0 ? (
+ comments.map((comment: Comments, i: number) => (
+
+ ))
+ ) : (
+
+ )}
+
+
+ {/* Load more */}
+ {selectedProject.activity?.total_parent_comments >
+ totalParentCommentsLoaded && (
+
+ )}
+
+
+ );
+};
+
+export default CommentsWrapper;
diff --git a/client/src/shared/components/organisms/comments-wrapper/states/index.ts b/client/src/shared/components/organisms/comments-wrapper/states/index.ts
new file mode 100644
index 000000000..7ba660dac
--- /dev/null
+++ b/client/src/shared/components/organisms/comments-wrapper/states/index.ts
@@ -0,0 +1,22 @@
+import { atom } from 'jotai';
+import {
+ GetCommentsResponse,
+ GetRepliesResponse,
+} from '../../../../../infra/rest/apis/comment/typing';
+
+export interface Comments extends GetCommentsResponse {
+ children_level?: number;
+ is_reply_loaded?: boolean;
+}
+
+export interface Replies extends GetRepliesResponse {
+ children_level?: number;
+}
+
+export const CommentsWrapperAtom = atom(false);
+
+export const TotalParentCommentsLoadedAtom = atom(0);
+
+export const AllCommentsAtom = atom(null);
+
+export const AllRepliesAtom = atom(null);
diff --git a/client/src/shared/components/organisms/navbar/components/render-menu.tsx b/client/src/shared/components/organisms/navbar/components/render-menu.tsx
new file mode 100644
index 000000000..6d07e29c5
--- /dev/null
+++ b/client/src/shared/components/organisms/navbar/components/render-menu.tsx
@@ -0,0 +1,38 @@
+import { FC } from 'react';
+import { Menu, MenuItem } from '@mui/material';
+import { menuId } from '../constants';
+
+interface RenderMenuProps {
+ anchorEl: HTMLElement | null;
+ isMenuOpen: boolean;
+ handleMenuClose: () => void;
+}
+
+const RenderMenu: FC = ({
+ anchorEl,
+ isMenuOpen,
+ handleMenuClose,
+}) => {
+ return (
+
+ );
+};
+
+export default RenderMenu;
diff --git a/client/src/shared/components/organisms/navbar/components/render-mobile-menu.tsx b/client/src/shared/components/organisms/navbar/components/render-mobile-menu.tsx
new file mode 100644
index 000000000..c39c905e5
--- /dev/null
+++ b/client/src/shared/components/organisms/navbar/components/render-mobile-menu.tsx
@@ -0,0 +1,147 @@
+import { Menu, MenuItem, Badge } from '@mui/material';
+import { FC } from 'react';
+import LightModeIcon from '@mui/icons-material/LightMode';
+import DarkModeIcon from '@mui/icons-material/DarkMode';
+import CreateIcon from '@mui/icons-material/Create';
+import LoginIcon from '@mui/icons-material/Login';
+import { menuId, mobileMenuId } from '../constants';
+import A2ZIconButton from '../../../atoms/icon-button';
+import A2ZTypography from '../../../atoms/typography';
+import AccountCircle from '@mui/icons-material/AccountCircle';
+import MailIcon from '@mui/icons-material/Mail';
+import NotificationsIcon from '@mui/icons-material/Notifications';
+import { THEME } from '../../../../states/theme';
+import { useA2ZTheme } from '../../../../hooks/use-theme';
+import { useNavigate } from 'react-router-dom';
+import { useAuth } from '../../../../hooks/use-auth';
+
+interface RenderMobileMenuProps {
+ mobileMoreAnchorEl: HTMLElement | null;
+ isMobileMenuOpen: boolean;
+ handleProfileMenuOpen: (event: React.MouseEvent) => void;
+ handleMobileMenuClose: () => void;
+ setShowSubscribeModal: (show: boolean) => void;
+ notificationCount: number;
+}
+
+const RenderMobileMenu: FC = ({
+ mobileMoreAnchorEl,
+ isMobileMenuOpen,
+ handleProfileMenuOpen,
+ handleMobileMenuClose,
+ setShowSubscribeModal,
+ notificationCount,
+}) => {
+ const { isAuthenticated } = useAuth();
+ const { theme, setTheme } = useA2ZTheme();
+ const navigate = useNavigate();
+ return (
+
+ );
+};
+
+export default RenderMobileMenu;
diff --git a/client/src/shared/components/organisms/navbar/components/subscribe.tsx b/client/src/shared/components/organisms/navbar/components/subscribe.tsx
new file mode 100644
index 000000000..5f80f9430
--- /dev/null
+++ b/client/src/shared/components/organisms/navbar/components/subscribe.tsx
@@ -0,0 +1,96 @@
+/** @jsxImportSource @emotion/react */
+import { Badge, Box, Button } from '@mui/material';
+import MailIcon from '@mui/icons-material/Mail';
+import CardMembershipIcon from '@mui/icons-material/CardMembership';
+import InputBox from '../../../atoms/input-box';
+import A2ZModal from '../../../atoms/modal';
+import A2ZTypography from '../../../atoms/typography';
+
+interface SubscribeModalProps {
+ subscribeEmailRef: React.RefObject;
+ showSubscribeModal: boolean;
+ setShowSubscribeModal: (show: boolean) => void;
+ handleSubscribe: () => void;
+}
+
+const SubscribeModal = ({
+ subscribeEmailRef,
+ showSubscribeModal,
+ setShowSubscribeModal,
+ handleSubscribe,
+}: SubscribeModalProps) => {
+ return (
+ {
+ setShowSubscribeModal(false);
+ }}
+ sx={{
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ }}
+ >
+
+
+
+
+
+ }
+ autoComplete="off"
+ fullWidth
+ />
+
+
+
+
+
+ );
+};
+
+export default SubscribeModal;
diff --git a/client/src/shared/components/organisms/navbar/constants/index.ts b/client/src/shared/components/organisms/navbar/constants/index.ts
new file mode 100644
index 000000000..2028c0360
--- /dev/null
+++ b/client/src/shared/components/organisms/navbar/constants/index.ts
@@ -0,0 +1,3 @@
+export const menuId = 'primary-search-account-menu';
+
+export const mobileMenuId = 'primary-search-account-menu-mobile';
diff --git a/client/src/shared/components/organisms/navbar/hooks/index.ts b/client/src/shared/components/organisms/navbar/hooks/index.ts
new file mode 100644
index 000000000..4ae4ea11a
--- /dev/null
+++ b/client/src/shared/components/organisms/navbar/hooks/index.ts
@@ -0,0 +1,146 @@
+import { useRef, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useDevice } from '../../../../hooks/use-device';
+import { useNotifications } from '../../../../hooks/use-notification';
+import { emailRegex } from '../../../../utils/regex';
+import { subscribeUser } from '../../../../../infra/rest/apis/subscriber';
+import { allNotificationCounts } from '../../../../../infra/rest/apis/notification';
+import { NOTIFICATION_FILTER_TYPE } from '../../../../../infra/rest/typings';
+
+export const useNavbar = () => {
+ const navigate = useNavigate();
+ const { isDesktop } = useDevice();
+
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [mobileMoreAnchorEl, setMobileMoreAnchorEl] =
+ useState(null);
+ const [searchBoxVisibility, setSearchBoxVisibility] =
+ useState(false);
+ const [notificationCount, setNotificationCount] = useState(0);
+
+ const searchRef = useRef(null);
+ const isMenuOpen = Boolean(anchorEl);
+ const isMobileMenuOpen = Boolean(mobileMoreAnchorEl);
+
+ const handleProfileMenuOpen = (event: React.MouseEvent) => {
+ setAnchorEl(event.currentTarget);
+ };
+
+ const handleMobileMenuClose = () => {
+ setMobileMoreAnchorEl(null);
+ };
+
+ const handleMenuClose = () => {
+ setAnchorEl(null);
+ handleMobileMenuClose();
+ };
+
+ const handleMobileMenuOpen = (event: React.MouseEvent) => {
+ setMobileMoreAnchorEl(event.currentTarget);
+ };
+
+ const handleSearch = (event: React.KeyboardEvent) => {
+ if (event.key === 'Enter' || event.keyCode === 13) {
+ const query = event.currentTarget.value;
+ if (!query.trim().length) {
+ navigate('/');
+ return;
+ }
+ navigate(`/search/${encodeURIComponent(query)}`);
+ }
+ };
+
+ const triggerSearchByKeyboard = (e: KeyboardEvent) => {
+ if ((e.metaKey && e.key === 'k') || (e.ctrlKey && e.key === 'k')) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (isDesktop) {
+ setTimeout(() => {
+ if (searchRef.current) {
+ searchRef.current.focus();
+ searchRef.current.select();
+ }
+ }, 10);
+ } else {
+ setSearchBoxVisibility(true);
+ setTimeout(() => {
+ if (searchRef.current) {
+ searchRef.current.focus();
+ }
+ }, 100);
+ }
+ }
+ };
+
+ const fetchNotificationCount = async () => {
+ try {
+ const response = await allNotificationCounts({
+ filter: NOTIFICATION_FILTER_TYPE.ALL,
+ });
+ if (response.status === 'success') {
+ setNotificationCount(response?.data?.totalDocs || 0);
+ }
+ } catch (error) {
+ console.error('Error fetching notification count:', error);
+ }
+ };
+
+ return {
+ anchorEl,
+ mobileMoreAnchorEl,
+ isMenuOpen,
+ isMobileMenuOpen,
+ handleProfileMenuOpen,
+ handleMobileMenuClose,
+ handleMenuClose,
+ handleMobileMenuOpen,
+ handleSearch,
+ searchRef,
+ searchBoxVisibility,
+ setSearchBoxVisibility,
+ triggerSearchByKeyboard,
+ notificationCount,
+ setNotificationCount,
+ fetchNotificationCount,
+ };
+};
+
+export const useSubscribe = () => {
+ const { addNotification } = useNotifications();
+ const subscribeEmailRef = useRef(null);
+ const [showSubscribeModal, setShowSubscribeModal] = useState(false);
+
+ const handleSubscribe = async () => {
+ const email = subscribeEmailRef.current?.value || '';
+ if (!email.trim().length) {
+ addNotification({
+ message: 'Email is required',
+ type: 'error',
+ });
+ return;
+ }
+
+ if (!emailRegex.test(email)) {
+ addNotification({
+ message: 'Please enter a valid email',
+ type: 'error',
+ });
+ return;
+ }
+
+ const response = await subscribeUser(email);
+ addNotification({
+ message: response.message,
+ type: response.status,
+ });
+ setShowSubscribeModal(false);
+ };
+
+ return {
+ subscribeEmailRef,
+ showSubscribeModal,
+ setShowSubscribeModal,
+ handleSubscribe,
+ };
+};
diff --git a/client/src/shared/components/organisms/navbar/index.tsx b/client/src/shared/components/organisms/navbar/index.tsx
new file mode 100644
index 000000000..349e8040d
--- /dev/null
+++ b/client/src/shared/components/organisms/navbar/index.tsx
@@ -0,0 +1,207 @@
+import { AppBar, Box, Toolbar, Badge } from '@mui/material';
+import SearchIcon from '@mui/icons-material/Search';
+import AccountCircle from '@mui/icons-material/AccountCircle';
+import MailIcon from '@mui/icons-material/Mail';
+import NotificationsIcon from '@mui/icons-material/Notifications';
+import LightModeIcon from '@mui/icons-material/LightMode';
+import DarkModeIcon from '@mui/icons-material/DarkMode';
+import LoginIcon from '@mui/icons-material/Login';
+import CreateIcon from '@mui/icons-material/Create';
+import MoreIcon from '@mui/icons-material/MoreVert';
+import A2ZIconButton from '../../atoms/icon-button';
+import A2ZTypography from '../../atoms/typography';
+import { useNavbar, useSubscribe } from './hooks';
+import { menuId, mobileMenuId } from './constants';
+import RenderMobileMenu from './components/render-mobile-menu';
+import RenderMenu from './components/render-menu';
+import SubscribeModal from './components/subscribe';
+import InputBox from '../../atoms/input-box';
+import { useEffect } from 'react';
+import { useDevice } from '../../../hooks/use-device';
+import { THEME } from '../../../states/theme';
+import { useA2ZTheme } from '../../../hooks/use-theme';
+import { Outlet, useNavigate } from 'react-router-dom';
+import { useAuth } from '../../../hooks/use-auth';
+import { useThrottledFetch } from '../../../hooks/use-throttle-fetch';
+
+const Navbar = () => {
+ const { isAuthenticated } = useAuth();
+ const { isDesktop } = useDevice();
+ const { theme, setTheme } = useA2ZTheme();
+ const navigate = useNavigate();
+ const {
+ anchorEl,
+ mobileMoreAnchorEl,
+ isMenuOpen,
+ isMobileMenuOpen,
+ handleProfileMenuOpen,
+ handleMobileMenuOpen,
+ handleMenuClose,
+ handleMobileMenuClose,
+ handleSearch,
+ searchRef,
+ searchBoxVisibility,
+ setSearchBoxVisibility,
+ triggerSearchByKeyboard,
+ notificationCount,
+ fetchNotificationCount,
+ } = useNavbar();
+
+ const {
+ subscribeEmailRef,
+ showSubscribeModal,
+ setShowSubscribeModal,
+ handleSubscribe,
+ } = useSubscribe();
+
+ useEffect(() => {
+ document.addEventListener('keydown', triggerSearchByKeyboard, true);
+ return () => {
+ document.removeEventListener('keydown', triggerSearchByKeyboard, true);
+ };
+ }, [isDesktop, triggerSearchByKeyboard]);
+
+ useThrottledFetch({
+ isAuthenticated: isAuthenticated(),
+ fetch: fetchNotificationCount,
+ storageKey: 'a2z_notifications_last_fetch',
+ });
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ }
+ sx={{
+ padding: { xs: 0, sm: '0 1.25rem' },
+ }}
+ slotProps={{
+ htmlInput: {
+ onFocus: () => setSearchBoxVisibility(true),
+ onBlur: () => setSearchBoxVisibility(false),
+ onKeyDown: handleSearch,
+ sx: {
+ padding: '10px 0',
+ },
+ },
+ }}
+ />
+
+
+
+
+
+
+ setTheme(theme === THEME.LIGHT ? THEME.DARK : THEME.LIGHT)
+ }
+ >
+ {theme === THEME.DARK ? : }
+
+
+
+
+
+
+
+
+
+ {isAuthenticated() ? (
+ <>
+
+
+
+
+
+
+
+
+ >
+ ) : (
+ <>
+
+ setShowSubscribeModal(true)}>
+
+
+
+
+ navigate('/login')}>
+
+
+
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default Navbar;
diff --git a/client/src/shared/components/organisms/sidebar/hooks/index.ts b/client/src/shared/components/organisms/sidebar/hooks/index.ts
new file mode 100644
index 000000000..6675607ba
--- /dev/null
+++ b/client/src/shared/components/organisms/sidebar/hooks/index.ts
@@ -0,0 +1,5 @@
+const useSidebar = () => {
+ return {};
+};
+
+export default useSidebar;
diff --git a/client/src/shared/components/organisms/sidebar/index.tsx b/client/src/shared/components/organisms/sidebar/index.tsx
new file mode 100644
index 000000000..7e2a79846
--- /dev/null
+++ b/client/src/shared/components/organisms/sidebar/index.tsx
@@ -0,0 +1,258 @@
+import { useAtomValue } from 'jotai';
+import { useEffect, useState } from 'react';
+import { Navigate, NavLink, Outlet, useLocation } from 'react-router-dom';
+import { UserAtom } from '../../../../infra/states/user';
+import { useAuth } from '../../../hooks/use-auth';
+import { notificationStatus } from '../../../../infra/rest/apis/notification';
+
+import {
+ AppBar,
+ Box,
+ Divider,
+ Drawer,
+ IconButton,
+ List,
+ ListItemButton,
+ ListItemIcon,
+ ListItemText,
+ Toolbar,
+ Typography,
+ useMediaQuery,
+ useTheme,
+} from '@mui/material';
+import MenuIcon from '@mui/icons-material/Menu';
+import NotificationsNoneIcon from '@mui/icons-material/NotificationsNone';
+import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined';
+import EditNoteOutlinedIcon from '@mui/icons-material/EditNoteOutlined';
+import PersonOutlineIcon from '@mui/icons-material/PersonOutline';
+import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
+
+const drawerWidth = 240;
+
+const Sidebar = () => {
+ const user = useAtomValue(UserAtom);
+ const { isAuthenticated } = useAuth();
+ const location = useLocation();
+ const page = location.pathname.split('/')[2];
+
+ const [pageState, setPageState] = useState(page?.replace('-', ' ') || '');
+ const [mobileOpen, setMobileOpen] = useState(false);
+ const [newNotificationAvailable, setNewNotificationAvailable] =
+ useState(false);
+
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'));
+
+ useEffect(() => {
+ const fetchNotificationStatus = async () => {
+ const response = await notificationStatus();
+ if (response.status === 'success' && response.data) {
+ setNewNotificationAvailable(response.data.new_notification_available);
+ }
+ };
+ fetchNotificationStatus();
+ }, []);
+
+ if (!user || !isAuthenticated()) {
+ return ;
+ }
+
+ const handleDrawerToggle = () => {
+ setMobileOpen(prev => !prev);
+ };
+
+ const handleNavClick = (text: string) => {
+ setPageState(text);
+ if (isMobile) setMobileOpen(false);
+ };
+
+ const drawer = (
+
+
+ Dashboard
+
+
+
+
+ handleNavClick('Projects')}
+ >
+
+
+
+
+
+
+ handleNavClick('Notification')}
+ >
+
+
+ {newNotificationAvailable && (
+
+ )}
+
+
+
+
+ handleNavClick('Write')}
+ >
+
+
+
+
+
+
+
+
+ Settings
+
+
+
+
+ handleNavClick('Edit Profile')}
+ >
+
+
+
+
+
+
+ handleNavClick('Change Password')}
+ >
+
+
+
+
+
+
+
+ );
+
+ return (
+
+ {/* Top bar for mobile */}
+ {isMobile && (
+
+
+
+
+
+
+ {pageState}
+
+ {/* spacer */}
+
+
+ )}
+
+ {/* Sidebar Drawer */}
+
+ {/* Mobile Drawer */}
+
+ {drawer}
+
+
+ {/* Desktop Drawer */}
+
+ {drawer}
+
+
+
+ {/* Main Content */}
+
+
+
+
+ );
+};
+
+export default Sidebar;
diff --git a/client/src/shared/hooks/upload-image.ts b/client/src/shared/hooks/upload-image.ts
deleted file mode 100644
index 18b12a38e..000000000
--- a/client/src/shared/hooks/upload-image.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { post } from '../../infra/rest';
-
-interface UploadResponse {
- uploadURL: string;
-}
-
-export const uploadImage = async (img: string | File) => {
- try {
- const formData = new FormData();
- formData.append('image', img);
-
- const response = await post(
- `/api/media/get-upload-url`,
- true,
- formData,
- undefined,
- { 'Content-Type': 'multipart/form-data' }
- );
-
- if (!response.uploadURL) throw new Error('Failed to get upload URL.');
-
- return response.uploadURL;
- } catch (error) {
- console.error('Image upload failed:', error);
- return null;
- }
-};
diff --git a/client/src/shared/hooks/use-auth.ts b/client/src/shared/hooks/use-auth.ts
new file mode 100644
index 000000000..03afcc0d2
--- /dev/null
+++ b/client/src/shared/hooks/use-auth.ts
@@ -0,0 +1,139 @@
+import { useSetAtom, useAtom } from 'jotai';
+import { useEffect, useState, useCallback } from 'react';
+import { UserAtom } from '../../infra/states/user';
+import { TokenAtom } from '../../infra/states/auth';
+import { refreshToken } from '../../infra/rest/apis/auth';
+import {
+ getAccessToken,
+ removeFromLocal,
+ setAccessToken,
+} from '../utils/local';
+import { TOKEN_CONFIG } from '../../config/env';
+
+export const useAuth = () => {
+ const [token, setToken] = useAtom(TokenAtom);
+ const setUser = useSetAtom(UserAtom);
+ const [initialized, setInitialized] = useState(false);
+
+ // Initialize tokens from localStorage on app start
+ useEffect(() => {
+ const accessToken = getAccessToken();
+
+ if (accessToken) {
+ setToken(accessToken);
+ }
+ // Mark auth as initialized after syncing from storage so components can wait
+ setInitialized(true);
+ }, [setToken]);
+
+ // Sync tokens with localStorage when they change (for local state management)
+ useEffect(() => {
+ if (token) {
+ setAccessToken(token);
+ }
+ }, [token]);
+
+ const clearToken = (): void => {
+ removeFromLocal(TOKEN_CONFIG.ACCESS_TOKEN_NAME);
+ setToken(null);
+ };
+
+ const logout = useCallback(() => {
+ setToken(null);
+ setUser(null);
+ clearToken();
+ }, [setToken, setUser]);
+
+ const login = (accessToken: string) => {
+ setToken(accessToken);
+ };
+
+ // Listen for token refresh events from api-interceptor
+ useEffect(() => {
+ const handleTokenRefreshed = (event: CustomEvent) => {
+ const newToken = event.detail?.token;
+ if (newToken) {
+ setToken(newToken);
+ }
+ };
+
+ const handleTokenRefreshFailed = () => {
+ logout();
+ };
+
+ window.addEventListener(
+ 'token-refreshed',
+ handleTokenRefreshed as EventListener
+ );
+ window.addEventListener('token-refresh-failed', handleTokenRefreshFailed);
+
+ return () => {
+ window.removeEventListener(
+ 'token-refreshed',
+ handleTokenRefreshed as EventListener
+ );
+ window.removeEventListener(
+ 'token-refresh-failed',
+ handleTokenRefreshFailed
+ );
+ };
+ }, [setToken, logout]);
+
+ const refreshAuthToken = async (): Promise => {
+ // Check localStorage first, as it might have been updated by interceptor
+ const storedToken = getAccessToken();
+ if (!token && !storedToken) {
+ logout();
+ return false;
+ }
+
+ try {
+ const response = await refreshToken();
+ if (response.status === 'success') {
+ if (response?.data?.access_token) {
+ const newToken = response.data.access_token;
+ setAccessToken(newToken);
+ setToken(newToken);
+ return true;
+ }
+ logout();
+ return false;
+ } else {
+ logout();
+ return false;
+ }
+ } catch (error) {
+ console.error('Token refresh failed:', error);
+ // Don't logout immediately on error - let the interceptor handle it
+ // Only logout if we definitely don't have a token
+ const currentToken = getAccessToken();
+ if (!currentToken) {
+ logout();
+ }
+ return false;
+ }
+ };
+
+ const isAuthenticated = () => {
+ return !!token;
+ };
+
+ const getAuthHeaders = () => {
+ return {
+ Authorization: `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ };
+ };
+
+ return {
+ token,
+ setToken,
+ clearToken,
+ login,
+ logout,
+ refreshAuthToken,
+ isAuthenticated,
+ getAuthHeaders,
+ initialized,
+ };
+};
diff --git a/client/src/shared/hooks/use-debounce.ts b/client/src/shared/hooks/use-debounce.ts
new file mode 100644
index 000000000..0bed64dc7
--- /dev/null
+++ b/client/src/shared/hooks/use-debounce.ts
@@ -0,0 +1,16 @@
+import { useEffect, useState } from 'react';
+
+function useDebounce(value: T, delay: number = 500): T {
+ const [debouncedValue, setDebouncedValue] = useState(value);
+
+ useEffect(() => {
+ const timer = setTimeout(() => setDebouncedValue(value), delay);
+ return () => {
+ clearTimeout(timer);
+ };
+ }, [value, delay]);
+
+ return debouncedValue;
+}
+
+export default useDebounce;
diff --git a/client/src/shared/hooks/use-notification.ts b/client/src/shared/hooks/use-notification.ts
index f52de3988..4caba584e 100644
--- a/client/src/shared/hooks/use-notification.ts
+++ b/client/src/shared/hooks/use-notification.ts
@@ -1,14 +1,16 @@
import { useSetAtom } from 'jotai';
-import { notificationsAtom } from '../states/notification';
-import { Notification } from '../../infra/rest/typings/notification';
+import { NotificationSystemAtom } from '../states/notification';
+import { NotificationSystem } from '../states/notification';
export function useNotifications() {
- const setNotifications = useSetAtom(notificationsAtom);
+ const setNotifications = useSetAtom(NotificationSystemAtom);
- const addNotification = (notification: Omit) => {
+ const addNotification = (
+ notification: Omit
+ ) => {
const id = Date.now().toString();
- const newNotification: Notification = {
+ const newNotification: NotificationSystem = {
id,
open: true,
autoHideDuration: 3000,
diff --git a/client/src/shared/hooks/use-theme.ts b/client/src/shared/hooks/use-theme.ts
index d937a3231..089520025 100644
--- a/client/src/shared/hooks/use-theme.ts
+++ b/client/src/shared/hooks/use-theme.ts
@@ -1,17 +1,22 @@
import { useAtom } from 'jotai';
-import { themeAtom, resolvedThemeAtom } from '../states/theme';
+import {
+ ThemeAtom,
+ ResolvedThemeAtom,
+ THEME,
+ THEME_LOCAL_STORAGE_KEY,
+} from '../states/theme';
import { useEffect } from 'react';
-export function useTheme() {
- const [theme, setTheme] = useAtom(themeAtom);
- const [resolved] = useAtom(resolvedThemeAtom);
+export function useA2ZTheme() {
+ const [theme, setTheme] = useAtom(ThemeAtom);
+ const [resolved] = useAtom(ResolvedThemeAtom);
useEffect(() => {
- localStorage.setItem('theme', theme);
+ localStorage.setItem(THEME_LOCAL_STORAGE_KEY, theme);
const root = document.documentElement;
- if (resolved === 'dark') root.classList.add('dark');
- else root.classList.remove('dark');
+ if (resolved === THEME.DARK) root.classList.add(THEME.DARK);
+ else root.classList.remove(THEME.DARK);
}, [theme, resolved]);
return { theme, resolvedTheme: resolved, setTheme };
diff --git a/client/src/shared/hooks/use-throttle-fetch.ts b/client/src/shared/hooks/use-throttle-fetch.ts
new file mode 100644
index 000000000..770475909
--- /dev/null
+++ b/client/src/shared/hooks/use-throttle-fetch.ts
@@ -0,0 +1,44 @@
+import { useEffect } from 'react';
+
+interface UseThrottledFetchOptions {
+ isAuthenticated: boolean;
+ fetch: () => Promise;
+ storageKey?: string;
+ throttleMs?: number;
+}
+
+export const useThrottledFetch = ({
+ isAuthenticated,
+ fetch,
+ storageKey = 'a2z_last_fetch',
+ throttleMs = 1000 * 60 * 30, // 30 minutes default
+}: UseThrottledFetchOptions) => {
+ useEffect(() => {
+ if (!isAuthenticated) return;
+
+ const shouldFetch = () => {
+ try {
+ const raw = localStorage.getItem(storageKey);
+ if (!raw) return true;
+ const ts = Number(raw);
+ return Number.isFinite(ts) ? Date.now() - ts > throttleMs : true;
+ } catch {
+ return true;
+ }
+ };
+
+ if (shouldFetch()) {
+ fetch()
+ .then(() => {
+ try {
+ localStorage.setItem(storageKey, String(Date.now()));
+ } catch {
+ // ignore storage write errors
+ }
+ })
+ .catch(() => {
+ // don’t update timestamp so it can retry later
+ });
+ }
+ }, [isAuthenticated, fetch, throttleMs, storageKey]);
+};
diff --git a/client/src/shared/requests/filter-pagination-data.ts b/client/src/shared/requests/filter-pagination-data.ts
deleted file mode 100644
index 2f9d0c52e..000000000
--- a/client/src/shared/requests/filter-pagination-data.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { post } from '../../infra/rest';
-
-interface PaginationState {
- results: T[];
- page: number;
- totalDocs?: number;
-}
-
-interface FilterPaginationDataParams {
- create_new_arr?: boolean;
- state: PaginationState | null;
- data: T[];
- page: number;
- countRoute: string;
- data_to_send?: Record;
-}
-
-export interface FilterPaginationDataResponse {
- totalDocs: number;
-}
-
-export const filterPaginationData = async ({
- create_new_arr = false,
- state,
- data,
- page,
- countRoute,
- data_to_send = {},
-}: FilterPaginationDataParams): Promise | undefined> => {
- let obj: PaginationState;
-
- if (state !== null && !create_new_arr) {
- obj = { ...state, results: [...state.results, ...data], page: page };
- } else {
- const response = await post<
- FilterPaginationDataResponse,
- Record
- >(countRoute, false, data_to_send);
- if (response.totalDocs >= 0) {
- obj = { results: data, page: 1, totalDocs: response.totalDocs };
- } else {
- console.error(response);
- return undefined;
- }
- }
-
- return obj;
-};
diff --git a/client/src/shared/states/emptyStates/profile.ts b/client/src/shared/states/emptyStates/profile.ts
deleted file mode 100644
index 0113d7901..000000000
--- a/client/src/shared/states/emptyStates/profile.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Profile } from '../../typings/profile';
-
-export const emptyProfileState: Profile = {
- personal_info: {
- fullname: '',
- username: '',
- profile_img: '',
- email: '',
- bio: '',
- },
- social_links: {
- youtube: '',
- github: '',
- twitter: '',
- facebook: '',
- instagram: '',
- website: '',
- },
- account_info: {
- total_posts: 0,
- total_reads: 0,
- },
- joinedAt: '',
- role: 0,
- _id: '',
-};
diff --git a/client/src/shared/states/emptyStates/project.ts b/client/src/shared/states/emptyStates/project.ts
deleted file mode 100644
index e15281a02..000000000
--- a/client/src/shared/states/emptyStates/project.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import {
- AllProjectsData,
- Project,
- TrendingProject,
-} from '../../typings/project';
-
-export const emptyProjectState: Project = {
- activity: {
- total_likes: 0,
- total_comments: 0,
- total_reads: 0,
- total_parent_comments: 0,
- },
- project_id: '',
- title: '',
- banner: '',
- des: '',
- content: [],
- tags: [],
- author: {
- _id: '',
- personal_info: {
- fullname: '',
- username: '',
- profile_img: '',
- },
- },
- publishedAt: '',
- comments: {
- results: [],
- },
- _id: '',
-};
-
-export const emptyAllProjectsState: AllProjectsData = {
- results: [],
- page: 1,
- totalDocs: 0,
-};
-
-export const emptyTrendingProjectsState: TrendingProject[] = [];
diff --git a/client/src/shared/states/emptyStates/user.ts b/client/src/shared/states/emptyStates/user.ts
deleted file mode 100644
index 58130c520..000000000
--- a/client/src/shared/states/emptyStates/user.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { User } from '../../typings/user';
-
-export const emptyUserState: User = {
- access_token: null,
- username: '',
- email: '',
- fullname: '',
- profile_img: '',
- name: '',
- role: 0,
- new_notification_available: false,
-};
diff --git a/client/src/shared/states/notification.ts b/client/src/shared/states/notification.ts
index c7ea4736d..d06e77f40 100644
--- a/client/src/shared/states/notification.ts
+++ b/client/src/shared/states/notification.ts
@@ -1,4 +1,18 @@
import { atom } from 'jotai';
-import { Notification } from '../typings/notification';
-export const notificationsAtom = atom([]);
+export enum NotificationType {
+ INFO = 'info',
+ SUCCESS = 'success',
+ WARNING = 'warning',
+ ERROR = 'error',
+}
+
+export interface NotificationSystem {
+ id: string;
+ message: string;
+ type?: 'info' | 'success' | 'warning' | 'error' | NotificationType;
+ open: boolean;
+ autoHideDuration?: number; // in milliseconds
+}
+
+export const NotificationSystemAtom = atom([]);
diff --git a/client/src/shared/states/profile.ts b/client/src/shared/states/profile.ts
deleted file mode 100644
index 17ab1222f..000000000
--- a/client/src/shared/states/profile.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { atom } from 'jotai';
-import { Profile } from '../typings/profile';
-import { emptyProfileState } from './emptyStates/profile';
-
-export const ProfileAtom = atom(emptyProfileState);
diff --git a/client/src/shared/states/project.ts b/client/src/shared/states/project.ts
deleted file mode 100644
index a6c592526..000000000
--- a/client/src/shared/states/project.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { atom } from 'jotai';
-import { AllProjectsData, Project, TrendingProject } from '../typings/project';
-import { emptyTrendingProjectsState } from './emptyStates/project';
-
-export const AllProjectsAtom = atom(null);
-
-export const ProjectAtom = atom(null);
-
-export const TrendingProjectAtom = atom(
- emptyTrendingProjectsState
-);
-
-export const DraftProjectAtom = atom(null);
diff --git a/client/src/shared/states/theme.ts b/client/src/shared/states/theme.ts
index 729bfb3a6..320aba8dc 100644
--- a/client/src/shared/states/theme.ts
+++ b/client/src/shared/states/theme.ts
@@ -1,22 +1,25 @@
import { atom } from 'jotai';
-export enum Theme {
+export const THEME_LOCAL_STORAGE_KEY = 'theme';
+
+export enum THEME {
LIGHT = 'light',
DARK = 'dark',
SYSTEM = 'system',
}
-const getSystemTheme = (): Theme =>
+const getSystemTheme = (): THEME =>
window.matchMedia('(prefers-color-scheme: dark)').matches
- ? Theme.DARK
- : Theme.LIGHT;
+ ? THEME.DARK
+ : THEME.LIGHT;
const savedTheme =
- (localStorage.getItem('theme') as Theme | null) || Theme.SYSTEM;
+ (localStorage.getItem(THEME_LOCAL_STORAGE_KEY) as THEME | null) ||
+ THEME.SYSTEM;
-export const themeAtom = atom(savedTheme);
+export const ThemeAtom = atom(savedTheme);
-export const resolvedThemeAtom = atom(get => {
- const theme = get(themeAtom);
- return theme === Theme.SYSTEM ? getSystemTheme() : theme;
+export const ResolvedThemeAtom = atom(get => {
+ const theme = get(ThemeAtom);
+ return theme === THEME.SYSTEM ? getSystemTheme() : theme;
});
diff --git a/client/src/shared/states/user.ts b/client/src/shared/states/user.ts
deleted file mode 100644
index 8267ec390..000000000
--- a/client/src/shared/states/user.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { atom } from 'jotai';
-import { User } from '../typings/user';
-import { emptyUserState } from './emptyStates/user';
-
-export const UserAtom = atom(emptyUserState);
diff --git a/client/src/shared/utils/api-interceptor.ts b/client/src/shared/utils/api-interceptor.ts
new file mode 100644
index 000000000..02d81da49
--- /dev/null
+++ b/client/src/shared/utils/api-interceptor.ts
@@ -0,0 +1,148 @@
+import axios from 'axios';
+import { refreshToken } from '../../infra/rest/apis/auth';
+import { getAccessToken, setAccessToken } from './local';
+
+let refreshPromise: Promise | null = null;
+let isInterceptorSetup = false;
+
+// Perform the actual token refresh
+const performTokenRefresh = async (): Promise => {
+ try {
+ const refreshResponse = await refreshToken();
+ if (refreshResponse.status === 'success') {
+ const newToken = refreshResponse?.data?.access_token || '';
+ if (newToken) {
+ setAccessToken(newToken);
+ // Trigger a custom event to notify token update
+ window.dispatchEvent(
+ new CustomEvent('token-refreshed', { detail: { token: newToken } })
+ );
+ return true;
+ }
+ return false;
+ } else {
+ console.log('Token refresh failed:', refreshResponse);
+ // Trigger logout event
+ window.dispatchEvent(new CustomEvent('token-refresh-failed'));
+ return false;
+ }
+ } catch (error: unknown) {
+ // Check if the error is due to missing refresh token
+ let errorMessage = '';
+ if (axios.isAxiosError(error) && error.response?.data) {
+ const data = error.response.data as { message?: string };
+ errorMessage = data.message || '';
+ } else if (
+ typeof error === 'object' &&
+ error !== null &&
+ 'message' in error
+ ) {
+ errorMessage = String((error as { message?: unknown }).message || '');
+ }
+
+ const isNoRefreshToken =
+ errorMessage.includes('No refresh token') ||
+ errorMessage.includes('refresh token');
+
+ if (isNoRefreshToken) {
+ // No refresh token available - don't log error, just return false
+ // This happens when refresh token cookie is missing/expired
+ console.log('Refresh token not available');
+ window.dispatchEvent(new CustomEvent('token-refresh-failed'));
+ return false;
+ }
+
+ console.error('Token refresh error:', error);
+ // Trigger logout event for other errors
+ window.dispatchEvent(new CustomEvent('token-refresh-failed'));
+ return false;
+ }
+};
+
+// Handle token refresh with deduplication
+const handleTokenRefresh = async (): Promise => {
+ // If already refreshing, wait for the existing refresh to complete
+ if (refreshPromise) {
+ return await refreshPromise;
+ }
+
+ // Start new refresh process
+ refreshPromise = performTokenRefresh();
+
+ try {
+ const result = await refreshPromise;
+ return result;
+ } finally {
+ refreshPromise = null;
+ }
+};
+
+// Setup axios response interceptor to handle 401 errors
+const setupAxiosInterceptor = () => {
+ if (isInterceptorSetup) return;
+
+ // Response interceptor to handle 401 errors
+ axios.interceptors.response.use(
+ response => response,
+ async error => {
+ const originalRequest = error.config;
+
+ // If error is 401 and we haven't already tried to refresh
+ if (error.response?.status === 401 && !originalRequest._retry) {
+ originalRequest._retry = true;
+
+ // Don't retry refresh token endpoint itself
+ if (originalRequest.url?.includes('/api/auth/refresh')) {
+ window.dispatchEvent(new CustomEvent('token-refresh-failed'));
+ return Promise.reject(error);
+ }
+
+ // Try to refresh token
+ const refreshSuccess = await handleTokenRefresh();
+
+ if (refreshSuccess) {
+ // Retry original request with new token
+ const newToken = getAccessToken();
+ if (newToken) {
+ originalRequest.headers.Authorization = `Bearer ${newToken}`;
+ return axios(originalRequest);
+ }
+ }
+
+ // If refresh failed, reject the request
+ window.dispatchEvent(new CustomEvent('token-refresh-failed'));
+ return Promise.reject(error);
+ }
+
+ return Promise.reject(error);
+ }
+ );
+
+ // Request interceptor to add token to requests
+ axios.interceptors.request.use(
+ config => {
+ const token = getAccessToken();
+ if (token && !config.headers.Authorization) {
+ config.headers.Authorization = `Bearer ${token}`;
+ }
+ return config;
+ },
+ error => {
+ return Promise.reject(error);
+ }
+ );
+
+ isInterceptorSetup = true;
+};
+
+// Setup automatic token refresh on app start
+export const setupTokenRefresh = () => {
+ // Setup axios interceptors first
+ setupAxiosInterceptor();
+
+ // Note: We don't do proactive periodic refresh anymore
+ // Token refresh will happen automatically when:
+ // 1. A request returns 401 (handled by response interceptor)
+ // 2. Manual refresh is called via refreshAuthToken()
+ // This prevents trying to refresh when refresh token cookie doesn't exist
+};
diff --git a/client/src/shared/utils/local.ts b/client/src/shared/utils/local.ts
new file mode 100644
index 000000000..ea382d127
--- /dev/null
+++ b/client/src/shared/utils/local.ts
@@ -0,0 +1,25 @@
+import { TOKEN_CONFIG } from '../../config/env';
+
+export const storeInLocal = (key: string, value: string) => {
+ localStorage.setItem(key, value);
+};
+
+export const lookInLocal = (key: string) => {
+ return localStorage.getItem(key);
+};
+
+export const removeFromLocal = (key: string) => {
+ localStorage.removeItem(key);
+};
+
+export const logOutUser = () => {
+ localStorage.clear();
+};
+
+export const getAccessToken = () => {
+ return lookInLocal(TOKEN_CONFIG.ACCESS_TOKEN_NAME);
+};
+
+export const setAccessToken = (token: string) => {
+ storeInLocal(TOKEN_CONFIG.ACCESS_TOKEN_NAME, token);
+};
diff --git a/client/src/shared/utils/session.ts b/client/src/shared/utils/session.ts
deleted file mode 100644
index d75307614..000000000
--- a/client/src/shared/utils/session.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-const storeInSession = (key: string, value: string) => {
- sessionStorage.setItem(key, value);
-};
-
-const lookInSession = (key: string) => {
- return sessionStorage.getItem(key);
-};
-
-const removeFromSession = (key: string) => {
- sessionStorage.removeItem(key);
-};
-
-const logOutUser = () => {
- sessionStorage.clear();
-};
-
-export { storeInSession, lookInSession, removeFromSession, logOutUser };
diff --git a/client/tsconfig.app.json b/client/tsconfig.app.json
deleted file mode 100644
index 5fca9ffbf..000000000
--- a/client/tsconfig.app.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "compilerOptions": {
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
- "target": "ES2022",
- "useDefineForClassFields": true,
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
- "module": "ESNext",
- "skipLibCheck": true,
-
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "verbatimModuleSyntax": false, // Disable direct type imports
- "moduleDetection": "force",
- "noEmit": true,
- "jsx": "react-jsx",
-
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "erasableSyntaxOnly": false, // Disable direct enum creates
- "noFallthroughCasesInSwitch": true,
- "noUncheckedSideEffectImports": true
- },
- "include": ["src", "src/infra/types"],
-}
diff --git a/client/tsconfig.json b/client/tsconfig.json
index 1ffef600d..85a360efa 100644
--- a/client/tsconfig.json
+++ b/client/tsconfig.json
@@ -1,7 +1,23 @@
{
- "files": [],
- "references": [
- { "path": "./tsconfig.app.json" },
- { "path": "./tsconfig.node.json" }
- ]
+ "compilerOptions": {
+ "target": "es2015",
+ "lib": ["dom", "dom.iterable", "es6", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "noImplicitAny": true,
+ "noImplicitThis": true,
+ "strictNullChecks": true
+ },
+ "include": ["src"]
}
diff --git a/client/tsconfig.node.json b/client/tsconfig.node.json
deleted file mode 100644
index f85a39906..000000000
--- a/client/tsconfig.node.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "compilerOptions": {
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
- "target": "ES2023",
- "lib": ["ES2023"],
- "module": "ESNext",
- "skipLibCheck": true,
-
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "verbatimModuleSyntax": true,
- "moduleDetection": "force",
- "noEmit": true,
-
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "erasableSyntaxOnly": true,
- "noFallthroughCasesInSwitch": true,
- "noUncheckedSideEffectImports": true
- },
- "include": ["vite.config.ts"]
-}
diff --git a/client/vite.config.ts b/client/vite.config.ts
index b8b7d5bf4..50453e78d 100644
--- a/client/vite.config.ts
+++ b/client/vite.config.ts
@@ -1,11 +1,15 @@
-import { defineConfig } from 'vite';
-import react from '@vitejs/plugin-react-swc';
-import tailwindcss from '@tailwindcss/vite';
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
// https://vite.dev/config/
-export default defineConfig({
- plugins: [
- react(),
- tailwindcss(),
- ],
+export default defineConfig(() => {
+ return {
+ plugins: [
+ react({
+ babel: {
+ plugins: [['babel-plugin-react-compiler']],
+ },
+ }),
+ ],
+ }
})
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 8b432027a..7e270a6c8 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -13,6 +13,14 @@ export default defineConfig([
languageOptions: { globals: { ...globals.browser, ...globals.node } },
},
pluginReact.configs.flat.recommended,
+ {
+ // Disable react-in-jsx-scope for React 17+ (not needed)
+ files: ['**/*.{tsx,jsx}'],
+ rules: {
+ 'react/react-in-jsx-scope': 'off',
+ 'react/jsx-uses-react': 'off',
+ },
+ },
{
...json.configs.recommended,
files: ["**/*.json"],
diff --git a/package.json b/package.json
index 5e9655556..81777c0d4 100644
--- a/package.json
+++ b/package.json
@@ -25,13 +25,15 @@
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
+ "install:client": "cd client && npm install",
+ "install:server": "cd server && npm install",
"install:all": "npm install && cd client && npm install && cd ../server && npm install",
"client": "cd client && npm run dev",
"server": "cd server && npm run dev",
"dev": "concurrently -n \"CLIENT,SERVER\" -c \"bgBlue.bold,bgMagenta.bold\" \"npm run client\" \"npm run server\"",
- "prepare": "husky install",
- "lint": "eslint client/src server/src --ext .js,.jsx,.ts,.tsx",
- "lint:fix": "eslint client/src server/src --ext .js,.jsx,.ts,.tsx --fix",
+ "prepare": "husky",
+ "lint": "cd client && npx eslint src --ext .ts,.tsx && cd ../server && npx eslint src --ext .js && cd ..",
+ "lint:fix": "cd client && npx eslint src --ext .ts,.tsx --fix && cd ../server && npx eslint src --ext .js --fix && cd ..",
"format": "prettier --write \"client/src/**/*.{ts,tsx,js,jsx,css,html}\" \"server/**/*.js\" \"*.{json,md,yml,yaml}\"",
"format:check": "prettier --check \"client/src/**/*.{ts,tsx,js,jsx,css,html}\" \"server/**/*.js\" \"*.{json,md,yml,yaml}\"",
"format:client": "prettier --write \"client/src/**/*.{ts,tsx,js,jsx,css,html}\"",
diff --git a/server/.env.example b/server/.env.example
index db0f05058..b5ec8cd51 100644
--- a/server/.env.example
+++ b/server/.env.example
@@ -2,6 +2,7 @@
PORT=8000
SERVER_ENV=production # Change to 'development' for development environment
VITE_SERVER_DOMAIN=https://code-a2z-server.vercel.app
+VITE_CLIENT_DOMAIN=https://code-a2z.vercel.app
# Database Configuration
MONGODB_URL=mongodb://localhost:27017/code-a2z
diff --git a/server/package-lock.json b/server/package-lock.json
index 5b281e021..47524e380 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -16,11 +16,9 @@
"crypto": "^1.0.1",
"dotenv": "^16.4.7",
"express": "^4.21.2",
- "firebase-admin": "^13.1.0",
"helmet": "^8.1.0",
"hpp": "^0.2.3",
"jsonwebtoken": "^9.0.2",
- "localtunnel": "^1.8.3",
"mongoose": "^8.11.0",
"morgan": "^1.10.1",
"multer": "^1.4.5-lts.1",
@@ -59,264 +57,6 @@
"kuler": "^2.0.0"
}
},
- "node_modules/@fastify/busboy": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz",
- "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==",
- "license": "MIT"
- },
- "node_modules/@firebase/app-check-interop-types": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz",
- "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==",
- "license": "Apache-2.0"
- },
- "node_modules/@firebase/app-types": {
- "version": "0.9.3",
- "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz",
- "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==",
- "license": "Apache-2.0"
- },
- "node_modules/@firebase/auth-interop-types": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz",
- "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==",
- "license": "Apache-2.0"
- },
- "node_modules/@firebase/component": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.0.tgz",
- "integrity": "sha512-wR9En2A+WESUHexjmRHkqtaVH94WLNKt6rmeqZhSLBybg4Wyf0Umk04SZsS6sBq4102ZsDBFwoqMqJYj2IoDSg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/util": "1.13.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/@firebase/database": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.0.tgz",
- "integrity": "sha512-gM6MJFae3pTyNLoc9VcJNuaUDej0ctdjn3cVtILo3D5lpp0dmUHHLFN/pUKe7ImyeB1KAvRlEYxvIHNF04Filg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/app-check-interop-types": "0.3.3",
- "@firebase/auth-interop-types": "0.2.4",
- "@firebase/component": "0.7.0",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.13.0",
- "faye-websocket": "0.11.4",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/@firebase/database-compat": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.0.tgz",
- "integrity": "sha512-8nYc43RqxScsePVd1qe1xxvWNf0OBnbwHxmXJ7MHSuuTVYFO3eLyLW3PiCKJ9fHnmIz4p4LbieXwz+qtr9PZDg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.0",
- "@firebase/database": "1.1.0",
- "@firebase/database-types": "1.0.16",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.13.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/@firebase/database-types": {
- "version": "1.0.16",
- "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.16.tgz",
- "integrity": "sha512-xkQLQfU5De7+SPhEGAXFBnDryUWhhlFXelEg2YeZOQMCdoe7dL64DDAd77SQsR+6uoXIZY5MB4y/inCs4GTfcw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/app-types": "0.9.3",
- "@firebase/util": "1.13.0"
- }
- },
- "node_modules/@firebase/logger": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz",
- "integrity": "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/@firebase/util": {
- "version": "1.13.0",
- "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.13.0.tgz",
- "integrity": "sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ==",
- "hasInstallScript": true,
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/@google-cloud/firestore": {
- "version": "7.11.6",
- "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.6.tgz",
- "integrity": "sha512-EW/O8ktzwLfyWBOsNuhRoMi8lrC3clHM5LVFhGvO1HCsLozCOOXRAlHrYBoE6HL42Sc8yYMuCb2XqcnJ4OOEpw==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "@opentelemetry/api": "^1.3.0",
- "fast-deep-equal": "^3.1.1",
- "functional-red-black-tree": "^1.0.1",
- "google-gax": "^4.3.3",
- "protobufjs": "^7.2.6"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@google-cloud/paginator": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz",
- "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "arrify": "^2.0.0",
- "extend": "^3.0.2"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@google-cloud/projectify": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz",
- "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==",
- "license": "Apache-2.0",
- "optional": true,
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@google-cloud/promisify": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz",
- "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==",
- "license": "Apache-2.0",
- "optional": true,
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/@google-cloud/storage": {
- "version": "7.17.2",
- "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.17.2.tgz",
- "integrity": "sha512-6xN0KNO8L/LIA5zu3CJwHkJiB6n65eykBLOb0E+RooiHYgX8CSao6lvQiKT9TBk2gL5g33LL3fmhDodZnt56rw==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "@google-cloud/paginator": "^5.0.0",
- "@google-cloud/projectify": "^4.0.0",
- "@google-cloud/promisify": "<4.1.0",
- "abort-controller": "^3.0.0",
- "async-retry": "^1.3.3",
- "duplexify": "^4.1.3",
- "fast-xml-parser": "^4.4.1",
- "gaxios": "^6.0.2",
- "google-auth-library": "^9.6.3",
- "html-entities": "^2.5.2",
- "mime": "^3.0.0",
- "p-limit": "^3.0.1",
- "retry-request": "^7.0.0",
- "teeny-request": "^9.0.0",
- "uuid": "^8.0.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/@google-cloud/storage/node_modules/uuid": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
- "license": "MIT",
- "optional": true,
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
- "node_modules/@grpc/grpc-js": {
- "version": "1.14.0",
- "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.0.tgz",
- "integrity": "sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "@grpc/proto-loader": "^0.8.0",
- "@js-sdsl/ordered-map": "^4.4.2"
- },
- "engines": {
- "node": ">=12.10.0"
- }
- },
- "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz",
- "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "lodash.camelcase": "^4.3.0",
- "long": "^5.0.0",
- "protobufjs": "^7.5.3",
- "yargs": "^17.7.2"
- },
- "bin": {
- "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@grpc/proto-loader": {
- "version": "0.7.15",
- "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz",
- "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "lodash.camelcase": "^4.3.0",
- "long": "^5.0.0",
- "protobufjs": "^7.2.5",
- "yargs": "^17.7.2"
- },
- "bin": {
- "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@js-sdsl/ordered-map": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
- "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==",
- "license": "MIT",
- "optional": true,
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/js-sdsl"
- }
- },
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
@@ -346,90 +86,6 @@
"sparse-bitfield": "^3.0.3"
}
},
- "node_modules/@opentelemetry/api": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
- "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
- "license": "Apache-2.0",
- "optional": true,
- "engines": {
- "node": ">=8.0.0"
- }
- },
- "node_modules/@protobufjs/aspromise": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
- "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
- "license": "BSD-3-Clause",
- "optional": true
- },
- "node_modules/@protobufjs/base64": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
- "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
- "license": "BSD-3-Clause",
- "optional": true
- },
- "node_modules/@protobufjs/codegen": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
- "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
- "license": "BSD-3-Clause",
- "optional": true
- },
- "node_modules/@protobufjs/eventemitter": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
- "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
- "license": "BSD-3-Clause",
- "optional": true
- },
- "node_modules/@protobufjs/fetch": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
- "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
- "license": "BSD-3-Clause",
- "optional": true,
- "dependencies": {
- "@protobufjs/aspromise": "^1.1.1",
- "@protobufjs/inquire": "^1.1.0"
- }
- },
- "node_modules/@protobufjs/float": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
- "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
- "license": "BSD-3-Clause",
- "optional": true
- },
- "node_modules/@protobufjs/inquire": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
- "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
- "license": "BSD-3-Clause",
- "optional": true
- },
- "node_modules/@protobufjs/path": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
- "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
- "license": "BSD-3-Clause",
- "optional": true
- },
- "node_modules/@protobufjs/pool": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
- "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
- "license": "BSD-3-Clause",
- "optional": true
- },
- "node_modules/@protobufjs/utf8": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
- "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
- "license": "BSD-3-Clause",
- "optional": true
- },
"node_modules/@so-ric/colorspace": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz",
@@ -446,172 +102,15 @@
"integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==",
"license": "MIT"
},
- "node_modules/@tootallnate/once": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
- "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@types/body-parser": {
- "version": "1.19.6",
- "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
- "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
- "license": "MIT",
- "dependencies": {
- "@types/connect": "*",
- "@types/node": "*"
- }
- },
- "node_modules/@types/caseless": {
- "version": "0.12.5",
- "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz",
- "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/@types/connect": {
- "version": "3.4.38",
- "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
- "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
- "license": "MIT",
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/express": {
- "version": "4.17.23",
- "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz",
- "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==",
- "license": "MIT",
- "dependencies": {
- "@types/body-parser": "*",
- "@types/express-serve-static-core": "^4.17.33",
- "@types/qs": "*",
- "@types/serve-static": "*"
- }
- },
- "node_modules/@types/express-serve-static-core": {
- "version": "4.19.7",
- "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz",
- "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==",
- "license": "MIT",
- "dependencies": {
- "@types/node": "*",
- "@types/qs": "*",
- "@types/range-parser": "*",
- "@types/send": "*"
- }
- },
- "node_modules/@types/http-errors": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
- "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
- "license": "MIT"
- },
- "node_modules/@types/jsonwebtoken": {
- "version": "9.0.10",
- "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
- "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
- "license": "MIT",
- "dependencies": {
- "@types/ms": "*",
- "@types/node": "*"
- }
- },
- "node_modules/@types/long": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
- "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/@types/mime": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
- "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
- "license": "MIT"
- },
- "node_modules/@types/ms": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
- "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
- "license": "MIT"
- },
"node_modules/@types/node": {
- "version": "22.18.12",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.12.tgz",
- "integrity": "sha512-BICHQ67iqxQGFSzfCFTT7MRQ5XcBjG5aeKh5Ok38UBbPe5fxTyE+aHFxwVrGyr8GNlqFMLKD1D3P2K/1ks8tog==",
+ "version": "22.18.13",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.13.tgz",
+ "integrity": "sha512-Bo45YKIjnmFtv6I1TuC8AaHBbqXtIo+Om5fE4QiU1Tj8QR/qt+8O3BAtOimG5IFmwaWiPmB3Mv3jtYzBA4Us2A==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
- "node_modules/@types/qs": {
- "version": "6.14.0",
- "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
- "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
- "license": "MIT"
- },
- "node_modules/@types/range-parser": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
- "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
- "license": "MIT"
- },
- "node_modules/@types/request": {
- "version": "2.48.13",
- "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz",
- "integrity": "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@types/caseless": "*",
- "@types/node": "*",
- "@types/tough-cookie": "*",
- "form-data": "^2.5.5"
- }
- },
- "node_modules/@types/send": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.0.tgz",
- "integrity": "sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ==",
- "license": "MIT",
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/serve-static": {
- "version": "1.15.9",
- "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.9.tgz",
- "integrity": "sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA==",
- "license": "MIT",
- "dependencies": {
- "@types/http-errors": "*",
- "@types/node": "*",
- "@types/send": "<1"
- }
- },
- "node_modules/@types/serve-static/node_modules/@types/send": {
- "version": "0.17.5",
- "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz",
- "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==",
- "license": "MIT",
- "dependencies": {
- "@types/mime": "^1",
- "@types/node": "*"
- }
- },
- "node_modules/@types/tough-cookie": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
- "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
- "license": "MIT",
- "optional": true
- },
"node_modules/@types/triple-beam": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
@@ -639,19 +138,6 @@
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"license": "ISC"
},
- "node_modules/abort-controller": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
- "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "event-target-shim": "^5.0.0"
- },
- "engines": {
- "node": ">=6.5"
- }
- },
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -709,42 +195,6 @@
"node": ">=8"
}
},
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/ansi-styles/node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/ansi-styles/node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "license": "MIT",
- "optional": true
- },
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -785,82 +235,38 @@
"node": ">=10"
}
},
+ "node_modules/are-we-there-yet/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT"
},
- "node_modules/arrify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
- "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/async": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"license": "MIT"
},
- "node_modules/async-retry": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz",
- "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "retry": "0.13.1"
- }
- },
- "node_modules/asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/axios": {
- "version": "0.19.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
- "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
- "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410",
- "license": "MIT",
- "dependencies": {
- "follow-redirects": "1.5.10",
- "is-buffer": "^2.0.2"
- }
- },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT"
},
- "node_modules/base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
"node_modules/basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
@@ -893,15 +299,6 @@
"node": ">= 10.0.0"
}
},
- "node_modules/bignumber.js": {
- "version": "9.3.1",
- "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
- "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -1032,15 +429,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/camelcase": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
- "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -1075,21 +463,6 @@
"node": ">=10"
}
},
- "node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/cloudinary": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-2.8.0.tgz",
@@ -1103,15 +476,6 @@
"node": ">=9"
}
},
- "node_modules/code-point-at": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
- "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/color": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/color/-/color-5.0.2.tgz",
@@ -1167,19 +531,6 @@
"color-support": "bin.js"
}
},
- "node_modules/combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "delayed-stream": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -1201,36 +552,6 @@
"typedarray": "^0.0.6"
}
},
- "node_modules/concat-stream/node_modules/readable-stream": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
- "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
- "license": "MIT",
- "dependencies": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "node_modules/concat-stream/node_modules/safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "license": "MIT"
- },
- "node_modules/concat-stream/node_modules/string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "~5.1.0"
- }
- },
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@@ -1321,15 +642,6 @@
"ms": "2.0.0"
}
},
- "node_modules/decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
@@ -1339,16 +651,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=0.4.0"
- }
- },
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -1464,19 +766,6 @@
"node": ">= 0.4"
}
},
- "node_modules/duplexify": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
- "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "end-of-stream": "^1.4.1",
- "inherits": "^2.0.3",
- "readable-stream": "^3.1.1",
- "stream-shift": "^1.0.2"
- }
- },
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -1513,16 +802,6 @@
"node": ">= 0.8"
}
},
- "node_modules/end-of-stream": {
- "version": "1.4.5",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
- "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "once": "^1.4.0"
- }
- },
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
@@ -1535,15 +814,6 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
- "node_modules/error-ex": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
- "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
- "license": "MIT",
- "dependencies": {
- "is-arrayish": "^0.2.1"
- }
- },
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -1574,38 +844,12 @@
"node": ">= 0.4"
}
},
- "node_modules/es-set-tostringtag": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
- "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.6",
- "has-tostringtag": "^1.0.2",
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
"license": "MIT"
},
- "node_modules/escalade": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -1633,16 +877,6 @@
"node": ">= 0.6"
}
},
- "node_modules/event-target-shim": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
- "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/express": {
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
@@ -1691,71 +925,19 @@
},
"node_modules/express/node_modules/cookie": {
"version": "0.7.1",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
- "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/extend": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
- "license": "MIT"
- },
- "node_modules/farmhash-modern": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz",
- "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==",
- "license": "MIT",
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/fast-deep-equal": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "license": "MIT"
- },
- "node_modules/fast-sha256": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz",
- "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
- "license": "Unlicense"
- },
- "node_modules/fast-xml-parser": {
- "version": "4.5.3",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz",
- "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/NaturalIntelligence"
- }
- ],
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "strnum": "^1.1.1"
- },
- "bin": {
- "fxparser": "src/cli/cli.js"
- }
- },
- "node_modules/faye-websocket": {
- "version": "0.11.4",
- "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
- "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
- "license": "Apache-2.0",
- "dependencies": {
- "websocket-driver": ">=0.5.1"
- },
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
+ "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
+ "license": "MIT",
"engines": {
- "node": ">=0.8.0"
+ "node": ">= 0.6"
}
},
+ "node_modules/fast-sha256": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz",
+ "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
+ "license": "Unlicense"
+ },
"node_modules/fecha": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
@@ -1793,90 +975,12 @@
"node": ">= 0.8"
}
},
- "node_modules/find-up": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
- "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==",
- "license": "MIT",
- "dependencies": {
- "path-exists": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/firebase-admin": {
- "version": "13.5.0",
- "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.5.0.tgz",
- "integrity": "sha512-QZOpv1DJRJpH8NcWiL1xXE10tw3L/bdPFlgjcWrqU3ufyOJDYfxB1MMtxiVTwxK16NlybQbEM6ciSich2uWEIQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@fastify/busboy": "^3.0.0",
- "@firebase/database-compat": "^2.0.0",
- "@firebase/database-types": "^1.0.6",
- "@types/node": "^22.8.7",
- "farmhash-modern": "^1.1.0",
- "fast-deep-equal": "^3.1.1",
- "google-auth-library": "^9.14.2",
- "jsonwebtoken": "^9.0.0",
- "jwks-rsa": "^3.1.0",
- "node-forge": "^1.3.1",
- "uuid": "^11.0.2"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@google-cloud/firestore": "^7.11.0",
- "@google-cloud/storage": "^7.14.0"
- }
- },
"node_modules/fn.name": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
"license": "MIT"
},
- "node_modules/follow-redirects": {
- "version": "1.5.10",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
- "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
- "license": "MIT",
- "dependencies": {
- "debug": "=3.1.0"
- },
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/follow-redirects/node_modules/debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/form-data": {
- "version": "2.5.5",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz",
- "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "es-set-tostringtag": "^2.1.0",
- "hasown": "^2.0.2",
- "mime-types": "^2.1.35",
- "safe-buffer": "^5.2.1"
- },
- "engines": {
- "node": ">= 0.12"
- }
- },
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -1949,13 +1053,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/functional-red-black-tree": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
- "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
- "license": "MIT",
- "optional": true
- },
"node_modules/gauge": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
@@ -1977,104 +1074,6 @@
"node": ">=10"
}
},
- "node_modules/gaxios": {
- "version": "6.7.1",
- "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
- "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "extend": "^3.0.2",
- "https-proxy-agent": "^7.0.1",
- "is-stream": "^2.0.0",
- "node-fetch": "^2.6.9",
- "uuid": "^9.0.1"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/gaxios/node_modules/agent-base": {
- "version": "7.1.4",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
- "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/gaxios/node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/gaxios/node_modules/https-proxy-agent": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
- "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.2",
- "debug": "4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/gaxios/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "license": "MIT"
- },
- "node_modules/gaxios/node_modules/uuid": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
- "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
- "funding": [
- "https://github.com/sponsors/broofa",
- "https://github.com/sponsors/ctavan"
- ],
- "license": "MIT",
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
- "node_modules/gcp-metadata": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
- "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
- "license": "Apache-2.0",
- "dependencies": {
- "gaxios": "^6.1.1",
- "google-logging-utils": "^0.0.2",
- "json-bigint": "^1.0.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "license": "ISC",
- "optional": true,
- "engines": {
- "node": "6.* || 8.* || >= 10.*"
- }
- },
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -2146,70 +1145,6 @@
"node": ">= 6"
}
},
- "node_modules/google-auth-library": {
- "version": "9.15.1",
- "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
- "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
- "license": "Apache-2.0",
- "dependencies": {
- "base64-js": "^1.3.0",
- "ecdsa-sig-formatter": "^1.0.11",
- "gaxios": "^6.1.1",
- "gcp-metadata": "^6.1.0",
- "gtoken": "^7.0.0",
- "jws": "^4.0.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/google-gax": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz",
- "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "@grpc/grpc-js": "^1.10.9",
- "@grpc/proto-loader": "^0.7.13",
- "@types/long": "^4.0.0",
- "abort-controller": "^3.0.0",
- "duplexify": "^4.0.0",
- "google-auth-library": "^9.3.0",
- "node-fetch": "^2.7.0",
- "object-hash": "^3.0.0",
- "proto3-json-serializer": "^2.0.2",
- "protobufjs": "^7.3.2",
- "retry-request": "^7.0.0",
- "uuid": "^9.0.1"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/google-gax/node_modules/uuid": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
- "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
- "funding": [
- "https://github.com/sponsors/broofa",
- "https://github.com/sponsors/ctavan"
- ],
- "license": "MIT",
- "optional": true,
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
- "node_modules/google-logging-utils": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
- "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=14"
- }
- },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -2222,25 +1157,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
- "license": "ISC"
- },
- "node_modules/gtoken": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
- "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
- "license": "MIT",
- "dependencies": {
- "gaxios": "^6.0.0",
- "jws": "^4.0.0"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -2263,22 +1179,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/has-tostringtag": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
- "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "has-symbols": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
@@ -2306,12 +1206,6 @@
"node": ">=18.0.0"
}
},
- "node_modules/hosted-git-info": {
- "version": "2.8.9",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
- "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
- "license": "ISC"
- },
"node_modules/hpp": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/hpp/-/hpp-0.2.3.tgz",
@@ -2325,23 +1219,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/html-entities": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz",
- "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/mdevils"
- },
- {
- "type": "patreon",
- "url": "https://patreon.com/mdevils"
- }
- ],
- "license": "MIT",
- "optional": true
- },
"node_modules/htmlparser2": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
@@ -2377,52 +1254,6 @@
"node": ">= 0.8"
}
},
- "node_modules/http-parser-js": {
- "version": "0.5.10",
- "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz",
- "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
- "license": "MIT"
- },
- "node_modules/http-proxy-agent": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
- "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@tootallnate/once": "2",
- "agent-base": "6",
- "debug": "4"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/http-proxy-agent/node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/http-proxy-agent/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "license": "MIT",
- "optional": true
- },
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
@@ -2495,15 +1326,6 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
- "node_modules/invert-kv": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
- "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -2513,12 +1335,6 @@
"node": ">= 0.10"
}
},
- "node_modules/is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "license": "MIT"
- },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -2532,44 +1348,6 @@
"node": ">=8"
}
},
- "node_modules/is-buffer": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
- "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/is-core-module": {
- "version": "2.16.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
- "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -2633,35 +1411,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-utf8": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
- "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==",
- "license": "MIT"
- },
- "node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
- "license": "MIT"
- },
- "node_modules/jose": {
- "version": "4.15.9",
- "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
- "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/panva"
- }
- },
- "node_modules/json-bigint": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
- "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
- "license": "MIT",
- "dependencies": {
- "bignumber.js": "^9.0.0"
- }
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "license": "MIT"
},
"node_modules/jsonwebtoken": {
"version": "9.0.2",
@@ -2685,27 +1439,6 @@
"npm": ">=6"
}
},
- "node_modules/jsonwebtoken/node_modules/jwa": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
- "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
- "license": "MIT",
- "dependencies": {
- "buffer-equal-constant-time": "^1.0.1",
- "ecdsa-sig-formatter": "1.0.11",
- "safe-buffer": "^5.0.1"
- }
- },
- "node_modules/jsonwebtoken/node_modules/jws": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
- "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
- "license": "MIT",
- "dependencies": {
- "jwa": "^1.4.1",
- "safe-buffer": "^5.0.1"
- }
- },
"node_modules/jsonwebtoken/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -2713,9 +1446,9 @@
"license": "MIT"
},
"node_modules/jwa": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
- "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
+ "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "^1.0.1",
@@ -2723,53 +1456,13 @@
"safe-buffer": "^5.0.1"
}
},
- "node_modules/jwks-rsa": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz",
- "integrity": "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==",
- "license": "MIT",
- "dependencies": {
- "@types/express": "^4.17.20",
- "@types/jsonwebtoken": "^9.0.4",
- "debug": "^4.3.4",
- "jose": "^4.15.4",
- "limiter": "^1.1.5",
- "lru-memoizer": "^2.2.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/jwks-rsa/node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/jwks-rsa/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "license": "MIT"
- },
"node_modules/jws": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
- "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
"license": "MIT",
"dependencies": {
- "jwa": "^2.0.0",
+ "jwa": "^1.4.1",
"safe-buffer": "^5.0.1"
}
},
@@ -2788,202 +1481,12 @@
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
"license": "MIT"
},
- "node_modules/lcid": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
- "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==",
- "license": "MIT",
- "dependencies": {
- "invert-kv": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/limiter": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
- "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
- },
- "node_modules/load-json-file": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
- "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.1.2",
- "parse-json": "^2.2.0",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0",
- "strip-bom": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/localtunnel": {
- "version": "1.9.2",
- "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-1.9.2.tgz",
- "integrity": "sha512-NEKF7bDJE9U3xzJu3kbayF0WTvng6Pww7tzqNb/XtEARYwqw7CKEX7BvOMg98FtE9es2CRizl61gkV3hS8dqYg==",
- "license": "MIT",
- "dependencies": {
- "axios": "0.19.0",
- "debug": "4.1.1",
- "openurl": "1.1.1",
- "yargs": "6.6.0"
- },
- "bin": {
- "lt": "bin/client"
- }
- },
- "node_modules/localtunnel/node_modules/ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/localtunnel/node_modules/cliui": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
- "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==",
- "license": "ISC",
- "dependencies": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wrap-ansi": "^2.0.0"
- }
- },
- "node_modules/localtunnel/node_modules/debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.1"
- }
- },
- "node_modules/localtunnel/node_modules/get-caller-file": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
- "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
- "license": "ISC"
- },
- "node_modules/localtunnel/node_modules/is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
- "license": "MIT",
- "dependencies": {
- "number-is-nan": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/localtunnel/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "license": "MIT"
- },
- "node_modules/localtunnel/node_modules/string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
- "license": "MIT",
- "dependencies": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/localtunnel/node_modules/strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/localtunnel/node_modules/wrap-ansi": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
- "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==",
- "license": "MIT",
- "dependencies": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/localtunnel/node_modules/y18n": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz",
- "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==",
- "license": "ISC"
- },
- "node_modules/localtunnel/node_modules/yargs": {
- "version": "6.6.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz",
- "integrity": "sha512-6/QWTdisjnu5UHUzQGst+UOEuEVwIzFVGBjq3jMTFNs5WJQsH/X6nMURSaScIdF5txylr1Ao9bvbWiKi2yXbwA==",
- "license": "MIT",
- "dependencies": {
- "camelcase": "^3.0.0",
- "cliui": "^3.2.0",
- "decamelize": "^1.1.1",
- "get-caller-file": "^1.0.1",
- "os-locale": "^1.4.0",
- "read-pkg-up": "^1.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^1.0.1",
- "set-blocking": "^2.0.0",
- "string-width": "^1.0.2",
- "which-module": "^1.0.0",
- "y18n": "^3.2.1",
- "yargs-parser": "^4.2.0"
- }
- },
- "node_modules/localtunnel/node_modules/yargs-parser": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz",
- "integrity": "sha512-+QQWqC2xeL0N5/TE+TY6OGEqyNRM+g2/r712PDNYgiCdXYCApXf1vzfmDSLBxfGRwV+moTq/V8FnMI24JCm2Yg==",
- "license": "ISC",
- "dependencies": {
- "camelcase": "^3.0.0"
- }
- },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
- "node_modules/lodash.camelcase": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
- "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/lodash.clonedeep": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
- "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
- "license": "MIT"
- },
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -3049,35 +1552,6 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
- "node_modules/long": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
- "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
- "license": "Apache-2.0",
- "optional": true
- },
- "node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/lru-memoizer": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz",
- "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==",
- "license": "MIT",
- "dependencies": {
- "lodash.clonedeep": "^4.5.0",
- "lru-cache": "6.0.0"
- }
- },
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -3145,16 +1619,15 @@
}
},
"node_modules/mime": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
- "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"license": "MIT",
- "optional": true,
"bin": {
"mime": "cli.js"
},
"engines": {
- "node": ">=10.0.0"
+ "node": ">=4"
}
},
"node_modules/mime-db": {
@@ -3192,158 +1665,60 @@
},
"node_modules/minimist": {
"version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/minipass": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
- "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
- "license": "ISC",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/minizlib": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
- "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
- "license": "MIT",
- "dependencies": {
- "minipass": "^3.0.0",
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/minizlib/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.6"
- },
- "bin": {
- "mkdirp": "bin/cmd.js"
- }
- },
- "node_modules/mongodb-connection-string-url": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
- "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@types/whatwg-url": "^11.0.2",
- "whatwg-url": "^14.1.0 || ^13.0.0"
- }
- },
- "node_modules/mongodb-connection-string-url/node_modules/tr46": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
- "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
- "license": "MIT",
- "dependencies": {
- "punycode": "^2.3.1"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
- "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=12"
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": {
- "version": "14.2.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
- "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
- "license": "MIT",
- "dependencies": {
- "tr46": "^5.1.0",
- "webidl-conversions": "^7.0.0"
- },
+ "node_modules/minipass": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+ "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+ "license": "ISC",
"engines": {
- "node": ">=18"
+ "node": ">=8"
}
},
- "node_modules/mongoose": {
- "version": "8.19.2",
- "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.19.2.tgz",
- "integrity": "sha512-ww2T4dBV+suCbOfG5YPwj9pLCfUVyj8FEA1D3Ux1HHqutpLxGyOYEPU06iPRBW4cKr3PJfOSYsIpHWPTkz5zig==",
+ "node_modules/minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"license": "MIT",
"dependencies": {
- "bson": "^6.10.4",
- "kareem": "2.6.3",
- "mongodb": "~6.20.0",
- "mpath": "0.9.0",
- "mquery": "5.0.0",
- "ms": "2.1.3",
- "sift": "17.1.3"
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
},
"engines": {
- "node": ">=16.20.1"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mongoose"
+ "node": ">= 8"
}
},
- "node_modules/mongoose/node_modules/gaxios": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz",
- "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==",
- "license": "Apache-2.0",
- "optional": true,
- "peer": true,
+ "node_modules/minizlib/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "license": "ISC",
"dependencies": {
- "extend": "^3.0.2",
- "https-proxy-agent": "^5.0.0",
- "is-stream": "^2.0.0",
- "node-fetch": "^2.6.9"
+ "yallist": "^4.0.0"
},
"engines": {
- "node": ">=12"
+ "node": ">=8"
}
},
- "node_modules/mongoose/node_modules/gcp-metadata": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
- "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
- "license": "Apache-2.0",
- "optional": true,
- "peer": true,
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "license": "MIT",
"dependencies": {
- "gaxios": "^5.0.0",
- "json-bigint": "^1.0.0"
+ "minimist": "^1.2.6"
},
- "engines": {
- "node": ">=12"
+ "bin": {
+ "mkdirp": "bin/cmd.js"
}
},
- "node_modules/mongoose/node_modules/mongodb": {
+ "node_modules/mongodb": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz",
"integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==",
@@ -3389,6 +1764,38 @@
}
}
},
+ "node_modules/mongodb-connection-string-url": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
+ "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/whatwg-url": "^11.0.2",
+ "whatwg-url": "^14.1.0 || ^13.0.0"
+ }
+ },
+ "node_modules/mongoose": {
+ "version": "8.19.2",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.19.2.tgz",
+ "integrity": "sha512-ww2T4dBV+suCbOfG5YPwj9pLCfUVyj8FEA1D3Ux1HHqutpLxGyOYEPU06iPRBW4cKr3PJfOSYsIpHWPTkz5zig==",
+ "license": "MIT",
+ "dependencies": {
+ "bson": "^6.10.4",
+ "kareem": "2.6.3",
+ "mongodb": "~6.20.0",
+ "mpath": "0.9.0",
+ "mquery": "5.0.0",
+ "ms": "2.1.3",
+ "sift": "17.1.3"
+ },
+ "engines": {
+ "node": ">=16.20.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mongoose"
+ }
+ },
"node_modules/mongoose/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -3545,13 +1952,26 @@
}
}
},
- "node_modules/node-forge": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
- "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
- "license": "(BSD-3-Clause OR GPL-2.0)",
- "engines": {
- "node": ">= 6.13.0"
+ "node_modules/node-fetch/node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "license": "MIT"
+ },
+ "node_modules/node-fetch/node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/node-fetch/node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
}
},
"node_modules/nodemailer": {
@@ -3632,27 +2052,6 @@
"node": ">=6"
}
},
- "node_modules/normalize-package-data": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
- "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "hosted-git-info": "^2.1.4",
- "resolve": "^1.10.0",
- "semver": "2 || 3 || 4 || 5",
- "validate-npm-package-license": "^3.0.1"
- }
- },
- "node_modules/normalize-package-data/node_modules/semver": {
- "version": "5.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
- "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver"
- }
- },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -3676,15 +2075,6 @@
"set-blocking": "^2.0.0"
}
},
- "node_modules/number-is-nan": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -3694,16 +2084,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/object-hash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
- "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@@ -3755,52 +2135,6 @@
"fn.name": "1.x.x"
}
},
- "node_modules/openurl": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz",
- "integrity": "sha512-d/gTkTb1i1GKz5k3XE3XFV/PxQ1k45zDqGP2OA7YhgsaLoqm6qRvARAZOFer1fcXritWlGBRCu/UgeS4HAnXAA==",
- "license": "MIT"
- },
- "node_modules/os-locale": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
- "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==",
- "license": "MIT",
- "dependencies": {
- "lcid": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "yocto-queue": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/parse-json": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
- "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==",
- "license": "MIT",
- "dependencies": {
- "error-ex": "^1.2.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/parse-srcset": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
@@ -3816,18 +2150,6 @@
"node": ">= 0.8"
}
},
- "node_modules/path-exists": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
- "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==",
- "license": "MIT",
- "dependencies": {
- "pinkie-promise": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -3837,32 +2159,12 @@
"node": ">=0.10.0"
}
},
- "node_modules/path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "license": "MIT"
- },
"node_modules/path-to-regexp": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"license": "MIT"
},
- "node_modules/path-type": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
- "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.1.2",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -3882,36 +2184,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
- "node_modules/pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/pinkie": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
- "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/pinkie-promise": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
- "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
- "license": "MIT",
- "dependencies": {
- "pinkie": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
@@ -3964,44 +2236,6 @@
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"license": "MIT"
},
- "node_modules/proto3-json-serializer": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz",
- "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "protobufjs": "^7.2.5"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/protobufjs": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
- "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
- "hasInstallScript": true,
- "license": "BSD-3-Clause",
- "optional": true,
- "dependencies": {
- "@protobufjs/aspromise": "^1.1.2",
- "@protobufjs/base64": "^1.1.2",
- "@protobufjs/codegen": "^2.0.4",
- "@protobufjs/eventemitter": "^1.1.0",
- "@protobufjs/fetch": "^1.1.0",
- "@protobufjs/float": "^1.0.2",
- "@protobufjs/inquire": "^1.1.0",
- "@protobufjs/path": "^1.1.2",
- "@protobufjs/pool": "^1.1.0",
- "@protobufjs/utf8": "^1.1.0",
- "@types/node": ">=13.7.0",
- "long": "^5.0.0"
- },
- "engines": {
- "node": ">=12.0.0"
- }
- },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -4093,47 +2327,27 @@
"node": ">= 0.8"
}
},
- "node_modules/read-pkg": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
- "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==",
- "license": "MIT",
- "dependencies": {
- "load-json-file": "^1.0.0",
- "normalize-package-data": "^2.3.2",
- "path-type": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/read-pkg-up": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
- "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==",
- "license": "MIT",
- "dependencies": {
- "find-up": "^1.0.0",
- "read-pkg": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"license": "MIT",
- "dependencies": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- },
- "engines": {
- "node": ">= 6"
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
}
},
+ "node_modules/readable-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "license": "MIT"
+ },
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -4147,21 +2361,6 @@
"node": ">=8.10.0"
}
},
- "node_modules/require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/require-main-filename": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
- "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==",
- "license": "ISC"
- },
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@@ -4169,9 +2368,9 @@
"license": "MIT"
},
"node_modules/resend": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/resend/-/resend-6.2.2.tgz",
- "integrity": "sha512-EF/wUj0y/CHBDqLV9iKrNHxGV/wdJfzfEzhPbd3tXD4wtMssabgVwys7N3dv+s1EsqnXjC0uN6ylpfvWTzF4Aw==",
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/resend/-/resend-6.4.0.tgz",
+ "integrity": "sha512-CTr4ix4RI5M/ucL58Wqr+LE8eI4JHtJEFaBAx6yUVNOI3eaPVtJjpNL0G/BdRSWMbwv6CtpprVOY8Xvpp6UJlA==",
"license": "MIT",
"dependencies": {
"svix": "1.76.1"
@@ -4188,51 +2387,6 @@
}
}
},
- "node_modules/resolve": {
- "version": "1.22.11",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
- "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
- "license": "MIT",
- "dependencies": {
- "is-core-module": "^2.16.1",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/retry": {
- "version": "0.13.1",
- "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
- "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/retry-request": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz",
- "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@types/request": "^2.48.8",
- "extend": "^3.0.2",
- "teeny-request": "^9.0.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -4343,18 +2497,6 @@
"node": ">= 0.8"
}
},
- "node_modules/send/node_modules/mime": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
- "license": "MIT",
- "bin": {
- "mime": "cli.js"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -4503,38 +2645,6 @@
"memory-pager": "^1.0.2"
}
},
- "node_modules/spdx-correct": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
- "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
- "license": "Apache-2.0",
- "dependencies": {
- "spdx-expression-parse": "^3.0.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-exceptions": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
- "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
- "license": "CC-BY-3.0"
- },
- "node_modules/spdx-expression-parse": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
- "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
- "license": "MIT",
- "dependencies": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-license-ids": {
- "version": "3.0.22",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz",
- "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==",
- "license": "CC0-1.0"
- },
"node_modules/stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
@@ -4553,23 +2663,6 @@
"node": ">= 0.8"
}
},
- "node_modules/stream-events": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
- "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "stubs": "^3.0.0"
- }
- },
- "node_modules/stream-shift": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
- "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
- "license": "MIT",
- "optional": true
- },
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@@ -4579,14 +2672,20 @@
}
},
"node_modules/string_decoder": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
- "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"license": "MIT",
"dependencies": {
- "safe-buffer": "~5.2.0"
+ "safe-buffer": "~5.1.0"
}
},
+ "node_modules/string_decoder/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "license": "MIT"
+ },
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
@@ -4613,38 +2712,6 @@
"node": ">=8"
}
},
- "node_modules/strip-bom": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
- "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==",
- "license": "MIT",
- "dependencies": {
- "is-utf8": "^0.2.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/strnum": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz",
- "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/NaturalIntelligence"
- }
- ],
- "license": "MIT",
- "optional": true
- },
- "node_modules/stubs": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
- "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==",
- "license": "MIT",
- "optional": true
- },
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -4658,18 +2725,6 @@
"node": ">=4"
}
},
- "node_modules/supports-preserve-symlinks-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/svix": {
"version": "1.76.1",
"resolved": "https://registry.npmjs.org/svix/-/svix-1.76.1.tgz",
@@ -4684,19 +2739,6 @@
"uuid": "^10.0.0"
}
},
- "node_modules/svix/node_modules/uuid": {
- "version": "10.0.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
- "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
- "funding": [
- "https://github.com/sponsors/broofa",
- "https://github.com/sponsors/ctavan"
- ],
- "license": "MIT",
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
"node_modules/tar": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
@@ -4726,37 +2768,6 @@
"node": ">=10"
}
},
- "node_modules/teeny-request": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz",
- "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "http-proxy-agent": "^5.0.0",
- "https-proxy-agent": "^5.0.0",
- "node-fetch": "^2.6.9",
- "stream-events": "^1.0.5",
- "uuid": "^9.0.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/teeny-request/node_modules/uuid": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
- "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
- "funding": [
- "https://github.com/sponsors/broofa",
- "https://github.com/sponsors/ctavan"
- ],
- "license": "MIT",
- "optional": true,
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
"node_modules/text-hex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
@@ -4796,10 +2807,16 @@
}
},
"node_modules/tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "license": "MIT"
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
+ "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
},
"node_modules/triple-beam": {
"version": "1.4.1",
@@ -4810,12 +2827,6 @@
"node": ">= 14.0.0"
}
},
- "node_modules/tslib": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
- "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
- "license": "0BSD"
- },
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -4883,26 +2894,16 @@
}
},
"node_modules/uuid": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
- "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
- "uuid": "dist/esm/bin/uuid"
- }
- },
- "node_modules/validate-npm-package-license": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
- "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
- "license": "Apache-2.0",
- "dependencies": {
- "spdx-correct": "^3.0.0",
- "spdx-expression-parse": "^3.0.0"
+ "uuid": "dist/bin/uuid"
}
},
"node_modules/vary": {
@@ -4915,50 +2916,27 @@
}
},
"node_modules/webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "license": "BSD-2-Clause"
- },
- "node_modules/websocket-driver": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
- "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
- "license": "Apache-2.0",
- "dependencies": {
- "http-parser-js": ">=0.5.1",
- "safe-buffer": ">=5.1.0",
- "websocket-extensions": ">=0.1.1"
- },
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/websocket-extensions": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
- "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
- "license": "Apache-2.0",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "license": "BSD-2-Clause",
"engines": {
- "node": ">=0.8.0"
+ "node": ">=12"
}
},
"node_modules/whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "version": "14.2.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+ "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
"license": "MIT",
"dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
+ "tr46": "^5.1.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/which-module": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
- "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==",
- "license": "ISC"
- },
"node_modules/wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
@@ -5004,22 +2982,32 @@
"node": ">= 12.0.0"
}
},
- "node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "node_modules/winston-transport/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"license": "MIT",
- "optional": true,
"dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
},
"engines": {
- "node": ">=10"
+ "node": ">= 6"
+ }
+ },
+ "node_modules/winston/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
},
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ "engines": {
+ "node": ">= 6"
}
},
"node_modules/wrappy": {
@@ -5037,63 +3025,11 @@
"node": ">=0.4"
}
},
- "node_modules/y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "license": "ISC",
- "optional": true,
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"license": "ISC"
- },
- "node_modules/yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "license": "ISC",
- "optional": true,
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
}
}
}
diff --git a/server/package.json b/server/package.json
index 5b8f4ceb7..b5e437713 100644
--- a/server/package.json
+++ b/server/package.json
@@ -33,11 +33,9 @@
"crypto": "^1.0.1",
"dotenv": "^16.4.7",
"express": "^4.21.2",
- "firebase-admin": "^13.1.0",
"helmet": "^8.1.0",
"hpp": "^0.2.3",
"jsonwebtoken": "^9.0.2",
- "localtunnel": "^1.8.3",
"mongoose": "^8.11.0",
"morgan": "^1.10.1",
"multer": "^1.4.5-lts.1",
diff --git a/server/src/controllers/auth/login.js b/server/src/controllers/auth/login.js
index bc2c4bdce..8911668a8 100644
--- a/server/src/controllers/auth/login.js
+++ b/server/src/controllers/auth/login.js
@@ -11,11 +11,7 @@ import SUBSCRIBER from '../../models/subscriber.model.js';
import { COOKIE_TOKEN, NODE_ENV } from '../../typings/index.js';
import { sendResponse } from '../../utils/response.js';
import { generateTokens } from './utils/index.js';
-import {
- JWT_ACCESS_EXPIRES_IN_NUM,
- JWT_REFRESH_EXPIRES_IN_NUM,
- SERVER_ENV,
-} from '../../config/env.js';
+import { JWT_REFRESH_EXPIRES_IN_NUM, SERVER_ENV } from '../../config/env.js';
const login = async (req, res) => {
const { email, password } = req.body;
@@ -57,23 +53,16 @@ const login = async (req, res) => {
subscriber_id: subscriber._id,
};
- const { accessToken, refreshToken } = generateTokens(payload);
-
- res.cookie(COOKIE_TOKEN.ACCESS_TOKEN, accessToken, {
- httpOnly: true,
- secure: SERVER_ENV === NODE_ENV.PRODUCTION,
- sameSite: 'strict',
- maxAge: JWT_ACCESS_EXPIRES_IN_NUM,
- });
-
- res.cookie(COOKIE_TOKEN.REFRESH_TOKEN, refreshToken, {
+ const { access_token, refresh_token } = generateTokens(payload);
+ res.cookie(COOKIE_TOKEN.REFRESH_TOKEN, refresh_token, {
httpOnly: true,
secure: SERVER_ENV === NODE_ENV.PRODUCTION,
sameSite: 'strict',
+ path: '/',
maxAge: JWT_REFRESH_EXPIRES_IN_NUM,
});
- return sendResponse(res, 200, 'Login successful', user);
+ return sendResponse(res, 200, 'Login successful', { user, access_token });
} catch (err) {
return sendResponse(res, 500, err.message || 'Internal Server Error');
}
diff --git a/server/src/controllers/auth/logout.js b/server/src/controllers/auth/logout.js
index 6b42e7fcd..1afb16780 100644
--- a/server/src/controllers/auth/logout.js
+++ b/server/src/controllers/auth/logout.js
@@ -9,17 +9,12 @@ import { SERVER_ENV } from '../../config/env.js';
const logout = async (req, res) => {
try {
- // Clear both access and refresh cookies
- res.clearCookie(COOKIE_TOKEN.ACCESS_TOKEN, {
- httpOnly: true,
- secure: SERVER_ENV === NODE_ENV.PRODUCTION,
- sameSite: 'strict',
- });
-
+ // Clear refresh cookies
res.clearCookie(COOKIE_TOKEN.REFRESH_TOKEN, {
httpOnly: true,
secure: SERVER_ENV === NODE_ENV.PRODUCTION,
sameSite: 'strict',
+ path: '/',
});
return sendResponse(res, 200, 'Logged out successfully');
diff --git a/server/src/controllers/auth/refresh.js b/server/src/controllers/auth/refresh.js
index adafda13f..ec66a5c84 100644
--- a/server/src/controllers/auth/refresh.js
+++ b/server/src/controllers/auth/refresh.js
@@ -5,13 +5,11 @@
import jwt from 'jsonwebtoken';
import { sendResponse } from '../../utils/response.js';
-import { COOKIE_TOKEN, NODE_ENV } from '../../typings/index.js';
+import { COOKIE_TOKEN } from '../../typings/index.js';
import {
JWT_SECRET_ACCESS_KEY,
JWT_SECRET_REFRESH_KEY,
JWT_ACCESS_EXPIRES_IN,
- JWT_ACCESS_EXPIRES_IN_NUM,
- SERVER_ENV,
} from '../../config/env.js';
const refresh = async (req, res) => {
@@ -38,15 +36,9 @@ const refresh = async (req, res) => {
expiresIn: JWT_ACCESS_EXPIRES_IN,
});
- // Replace old access token cookie
- res.cookie(COOKIE_TOKEN.ACCESS_TOKEN, new_access_token, {
- httpOnly: true,
- secure: SERVER_ENV === NODE_ENV.PRODUCTION,
- sameSite: 'strict',
- maxAge: JWT_ACCESS_EXPIRES_IN_NUM,
+ return sendResponse(res, 200, 'Access token refreshed successfully', {
+ access_token: new_access_token,
});
-
- return sendResponse(res, 200, 'Access token refreshed successfully');
});
} catch (err) {
return sendResponse(res, 500, err.message || 'Internal Server Error');
diff --git a/server/src/controllers/auth/signup.js b/server/src/controllers/auth/signup.js
index 58285e703..9d819641b 100644
--- a/server/src/controllers/auth/signup.js
+++ b/server/src/controllers/auth/signup.js
@@ -14,11 +14,7 @@ import { SALT_ROUNDS } from '../../constants/index.js';
import { COOKIE_TOKEN, NODE_ENV } from '../../typings/index.js';
import { sendResponse } from '../../utils/response.js';
import { generateTokens, generateUsername } from './utils/index.js';
-import {
- JWT_ACCESS_EXPIRES_IN_NUM,
- JWT_REFRESH_EXPIRES_IN_NUM,
- SERVER_ENV,
-} from '../../config/env.js';
+import { JWT_REFRESH_EXPIRES_IN_NUM, SERVER_ENV } from '../../config/env.js';
const signup = async (req, res) => {
const { fullname, email, password } = req.body;
@@ -85,23 +81,19 @@ const signup = async (req, res) => {
subscriber_id: subscriber._id,
};
- const { accessToken, refreshToken } = generateTokens(payload);
-
- res.cookie(COOKIE_TOKEN.ACCESS_TOKEN, accessToken, {
- httpOnly: true,
- secure: SERVER_ENV === NODE_ENV.PRODUCTION,
- sameSite: 'strict',
- maxAge: JWT_ACCESS_EXPIRES_IN_NUM,
- });
-
- res.cookie(COOKIE_TOKEN.REFRESH_TOKEN, refreshToken, {
+ const { access_token, refresh_token } = generateTokens(payload);
+ res.cookie(COOKIE_TOKEN.REFRESH_TOKEN, refresh_token, {
httpOnly: true,
secure: SERVER_ENV === NODE_ENV.PRODUCTION,
sameSite: 'strict',
+ path: '/',
maxAge: JWT_REFRESH_EXPIRES_IN_NUM,
});
- return sendResponse(res, 201, 'User registered successfully', saved_user);
+ return sendResponse(res, 201, 'User registered successfully', {
+ user: saved_user,
+ access_token,
+ });
} catch (err) {
return sendResponse(res, 500, err.message || 'Internal Server Error');
}
diff --git a/server/src/controllers/auth/utils/index.js b/server/src/controllers/auth/utils/index.js
index 23c62f04e..16ea826e4 100644
--- a/server/src/controllers/auth/utils/index.js
+++ b/server/src/controllers/auth/utils/index.js
@@ -27,14 +27,14 @@ export const generateUsername = async email => {
/**
* Generate access and refresh JWT tokens
* @param {Object} payload - JWT payload data
- * @returns {Object} - Object containing accessToken and refreshToken
+ * @returns {Object} - Object containing access_token and refresh_token
*/
export const generateTokens = payload => {
- const accessToken = jwt.sign(payload, JWT_SECRET_ACCESS_KEY, {
+ const access_token = jwt.sign(payload, JWT_SECRET_ACCESS_KEY, {
expiresIn: JWT_ACCESS_EXPIRES_IN,
});
- const refreshToken = jwt.sign(payload, JWT_SECRET_REFRESH_KEY, {
+ const refresh_token = jwt.sign(payload, JWT_SECRET_REFRESH_KEY, {
expiresIn: JWT_REFRESH_EXPIRES_IN,
});
- return { accessToken, refreshToken };
+ return { access_token, refresh_token };
};
diff --git a/server/src/controllers/project/create-project.js b/server/src/controllers/project/create-project.js
index 6f1a7d1ec..91d9018a8 100644
--- a/server/src/controllers/project/create-project.js
+++ b/server/src/controllers/project/create-project.js
@@ -55,7 +55,12 @@ const createProject = async (req, res) => {
if (!tags?.length || tags.length > 10) {
return sendResponse(res, 400, 'Provide up to 10 tags');
}
- if (!content_blocks?.[0]?.blocks?.length) {
+ if (
+ !(
+ (Array.isArray(content_blocks) && content_blocks.length > 0) ||
+ (!Array.isArray(content_blocks) && !!content_blocks?.blocks?.length)
+ )
+ ) {
return sendResponse(res, 400, 'Project content required');
}
}
diff --git a/server/src/controllers/project/get-all-projects.js b/server/src/controllers/project/get-all-projects.js
index 04c63a817..223447f5e 100644
--- a/server/src/controllers/project/get-all-projects.js
+++ b/server/src/controllers/project/get-all-projects.js
@@ -9,7 +9,7 @@ import { sendResponse } from '../../utils/response.js';
const getAllProjects = async (req, res) => {
let page = req.query.page || 1;
- const maxLimit = 5;
+ const maxLimit = 10;
if (page < 1) page = 1;
diff --git a/server/src/controllers/project/search-projects.js b/server/src/controllers/project/search-projects.js
index 1570c96a7..b61525be9 100644
--- a/server/src/controllers/project/search-projects.js
+++ b/server/src/controllers/project/search-projects.js
@@ -18,21 +18,23 @@ const searchProjects = async (req, res) => {
query,
user_id,
page = 1,
- limit = 2,
+ limit = 10,
rmv_project_by_id,
} = req.query;
const findQuery = { is_draft: false };
- if (tag) {
+ if (tag && tag !== 'undefined') {
findQuery.tags = tag;
if (rmv_project_by_id) findQuery._id = { $ne: rmv_project_by_id };
- } else if (query) {
+ }
+ if (query && query !== 'undefined') {
findQuery.title = new RegExp(query, 'i');
- } else if (user_id) {
+ }
+ if (user_id && user_id !== 'undefined') {
findQuery.user_id = user_id;
}
- const maxLimit = parseInt(limit) || 2;
+ const maxLimit = parseInt(limit) || 10;
const currentPage = parseInt(page) || 1;
try {
diff --git a/server/src/controllers/user/search-user.js b/server/src/controllers/user/search-user.js
index a42d56bbf..67e8865de 100644
--- a/server/src/controllers/user/search-user.js
+++ b/server/src/controllers/user/search-user.js
@@ -1,6 +1,7 @@
/**
- * GET /api/user/search?q= - Search users by username
+ * GET /api/user/search?query=page= - Search users by username
* @param {string} query - Search query (query param)
+ * @param {number} [page=1] - Page number (query param)
* @returns {Object[]} Array of users
*/
@@ -9,19 +10,24 @@ import { sendResponse } from '../../utils/response.js';
const searchUser = async (req, res) => {
const query = req.query.query;
- if (!query) {
+ let page = req.query.page || 1;
+ const maxLimit = 10;
+
+ if (page < 1) page = 1;
+ if (!query || query === 'undefined') {
return sendResponse(res, 400, 'Search query is required');
}
try {
- // TODO: Implement pagination for large result sets
const users = await USER.find({
'personal_info.username': new RegExp(query, 'i'),
})
- .limit(50)
.select(
'personal_info.fullname personal_info.username personal_info.profile_img -_id'
- );
+ )
+ .skip((page - 1) * maxLimit)
+ .limit(maxLimit)
+ .lean();
return sendResponse(res, 200, 'Users fetched successfully', users);
} catch (error) {
diff --git a/server/src/middlewares/auth.middleware.js b/server/src/middlewares/auth.middleware.js
index a1df57ffd..957f1c486 100644
--- a/server/src/middlewares/auth.middleware.js
+++ b/server/src/middlewares/auth.middleware.js
@@ -4,7 +4,9 @@ import { COOKIE_TOKEN } from '../typings/index.js';
import { JWT_SECRET_ACCESS_KEY } from '../config/env.js';
const authenticateUser = (req, res, next) => {
- const token = req.cookies?.[COOKIE_TOKEN.ACCESS_TOKEN];
+ const token =
+ req.headers['authorization']?.split(' ')[1] ||
+ req.cookies?.[COOKIE_TOKEN.ACCESS_TOKEN];
if (!token) {
return sendResponse(res, 401, 'Access Denied: No access token provided');
diff --git a/server/src/middlewares/logging.middleware.js b/server/src/middlewares/logging.middleware.js
index afc106dc3..94ba9463d 100644
--- a/server/src/middlewares/logging.middleware.js
+++ b/server/src/middlewares/logging.middleware.js
@@ -1,7 +1,7 @@
import morganMiddleware from '../logger/morgan.js';
import logger from '../logger/winston.js';
-export const loggingMiddleware = app => {
+const loggingMiddleware = app => {
app.use(morganMiddleware);
app.use((req, res, next) => {
@@ -9,3 +9,5 @@ export const loggingMiddleware = app => {
next();
});
};
+
+export default loggingMiddleware;
diff --git a/server/src/schemas/project.schema.js b/server/src/schemas/project.schema.js
index 6b0a368dc..eb1d79510 100644
--- a/server/src/schemas/project.schema.js
+++ b/server/src/schemas/project.schema.js
@@ -15,7 +15,6 @@ const PROJECT_SCHEMA = Schema(
description: {
type: String,
maxlength: [200, 'Bio should not be more than 200'],
- required: true,
},
repository_url: {
type: String,
diff --git a/server/src/server.js b/server/src/server.js
index 42672e007..ffcb04c92 100644
--- a/server/src/server.js
+++ b/server/src/server.js
@@ -5,19 +5,19 @@ import dotenv from 'dotenv';
// Configs
import connectDB from './config/db.js';
+import { SERVER_ENV } from './config/env.js';
+import { NODE_ENV } from './typings/index.js';
// Middlewares
import errorHandler from './middlewares/error.handler.js';
import securityMiddleware from './middlewares/security.middleware.js';
import sanitizeInput from './middlewares/sanitize.middleware.js';
+import loggingMiddleware from './middlewares/logging.middleware.js';
// Routes
import monitorRoutes from './routes/api/monitor.routes.js';
import router from './routes/index.js';
-// Logger
-import { loggingMiddleware } from './middlewares/logging.middleware.js';
-
dotenv.config();
const server = express();
@@ -25,7 +25,12 @@ const server = express();
// Middleware
server.use(express.json());
server.use(cookieParser());
-server.use(cors());
+server.use(
+ cors({
+ origin: process.env.VITE_CLIENT_DOMAIN || 'http://localhost:5173',
+ credentials: true,
+ })
+);
// securityMiddleware
securityMiddleware(server);
@@ -33,8 +38,10 @@ securityMiddleware(server);
// sanitizationMiddleware (global)
server.use(sanitizeInput());
-// Logging middleware
-loggingMiddleware(server);
+// Logging middleware (only in development)
+if (SERVER_ENV === NODE_ENV.DEVELOPMENT) {
+ loggingMiddleware(server);
+}
// Connect to Database
connectDB();