From ee289d27b2fb36f2525c9c0ecc2f56de573d4c49 Mon Sep 17 00:00:00 2001 From: Sina Blattmann Date: Thu, 20 Apr 2023 11:13:29 +0200 Subject: [PATCH 1/2] fix some bugs --- typescript/Frontend/src/App.tsx | 16 +++++--- typescript/Frontend/src/Login.tsx | 5 ++- typescript/Frontend/src/ResetPassword.tsx | 5 ++- .../Context/UserContextProvider.tsx | 20 +++++++++- .../Context/UsersContextProvider.tsx | 27 +++++++++---- .../AccessManagement/AccessManagement.tsx | 4 +- .../AccessManagement/AvailableUserDialog.tsx | 12 ++++-- .../UsersWithDirectAccess.tsx | 4 +- .../src/components/Groups/FolderForm.tsx | 4 +- .../Installations/InstallationForm.tsx | 4 +- .../Installations/InstallationList.tsx | 3 +- .../Installations/InstallationPage.tsx | 2 +- .../Installations/Installations.tsx | 2 +- .../Frontend/src/components/Layout/Search.tsx | 4 +- .../Frontend/src/components/Users/User.tsx | 4 +- .../src/components/Users/UserForm.tsx | 10 +++-- .../src/components/Users/UserTabs.tsx | 36 ++++++++---------- .../Frontend/src/components/Users/Users.tsx | 6 +-- .../Frontend/src/config/axiosConfig.tsx | 2 +- typescript/Frontend/src/hooks/useToken.tsx | 6 +-- typescript/Frontend/src/index.tsx | 9 +++++ .../resources/innovenergy_Logo_onOrange.png | Bin 0 -> 51620 bytes .../Frontend/src/util/installation.util.tsx | 17 +++++---- 23 files changed, 129 insertions(+), 73 deletions(-) create mode 100644 typescript/Frontend/src/resources/innovenergy_Logo_onOrange.png diff --git a/typescript/Frontend/src/App.tsx b/typescript/Frontend/src/App.tsx index 586cf423d..8847fb18a 100644 --- a/typescript/Frontend/src/App.tsx +++ b/typescript/Frontend/src/App.tsx @@ -14,12 +14,13 @@ import NavigationButtons from "./components/Layout/NavigationButtons"; import InstallationPage from "./components/Installations/InstallationPage"; import { UserContext } from "./components/Context/UserContextProvider"; import ResetPassword from "./ResetPassword"; +import innovenergyLogo from "./resources/innovenergy_Logo_onOrange.png"; const App = () => { const { token, setToken, removeToken } = useToken(); const [language, setLanguage] = useState("EN"); - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); const getTranslations = () => { if (language === "DE") { @@ -31,7 +32,7 @@ const App = () => { if (!token) { return ; } - if (token && currentUser?.mustResetPassword) { + if (token && getCurrentUser()?.mustResetPassword) { return ; } return ( @@ -42,15 +43,20 @@ const App = () => { defaultLocale="EN" > - - - + + + innovenergy logo + + + + + { const [password, setPassword] = useState(""); const [loading, setLoading] = useState(false); const [error, setError] = useState(); - const { setCurrentUser } = useContext(UserContext); + const { saveCurrentUser } = useContext(UserContext); const verifyToken = async (token: string) => { axiosConfigWithoutToken.get("/GetAllInstallations", { @@ -39,7 +39,8 @@ const Login = ({ setToken, setLanguage }: I_LoginProps) => { verifyToken(data.token) .then(() => { setToken(data.token); - setCurrentUser(data.user); + saveCurrentUser(data.user); + console.log(data.user); setLoading(false); setLanguage(data.user.language); }) diff --git a/typescript/Frontend/src/ResetPassword.tsx b/typescript/Frontend/src/ResetPassword.tsx index c43db5d49..3b07db745 100644 --- a/typescript/Frontend/src/ResetPassword.tsx +++ b/typescript/Frontend/src/ResetPassword.tsx @@ -12,7 +12,7 @@ import * as Yup from "yup"; const ResetPassword = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(); - const { setCurrentUser } = useContext(UserContext); + const { saveCurrentUser } = useContext(UserContext); const validationSchema = Yup.object().shape({ password: Yup.string().required("*Password is required"), @@ -33,7 +33,8 @@ const ResetPassword = () => { params: { newPassword: formikValues.verifyPassword }, }) .then((res) => { - setCurrentUser(res.data); + saveCurrentUser(res.data); + console.log(res.data); setLoading(false); }) .catch((err) => setError(err)); diff --git a/typescript/Frontend/src/components/Context/UserContextProvider.tsx b/typescript/Frontend/src/components/Context/UserContextProvider.tsx index 9b18056d2..6b9f35adb 100644 --- a/typescript/Frontend/src/components/Context/UserContextProvider.tsx +++ b/typescript/Frontend/src/components/Context/UserContextProvider.tsx @@ -4,18 +4,36 @@ import { I_User } from "../../util/user.util"; interface InstallationContextProviderProps { currentUser?: I_User; setCurrentUser: (value: I_User) => void; + saveCurrentUser: (user: I_User) => void; + getCurrentUser: () => I_User; } export const UserContext = createContext({ currentUser: {} as I_User, setCurrentUser: () => {}, + saveCurrentUser: () => {}, + getCurrentUser: () => { + return {} as I_User; + }, }); const UserContextProvider = ({ children }: { children: ReactNode }) => { const [currentUser, setCurrentUser] = useState(); + const saveCurrentUser = (user: I_User) => { + localStorage.setItem("currentUser", JSON.stringify(user)); + setCurrentUser(user); + }; + + const getCurrentUser = (): I_User => { + const tokenString = localStorage.getItem("currentUser"); + return tokenString !== null ? JSON.parse(tokenString) : ""; + }; + return ( - + {children} ); diff --git a/typescript/Frontend/src/components/Context/UsersContextProvider.tsx b/typescript/Frontend/src/components/Context/UsersContextProvider.tsx index d6150b0e3..af77b5e9c 100644 --- a/typescript/Frontend/src/components/Context/UsersContextProvider.tsx +++ b/typescript/Frontend/src/components/Context/UsersContextProvider.tsx @@ -21,6 +21,7 @@ interface UsersContextProviderProps { getAvailableUsersForResource: () => I_User[]; fetchUsersWithInheritedAccessForResource: () => Promise; fetchUsersWithDirectAccessForResource: () => Promise; + fetchAvailableUsers: () => Promise; } export const UsersContext = createContext({ @@ -39,6 +40,9 @@ export const UsersContext = createContext({ fetchUsersWithDirectAccessForResource: () => { return Promise.resolve(); }, + fetchAvailableUsers: () => { + return Promise.resolve(); + }, }); const UsersContextProvider = ({ children }: { children: ReactNode }) => { @@ -51,7 +55,7 @@ const UsersContextProvider = ({ children }: { children: ReactNode }) => { const { currentType } = useContext(GroupContext); const { id } = useParams(); - const fetchUsers = useCallback( + const fetchUsersWithAccess = useCallback( async (route: string, setState: (value: any) => void) => { axiosConfig.get(route + currentType, { params: { id } }).then((res) => { setState(res.data); @@ -61,12 +65,15 @@ const UsersContextProvider = ({ children }: { children: ReactNode }) => { ); const fetchUsersWithInheritedAccessForResource = useCallback(async () => { - fetchUsers("/GetUsersWithInheritedAccessTo", setInheritedAccessUsers); - }, [fetchUsers]); + fetchUsersWithAccess( + "/GetUsersWithInheritedAccessTo", + setInheritedAccessUsers + ); + }, [fetchUsersWithAccess]); const fetchUsersWithDirectAccessForResource = useCallback(async () => { - fetchUsers("/GetUsersWithDirectAccessTo", setDirectAccessUsers); - }, [fetchUsers]); + fetchUsersWithAccess("/GetUsersWithDirectAccessTo", setDirectAccessUsers); + }, [fetchUsersWithAccess]); const getAvailableUsersForResource = () => { const inheritedUsers = inheritedAccessUsers.map( @@ -81,10 +88,15 @@ const UsersContextProvider = ({ children }: { children: ReactNode }) => { ); }; - useEffect(() => { - axiosConfig.get("/GetAllChildUsers").then((res) => { + const fetchAvailableUsers = async (): Promise => { + return axiosConfig.get("/GetAllChildUsers").then((res) => { + console.log(res); setAvailableUsers(res.data); }); + }; + + useEffect(() => { + fetchAvailableUsers(); }, []); return ( @@ -99,6 +111,7 @@ const UsersContextProvider = ({ children }: { children: ReactNode }) => { getAvailableUsersForResource, fetchUsersWithInheritedAccessForResource, fetchUsersWithDirectAccessForResource, + fetchAvailableUsers, }} > {children} diff --git a/typescript/Frontend/src/components/Groups/AccessManagement/AccessManagement.tsx b/typescript/Frontend/src/components/Groups/AccessManagement/AccessManagement.tsx index 3cc3c7a98..733784022 100644 --- a/typescript/Frontend/src/components/Groups/AccessManagement/AccessManagement.tsx +++ b/typescript/Frontend/src/components/Groups/AccessManagement/AccessManagement.tsx @@ -8,13 +8,13 @@ import { useContext } from "react"; import { UserContext } from "../../Context/UserContextProvider"; const AccessManagement = () => { - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); return ( - {currentUser?.hasWriteAccess && } + {getCurrentUser().hasWriteAccess && } diff --git a/typescript/Frontend/src/components/Groups/AccessManagement/AvailableUserDialog.tsx b/typescript/Frontend/src/components/Groups/AccessManagement/AvailableUserDialog.tsx index 96ed26adf..0fa267e85 100644 --- a/typescript/Frontend/src/components/Groups/AccessManagement/AvailableUserDialog.tsx +++ b/typescript/Frontend/src/components/Groups/AccessManagement/AvailableUserDialog.tsx @@ -59,15 +59,16 @@ const AvailableUserDialog = () => { scroll="paper" > - Grant access + option.name} + value={selectedUsers} renderInput={(params) => ( { sx={{ height: 40, ml: 2 }} onClick={handleGrant} > - + @@ -95,7 +99,7 @@ const AvailableUserDialog = () => { type="submit" onClick={() => setOpen(true)} > - + ); diff --git a/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithDirectAccess.tsx b/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithDirectAccess.tsx index 791d354f5..aba3365f5 100644 --- a/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithDirectAccess.tsx +++ b/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithDirectAccess.tsx @@ -20,7 +20,7 @@ const UsersWithDirectAccess = () => { useContext(UsersContext); const { currentType } = useContext(GroupContext); const { id } = useParams(); - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); useEffect(() => { fetchUsersWithDirectAccessForResource(); @@ -47,7 +47,7 @@ const UsersWithDirectAccess = () => { handleIconClick(user.id)} diff --git a/typescript/Frontend/src/components/Groups/FolderForm.tsx b/typescript/Frontend/src/components/Groups/FolderForm.tsx index 2796612e7..24c11f99e 100644 --- a/typescript/Frontend/src/components/Groups/FolderForm.tsx +++ b/typescript/Frontend/src/components/Groups/FolderForm.tsx @@ -23,13 +23,13 @@ const FolderForm = (props: I_CustomerFormProps) => { const { values, additionalButtons, handleSubmit } = props; const intl = useIntl(); const { fetchData } = useContext(GroupContext); - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); const [snackbarOpen, setSnackbarOpen] = useState(false); const [error, setError] = useState(); const [loading, setLoading] = useState(false); - const readOnly = !currentUser?.hasWriteAccess; + const readOnly = !getCurrentUser().hasWriteAccess; const formik = useFormik({ initialValues: { diff --git a/typescript/Frontend/src/components/Installations/InstallationForm.tsx b/typescript/Frontend/src/components/Installations/InstallationForm.tsx index 52d628278..b8aed2c2a 100644 --- a/typescript/Frontend/src/components/Installations/InstallationForm.tsx +++ b/typescript/Frontend/src/components/Installations/InstallationForm.tsx @@ -21,9 +21,9 @@ const InstallationForm = (props: I_InstallationFormProps) => { const intl = useIntl(); const { fetchData } = useContext(InstallationContext); - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); - const readOnly = !currentUser?.hasWriteAccess; + const readOnly = !getCurrentUser().hasWriteAccess; const formik = useFormik({ initialValues: { diff --git a/typescript/Frontend/src/components/Installations/InstallationList.tsx b/typescript/Frontend/src/components/Installations/InstallationList.tsx index 2988f2849..297e942bd 100644 --- a/typescript/Frontend/src/components/Installations/InstallationList.tsx +++ b/typescript/Frontend/src/components/Installations/InstallationList.tsx @@ -64,9 +64,10 @@ const InstallationList = (props: InstallationListProps) => { bgcolor: "background.paper", position: "relative", overflow: "auto", - maxHeight: 400, py: 0, mt: 1, + height: "80vh", + maxHeight: "70vh", }} component="nav" aria-labelledby="nested-list-subheader" diff --git a/typescript/Frontend/src/components/Installations/InstallationPage.tsx b/typescript/Frontend/src/components/Installations/InstallationPage.tsx index 7650d1d67..4203a5d0e 100644 --- a/typescript/Frontend/src/components/Installations/InstallationPage.tsx +++ b/typescript/Frontend/src/components/Installations/InstallationPage.tsx @@ -8,7 +8,7 @@ import { Grid } from "@mui/material"; const InstallationPage = () => { return ( <> - + diff --git a/typescript/Frontend/src/components/Installations/Installations.tsx b/typescript/Frontend/src/components/Installations/Installations.tsx index 4a09e0221..b79f34d9b 100644 --- a/typescript/Frontend/src/components/Installations/Installations.tsx +++ b/typescript/Frontend/src/components/Installations/Installations.tsx @@ -12,7 +12,7 @@ import Installation from "./Installation"; const Installations = () => { return ( - + { const intl = useIntl(); return ( - <> +
{ onChange={(e) => setSearchQuery(e.target.value)} /> - +
); }; diff --git a/typescript/Frontend/src/components/Users/User.tsx b/typescript/Frontend/src/components/Users/User.tsx index c18572f00..7c6e3ee8d 100644 --- a/typescript/Frontend/src/components/Users/User.tsx +++ b/typescript/Frontend/src/components/Users/User.tsx @@ -7,10 +7,10 @@ import { I_User } from "../../util/user.util"; import UserForm from "./UserForm"; import { useIntl } from "react-intl"; -interface I_DetailProps { +interface I_DetailProps { hasMoveButton?: boolean; } -const Detail = (props: I_DetailProps) => { +const Detail = (props: I_DetailProps) => { const { id } = useParams(); const { locale } = useIntl(); const [values, setValues] = useState(); diff --git a/typescript/Frontend/src/components/Users/UserForm.tsx b/typescript/Frontend/src/components/Users/UserForm.tsx index 8fb9e5f6e..d7ab9f5ec 100644 --- a/typescript/Frontend/src/components/Users/UserForm.tsx +++ b/typescript/Frontend/src/components/Users/UserForm.tsx @@ -7,6 +7,7 @@ import { I_User } from "../../util/user.util"; import InnovenergyButton from "../Layout/InnovenergyButton"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; import { UserContext } from "../Context/UserContextProvider"; +import { UsersContext } from "../Context/UsersContextProvider"; interface I_UserFormProps { handleSubmit: (formikValues: Partial) => Promise; @@ -19,9 +20,11 @@ const UserForm = (props: I_UserFormProps) => { const [error, setError] = useState(); const intl = useIntl(); - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); + const { fetchAvailableUsers } = useContext(UsersContext); - const readOnly = !currentUser?.hasWriteAccess; + const currentUser = getCurrentUser(); + const readOnly = !currentUser.hasWriteAccess; const formik = useFormik({ initialValues: { @@ -34,6 +37,7 @@ const UserForm = (props: I_UserFormProps) => { handleSubmit(formikValues) .then(() => { setOpen(true); + fetchAvailableUsers(); setLoading(false); }) .catch((err: AxiosError) => { @@ -85,7 +89,7 @@ const UserForm = (props: I_UserFormProps) => { /> {loading && } - {currentUser?.hasWriteAccess && ( + {currentUser.hasWriteAccess && ( diff --git a/typescript/Frontend/src/components/Users/UserTabs.tsx b/typescript/Frontend/src/components/Users/UserTabs.tsx index c44fac79b..6a3068d02 100644 --- a/typescript/Frontend/src/components/Users/UserTabs.tsx +++ b/typescript/Frontend/src/components/Users/UserTabs.tsx @@ -14,26 +14,22 @@ const UserTabs = () => { if (id) { return ( - - - - - - - + + + ); } return null; diff --git a/typescript/Frontend/src/components/Users/Users.tsx b/typescript/Frontend/src/components/Users/Users.tsx index 03cc73db2..accfdbf31 100644 --- a/typescript/Frontend/src/components/Users/Users.tsx +++ b/typescript/Frontend/src/components/Users/Users.tsx @@ -11,13 +11,13 @@ import { useContext } from "react"; import { UserContext } from "../Context/UserContextProvider"; const Users = () => { - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); return ( - + - {currentUser?.hasWriteAccess && } + {getCurrentUser().hasWriteAccess && } diff --git a/typescript/Frontend/src/config/axiosConfig.tsx b/typescript/Frontend/src/config/axiosConfig.tsx index f71357136..38e7e1f81 100644 --- a/typescript/Frontend/src/config/axiosConfig.tsx +++ b/typescript/Frontend/src/config/axiosConfig.tsx @@ -10,7 +10,7 @@ const axiosConfig = axios.create({ axiosConfig.defaults.params = {}; axiosConfig.interceptors.request.use( (config) => { - const tokenString = sessionStorage.getItem("token"); + const tokenString = localStorage.getItem("token"); const token = tokenString !== null ? JSON.parse(tokenString) : ""; if (token) { config.params["authToken"] = token; diff --git a/typescript/Frontend/src/hooks/useToken.tsx b/typescript/Frontend/src/hooks/useToken.tsx index e20c7198b..b7e7ed706 100644 --- a/typescript/Frontend/src/hooks/useToken.tsx +++ b/typescript/Frontend/src/hooks/useToken.tsx @@ -2,18 +2,18 @@ import { useState } from "react"; const useToken = () => { const getToken = () => { - const tokenString = sessionStorage.getItem("token"); + const tokenString = localStorage.getItem("token"); return tokenString !== null ? JSON.parse(tokenString) : ""; }; const [token, setToken] = useState(getToken()); const saveToken = (userToken: any) => { - sessionStorage.setItem("token", JSON.stringify(userToken)); + localStorage.setItem("token", JSON.stringify(userToken)); setToken(userToken); }; const removeToken = () => { - sessionStorage.removeItem("token"); + localStorage.removeItem("token"); setToken(null); }; diff --git a/typescript/Frontend/src/index.tsx b/typescript/Frontend/src/index.tsx index a5c14d630..262364be0 100644 --- a/typescript/Frontend/src/index.tsx +++ b/typescript/Frontend/src/index.tsx @@ -15,6 +15,15 @@ const theme = createTheme({ primary: { main: "#F59100", }, + text: { + primary: "#000000", + secondary: "#000000", + disabled: "#000000", + }, + }, + typography: { + fontFamily: `"Ubuntu", sans-serif`, + fontWeightRegular: 300, }, }); diff --git a/typescript/Frontend/src/resources/innovenergy_Logo_onOrange.png b/typescript/Frontend/src/resources/innovenergy_Logo_onOrange.png new file mode 100644 index 0000000000000000000000000000000000000000..22e9c64127c7788b35ebe595716191392a867e47 GIT binary patch literal 51620 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 literal 0 HcmV?d00001 diff --git a/typescript/Frontend/src/util/installation.util.tsx b/typescript/Frontend/src/util/installation.util.tsx index e1719c224..648393443 100644 --- a/typescript/Frontend/src/util/installation.util.tsx +++ b/typescript/Frontend/src/util/installation.util.tsx @@ -3,9 +3,9 @@ import { styled, Tab, Tabs } from "@mui/material"; export const StyledTab = styled((props: any) => ( ))(({ theme }) => ({ - textTransform: "none", + textTransform: "uppercase", fontWeight: theme.typography.fontWeightRegular, - fontSize: theme.typography.pxToRem(15), + fontSize: theme.typography.pxToRem(14), marginRight: theme.spacing(1), background: "0 0", border: "1px solid transparent", @@ -15,9 +15,9 @@ export const StyledTab = styled((props: any) => ( textDecoration: "none", transition: `color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out`, "&.Mui-selected": { - color: "#495057", + color: "#000000", backgroundColor: "#fff", - borderColor: "#dee2e6 #dee2e6 #fff", + borderColor: "#bdbdbd #bdbdbd #fff", marginBottom: "-3px", }, "&.Mui-focusVisible": { @@ -26,19 +26,22 @@ export const StyledTab = styled((props: any) => ( })); export const AntTabs = styled(Tabs)({ - borderBottom: "1px solid #dee2e6", + borderBottom: "1px solid #bdbdbd", overflow: "visible!important", "& div.MuiTabs-scroller": { overflow: "visible!important", }, "&.Mui-selected": { - color: "#495057", + color: "#000000", backgroundColor: "red", - borderColor: `#dee2e6 #dee2e6 #fff`, + borderColor: `#bdbdbd #bdbdbd #fff`, }, "& .MuiTabs-indicator": { display: "flex", justifyContent: "center", backgroundColor: "transparent", }, + "&.MuiTabs-root": { + width: "100%", + }, }); From 39b414d0f0174ffaaa64640312c084560209ddbb Mon Sep 17 00:00:00 2001 From: Sina Blattmann Date: Mon, 24 Apr 2023 09:35:01 +0200 Subject: [PATCH 2/2] add datacache (ivos code), first working version with diagrams, only sample data --- csharp/App/Backend/db.sqlite | Bin 540672 -> 540672 bytes typescript/Frontend/package-lock.json | 2450 +++++++++++++++++ typescript/Frontend/package.json | 4 + .../Installations/InstallationForm.tsx | 41 +- .../src/components/Installations/Log/Log.tsx | 63 +- .../Installations/Log/ScalarGraph.tsx | 60 +- .../Layout/InnovenergyTextfield.tsx | 7 + .../Frontend/src/dataCache/S3/S3Access.ts | 79 + typescript/Frontend/src/dataCache/S3/Sha1.ts | 125 + .../Frontend/src/dataCache/S3/UInt8Utils.ts | 56 + typescript/Frontend/src/dataCache/S3/Utf8.ts | 10 + typescript/Frontend/src/dataCache/data.ts | 21 + .../Frontend/src/dataCache/dataCache.ts | 169 ++ typescript/Frontend/src/dataCache/linq.ts | 20 + .../Frontend/src/dataCache/observableUtils.ts | 35 + .../Frontend/src/dataCache/promiseQueue.ts | 51 + .../src/dataCache/skipList/skipList.ts | 80 + .../src/dataCache/skipList/skipListNode.ts | 52 + .../Frontend/src/dataCache/stringUtils.ts | 5 + typescript/Frontend/src/dataCache/time.ts | 302 ++ typescript/Frontend/src/dataCache/types.ts | 21 + typescript/Frontend/src/dataCache/utils.ts | 119 + .../src/dataCache/utils/fileSystem.ts | 46 + .../src/dataCache/utils/httpResponse.ts | 127 + .../Frontend/src/dataCache/utils/linq.ts | 21 + .../Frontend/src/dataCache/utils/logging.ts | 11 + .../Frontend/src/dataCache/utils/match.ts | 176 ++ .../Frontend/src/dataCache/utils/maybe.ts | 16 + .../src/dataCache/utils/milliseconds.ts | 18 + .../Frontend/src/dataCache/utils/mime.ts | 32 + .../Frontend/src/dataCache/utils/path.ts | 81 + .../src/dataCache/utils/requestUtils.ts | 45 + .../dataCache/utils/runtimeTypeChecking.ts | 66 + .../src/dataCache/utils/stringUtils.ts | 22 + .../Frontend/src/dataCache/utils/tree.ts | 71 + .../Frontend/src/dataCache/utils/type.ts | 115 + .../src/dataCache/utils/utilityTypes.ts | 52 + .../Frontend/src/dataCache/utils/utils.ts | 55 + typescript/Frontend/src/util/graph.util.tsx | 68 + typescript/Frontend/tsconfig.json | 1 + 40 files changed, 4738 insertions(+), 55 deletions(-) create mode 100644 typescript/Frontend/src/dataCache/S3/S3Access.ts create mode 100644 typescript/Frontend/src/dataCache/S3/Sha1.ts create mode 100644 typescript/Frontend/src/dataCache/S3/UInt8Utils.ts create mode 100644 typescript/Frontend/src/dataCache/S3/Utf8.ts create mode 100644 typescript/Frontend/src/dataCache/data.ts create mode 100644 typescript/Frontend/src/dataCache/dataCache.ts create mode 100644 typescript/Frontend/src/dataCache/linq.ts create mode 100644 typescript/Frontend/src/dataCache/observableUtils.ts create mode 100644 typescript/Frontend/src/dataCache/promiseQueue.ts create mode 100644 typescript/Frontend/src/dataCache/skipList/skipList.ts create mode 100644 typescript/Frontend/src/dataCache/skipList/skipListNode.ts create mode 100644 typescript/Frontend/src/dataCache/stringUtils.ts create mode 100644 typescript/Frontend/src/dataCache/time.ts create mode 100644 typescript/Frontend/src/dataCache/types.ts create mode 100644 typescript/Frontend/src/dataCache/utils.ts create mode 100644 typescript/Frontend/src/dataCache/utils/fileSystem.ts create mode 100644 typescript/Frontend/src/dataCache/utils/httpResponse.ts create mode 100644 typescript/Frontend/src/dataCache/utils/linq.ts create mode 100644 typescript/Frontend/src/dataCache/utils/logging.ts create mode 100644 typescript/Frontend/src/dataCache/utils/match.ts create mode 100644 typescript/Frontend/src/dataCache/utils/maybe.ts create mode 100644 typescript/Frontend/src/dataCache/utils/milliseconds.ts create mode 100644 typescript/Frontend/src/dataCache/utils/mime.ts create mode 100644 typescript/Frontend/src/dataCache/utils/path.ts create mode 100644 typescript/Frontend/src/dataCache/utils/requestUtils.ts create mode 100644 typescript/Frontend/src/dataCache/utils/runtimeTypeChecking.ts create mode 100644 typescript/Frontend/src/dataCache/utils/stringUtils.ts create mode 100644 typescript/Frontend/src/dataCache/utils/tree.ts create mode 100644 typescript/Frontend/src/dataCache/utils/type.ts create mode 100644 typescript/Frontend/src/dataCache/utils/utilityTypes.ts create mode 100644 typescript/Frontend/src/dataCache/utils/utils.ts create mode 100644 typescript/Frontend/src/util/graph.util.tsx diff --git a/csharp/App/Backend/db.sqlite b/csharp/App/Backend/db.sqlite index c2a26b55c03b54b1684eae01291622403e9f47eb..f344d58ad6952a255d4d921dad91754336e87d73 100644 GIT binary patch delta 2447 zcma)83rt(r89wK}uYK=55JF+t0Yjh!QEY>G#A!l!*ufCMU|s<&YYZ4;u#GVW2ebiA z(@fo_$mC9So28JYs}+;K16Ss>wQ%M1zCi=blP?$wQmp{L)cmdyZ@(+3Qr1>} zP-czQgz{lCZWs6|K^C_Pi{j74mDrr|3TH?r^P#7IxXSJHdA$0*5uY`kEQ1qHU%(Y~ zdA+VGx6hf_uxQC0Fxq0^(qk{1I~dX`Jt4DI21@Feo`{uKs2@`^s#~g)szWNfDxJT}pW>(a8eYZyiCf~1ag&^#)3Tql@3JS^Nw${dnZGdS zm>)CaOc%3-{&E|Ajb5aW(BGy_v`F2f&QLNnNZF_?@NXx|wdFx!e?8$&s^ulTA#$7uvLF!UkrRcgM%VsH-7fD)kct)jv=L^;TTF1UJ!?;0JVk$1;4HW@r*ZijTi@ z5t={a>673hNo`~JsIWwo6C@S2ED?c}2tdD+ z?x5o+fT~cE^oleqwTK^!OOj5E#Iud0U!2kBF-<%GjKi|W<~NjewgycBv%5KLtt&NG zScaTKzNVJ?#tt`kqsC{heD4jtF2N7?h5hxxDVI0cWO3JpoxR}(Pbg$7tY{t%kK3n4 zxEtHA(_^pL^_dC&?qss>@Oq zkglQ}DTd~xFC>q2Sjs|YP#yA07IXu<{DnA^OE$sv9L~d}^=#E)(l>bdZ!0q~=|BCO z`8or0Fl#;6w3w8z>YJU?VAB7xhsXY8U{>_cm85R>2E1I)nRHBA&yKX{oSh7;`hd;? z^bd3weU2WW`{+Y-3tdH*(FOD?^e#FZ&u$@KKz+D9k-(SP5VyfRzAN0$2%PC4iLxOp8AlAybT`Rh=7UkCR=L zI#;caf9E*)mMVoO(_mY@-|4h+WGciLjwN1kioEzi;^18XJw%J>9=e8pjn1N%&~xaA zXjEx<)2Is{ybR3?G#C6&GwDD^__DNpSZ-2^U{<_TWpiLN1Vxgd z3;}orKr+`K^f>zl`>TdL&d{J|49_JNr{B}(a^UX?YjZ51O(r21J&?s6&| zhn?H-vIy{Fno*~z+{x!;O(+q`WR@i+m@;NolQt>V)HtKPb;gc_bLv1lzPf9wg)p8Cny1*609$JGiMv#0xH1>M=p^=;7`&) z%9Y#1p&U>)C_d#0<$LoP^I`K=bD8;ZGmy{82jp#V1{jg8rr%7*O>dYQO-oF%(jDon z)F-_p(RWr;#u_C4&WaH>4>W6N4vv z{46xldA*suLICed5b0|)RuJN+vmiMM(uU1Z!Li;37^9p4F^Tb_%T`+JZ?A8xvsF}Y z@>Tn5YFp}T%Np0xn!XpHK+pFj&o?Nsj%b+$iSh7Z?r0&$f|LqKZ*EN!a6Hcfhk-o0 zHJZ1vEJ#X#)V?i}?&x>&PQ-$=MIwz3q|i(KiIFgg5l56y&D-%FzFg?$BIKoLjfpjm z$AWuR5e94brNMBwNFvSQB@21J1jLGDLS(p0k|?Hde46NoHbH92{$vC_Rbe52wi%~2ueZRE%0;75=npM6xuyQ zkTa!!_$=`l-K&o4wz@n znGqpn38`@DA%VkWRsuExY{EnmgTenZ=r^0NMa3SIOXCM{g=2|!wjU6~3h sVK<0&Cq@uwNpch*2T87SPT8WwnTO1)=6.0.0" } }, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/crc32c": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-3.0.0.tgz", + "integrity": "sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32c/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-3.0.0.tgz", + "integrity": "sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-sdk/abort-controller": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.310.0.tgz", + "integrity": "sha512-v1zrRQxDLA1MdPim159Vx/CPHqsB4uybSxRi1CnfHO5ZjHryx3a5htW2gdGAykVCul40+yJXvfpufMrELVxH+g==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/chunked-blob-reader": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/chunked-blob-reader/-/chunked-blob-reader-3.310.0.tgz", + "integrity": "sha512-CrJS3exo4mWaLnWxfCH+w88Ou0IcAZSIkk4QbmxiHl/5Dq705OLoxf4385MVyExpqpeVJYOYQ2WaD8i/pQZ2fg==", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.316.0.tgz", + "integrity": "sha512-nmlvXf7c1Nz9VR/VlplgsZqPqe7eg5RYuuIb70rX/4Joe5x98sJqz72hyLblwQTB47lwPLK+qvHps9aVOY2QhA==", + "dependencies": { + "@aws-crypto/sha1-browser": "3.0.0", + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.316.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-node": "3.316.0", + "@aws-sdk/eventstream-serde-browser": "3.310.0", + "@aws-sdk/eventstream-serde-config-resolver": "3.310.0", + "@aws-sdk/eventstream-serde-node": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-blob-browser": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/hash-stream-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/md5-js": "3.310.0", + "@aws-sdk/middleware-bucket-endpoint": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-expect-continue": "3.310.0", + "@aws-sdk/middleware-flexible-checksums": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-location-constraint": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-sdk-s3": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/middleware-ssec": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4-multi-region": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-stream-browser": "3.310.0", + "@aws-sdk/util-stream-node": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "@aws-sdk/util-waiter": "3.310.0", + "@aws-sdk/xml-builder": "3.310.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.316.0.tgz", + "integrity": "sha512-wGXfIhR0lJGB8QTT0fwSwwklHePHxd2GW3IQt3trXnEYe0frmJ7vYRnVL5CSRKsikLDmaU7ll3SdsshMzQzo3w==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.316.0.tgz", + "integrity": "sha512-e2fvC7o42YV+LcZYfXCcvBn4L7NM9oNccnZ7T+pS6SFpHZlaqkw4uuQMRE6iUAof+Id7Mt7xDrz1x2yGlP+8GA==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.316.0.tgz", + "integrity": "sha512-5SD59+DRVy1mKckGs/5J8OwWpRS3E5v4BX19XaX/s9JJ5Rw9aZd9DP4SZVpeNXztIPjkQSEzHgrUVlZFB1QJgg==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-node": "3.316.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-sdk-sts": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/config-resolver": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.310.0.tgz", + "integrity": "sha512-8vsT+/50lOqfDxka9m/rRt6oxv1WuGZoP8oPMk0Dt+TxXMbAzf4+rejBgiB96wshI1k3gLokYRjSQZn+dDtT8g==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-config-provider": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.310.0.tgz", + "integrity": "sha512-vvIPQpI16fj95xwS7M3D48F7QhZJBnnCgB5lR+b7So+vsG9ibm1mZRVGzVpdxCvgyOhHFbvrby9aalNJmmIP1A==", + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-imds": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.310.0.tgz", + "integrity": "sha512-baxK7Zp6dai5AGW01FIW27xS2KAaPUmKLIXv5SvFYsUgXXvNW55im4uG3b+2gA0F7V+hXvVBH08OEqmwW6we5w==", + "dependencies": { + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.316.0.tgz", + "integrity": "sha512-ZADkpdEjFCAXyzEpYbCRENlZ/AQEwevWdPd2yshjNo7xvOcepv4pPIBpYd8h9LvRafSLGA7zlWDz84hkIt+HKA==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/credential-provider-process": "3.310.0", + "@aws-sdk/credential-provider-sso": "3.316.0", + "@aws-sdk/credential-provider-web-identity": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.316.0.tgz", + "integrity": "sha512-oE1LTXP8XZp4bT8LhBeolMRiz0RwnmHDC2XpUmWO8LTmbDNrQO0mVzxEvXDLeKaN5BIFIJqNFlMgjWUMa9Kwcw==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/credential-provider-ini": "3.316.0", + "@aws-sdk/credential-provider-process": "3.310.0", + "@aws-sdk/credential-provider-sso": "3.316.0", + "@aws-sdk/credential-provider-web-identity": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.310.0.tgz", + "integrity": "sha512-h73sg6GPMUWC+3zMCbA1nZ2O03nNJt7G96JdmnantiXBwHpRKWW8nBTLzx5uhXn6hTuTaoQRP/P+oxQJKYdMmA==", + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.316.0.tgz", + "integrity": "sha512-8/O2twlsoV1bDkZ9jd7JCMWsftfyoTyRT1UYscsKZGUDEgZRAxRkzS3GLYuLXEWNuxb1OB9rYk/cEJoxwy7T9g==", + "dependencies": { + "@aws-sdk/client-sso": "3.316.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/token-providers": "3.316.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.310.0.tgz", + "integrity": "sha512-H4SzuZXILNhK6/IR1uVvsUDZvzc051hem7GLyYghBCu8mU+tq28YhKE8MfSroi6eL2e5Vujloij1OM2EQQkPkw==", + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-codec": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.310.0.tgz", + "integrity": "sha512-clIeSgWbZbxwtsxZ/yoedNM0/kJFSIjjHPikuDGhxhqc+vP6TN3oYyVMFrYwFaTFhk2+S5wZcWYMw8Op1pWo+A==", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/eventstream-serde-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-browser/-/eventstream-serde-browser-3.310.0.tgz", + "integrity": "sha512-3S6ziuQVALgEyz0TANGtYDVeG8ArK4Y05mcgrs8qUTmsvlDIXX37cR/DvmVbNB76M4IrsZeSAIajL9644CywkA==", + "dependencies": { + "@aws-sdk/eventstream-serde-universal": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-serde-config-resolver": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.310.0.tgz", + "integrity": "sha512-8s1Qdn9STj+sV75nUp9yt0W6fHS4BZ2jTm4Z/1Pcbvh2Gqs0WjH5n2StS+pDW5Y9J/HSGBl0ogmUr5lC5bXFHg==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-serde-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-node/-/eventstream-serde-node-3.310.0.tgz", + "integrity": "sha512-kSnRomCgW43K9TmQYuwN9+AoYPnhyOKroanUMyZEzJk7rpCPMj4OzaUpXfDYOvznFNYn7NLaH6nHLJAr0VPlJA==", + "dependencies": { + "@aws-sdk/eventstream-serde-universal": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-serde-universal": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-universal/-/eventstream-serde-universal-3.310.0.tgz", + "integrity": "sha512-Qyjt5k/waV5cDukpgT824ISZAz5U0pwzLz5ztR409u85AGNkF/9n7MS+LSyBUBSb0WJ5pUeSD47WBk+nLq9Nhw==", + "dependencies": { + "@aws-sdk/eventstream-codec": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/fetch-http-handler": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.310.0.tgz", + "integrity": "sha512-Bi9vIwzdkw1zMcvi/zGzlWS9KfIEnAq4NNhsnCxbQ4OoIRU9wvU+WGZdBBhxg0ZxZmpp1j1aZhU53lLjA07MHw==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/querystring-builder": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/hash-blob-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-blob-browser/-/hash-blob-browser-3.310.0.tgz", + "integrity": "sha512-OoR8p0cbypToysLT0v3o2oyjy6+DKrY7GNCAzHOHJK9xmqXCt+DsjKoPeiY7o1sWX2aN6Plmvubj/zWxMKEn/A==", + "dependencies": { + "@aws-sdk/chunked-blob-reader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/hash-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.310.0.tgz", + "integrity": "sha512-NvE2fhRc8GRwCXBfDehxVAWCmVwVMILliAKVPAEr4yz2CkYs0tqU51S48x23dtna07H4qHtgpeNqVTthcIQOEQ==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/hash-stream-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-stream-node/-/hash-stream-node-3.310.0.tgz", + "integrity": "sha512-ZoXdybNgvMz1Hl6k/e32xVL3jmG5p2IEk5mTtLfFEuskTJ74Z+VMYKkkF1whyy7KQfH83H+TQGnsGtlRCchQKw==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/invalid-dependency": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.310.0.tgz", + "integrity": "sha512-1s5RG5rSPXoa/aZ/Kqr5U/7lqpx+Ry81GprQ2bxWqJvWQIJ0IRUwo5pk8XFxbKVr/2a+4lZT/c3OGoBOM1yRRA==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/is-array-buffer": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", + "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/md5-js": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/md5-js/-/md5-js-3.310.0.tgz", + "integrity": "sha512-x5sRBUrEfLWAS1EhwbbDQ7cXq6uvBxh3qR2XAsnGvFFceTeAadk7cVogWxlk3PC+OCeeym7c3/6Bv2HQ2f1YyQ==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.310.0.tgz", + "integrity": "sha512-uJJfHI7v4AgbJZRLtyI8ap2QRWkBokGc3iyUoQ+dVNT3/CE2ZCu694A6W+H0dRqg79dIE+f9CRNdtLGa/Ehhvg==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-arn-parser": "3.310.0", + "@aws-sdk/util-config-provider": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-content-length": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.310.0.tgz", + "integrity": "sha512-P8tQZxgDt6CAh1wd/W6WPzjc+uWPJwQkm+F7rAwRlM+k9q17HrhnksGDKcpuuLyIhPQYdmOMIkpKVgXGa4avhQ==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-endpoint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.310.0.tgz", + "integrity": "sha512-Z+N2vOL8K354/lstkClxLLsr6hCpVRh+0tCMXrVj66/NtKysCEZ/0b9LmqOwD9pWHNiI2mJqXwY0gxNlKAroUg==", + "dependencies": { + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.310.0.tgz", + "integrity": "sha512-l3d1z2gt+gINJDnPSyu84IxfzjzPfCQrqC1sunw2cZGo/sXtEiq698Q3SiTcO2PGP4LBQAy2RHb5wVBJP708CQ==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.310.0.tgz", + "integrity": "sha512-5ndnLgzgGVpWkmHBAiYkagHqiSuow8q62J4J6E2PzaQ77+fm8W3nfdy7hK5trHokEyouCZdxT/XK/IRhgj/4PA==", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@aws-crypto/crc32c": "3.0.0", + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.310.0.tgz", + "integrity": "sha512-QWSA+46/hXorXyWa61ic2K7qZzwHTiwfk2e9mRRjeIRepUgI3qxFjsYqrWtrOGBjmFmq0pYIY8Bb/DCJuQqcoA==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.310.0.tgz", + "integrity": "sha512-LFm0JTQWwTPWL/tZU2wsQTl8J5PpDEkXjEhaXVKamtyH0xhysRqd+0n92n65dc8oztAuQkb9xUbErGn5b6gsew==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.310.0.tgz", + "integrity": "sha512-Lurm8XofrASBRnAVtiSNuDSRsRqPNg27RIFLLsLp/pqog9nFJ0vz0kgdb9S5Z+zw83Mm+UlqOe6D8NTUNp4fVg==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.310.0.tgz", + "integrity": "sha512-SuB75/xk/gyue24gkriTwO2jFd7YcUGZDClQYuRejgbXSa3CO0lWyawQtfLcSSEBp9izrEVXuFH24K1eAft5nQ==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-retry": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.310.0.tgz", + "integrity": "sha512-oTPsRy2W4s+dfxbJPW7Km+hHtv/OMsNsVfThAq8DDYKC13qlr1aAyOqGLD+dpBy2aKe7ss517Sy2HcHtHqm7/g==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/service-error-classification": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.310.0.tgz", + "integrity": "sha512-QK9x9g2ksg0hOjjYgqddeFcn5ctUEGdxJVu4OumPXceulefMcSO2jyH2qTybYSA93nqNQFdFmg5wQfvIRUWFCQ==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-arn-parser": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.310.0.tgz", + "integrity": "sha512-+5PFwlYNLvLLIfw0ASAoWV/iIF8Zv6R6QGtyP0CclhRSvNjgbQDVnV0g95MC5qvh+GB/Yjlkt8qAjLSPjHfsrQ==", + "dependencies": { + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-serde": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.310.0.tgz", + "integrity": "sha512-RNeeTVWSLTaentUeCgQKZhAl+C6hxtwD78cQWS10UymWpQFwbaxztzKUu4UQS5xA2j6PxwPRRUjqa4jcFjfLsg==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.310.0.tgz", + "integrity": "sha512-f9mKq+XMdW207Af3hKjdTnpNhdtwqWuvFs/ZyXoOkp/g1MY1O6L23Jy6i52m29LxbT4AuNRG1oKODfXM0vYVjQ==", + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.310.0.tgz", + "integrity": "sha512-CnEwNKVpd5bXnrCKPaePF8mWTA9ET21OMBb54y9b0fd8K02zoOcdBz4DWfh1SjFD4HkgCdja4egd8l2ivyvqmw==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-stack": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.310.0.tgz", + "integrity": "sha512-010O1PD+UAcZVKRvqEusE1KJqN96wwrf6QsqbRM0ywsKQ21NDweaHvEDlds2VHpgmofxkRLRu/IDrlPkKRQrRg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.310.0.tgz", + "integrity": "sha512-x3IOwSwSbwKidlxRk3CNVHVUb06SRuaELxggCaR++QVI8NU6qD/l4VHXKVRvbTHiC/cYxXE/GaBDgQVpDR7V/g==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-endpoints": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/node-config-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.310.0.tgz", + "integrity": "sha512-T/Pp6htc6hq/Cq+MLNDSyiwWCMVF6GqbBbXKVlO5L8rdHx4sq9xPdoPveZhGWrxvkanjA6eCwUp6E0riBOSVng==", + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/node-http-handler": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.310.0.tgz", + "integrity": "sha512-irv9mbcM9xC2xYjArQF5SYmHBMu4ciMWtGsoHII1nRuFOl9FoT4ffTvEPuLlfC6pznzvKt9zvnm6xXj7gDChKg==", + "dependencies": { + "@aws-sdk/abort-controller": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/querystring-builder": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/property-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.310.0.tgz", + "integrity": "sha512-3lxDb0akV6BBzmFe4nLPaoliQbAifyWJhuvuDOu7e8NzouvpQXs0275w9LePhhcgjKAEVXUIse05ZW2DLbxo/g==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.310.0.tgz", + "integrity": "sha512-fgZ1aw/irQtnrsR58pS8ThKOWo57Py3xX6giRvwSgZDEcxHfVzuQjy9yPuV++v04fdmdtgpbGf8WfvAAJ11yXQ==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/querystring-builder": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.310.0.tgz", + "integrity": "sha512-ZHH8GV/80+pWGo7DzsvwvXR5xVxUHXUvPJPFAkhr6nCf78igdoF8gR10ScFoEKbtEapoNTaZlKHPXxpD8aPG7A==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/querystring-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.310.0.tgz", + "integrity": "sha512-YkIznoP6lsiIUHinx++/lbb3tlMURGGqMpo0Pnn32zYzGrJXA6eC3D0as2EcMjo55onTfuLcIiX4qzXes2MYOA==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/service-error-classification": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.310.0.tgz", + "integrity": "sha512-PuyC7k3qfIKeH2LCnDwbttMOKq3qAx4buvg0yfnJtQOz6t1AR8gsnAq0CjKXXyfkXwNKWTqCpE6lVNUIkXgsMw==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/shared-ini-file-loader": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.310.0.tgz", + "integrity": "sha512-N0q9pG0xSjQwc690YQND5bofm+4nfUviQ/Ppgan2kU6aU0WUq8KwgHJBto/YEEI+VlrME30jZJnxtOvcZJc2XA==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.310.0.tgz", + "integrity": "sha512-1M60P1ZBNAjCFv9sYW29OF6okktaeibWyW3lMXqzoHF70lHBZh+838iUchznXUA5FLabfn4jBFWMRxlAXJUY2Q==", + "dependencies": { + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.310.0.tgz", + "integrity": "sha512-q8W+RIomTS/q85Ntgks/CoDElwqkC9+4OCicee5YznNHjQ4gtNWhUkYIyIRWRmXa/qx/AUreW9DM8FAecCOdng==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/signature-v4-crt": "^3.118.0" + }, + "peerDependenciesMeta": { + "@aws-sdk/signature-v4-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/smithy-client": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.316.0.tgz", + "integrity": "sha512-6YXOKbRnXeS8r8RWzuL6JMBolDYM5Wa4fD/VY6x/wK78i2xErHOvqzHgyyeLI1MMw4uqyd4wRNJNWC9TMPduXw==", + "dependencies": { + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.316.0.tgz", + "integrity": "sha512-foJ2YmB8A/mtp52riO2zdmBgzA3IpASNgUhY9FZg1BKpGcjqLQDGYP+BY3BA0H7CFsMa4PCf13M5wWwn1onyBA==", + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.316.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.310.0.tgz", + "integrity": "sha512-j8eamQJ7YcIhw7fneUfs8LYl3t01k4uHi4ZDmNRgtbmbmTTG3FZc2MotStZnp3nZB6vLiPF1o5aoJxWVvkzS6A==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/url-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.310.0.tgz", + "integrity": "sha512-mCLnCaSB9rQvAgx33u0DujLvr4d5yEm/W5r789GblwwQnlNXedVu50QRizMLTpltYWyAUoXjJgQnJHmJMaKXhw==", + "dependencies": { + "@aws-sdk/querystring-parser": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.310.0.tgz", + "integrity": "sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-base64": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.310.0.tgz", + "integrity": "sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==", + "dependencies": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-body-length-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.310.0.tgz", + "integrity": "sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-body-length-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.310.0.tgz", + "integrity": "sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-buffer-from": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", + "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", + "dependencies": { + "@aws-sdk/is-array-buffer": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-config-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.310.0.tgz", + "integrity": "sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-defaults-mode-browser": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.316.0.tgz", + "integrity": "sha512-6FSqLhYmaihtH2n1s4b2rlLW0ABU8N6VZIfzLfe2ING4PF0MzfaMMhnTFUHVXfKCVGoR8yP6iyFTRCyHGVEL1w==", + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-defaults-mode-node": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.316.0.tgz", + "integrity": "sha512-dkYy10hdjPSScXXvnjGpZpnJxllkb6ICHgLMwZ4JczLHhPM12T/4PQ758YN8HS+muiYDGX1Bl2z1jd/bMcewBQ==", + "dependencies": { + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.310.0.tgz", + "integrity": "sha512-zG+/d/O5KPmAaeOMPd6bW1abifdT0H03f42keLjYEoRZzYtHPC5DuPE0UayiWGckI6BCDgy0sRKXCYS49UNFaQ==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-hex-encoding": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", + "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", + "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-middleware": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.310.0.tgz", + "integrity": "sha512-FTSUKL/eRb9X6uEZClrTe27QFXUNNp7fxYrPndZwk1hlaOP5ix+MIHBcI7pIiiY/JPfOUmPyZOu+HetlFXjWog==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-retry": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.310.0.tgz", + "integrity": "sha512-FwWGhCBLfoivTMUHu1LIn4NjrN9JLJ/aX5aZmbcPIOhZVFJj638j0qDgZXyfvVqBuBZh7M8kGq0Oahy3dp69OA==", + "dependencies": { + "@aws-sdk/service-error-classification": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/util-stream-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-stream-browser/-/util-stream-browser-3.310.0.tgz", + "integrity": "sha512-bysXZHwFwvbqOTCScCdCnoLk1K3GCo0HRIYEZuL7O7MHrQmfaYRXcaft/p22+GUv9VeFXS/eJJZ5r4u32az94w==", + "dependencies": { + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-stream-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-stream-node/-/util-stream-node-3.310.0.tgz", + "integrity": "sha512-hueAXFK0GVvnfYFgqbF7587xZfMZff5jlIFZOHqx7XVU7bl7qrRUCnphHk8H6yZ7RoQbDPcfmHJgtEoAJg1T1Q==", + "dependencies": { + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-uri-escape": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", + "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.310.0.tgz", + "integrity": "sha512-yU/4QnHHuQ5z3vsUqMQVfYLbZGYwpYblPiuZx4Zo9+x0PBkNjYMqctdDcrpoH9Z2xZiDN16AmQGK1tix117ZKw==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.310.0.tgz", + "integrity": "sha512-Ra3pEl+Gn2BpeE7KiDGpi4zj7WJXZA5GXnGo3mjbi9+Y3zrbuhJAbdZO3mO/o7xDgMC6ph4xCTbaSGzU6b6EDg==", + "dependencies": { + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-utf8": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.310.0.tgz", + "integrity": "sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==", + "dependencies": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-waiter": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-waiter/-/util-waiter-3.310.0.tgz", + "integrity": "sha512-AV5j3guH/Y4REu+Qh3eXQU9igljHuU4XjX2sADAgf54C0kkhcCCkkiuzk3IsX089nyJCqIcj5idbjdvpnH88Vw==", + "dependencies": { + "@aws-sdk/abort-controller": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.310.0.tgz", + "integrity": "sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -10301,6 +11592,21 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "node_modules/fast-xml-parser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz", + "integrity": "sha512-CDYeykkle1LiA/uqQyNwYpFbyF6Axec6YapmpUP+/RHWIoR1zKjocdvNaTsxCxZzQ6v9MLXaSYm9Qq0thv0DHg==", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -15220,6 +16526,14 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/linq-to-typescript": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/linq-to-typescript/-/linq-to-typescript-11.0.0.tgz", + "integrity": "sha512-6K1JHKDp90cLVk09xsTJn/ccw6YlEFxCC2iiL/4UmReZskrLCEi3JKU3iKkXspCY44vAzM95t49J13rdXQaCOw==", + "engines": { + "node": ">=17" + } + }, "node_modules/linux-platform-info": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/linux-platform-info/-/linux-platform-info-0.0.3.tgz", @@ -19542,6 +20856,14 @@ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, + "node_modules/rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -19908,6 +21230,14 @@ "resolved": "https://registry.npmjs.org/signum/-/signum-1.0.0.tgz", "integrity": "sha512-yodFGwcyt59XRh7w5W3jPcIQb3Bwi21suEfT7MAWnBX3iCdklJpgDgvGT9o04UonglZN5SNMfJFkHIR/jO8GHw==" }, + "node_modules/simplytyped": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/simplytyped/-/simplytyped-3.3.0.tgz", + "integrity": "sha512-mz4RaNdKTZiaKXgi6P1k/cdsxV3gz+y1Wh2NXHWD40dExktLh4Xx/h6MFakmQWODZHj/2rKe59acacpL74ZhQA==", + "peerDependencies": { + "typescript": ">=2.8.0" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -20430,6 +21760,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, "node_modules/strongly-connected-components": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strongly-connected-components/-/strongly-connected-components-1.0.1.tgz", @@ -23430,6 +24765,1089 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/crc32c": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-3.0.0.tgz", + "integrity": "sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==", + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "requires": { + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/sha1-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-3.0.0.tgz", + "integrity": "sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==", + "requires": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "requires": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "requires": { + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "requires": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-sdk/abort-controller": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.310.0.tgz", + "integrity": "sha512-v1zrRQxDLA1MdPim159Vx/CPHqsB4uybSxRi1CnfHO5ZjHryx3a5htW2gdGAykVCul40+yJXvfpufMrELVxH+g==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/chunked-blob-reader": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/chunked-blob-reader/-/chunked-blob-reader-3.310.0.tgz", + "integrity": "sha512-CrJS3exo4mWaLnWxfCH+w88Ou0IcAZSIkk4QbmxiHl/5Dq705OLoxf4385MVyExpqpeVJYOYQ2WaD8i/pQZ2fg==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/client-s3": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.316.0.tgz", + "integrity": "sha512-nmlvXf7c1Nz9VR/VlplgsZqPqe7eg5RYuuIb70rX/4Joe5x98sJqz72hyLblwQTB47lwPLK+qvHps9aVOY2QhA==", + "requires": { + "@aws-crypto/sha1-browser": "3.0.0", + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.316.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-node": "3.316.0", + "@aws-sdk/eventstream-serde-browser": "3.310.0", + "@aws-sdk/eventstream-serde-config-resolver": "3.310.0", + "@aws-sdk/eventstream-serde-node": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-blob-browser": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/hash-stream-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/md5-js": "3.310.0", + "@aws-sdk/middleware-bucket-endpoint": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-expect-continue": "3.310.0", + "@aws-sdk/middleware-flexible-checksums": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-location-constraint": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-sdk-s3": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/middleware-ssec": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4-multi-region": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-stream-browser": "3.310.0", + "@aws-sdk/util-stream-node": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "@aws-sdk/util-waiter": "3.310.0", + "@aws-sdk/xml-builder": "3.310.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/client-sso": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.316.0.tgz", + "integrity": "sha512-wGXfIhR0lJGB8QTT0fwSwwklHePHxd2GW3IQt3trXnEYe0frmJ7vYRnVL5CSRKsikLDmaU7ll3SdsshMzQzo3w==", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/client-sso-oidc": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.316.0.tgz", + "integrity": "sha512-e2fvC7o42YV+LcZYfXCcvBn4L7NM9oNccnZ7T+pS6SFpHZlaqkw4uuQMRE6iUAof+Id7Mt7xDrz1x2yGlP+8GA==", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/client-sts": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.316.0.tgz", + "integrity": "sha512-5SD59+DRVy1mKckGs/5J8OwWpRS3E5v4BX19XaX/s9JJ5Rw9aZd9DP4SZVpeNXztIPjkQSEzHgrUVlZFB1QJgg==", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-node": "3.316.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-sdk-sts": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/config-resolver": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.310.0.tgz", + "integrity": "sha512-8vsT+/50lOqfDxka9m/rRt6oxv1WuGZoP8oPMk0Dt+TxXMbAzf4+rejBgiB96wshI1k3gLokYRjSQZn+dDtT8g==", + "requires": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-config-provider": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.310.0.tgz", + "integrity": "sha512-vvIPQpI16fj95xwS7M3D48F7QhZJBnnCgB5lR+b7So+vsG9ibm1mZRVGzVpdxCvgyOhHFbvrby9aalNJmmIP1A==", + "requires": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-imds": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.310.0.tgz", + "integrity": "sha512-baxK7Zp6dai5AGW01FIW27xS2KAaPUmKLIXv5SvFYsUgXXvNW55im4uG3b+2gA0F7V+hXvVBH08OEqmwW6we5w==", + "requires": { + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.316.0.tgz", + "integrity": "sha512-ZADkpdEjFCAXyzEpYbCRENlZ/AQEwevWdPd2yshjNo7xvOcepv4pPIBpYd8h9LvRafSLGA7zlWDz84hkIt+HKA==", + "requires": { + "@aws-sdk/credential-provider-env": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/credential-provider-process": "3.310.0", + "@aws-sdk/credential-provider-sso": "3.316.0", + "@aws-sdk/credential-provider-web-identity": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.316.0.tgz", + "integrity": "sha512-oE1LTXP8XZp4bT8LhBeolMRiz0RwnmHDC2XpUmWO8LTmbDNrQO0mVzxEvXDLeKaN5BIFIJqNFlMgjWUMa9Kwcw==", + "requires": { + "@aws-sdk/credential-provider-env": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/credential-provider-ini": "3.316.0", + "@aws-sdk/credential-provider-process": "3.310.0", + "@aws-sdk/credential-provider-sso": "3.316.0", + "@aws-sdk/credential-provider-web-identity": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.310.0.tgz", + "integrity": "sha512-h73sg6GPMUWC+3zMCbA1nZ2O03nNJt7G96JdmnantiXBwHpRKWW8nBTLzx5uhXn6hTuTaoQRP/P+oxQJKYdMmA==", + "requires": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.316.0.tgz", + "integrity": "sha512-8/O2twlsoV1bDkZ9jd7JCMWsftfyoTyRT1UYscsKZGUDEgZRAxRkzS3GLYuLXEWNuxb1OB9rYk/cEJoxwy7T9g==", + "requires": { + "@aws-sdk/client-sso": "3.316.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/token-providers": "3.316.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.310.0.tgz", + "integrity": "sha512-H4SzuZXILNhK6/IR1uVvsUDZvzc051hem7GLyYghBCu8mU+tq28YhKE8MfSroi6eL2e5Vujloij1OM2EQQkPkw==", + "requires": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/eventstream-codec": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.310.0.tgz", + "integrity": "sha512-clIeSgWbZbxwtsxZ/yoedNM0/kJFSIjjHPikuDGhxhqc+vP6TN3oYyVMFrYwFaTFhk2+S5wZcWYMw8Op1pWo+A==", + "requires": { + "@aws-crypto/crc32": "3.0.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/eventstream-serde-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-browser/-/eventstream-serde-browser-3.310.0.tgz", + "integrity": "sha512-3S6ziuQVALgEyz0TANGtYDVeG8ArK4Y05mcgrs8qUTmsvlDIXX37cR/DvmVbNB76M4IrsZeSAIajL9644CywkA==", + "requires": { + "@aws-sdk/eventstream-serde-universal": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/eventstream-serde-config-resolver": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.310.0.tgz", + "integrity": "sha512-8s1Qdn9STj+sV75nUp9yt0W6fHS4BZ2jTm4Z/1Pcbvh2Gqs0WjH5n2StS+pDW5Y9J/HSGBl0ogmUr5lC5bXFHg==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/eventstream-serde-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-node/-/eventstream-serde-node-3.310.0.tgz", + "integrity": "sha512-kSnRomCgW43K9TmQYuwN9+AoYPnhyOKroanUMyZEzJk7rpCPMj4OzaUpXfDYOvznFNYn7NLaH6nHLJAr0VPlJA==", + "requires": { + "@aws-sdk/eventstream-serde-universal": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/eventstream-serde-universal": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-universal/-/eventstream-serde-universal-3.310.0.tgz", + "integrity": "sha512-Qyjt5k/waV5cDukpgT824ISZAz5U0pwzLz5ztR409u85AGNkF/9n7MS+LSyBUBSb0WJ5pUeSD47WBk+nLq9Nhw==", + "requires": { + "@aws-sdk/eventstream-codec": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/fetch-http-handler": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.310.0.tgz", + "integrity": "sha512-Bi9vIwzdkw1zMcvi/zGzlWS9KfIEnAq4NNhsnCxbQ4OoIRU9wvU+WGZdBBhxg0ZxZmpp1j1aZhU53lLjA07MHw==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/querystring-builder": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/hash-blob-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-blob-browser/-/hash-blob-browser-3.310.0.tgz", + "integrity": "sha512-OoR8p0cbypToysLT0v3o2oyjy6+DKrY7GNCAzHOHJK9xmqXCt+DsjKoPeiY7o1sWX2aN6Plmvubj/zWxMKEn/A==", + "requires": { + "@aws-sdk/chunked-blob-reader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/hash-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.310.0.tgz", + "integrity": "sha512-NvE2fhRc8GRwCXBfDehxVAWCmVwVMILliAKVPAEr4yz2CkYs0tqU51S48x23dtna07H4qHtgpeNqVTthcIQOEQ==", + "requires": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/hash-stream-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-stream-node/-/hash-stream-node-3.310.0.tgz", + "integrity": "sha512-ZoXdybNgvMz1Hl6k/e32xVL3jmG5p2IEk5mTtLfFEuskTJ74Z+VMYKkkF1whyy7KQfH83H+TQGnsGtlRCchQKw==", + "requires": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/invalid-dependency": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.310.0.tgz", + "integrity": "sha512-1s5RG5rSPXoa/aZ/Kqr5U/7lqpx+Ry81GprQ2bxWqJvWQIJ0IRUwo5pk8XFxbKVr/2a+4lZT/c3OGoBOM1yRRA==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/is-array-buffer": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", + "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/md5-js": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/md5-js/-/md5-js-3.310.0.tgz", + "integrity": "sha512-x5sRBUrEfLWAS1EhwbbDQ7cXq6uvBxh3qR2XAsnGvFFceTeAadk7cVogWxlk3PC+OCeeym7c3/6Bv2HQ2f1YyQ==", + "requires": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-bucket-endpoint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.310.0.tgz", + "integrity": "sha512-uJJfHI7v4AgbJZRLtyI8ap2QRWkBokGc3iyUoQ+dVNT3/CE2ZCu694A6W+H0dRqg79dIE+f9CRNdtLGa/Ehhvg==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-arn-parser": "3.310.0", + "@aws-sdk/util-config-provider": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-content-length": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.310.0.tgz", + "integrity": "sha512-P8tQZxgDt6CAh1wd/W6WPzjc+uWPJwQkm+F7rAwRlM+k9q17HrhnksGDKcpuuLyIhPQYdmOMIkpKVgXGa4avhQ==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-endpoint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.310.0.tgz", + "integrity": "sha512-Z+N2vOL8K354/lstkClxLLsr6hCpVRh+0tCMXrVj66/NtKysCEZ/0b9LmqOwD9pWHNiI2mJqXwY0gxNlKAroUg==", + "requires": { + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-expect-continue": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.310.0.tgz", + "integrity": "sha512-l3d1z2gt+gINJDnPSyu84IxfzjzPfCQrqC1sunw2cZGo/sXtEiq698Q3SiTcO2PGP4LBQAy2RHb5wVBJP708CQ==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-flexible-checksums": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.310.0.tgz", + "integrity": "sha512-5ndnLgzgGVpWkmHBAiYkagHqiSuow8q62J4J6E2PzaQ77+fm8W3nfdy7hK5trHokEyouCZdxT/XK/IRhgj/4PA==", + "requires": { + "@aws-crypto/crc32": "3.0.0", + "@aws-crypto/crc32c": "3.0.0", + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-host-header": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.310.0.tgz", + "integrity": "sha512-QWSA+46/hXorXyWa61ic2K7qZzwHTiwfk2e9mRRjeIRepUgI3qxFjsYqrWtrOGBjmFmq0pYIY8Bb/DCJuQqcoA==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-location-constraint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.310.0.tgz", + "integrity": "sha512-LFm0JTQWwTPWL/tZU2wsQTl8J5PpDEkXjEhaXVKamtyH0xhysRqd+0n92n65dc8oztAuQkb9xUbErGn5b6gsew==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-logger": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.310.0.tgz", + "integrity": "sha512-Lurm8XofrASBRnAVtiSNuDSRsRqPNg27RIFLLsLp/pqog9nFJ0vz0kgdb9S5Z+zw83Mm+UlqOe6D8NTUNp4fVg==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-recursion-detection": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.310.0.tgz", + "integrity": "sha512-SuB75/xk/gyue24gkriTwO2jFd7YcUGZDClQYuRejgbXSa3CO0lWyawQtfLcSSEBp9izrEVXuFH24K1eAft5nQ==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-retry": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.310.0.tgz", + "integrity": "sha512-oTPsRy2W4s+dfxbJPW7Km+hHtv/OMsNsVfThAq8DDYKC13qlr1aAyOqGLD+dpBy2aKe7ss517Sy2HcHtHqm7/g==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/service-error-classification": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + } + }, + "@aws-sdk/middleware-sdk-s3": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.310.0.tgz", + "integrity": "sha512-QK9x9g2ksg0hOjjYgqddeFcn5ctUEGdxJVu4OumPXceulefMcSO2jyH2qTybYSA93nqNQFdFmg5wQfvIRUWFCQ==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-arn-parser": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-sdk-sts": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.310.0.tgz", + "integrity": "sha512-+5PFwlYNLvLLIfw0ASAoWV/iIF8Zv6R6QGtyP0CclhRSvNjgbQDVnV0g95MC5qvh+GB/Yjlkt8qAjLSPjHfsrQ==", + "requires": { + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-serde": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.310.0.tgz", + "integrity": "sha512-RNeeTVWSLTaentUeCgQKZhAl+C6hxtwD78cQWS10UymWpQFwbaxztzKUu4UQS5xA2j6PxwPRRUjqa4jcFjfLsg==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-signing": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.310.0.tgz", + "integrity": "sha512-f9mKq+XMdW207Af3hKjdTnpNhdtwqWuvFs/ZyXoOkp/g1MY1O6L23Jy6i52m29LxbT4AuNRG1oKODfXM0vYVjQ==", + "requires": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-ssec": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.310.0.tgz", + "integrity": "sha512-CnEwNKVpd5bXnrCKPaePF8mWTA9ET21OMBb54y9b0fd8K02zoOcdBz4DWfh1SjFD4HkgCdja4egd8l2ivyvqmw==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-stack": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.310.0.tgz", + "integrity": "sha512-010O1PD+UAcZVKRvqEusE1KJqN96wwrf6QsqbRM0ywsKQ21NDweaHvEDlds2VHpgmofxkRLRu/IDrlPkKRQrRg==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.310.0.tgz", + "integrity": "sha512-x3IOwSwSbwKidlxRk3CNVHVUb06SRuaELxggCaR++QVI8NU6qD/l4VHXKVRvbTHiC/cYxXE/GaBDgQVpDR7V/g==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-endpoints": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/node-config-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.310.0.tgz", + "integrity": "sha512-T/Pp6htc6hq/Cq+MLNDSyiwWCMVF6GqbBbXKVlO5L8rdHx4sq9xPdoPveZhGWrxvkanjA6eCwUp6E0riBOSVng==", + "requires": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/node-http-handler": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.310.0.tgz", + "integrity": "sha512-irv9mbcM9xC2xYjArQF5SYmHBMu4ciMWtGsoHII1nRuFOl9FoT4ffTvEPuLlfC6pznzvKt9zvnm6xXj7gDChKg==", + "requires": { + "@aws-sdk/abort-controller": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/querystring-builder": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/property-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.310.0.tgz", + "integrity": "sha512-3lxDb0akV6BBzmFe4nLPaoliQbAifyWJhuvuDOu7e8NzouvpQXs0275w9LePhhcgjKAEVXUIse05ZW2DLbxo/g==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/protocol-http": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.310.0.tgz", + "integrity": "sha512-fgZ1aw/irQtnrsR58pS8ThKOWo57Py3xX6giRvwSgZDEcxHfVzuQjy9yPuV++v04fdmdtgpbGf8WfvAAJ11yXQ==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/querystring-builder": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.310.0.tgz", + "integrity": "sha512-ZHH8GV/80+pWGo7DzsvwvXR5xVxUHXUvPJPFAkhr6nCf78igdoF8gR10ScFoEKbtEapoNTaZlKHPXxpD8aPG7A==", + "requires": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/querystring-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.310.0.tgz", + "integrity": "sha512-YkIznoP6lsiIUHinx++/lbb3tlMURGGqMpo0Pnn32zYzGrJXA6eC3D0as2EcMjo55onTfuLcIiX4qzXes2MYOA==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/service-error-classification": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.310.0.tgz", + "integrity": "sha512-PuyC7k3qfIKeH2LCnDwbttMOKq3qAx4buvg0yfnJtQOz6t1AR8gsnAq0CjKXXyfkXwNKWTqCpE6lVNUIkXgsMw==" + }, + "@aws-sdk/shared-ini-file-loader": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.310.0.tgz", + "integrity": "sha512-N0q9pG0xSjQwc690YQND5bofm+4nfUviQ/Ppgan2kU6aU0WUq8KwgHJBto/YEEI+VlrME30jZJnxtOvcZJc2XA==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/signature-v4": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.310.0.tgz", + "integrity": "sha512-1M60P1ZBNAjCFv9sYW29OF6okktaeibWyW3lMXqzoHF70lHBZh+838iUchznXUA5FLabfn4jBFWMRxlAXJUY2Q==", + "requires": { + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/signature-v4-multi-region": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.310.0.tgz", + "integrity": "sha512-q8W+RIomTS/q85Ntgks/CoDElwqkC9+4OCicee5YznNHjQ4gtNWhUkYIyIRWRmXa/qx/AUreW9DM8FAecCOdng==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/smithy-client": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.316.0.tgz", + "integrity": "sha512-6YXOKbRnXeS8r8RWzuL6JMBolDYM5Wa4fD/VY6x/wK78i2xErHOvqzHgyyeLI1MMw4uqyd4wRNJNWC9TMPduXw==", + "requires": { + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/token-providers": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.316.0.tgz", + "integrity": "sha512-foJ2YmB8A/mtp52riO2zdmBgzA3IpASNgUhY9FZg1BKpGcjqLQDGYP+BY3BA0H7CFsMa4PCf13M5wWwn1onyBA==", + "requires": { + "@aws-sdk/client-sso-oidc": "3.316.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/types": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.310.0.tgz", + "integrity": "sha512-j8eamQJ7YcIhw7fneUfs8LYl3t01k4uHi4ZDmNRgtbmbmTTG3FZc2MotStZnp3nZB6vLiPF1o5aoJxWVvkzS6A==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/url-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.310.0.tgz", + "integrity": "sha512-mCLnCaSB9rQvAgx33u0DujLvr4d5yEm/W5r789GblwwQnlNXedVu50QRizMLTpltYWyAUoXjJgQnJHmJMaKXhw==", + "requires": { + "@aws-sdk/querystring-parser": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-arn-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.310.0.tgz", + "integrity": "sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-base64": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.310.0.tgz", + "integrity": "sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==", + "requires": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-body-length-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.310.0.tgz", + "integrity": "sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-body-length-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.310.0.tgz", + "integrity": "sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-buffer-from": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", + "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", + "requires": { + "@aws-sdk/is-array-buffer": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-config-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.310.0.tgz", + "integrity": "sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-defaults-mode-browser": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.316.0.tgz", + "integrity": "sha512-6FSqLhYmaihtH2n1s4b2rlLW0ABU8N6VZIfzLfe2ING4PF0MzfaMMhnTFUHVXfKCVGoR8yP6iyFTRCyHGVEL1w==", + "requires": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-defaults-mode-node": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.316.0.tgz", + "integrity": "sha512-dkYy10hdjPSScXXvnjGpZpnJxllkb6ICHgLMwZ4JczLHhPM12T/4PQ758YN8HS+muiYDGX1Bl2z1jd/bMcewBQ==", + "requires": { + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-endpoints": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.310.0.tgz", + "integrity": "sha512-zG+/d/O5KPmAaeOMPd6bW1abifdT0H03f42keLjYEoRZzYtHPC5DuPE0UayiWGckI6BCDgy0sRKXCYS49UNFaQ==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-hex-encoding": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", + "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-locate-window": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", + "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-middleware": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.310.0.tgz", + "integrity": "sha512-FTSUKL/eRb9X6uEZClrTe27QFXUNNp7fxYrPndZwk1hlaOP5ix+MIHBcI7pIiiY/JPfOUmPyZOu+HetlFXjWog==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-retry": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.310.0.tgz", + "integrity": "sha512-FwWGhCBLfoivTMUHu1LIn4NjrN9JLJ/aX5aZmbcPIOhZVFJj638j0qDgZXyfvVqBuBZh7M8kGq0Oahy3dp69OA==", + "requires": { + "@aws-sdk/service-error-classification": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-stream-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-stream-browser/-/util-stream-browser-3.310.0.tgz", + "integrity": "sha512-bysXZHwFwvbqOTCScCdCnoLk1K3GCo0HRIYEZuL7O7MHrQmfaYRXcaft/p22+GUv9VeFXS/eJJZ5r4u32az94w==", + "requires": { + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-stream-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-stream-node/-/util-stream-node-3.310.0.tgz", + "integrity": "sha512-hueAXFK0GVvnfYFgqbF7587xZfMZff5jlIFZOHqx7XVU7bl7qrRUCnphHk8H6yZ7RoQbDPcfmHJgtEoAJg1T1Q==", + "requires": { + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-uri-escape": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", + "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-user-agent-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.310.0.tgz", + "integrity": "sha512-yU/4QnHHuQ5z3vsUqMQVfYLbZGYwpYblPiuZx4Zo9+x0PBkNjYMqctdDcrpoH9Z2xZiDN16AmQGK1tix117ZKw==", + "requires": { + "@aws-sdk/types": "3.310.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-user-agent-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.310.0.tgz", + "integrity": "sha512-Ra3pEl+Gn2BpeE7KiDGpi4zj7WJXZA5GXnGo3mjbi9+Y3zrbuhJAbdZO3mO/o7xDgMC6ph4xCTbaSGzU6b6EDg==", + "requires": { + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-utf8": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.310.0.tgz", + "integrity": "sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==", + "requires": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-waiter": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-waiter/-/util-waiter-3.310.0.tgz", + "integrity": "sha512-AV5j3guH/Y4REu+Qh3eXQU9igljHuU4XjX2sADAgf54C0kkhcCCkkiuzk3IsX089nyJCqIcj5idbjdvpnH88Vw==", + "requires": { + "@aws-sdk/abort-controller": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/xml-builder": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.310.0.tgz", + "integrity": "sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==", + "requires": { + "tslib": "^2.5.0" + } + }, "@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -31009,6 +33427,14 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "fast-xml-parser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz", + "integrity": "sha512-CDYeykkle1LiA/uqQyNwYpFbyF6Axec6YapmpUP+/RHWIoR1zKjocdvNaTsxCxZzQ6v9MLXaSYm9Qq0thv0DHg==", + "requires": { + "strnum": "^1.0.5" + } + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -34704,6 +37130,11 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "linq-to-typescript": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/linq-to-typescript/-/linq-to-typescript-11.0.0.tgz", + "integrity": "sha512-6K1JHKDp90cLVk09xsTJn/ccw6YlEFxCC2iiL/4UmReZskrLCEi3JKU3iKkXspCY44vAzM95t49J13rdXQaCOw==" + }, "linux-platform-info": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/linux-platform-info/-/linux-platform-info-0.0.3.tgz", @@ -37774,6 +40205,14 @@ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, + "rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "requires": { + "tslib": "^2.1.0" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -38049,6 +40488,12 @@ "resolved": "https://registry.npmjs.org/signum/-/signum-1.0.0.tgz", "integrity": "sha512-yodFGwcyt59XRh7w5W3jPcIQb3Bwi21suEfT7MAWnBX3iCdklJpgDgvGT9o04UonglZN5SNMfJFkHIR/jO8GHw==" }, + "simplytyped": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/simplytyped/-/simplytyped-3.3.0.tgz", + "integrity": "sha512-mz4RaNdKTZiaKXgi6P1k/cdsxV3gz+y1Wh2NXHWD40dExktLh4Xx/h6MFakmQWODZHj/2rKe59acacpL74ZhQA==", + "requires": {} + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -38471,6 +40916,11 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, + "strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, "strongly-connected-components": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strongly-connected-components/-/strongly-connected-components-1.0.1.tgz", diff --git a/typescript/Frontend/package.json b/typescript/Frontend/package.json index 7f6499cb8..6767b52e0 100644 --- a/typescript/Frontend/package.json +++ b/typescript/Frontend/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@aws-sdk/client-s3": "^3.316.0", "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@minoru/react-dnd-treeview": "^3.4.1", @@ -21,6 +22,7 @@ "chart.js": "^4.2.1", "css-loader": "^6.7.3", "formik": "^2.2.9", + "linq-to-typescript": "^11.0.0", "package.json": "^2.0.1", "plotly.js": "^2.20.0", "react": "^18.2.0", @@ -34,8 +36,10 @@ "react-router-dom": "^6.8.0", "react-scripts": "5.0.1", "reactflow": "^11.5.6", + "rxjs": "^7.8.0", "sass": "^1.58.3", "sass-loader": "^13.2.0", + "simplytyped": "^3.3.0", "style-loader": "^3.3.1", "testcafe": "^2.4.0", "typescript": "^4.9.5", diff --git a/typescript/Frontend/src/components/Installations/InstallationForm.tsx b/typescript/Frontend/src/components/Installations/InstallationForm.tsx index b8aed2c2a..46681a51c 100644 --- a/typescript/Frontend/src/components/Installations/InstallationForm.tsx +++ b/typescript/Frontend/src/components/Installations/InstallationForm.tsx @@ -9,6 +9,7 @@ import MoveDialog from "../Groups/Tree/MoveDialog"; import InnovenergyButton from "../Layout/InnovenergyButton"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; import { UserContext } from "../Context/UserContextProvider"; +import * as Yup from "yup"; interface I_InstallationFormProps { values: I_Installation; @@ -19,11 +20,32 @@ const InstallationForm = (props: I_InstallationFormProps) => { const { values, id, hasMoveButton } = props; const [open, setOpen] = useState(false); - const intl = useIntl(); const { fetchData } = useContext(InstallationContext); const { getCurrentUser } = useContext(UserContext); const readOnly = !getCurrentUser().hasWriteAccess; + const intl = useIntl(); + + const validationSchema = Yup.object().shape({ + name: Yup.string().required( + intl.formatMessage({ + id: "requiredName", + defaultMessage: "Name is required", + }) + ), + region: Yup.string().required( + intl.formatMessage({ + id: "requiredRegion", + defaultMessage: "Region is required", + }) + ), + location: Yup.string().required( + intl.formatMessage({ + id: "requiredLocation", + defaultMessage: "Location is required", + }) + ), + }); const formik = useFormik({ initialValues: { @@ -44,6 +66,7 @@ const InstallationForm = (props: I_InstallationFormProps) => { fetchData(); }); }, + validationSchema, }); const handleClose = () => { @@ -62,6 +85,10 @@ const InstallationForm = (props: I_InstallationFormProps) => { name="name" value={formik.values.name} handleChange={formik.handleChange} + helperText={ + formik.errors.name && formik.touched.name ? formik.errors.name : "" + } + error={!!formik.errors.name && formik.touched.name} /> { name="region" value={formik.values.region} handleChange={formik.handleChange} + helperText={ + formik.errors.region && formik.touched.region + ? formik.errors.region + : "" + } + error={!!formik.errors.region && formik.touched.region} /> { name="location" value={formik.values.location} handleChange={formik.handleChange} + helperText={ + formik.errors.location && formik.touched.location + ? formik.errors.location + : "" + } + error={!!formik.errors.location && formik.touched.location} /> { - return ; + const [timeSeries, setTimeSeries] = useState([]); + const s3Access = new S3Access( + "saliomameiringen", + "sos-ch-dk-2", + "exo.io", + "EXO18e7ae9e53fae71ee55cf35b", + "3Cyonq8gMQ0a3elTH2vP7Yv-czcCj8iE2lBcPB9XhSc", + "" + ); + + const fetchData = ( + timestamp: UnixTime + ): Promise>> => { + const s3Path = `${timestamp.ticks}.csv`; + return s3Access + .get(s3Path) + .then(async (r) => { + if (r.status === 404) { + return Promise.resolve(FetchResult.notAvailable); + } else if (r.status === 200) { + const text = await r.text(); + return parseCsv(text); + } else { + console.error("unexpected status code"); + return Promise.resolve(FetchResult.notAvailable); + } + }) + .catch((e) => { + console.log(e); + return Promise.resolve(FetchResult.tryLater); + }); + }; + + const cache = new DataCache(fetchData, TimeSpan.fromSeconds(2)); + + const sampleTimes = UnixTime.fromTicks(1682085650) + .earlier(TimeSpan.fromMinutes(1)) + .rangeBefore(TimeSpan.fromMinutes(1)) + .sample(TimeSpan.fromSeconds(2)); + + cache.getSeries(sampleTimes); + + const update = cache.gotData.pipe( + debounceTime(2000), + map((_) => setTimeSeries(cache.getSeries(sampleTimes))) + ); + + update.subscribe(); + + return ( + <> + + + ); }; export default Log; diff --git a/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx b/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx index 7a6fa4b87..69e6e5218 100644 --- a/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx +++ b/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx @@ -1,59 +1,15 @@ import Plot from "react-plotly.js"; -import { TimeSeries, timeSeries } from "../ExampleLogData"; +import { RecordSeries } from "../../../dataCache/data"; +import { transformToGraphData } from "../../../util/graph.util"; -const ScalarGraph = () => { - const transformToGraphData = (timeStampData: TimeSeries) => { - return Object.keys(timeSeries).reduce( - (timeSeriesAcc, timeSeriesKey) => { - const logData = timeStampData[parseInt(timeSeriesKey)]; - const transformedTimeSeries = Object.keys(logData).reduce( - (logAcc, logDataKey) => { - return { - ...logAcc, - [logDataKey]: { - [timeSeriesKey]: logData[logDataKey], - }, - }; - }, - {} as any - ); - Object.keys(transformedTimeSeries).forEach( - (transformedTimeSeriesKey) => { - const date = new Date(parseInt(timeSeriesKey)); - if (timeSeriesAcc[transformedTimeSeriesKey]) { - timeSeriesAcc[transformedTimeSeriesKey] = { - x: [...timeSeriesAcc[transformedTimeSeriesKey].x, date], - y: [ - ...timeSeriesAcc[transformedTimeSeriesKey].y, - transformedTimeSeries[transformedTimeSeriesKey][ - timeSeriesKey - ], - ], - }; - } else { - timeSeriesAcc[transformedTimeSeriesKey] = { - x: [date], - y: [ - transformedTimeSeries[transformedTimeSeriesKey][ - timeSeriesKey - ], - ], - }; - } - } - ); - return timeSeriesAcc; - }, - {} as { - [path: string]: { x: Date[]; y: number[] }; - } - ); - }; +interface I_ScalarGraphProps { + data: RecordSeries; +} +const ScalarGraph = (props: I_ScalarGraphProps) => { const renderGraphs = () => { - const coordinateTimeSeries = transformToGraphData(timeSeries); + const coordinateTimeSeries = transformToGraphData(props.data); return Object.keys(coordinateTimeSeries).map((path) => { - console.log(timeSeries[parseInt(path)]); return ( { ], }} onUpdate={(figure) => { - console.log(figure); + //console.log(figure); }} /> ); diff --git a/typescript/Frontend/src/components/Layout/InnovenergyTextfield.tsx b/typescript/Frontend/src/components/Layout/InnovenergyTextfield.tsx index cb208092f..d94b24430 100644 --- a/typescript/Frontend/src/components/Layout/InnovenergyTextfield.tsx +++ b/typescript/Frontend/src/components/Layout/InnovenergyTextfield.tsx @@ -9,6 +9,8 @@ interface I_InnovenergyTextfieldProps { type?: string; readOnly?: boolean; disabled?: boolean; + helperText?: string; + error?: boolean; } const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => { @@ -30,6 +32,9 @@ const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => { "-webkit-text-fill-color": "black", color: "black", }, + ".MuiFormHelperText-root": { + marginLeft: 0, + }, }} value={props.value || ""} onChange={props.handleChange} @@ -37,6 +42,8 @@ const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => { readOnly: props.readOnly, }} disabled={props.disabled} + helperText={props.helperText} + error={props.error} /> diff --git a/typescript/Frontend/src/dataCache/S3/S3Access.ts b/typescript/Frontend/src/dataCache/S3/S3Access.ts new file mode 100644 index 000000000..6780f0ced --- /dev/null +++ b/typescript/Frontend/src/dataCache/S3/S3Access.ts @@ -0,0 +1,79 @@ +import {sha1Hmac} from "./Sha1"; +import {Utf8} from "./Utf8"; +import {toBase64} from "./UInt8Utils"; + +export class S3Access +{ + constructor + ( + readonly bucket: string, + readonly region: string, + readonly provider: string, + readonly key: string, + readonly secret: string, + readonly contentType: string + ) + {} + + get host() : string { return `${this.bucket}.${this.region}.${this.provider}` } + get url() : string { return `https://${this.host}` } + + public get(s3Path : string): Promise + { + const method = "GET"; + const auth = this.createAuthorizationHeader(method, s3Path, ""); + const url = this.url + "/" + s3Path + const headers = {"Host": this.host, "Authorization": auth}; + + try + { + return fetch(url, {method: method, mode: "cors", headers: headers}) + } + catch + { + return Promise.reject() + } + } + + private createAuthorizationHeader(method: string, + s3Path: string, + date: string) + { + return createAuthorizationHeader + ( + method, + this.bucket, + s3Path, + date, + this.key, + this.secret, + this.contentType + ); + } +} + +function createAuthorizationHeader(method: string, + bucket: string, + s3Path: string, + date: string, + s3Key: string, + s3Secret: string, + contentType: string, + md5Hash: string = "") +{ + // StringToSign = HTTP-Verb + "\n" + + // Content-MD5 + "\n" + + // Content-Type + "\n" + + // Date + "\n" + + // CanonicalizedAmzHeaders + + // CanonicalizedResource; + + const payload = Utf8.encode(`${method}\n${md5Hash}\n${contentType}\n${date}\n/${bucket}/${s3Path}`) + + //console.log(`${method}\n${md5Hash}\n${contentType}\n${date}\n/${bucket}/${s3Path}`) + + const secret = Utf8.encode(s3Secret) + const signature = toBase64(sha1Hmac(payload, secret)); + + return `AWS ${s3Key}:${signature}` +} diff --git a/typescript/Frontend/src/dataCache/S3/Sha1.ts b/typescript/Frontend/src/dataCache/S3/Sha1.ts new file mode 100644 index 000000000..17dcea21f --- /dev/null +++ b/typescript/Frontend/src/dataCache/S3/Sha1.ts @@ -0,0 +1,125 @@ +import {concat, pad} from "./UInt8Utils"; + +const BigEndian = false + +export function sha1Hmac(msg: Uint8Array, key: Uint8Array): Uint8Array +{ + if (key.byteLength > 64) + key = sha1(key) + if (key.byteLength < 64) + key = pad(key, 64) + + const oKey = key.map(b => b ^ 0x5C); + const iKey = key.map(b => b ^ 0x36); + + const iData = concat(iKey, msg); + const iHash = sha1(iData); + const oData = concat(oKey, iHash); + + return sha1(oData); +} + +export function sha1(data: Uint8Array): Uint8Array +{ + const paddedData: DataView = initData(data) + + const H = new Uint32Array([0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]) + const S = new Uint32Array(5) // State + const round = new Uint32Array(80); + + function initRound(startOffset: number) + { + for (let i = 0; i < 16; i++) + round[i] = paddedData.getUint32((startOffset + i) * 4, BigEndian); + + for (let i = 16; i < 80; i++) + { + const int32 = round[i - 3] ^ round[i - 8] ^ round[i - 14] ^ round[i - 16]; + round[i] = rotate1(int32); // SHA0 has no rotate + } + } + + const functions = + [ + () => (S[1] & S[2] | ~S[1] & S[3]) + 0x5A827999, + () => (S[1] ^ S[2] ^ S[3]) + 0x6ED9EBA1, + () => (S[1] & S[2] | S[1] & S[3] | S[2] & S[3]) + 0x8F1BBCDC, + () => (S[1] ^ S[2] ^ S[3]) + 0xCA62C1D6 + ] + + for (let startOffset = 0; startOffset < paddedData.byteLength / 4; startOffset += 16) + { + initRound(startOffset); + + S.set(H) + + for (let r = 0, i = 0; r < 4; r++) + { + const f = functions[r] + const end = i + 20; + + do + { + const S0 = rotate5(S[0]) + f() + S[4] + round[i]; + S[4] = S[3]; + S[3] = S[2]; + S[2] = rotate30(S[1]); + S[1] = S[0]; + S[0] = S0; + } + while (++i < end) + } + + for (let i = 0; i < 5; i++) + H[i] += S[i] + } + + swapEndianness(H); + + return new Uint8Array(H.buffer) +} + +function rotate5(int32: number) +{ + return (int32 << 5) | (int32 >>> 27); // >>> for unsigned shift +} + +function rotate30(int32: number) +{ + return (int32 << 30) | (int32 >>> 2); +} + +function rotate1(int32: number) +{ + return (int32 << 1) | (int32 >>> 31); +} + +function initData(data: Uint8Array): DataView +{ + const dataLength = data.length + const extendedLength = dataLength + 9; // add 8 bytes for UInt64 length + 1 byte for "stop-bit" (0x80) + const paddedLength = Math.ceil(extendedLength / 64) * 64; // pad to 512 bits block + const paddedData = new Uint8Array(paddedLength) + + paddedData.set(data) + paddedData[dataLength] = 0x80 // append single 1 bit at end of data + + const dataView = new DataView(paddedData.buffer) + + // append UInt64 length + dataView.setUint32(paddedData.length - 4, dataLength << 3 , BigEndian) // dataLength in *bits* LO, (<< 3: x8 bits per byte) + dataView.setUint32(paddedData.length - 8, dataLength >>> 29, BigEndian) // dataLength in *bits* HI + + return dataView +} + +function swapEndianness(uint32Array: Uint32Array) +{ + const dv = new DataView(uint32Array.buffer) + for (let i = 0; i < uint32Array.byteLength; i += 4) + { + const uint32 = dv.getUint32(i, false) + dv.setUint32(i, uint32, true) + } +} + diff --git a/typescript/Frontend/src/dataCache/S3/UInt8Utils.ts b/typescript/Frontend/src/dataCache/S3/UInt8Utils.ts new file mode 100644 index 000000000..7e6c5d607 --- /dev/null +++ b/typescript/Frontend/src/dataCache/S3/UInt8Utils.ts @@ -0,0 +1,56 @@ +export function pad(data: Uint8Array, length: number): Uint8Array +{ + if (length < data.byteLength) + throw new RangeError("length") + + const padded = new Uint8Array(length) + padded.set(data) + + return padded; +} + +export function concat(left: Uint8Array, right: Uint8Array): Uint8Array +{ + const c = new Uint8Array(left.length + right.length); + c.set(left); + c.set(right, left.length); + return c +} + +export function toHexString(data: Uint8Array) +{ + return [...data].map(byteToHex).join(''); +} + +function byteToHex(b: number) +{ + return b.toString(16).padStart(2, "0"); +} + +const b64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + +export function toBase64(data : Uint8Array) : string +{ + const byteLength = data.byteLength + const base64LengthPadded = 4 * Math.ceil(byteLength / 3) + const base64Length = Math.ceil(byteLength / 3 * 4); + + const base64 = new Array(base64LengthPadded) + + for (let i = 0, o = 0; i < byteLength;) + { + const x = data[i++] + const y = data[i++] ?? 0 + const z = data[i++] ?? 0 + + base64[o++] = b64Chars[x >>> 2] + base64[o++] = b64Chars[(x << 4 | y >>> 4) & 63] + base64[o++] = b64Chars[(y << 2 | z >>> 6) & 63] + base64[o++] = b64Chars[z & 63] + } + + for (let i = base64LengthPadded; i > base64Length ;) + base64[--i] = "=" + + return base64.join('') +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/S3/Utf8.ts b/typescript/Frontend/src/dataCache/S3/Utf8.ts new file mode 100644 index 000000000..a7ea2951d --- /dev/null +++ b/typescript/Frontend/src/dataCache/S3/Utf8.ts @@ -0,0 +1,10 @@ +export namespace Utf8 +{ + const encoder = new TextEncoder() + const decoder = new TextDecoder() + + export const encode = (text: string): Uint8Array => encoder.encode(text); + export const decode = (data: Uint8Array): string => decoder.decode(data); +} + + diff --git a/typescript/Frontend/src/dataCache/data.ts b/typescript/Frontend/src/dataCache/data.ts new file mode 100644 index 000000000..8fdb168e1 --- /dev/null +++ b/typescript/Frontend/src/dataCache/data.ts @@ -0,0 +1,21 @@ +import { Maybe } from "yup"; +import {Timestamped} from "./types"; +import { isDefined } from "./utils/maybe"; + +export type DataRecord = Record + +export type DataPoint = Timestamped> +export type RecordSeries = Array +export type PointSeries = Array>> +export type DataSeries = Array> + +export function getPoints(recordSeries: RecordSeries, series: keyof DataRecord): PointSeries +{ + return recordSeries.map(p => ({time: p.time, value: isDefined(p.value) ? p.value[series] : undefined})) +} + +export function getData(recordSeries: RecordSeries, series: keyof DataRecord): DataSeries +{ + return recordSeries.map(p => (isDefined(p.value) ? p.value[series] : undefined)) +} + diff --git a/typescript/Frontend/src/dataCache/dataCache.ts b/typescript/Frontend/src/dataCache/dataCache.ts new file mode 100644 index 000000000..71f230a99 --- /dev/null +++ b/typescript/Frontend/src/dataCache/dataCache.ts @@ -0,0 +1,169 @@ +/* eslint-disable no-mixed-operators */ +import {TimeSpan, UnixTime} from "./time"; +import {Observable, Subject} from "rxjs"; +import {SkipList} from "./skipList/skipList"; +import {createDispatchQueue} from "./promiseQueue"; +import {SkipListNode} from "./skipList/skipListNode"; +import {RecordSeries} from "./data"; +import { Maybe, isUndefined } from "./utils/maybe"; + + +export const FetchResult = +{ + notAvailable : "N/A", + tryLater : "Try Later" +} as const + +export type FetchResult = + | T + | typeof FetchResult.notAvailable + | typeof FetchResult.tryLater + +function reverseBits(x : number): number +{ + // https://stackoverflow.com/a/60227327/141397 + + x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1; + x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2; + x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4; + x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8; + x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16; + + return x >>> 0; +} + + +export default class DataCache> +{ + private readonly cache: SkipList> = new SkipList>() + private readonly resolution: TimeSpan; + + readonly _fetch: (t: UnixTime) => Promise>; + + private readonly fetchQueue = createDispatchQueue(6) + private readonly fetching: Set = new Set() + + public readonly gotData: Observable; + + constructor(fetch: (t: UnixTime) => Promise>, resolution: TimeSpan) + { + this._fetch = fetch; + this.resolution = resolution; + this.gotData = new Subject() + } + + public prefetch(times: Array, clear = true) + { + if (clear) + { + this.fetching.clear() + this.fetchQueue.clear() + } + + const timesWithPriority = times.map((time, index) => ({time, priority: reverseBits(index)})) + timesWithPriority.sort((x, y) => x.priority - y.priority) + + for (let i = 0; i < timesWithPriority.length; i++) + { + const time = timesWithPriority[i].time.round(this.resolution) + const t = time.ticks; + + const node = this.cache.find(t); + if (node.index !== t) + this.fetchData(time); + } + } + + public get(timeStamp: UnixTime, fetch = true): Maybe + { + const time = timeStamp.round(this.resolution) + const t = time.ticks; + + const node = this.cache.find(t); + if (node.index === t) + return node.value + + if (fetch) + this.fetchData(time); + + return this.interpolate(node, t) + } + + public getSeries(sampleTimes: UnixTime[]): RecordSeries + { + this.prefetch(sampleTimes) + return sampleTimes.map(time => ({time, value: this.get(time, false)})) + } + + private interpolate(before: SkipListNode>, t: number): Maybe + { + const dataBefore = before.value + const after = before.next[0]; + const dataAfter = after.value + + if (isUndefined(dataBefore) && isUndefined(dataAfter)) + return undefined + + if (isUndefined(dataBefore)) + return dataAfter + + if (isUndefined(dataAfter)) + return dataBefore + + const p = t - before.index + const n = after.index - t + const pn = p + n + + let interpolated: Partial> = {} + + //What about string nodes? like Alarms + for (const k of Object.keys(dataBefore)) + { + interpolated[k] = (dataBefore[k] * n + dataAfter[k] * p) / pn + } + + return interpolated as T + } + + private fetchData(time: UnixTime) + { + const t = time.ticks; + + if (this.fetching.has(t)) // we are already fetching t + return + + const fetchTask = () => + { + const onSuccess = (data: FetchResult) => + { + if (data === FetchResult.tryLater) + { + console.warn(FetchResult.tryLater) + return + } + + const value = data === FetchResult.notAvailable ? undefined : data; + this.cache.insert(value, t) + } + + const onFailure = (_: unknown) => + { + console.error(time.ticks + " FAILED!") // should not happen + } + + const dispatch = () => + { + this.fetching.delete(time.ticks); + (this.gotData as Subject).next(time); + } + + return this._fetch(time) + .then(d => onSuccess(d), f => onFailure(f)) + .finally(() => dispatch()) + }; + + this.fetching.add(t) + this.fetchQueue.dispatch(() => fetchTask()); + } + +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/linq.ts b/typescript/Frontend/src/dataCache/linq.ts new file mode 100644 index 000000000..1d566f445 --- /dev/null +++ b/typescript/Frontend/src/dataCache/linq.ts @@ -0,0 +1,20 @@ +// 0. Import Module +import { initializeLinq, IEnumerable } from "linq-to-typescript" +// 1. Declare that the JS types implement the IEnumerable interface +declare global { + interface Array extends IEnumerable { } + interface Uint8Array extends IEnumerable { } + interface Uint8ClampedArray extends IEnumerable { } + interface Uint16Array extends IEnumerable { } + interface Uint32Array extends IEnumerable { } + interface Int8Array extends IEnumerable { } + interface Int16Array extends IEnumerable { } + interface Int32Array extends IEnumerable { } + interface Float32Array extends IEnumerable { } + interface Float64Array extends IEnumerable { } + interface Map extends IEnumerable<[K, V]> { } + interface Set extends IEnumerable { } + interface String extends IEnumerable { } +} +// 2. Bind Linq Functions to Array, Map, etc +initializeLinq() \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/observableUtils.ts b/typescript/Frontend/src/dataCache/observableUtils.ts new file mode 100644 index 000000000..2869e2d60 --- /dev/null +++ b/typescript/Frontend/src/dataCache/observableUtils.ts @@ -0,0 +1,35 @@ +import {map, MonoTypeOperatorFunction, Observable, tap} from "rxjs"; +import {fastHash} from "./utils"; + +type ConcatX = [ + ...T[0], ...T[1], ...T[2], ...T[3], ...T[4], + ...T[5], ...T[6], ...T[7], ...T[8], ...T[9], + ...T[10], ...T[11], ...T[12], ...T[13], ...T[14], + ...T[15], ...T[16], ...T[17], ...T[18], ...T[19] +]; +type Flatten = + ConcatX<[...{ [K in keyof T]: T[K] extends any[] ? T[K] : [T[K]] }, ...[][]]> + + +export function flatten() +{ + return function>(source: Observable) + { + return source.pipe + ( + map(a => a.flat() as Flatten) + ) + } +} + +type RecursiveObject = T extends object ? T : never; + +type Terminals = +{ + [Key in keyof TModel]: TModel[Key] extends RecursiveObject + ? Terminals + : T; +}; + + + diff --git a/typescript/Frontend/src/dataCache/promiseQueue.ts b/typescript/Frontend/src/dataCache/promiseQueue.ts new file mode 100644 index 000000000..fd2b6bc0c --- /dev/null +++ b/typescript/Frontend/src/dataCache/promiseQueue.ts @@ -0,0 +1,51 @@ + + +export function createDispatchQueue(maxInflight: number, debug = false): { dispatch: (task: () => Promise) => number; clear: () => void } +{ + const queue: Array<() => Promise> = [] + + let inflight = 0; + + function done() + { + inflight-- + + if (debug && inflight + queue.length === 0) + console.log("queue empty") + + if (inflight < maxInflight && queue.length > 0) + { + const task = queue.pop()! + inflight++ + task().finally(() => done()) + } + } + + function dispatch(task: () => Promise) : number + { + if (inflight < maxInflight) + { + inflight++; + task().finally(() => done()) + } + else + { + if (debug && queue.length === 0) + console.log("queue in use") + + queue.push(task) + } + + return queue.length + } + + function clear() + { + // https://stackoverflow.com/questions/1232040/how-do-i-empty-an-array-in-javascript + queue.length = 0 + if (debug) + console.log("queue cleared") + } + + return {dispatch, clear} +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/skipList/skipList.ts b/typescript/Frontend/src/dataCache/skipList/skipList.ts new file mode 100644 index 000000000..24187502d --- /dev/null +++ b/typescript/Frontend/src/dataCache/skipList/skipList.ts @@ -0,0 +1,80 @@ +import {find, findPath, insert, Path, SkipListNode} from "./skipListNode"; + +export class SkipList +{ + public readonly head: SkipListNode; + public readonly tail: SkipListNode; + + private readonly nLevels: number; + + private _length = 0 + + constructor(nLevels: number = 20) + { + // TODO: auto-levels + + this.tail = + { + index: Number.MAX_VALUE, + next: [], + value: undefined! + }; + + this.head = + { + index: Number.MIN_VALUE, + next: Array(nLevels).fill(this.tail), + value: undefined! + }; + + this.nLevels = nLevels + } + + public find(index: number, startNode = this.head, endNode = this.tail): SkipListNode + { + return find(index, startNode, endNode) + } + + private findPath(index: number, startNode = this.head, endNode = this.tail): Path + { + return findPath(index, startNode, endNode) + } + + public insert(value: T, index: number): SkipListNode + { + const path = this.findPath(index) + const node = path[0]; + + if (node.index === index) // overwrite + { + node.value = value + return node + } + + const nodeToInsert = {value, index, next: []} as SkipListNode + + const rnd = (Math.random() * (1 << this.nLevels)) << 0; + + for (let level = 0; level < this.nLevels; level++) + { + insert(nodeToInsert, path[level], level) + + if ((rnd & (1 << level)) === 0) + break + } + + this._length += 1 + + return nodeToInsert; + } + + get length(): number + { + return this._length; + } + + // public remove(index: number): void + // { + // // TODO + // } +} diff --git a/typescript/Frontend/src/dataCache/skipList/skipListNode.ts b/typescript/Frontend/src/dataCache/skipList/skipListNode.ts new file mode 100644 index 000000000..d523c6de2 --- /dev/null +++ b/typescript/Frontend/src/dataCache/skipList/skipListNode.ts @@ -0,0 +1,52 @@ +import {asMutableArray} from "../types"; + +export type Next = { readonly next: ReadonlyArray> } +export type Index = { readonly index: number } +export type Indexed = Index & { value: T } +export type SkipListNode = Next & Indexed +export type Path = SkipListNode[]; + +export function find(index: number, startNode: SkipListNode, endNode: SkipListNode): SkipListNode +{ + let node = startNode + + for (let level = startNode.next.length - 1; level >= 0; level--) + node = findOnLevel(index, node, endNode, level) + + return node +} + +export function findOnLevel(index: number, startNode: SkipListNode, endNode: SkipListNode, level: number): SkipListNode +{ + let node: SkipListNode = startNode + + while (true) + { + const next = node.next[level] + + if (index < next.index || endNode.index < next.index) + return node + + node = next + } +} + +export function findPath(index: number, startNode: SkipListNode, endNode: SkipListNode): Path +{ + const path = Array(startNode.next.length - 1) + let node = startNode + + for (let level = startNode.next.length - 1; level >= 0; level--) + { + node = findOnLevel(index, node, endNode, level) + path[level] = node + } + + return path +} + +export function insert(nodeToInsert: SkipListNode, after: SkipListNode, onLevel: number): void +{ + asMutableArray(nodeToInsert.next)[onLevel] = after.next[onLevel] + asMutableArray(after.next)[onLevel] = nodeToInsert +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/stringUtils.ts b/typescript/Frontend/src/dataCache/stringUtils.ts new file mode 100644 index 000000000..e9cbaca4a --- /dev/null +++ b/typescript/Frontend/src/dataCache/stringUtils.ts @@ -0,0 +1,5 @@ +export function trim(str: string, string: string = " "): string +{ + const pattern = '^[' + string + ']*(.*?)[' + string + ']*$'; + return str.replace(new RegExp(pattern), '$1') +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/time.ts b/typescript/Frontend/src/dataCache/time.ts new file mode 100644 index 000000000..43b8501c5 --- /dev/null +++ b/typescript/Frontend/src/dataCache/time.ts @@ -0,0 +1,302 @@ +import {trim} from "./stringUtils"; + +export class UnixTime +{ + private constructor(readonly ticks: number) + { + } + + public static readonly Epoch = new UnixTime(0) + + public static now(): UnixTime + { + return UnixTime.fromTicks(Date.now() / 1000) + } + + public static fromDate(date: Date): UnixTime + { + return UnixTime.fromTicks(date.getTime() / 1000) + } + + public toDate(): Date + { + return new Date(this.ticks * 1000) + } + + public static fromTicks(ticks: number): UnixTime + { + return new UnixTime(ticks) + } + + public later(timeSpan: TimeSpan): UnixTime + { + return new UnixTime(this.ticks + timeSpan.ticks) + } + + public move(ticks: number): UnixTime + { + return new UnixTime(this.ticks + ticks) + } + + public earlier(timeSpan: TimeSpan): UnixTime + { + return new UnixTime(this.ticks - timeSpan.ticks) + } + + public isEarlierThan(time: UnixTime): boolean + { + return this.ticks < time.ticks + } + + public isEarlierThanOrEqual(time: UnixTime): boolean + { + return this.ticks <= time.ticks + } + + public isLaterThan(time: UnixTime): boolean + { + return this.ticks > time.ticks + } + + public isLaterThanOrEqual(time: UnixTime): boolean + { + return this.ticks >= time.ticks + } + + public isEqual(time: UnixTime): boolean + { + return this.ticks === time.ticks + } + + public isInTheFuture(): boolean + { + return this.isLaterThan(UnixTime.now()) + } + + public isInThePast(): boolean + { + return this.ticks < UnixTime.now().ticks + } + + public round(ticks:number) : UnixTime + public round(duration: TimeSpan) : UnixTime + public round(durationOrTicks: TimeSpan | number) : UnixTime + { + const ticks = (typeof durationOrTicks === "number") ? durationOrTicks : durationOrTicks.ticks + + return new UnixTime(Math.round(this.ticks / ticks) * ticks) + } + + public rangeTo(time: UnixTime): TimeRange + { + return TimeRange.fromTimes(this, time); + } + + public rangeBefore(timeSpan: TimeSpan): TimeRange + { + return TimeRange.fromTimes(this.earlier(timeSpan), this); + } + + public rangeAfter(timeSpan: TimeSpan): TimeRange + { + return TimeRange.fromTimes(this, this.later(timeSpan)); + } + + public toString() : string + { + return this.ticks.toString() + } +} + + +export class TimeSpan +{ + private constructor(readonly ticks: number) {} + + get milliSeconds(): number { return this.ticks * 1000 } + get seconds() : number { return this.ticks } + get minutes() : number { return this.ticks / 60 } + get hours() : number { return this.minutes / 60 } + get days() : number { return this.hours / 24 } + get weeks() : number { return this.days / 7 } + + public static fromTicks (t: number): TimeSpan { return new TimeSpan(t) } + public static fromSeconds(t: number): TimeSpan { return TimeSpan.fromTicks(t) } + public static fromMinutes(t: number): TimeSpan { return TimeSpan.fromSeconds(t*60) } + public static fromHours (t: number): TimeSpan { return TimeSpan.fromMinutes(t*60) } + public static fromDays (t: number): TimeSpan { return TimeSpan.fromHours(t*24) } + public static fromWeeks (t: number): TimeSpan { return TimeSpan.fromDays(t*7) } + + public static span(from: UnixTime, to: UnixTime) : TimeSpan + { + return TimeSpan.fromTicks(Math.abs(to.ticks - from.ticks)) + } + + public add(timeSpan: TimeSpan) : TimeSpan + { + return TimeSpan.fromTicks(this.ticks + timeSpan.ticks) + } + + public subtract(timeSpan: TimeSpan) : TimeSpan + { + return TimeSpan.fromTicks(this.ticks - timeSpan.ticks) + } + + public divide(n: number) : TimeSpan + { + if (n <= 0) + throw 'n must be positive'; + + return TimeSpan.fromTicks(this.ticks/n) + } + + public multiply(n: number) : TimeSpan + { + if (n < 0) + throw 'n cannot be negative'; + + return TimeSpan.fromTicks(this.ticks * n) + } + + public round(ticks:number) : TimeSpan + public round(duration: TimeSpan) : TimeSpan + public round(durationOrTicks: TimeSpan | number) : TimeSpan + { + const ticks = (typeof durationOrTicks === "number") + ? durationOrTicks + : durationOrTicks.ticks + + return TimeSpan.fromTicks(Math.round(this.ticks / ticks) * ticks) + } + + + public toString() : string + { + let dt = 60*60*24*7 + + let ticks = this.ticks; + + if (ticks === 0) + return "0s" + + ticks = Math.abs(ticks) + + const nWeeks = Math.floor(ticks / dt) + ticks -= nWeeks * dt + + dt /= 7 + const nDays = Math.floor(ticks / dt) + ticks -= nDays * dt + + dt /= 24 + const nHours = Math.floor(ticks / dt) + ticks -= nHours * dt + + dt /= 60 + const nMinutes = Math.floor(ticks / dt) + ticks -= nMinutes * dt + + dt /= 60 + const nSeconds = Math.floor(ticks / dt) + + let s = "" + + if (nWeeks > 0) s += nWeeks .toString() + "w " + if (nDays > 0) s += nDays .toString() + "d " + if (nHours > 0) s += nHours .toString() + "h " + if (nMinutes > 0) s += nMinutes.toString() + "m " + if (nSeconds > 0) s += nSeconds.toString() + "s" + + return trim(s); + } +} + +export class TimeRange +{ + private constructor(private readonly from: number, private readonly to: number) + { + } + + public get start(): UnixTime + { + return UnixTime.fromTicks(this.from) + } + + public get mid(): UnixTime + { + return UnixTime.fromTicks((this.from + this.to) / 2) + } + + + public get end(): UnixTime + { + return UnixTime.fromTicks(this.to) + } + + public get duration(): TimeSpan + { + return TimeSpan.fromTicks(this.to - this.from) + } + + public static fromTimes(from: UnixTime, to: UnixTime): TimeRange + { + return from.isLaterThan(to) + ? new TimeRange(to.ticks, from.ticks) + : new TimeRange(from.ticks, to.ticks) + } + + public isInside(time: number) : boolean; + public isInside(time: UnixTime) : boolean; + public isInside(time: UnixTime | number) + { + const t = time instanceof UnixTime ? time.ticks : time + + return t >= this.from && t < this.to + } + + public sample(period: TimeSpan): UnixTime[] + { + const samples = [] + + for (let t = this.from; t < this.to; t += period.ticks) + samples.push(UnixTime.fromTicks(t)); + + return samples + } + + public subdivide(n: number) : TimeRange[] + { + if (n <= 0) + throw 'n must be positive'; + + const period = TimeSpan.fromTicks(this.duration.ticks / n); + if (period === this.duration) + return [this]; + + const samples = this.sample(period); + + const ranges : TimeRange[] = [] + + for (let i = 0; i < samples.length;) + ranges.push(TimeRange.fromTimes(samples[i], samples[++i])) + + return ranges + } + + + public earlier(dt: TimeSpan) : TimeRange + { + return new TimeRange(this.from - dt.ticks, this.to - dt.ticks) + } + + public later(dt: TimeSpan) : TimeRange + { + return new TimeRange(this.from + dt.ticks, this.to + dt.ticks) + } + + public move(ticks: number) : TimeRange + { + return new TimeRange(this.from + ticks, this.to + ticks) + } +} + diff --git a/typescript/Frontend/src/dataCache/types.ts b/typescript/Frontend/src/dataCache/types.ts new file mode 100644 index 000000000..665fd3663 --- /dev/null +++ b/typescript/Frontend/src/dataCache/types.ts @@ -0,0 +1,21 @@ +import {UnixTime} from "./time"; + +export type Timestamped = { time: UnixTime, value: T } + +export type Pair = [T1, T2] + +export type Position = { readonly x: number, readonly y: number } +export type Direction = { readonly dx: number, readonly dy: number } +export type Size = { readonly width: number, readonly height: number } +export type Rect = Position & Size + +export type Mutable = { -readonly [P in keyof T]: T[P] }; + +export type FieldKey = { [P in keyof T]: T[P] extends (...args: any) => any ? never : P }[keyof T]; +export type AllFields = Pick>; +export type SomeFields = Partial> +export const asMutable = (t: T) => (t as Mutable); +export const asMutableArray = (t: ReadonlyArray) => (t as Array); +export const cast = (t: unknown) => (t as T); + +export type Rename = Pick> & { [P in N]: T[K] } \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/utils.ts b/typescript/Frontend/src/dataCache/utils.ts new file mode 100644 index 000000000..bbf0350b1 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils.ts @@ -0,0 +1,119 @@ +import {IEnumerable} from "linq-to-typescript"; +import { isDefined } from "./utils/maybe"; + +//export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never + +export type Nothing = Record +// eslint-disable-next-line @typescript-eslint/no-empty-function + + + +export function fastHash(str: string): number +{ + const signed = str + .split('') + .reduce((p, c) => ((p << 5) - p) + c.charCodeAt(0) | 0, 0); + + return Math.abs(signed); +} + + + + + +// export function flattenObject(obj: object) : object +// { +// const flattened = {} +// +// for (const key of Object.keys(obj)) +// { +// // @ts-ignore +// const value = obj[key] +// +// if (typeof value === 'object' && value !== null && !Array.isArray(value)) +// { +// Object.assign(flattened, flattenObject(value)) +// } +// else +// { +// // @ts-ignore +// flattened[key] = value +// } +// } +// +// return flattened +// } +//return function>(source: Observable) + + + +export function* pairwise(iterable: Iterable, init?: T): Generator<[T, T]> +{ + const it = iterable[Symbol.iterator]() + + let first : T; + + if (isDefined(init)) + { + first = init + } + else + { + const f = it.next() + if (f.done) + return + + first = f.value + } + + let second = it.next() + + while(!second.done) + { + yield [first, second.value] + first = second.value + second = it.next() + } +} + +export function arraysEqual(a: Array, b: Array) +{ + if (a === b) return true; + if (a.length !== b.length) return false; + + for (let i = 0; i < a.length; ++i) + { + if (a[i] !== b[i]) return false; + } + + return true; +} + +export function mod(a:number, b:number) +{ + return ((a % b) + b) % b; +} + +export function clamp(a: number, min: number, max: number) +{ + return a > max ? max + : a < min ? min + : a +} + + +export function isDST(d : Date) +{ + const jan = new Date(d.getFullYear(), 0, 1).getTimezoneOffset(); + const jul = new Date(d.getFullYear(), 6, 1).getTimezoneOffset(); + + return Math.max(jan, jul) != d.getTimezoneOffset(); +} + +export function Transpose(src: IEnumerable>): IEnumerable> +{ + return src + .selectMany(line => line.select((element, column) => ({element, column}))) + .groupBy(i => i.column) + .select(g => g.select(e => e.element)); +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/utils/fileSystem.ts b/typescript/Frontend/src/dataCache/utils/fileSystem.ts new file mode 100644 index 000000000..f9975e8d8 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/fileSystem.ts @@ -0,0 +1,46 @@ +import fs from "fs"; + +export function doesFileExist(path: string): boolean +{ + try + { + fs.accessSync(path, fs.constants.F_OK); + return true; + } + catch (e) + { + return false; + } +} + +export function doesDirExist(path: string): boolean +{ + try + { + fs.readdirSync(path) + return true; + } + catch (e) + { + return false; + } +} + +export function readJsonFile(path: string) +{ + const data = fs.readFileSync(path, "utf-8") + return JSON.parse(data) as T +} + +export function writeJsonFile(path: string, contents: T) +{ + const data = JSON.stringify(contents) + return fs.writeFileSync(path, data, "utf-8") +} + +export function writeJsonFilePretty(path: string, contents: T) +{ + const data = JSON.stringify(contents,undefined,2) + return fs.writeFileSync(path, data, "utf-8") +} + diff --git a/typescript/Frontend/src/dataCache/utils/httpResponse.ts b/typescript/Frontend/src/dataCache/utils/httpResponse.ts new file mode 100644 index 000000000..c0fa1ee1e --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/httpResponse.ts @@ -0,0 +1,127 @@ + +import MimeType from "./mime"; +import fs from "fs"; +import http, {IncomingMessage, ServerResponse} from "http"; +import { Maybe } from "yup"; +import { getLogger } from "./logging"; +import { isDefined, isUndefined } from "./maybe"; +import { Dictionary } from "./utilityTypes"; +import { promisify } from "util"; +import { entries } from "./utils"; + + +const log = getLogger("HTTP") +const readFile = promisify(fs.readFile) + +export type HttpResponse = { + body: Maybe + headers : Dictionary + statusCode: number +} + + +function contentTypeHeader(mimeType: string) +{ + return {'Content-type': mimeType}; +} + +function forbidden(message = "403 : forbidden", headers: Dictionary = {}): HttpResponse +{ + return text(message, 403, headers) +} + +function notFound(message ="404 : not found", headers: Dictionary = {}): HttpResponse +{ + return text(message, 404, headers) +} + +function text(text: string, statusCode = 200, headers: Dictionary = {}): HttpResponse +{ + return { + statusCode: statusCode, + headers : {...headers, ...contentTypeHeader('text/plain')}, + body : text + }; +} + +function json(json: Dictionary, + replacer?: (k: string, v: unknown) => unknown, + headers: Dictionary = {}): HttpResponse +{ + return { + statusCode: 200, + headers : {...headers, 'Content-type': MimeType.json}, + body: JSON.stringify(json, replacer) + } +} + +function empty(headers: Dictionary = {}): HttpResponse +{ + return { + statusCode: 200, + headers, + body: undefined + } +} + +function ok(body: Maybe, headers: Dictionary = {}): HttpResponse +{ + return { + statusCode: 200, + headers, + body + } +} + +async function file(localRootPath: string, urlPath: string, headers: Dictionary = {}, defaultPath = "/"): Promise +{ + if (urlPath.includes('..')) + return HTTP.forbidden(); + + const localPath = localRootPath + (urlPath === "/" ? defaultPath : urlPath); + + const body = await readFile(localPath).catch(_ => undefined) + + if (isUndefined(body)) + return HTTP.notFound(); + + if (!('Content-type' in headers)) + { + headers = {...headers, ...contentTypeHeader(MimeType.guessFromPath(localPath))} + } + + return HTTP.ok(body, headers) +} + +function createServer(serve: (request: IncomingMessage) => Promise) +{ + async function wrapServe(request: IncomingMessage, response: ServerResponse): Promise + { + const r = await serve(request) + + entries(r.headers).forEach(([k, v]) => response.setHeader(k, v as any)) + + response.statusCode = r.statusCode + + if (isDefined(r.body)) + response.end(r.body) + else + response.end() + } + + return http.createServer(wrapServe); +} + +const HTTP = +{ + contentTypeHeader, + forbidden, + notFound, + ok, + json, + empty, + file, + createServer +} + +export default HTTP; diff --git a/typescript/Frontend/src/dataCache/utils/linq.ts b/typescript/Frontend/src/dataCache/utils/linq.ts new file mode 100644 index 000000000..e68de7db4 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/linq.ts @@ -0,0 +1,21 @@ +// 0. Import Module + +import {IEnumerable, initializeLinq} from "linq-to-typescript" +// 1. Declare that the JS types implement the IEnumerable interface +declare global { + interface Array extends IEnumerable { } + interface Uint8Array extends IEnumerable { } + interface Uint8ClampedArray extends IEnumerable { } + interface Uint16Array extends IEnumerable { } + interface Uint32Array extends IEnumerable { } + interface Int8Array extends IEnumerable { } + interface Int16Array extends IEnumerable { } + interface Int32Array extends IEnumerable { } + interface Float32Array extends IEnumerable { } + interface Float64Array extends IEnumerable { } + interface Map extends IEnumerable<[K, V]> { } + interface Set extends IEnumerable { } + interface String extends IEnumerable { } +} +// 2. Bind Linq Functions to Array, Map, etc +initializeLinq() \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/utils/logging.ts b/typescript/Frontend/src/dataCache/utils/logging.ts new file mode 100644 index 000000000..ec5bcd1f0 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/logging.ts @@ -0,0 +1,11 @@ +let subsystemPadding = 0 + +export function getLogger(subsystem: string): (msg: string) => void +{ + subsystemPadding = Math.max(subsystem.length, subsystemPadding) + + // eslint-disable-next-line no-console + return (msg: string) => console.log(`${new Date().toLocaleString()} | ${(subsystem.padEnd(subsystemPadding))} | ${msg}`); +} + + diff --git a/typescript/Frontend/src/dataCache/utils/match.ts b/typescript/Frontend/src/dataCache/utils/match.ts new file mode 100644 index 000000000..71106e986 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/match.ts @@ -0,0 +1,176 @@ +import {Dictionary, Func, Normalize1} from "./utilityTypes"; +import {isUndefined} from "./maybe"; +import {UnionToIntersection} from "simplytyped"; +import {current} from "immer"; +import { keys, valueToFunction } from "./utils"; + +// Type Compatibility +// https://www.typescriptlang.org/docs/handbook/type-compatibility.html + + +//TODO: review +export type IsUnionCase = + T extends Dictionary + ? [UnionToIntersection] extends [keyof T] + ? [keyof T] extends [UnionToIntersection] + ? true + : false + : false + : false + +//TODO: review +export type IsTaggedUnion = true extends UnionToIntersection> ? Dictionary : never + +export type Unwrap> = UnionToIntersection[keyof UnionToIntersection] ; + +export function update>(u: U, e: Partial>) +{ + const v = u as UnionToIntersection + const o = current(v) + + const ks = keys(v) + + if (ks.length != 1) + throw new Error("not a valid union case") + + const tag = ks[0] + + const before = v[tag]; + const before2 = current(before); + + + const newVar = {...before, ...e}; + v[tag] = newVar +} + +export function unwrap>(u: U) : Normalize1> +{ + const v = u as UnionToIntersection + + const ks = keys(v) + + if (ks.length != 1) + throw new Error("not a valid union case") + + const key = ks[0] + return v[key] as any; +} + +export function base>(u: U): Normalize1 & Partial>>> +{ + return unwrap(u) as Normalize1 & Partial>>> +} + +export function tag>(u: U) : keyof UnionToIntersection +{ + const v = u as UnionToIntersection + + const ks = keys(v) + + if (ks.length != 1) + throw new Error("not a valid union case") + + return ks[0] +} + +export function tagsEqual>(u: U, v: U) : v is U +{ + return tag(u) === tag(v) +} + + +type MapFuncs = { [k in keyof UnionToIntersection]: Func[k]> } +type OtherwiseKeys = Exclude, keyof M>; + +type OtherwiseArg = { + [k in keyof UnionToIntersection]: Record[k]> +}[OtherwiseKeys] + +type OtherwiseFunc>, R> = Func extends never ? unknown : OtherwiseArg, R>; + + +export function match, M extends Partial>, R>(uCase: U, matchFuncs: M, otherwise: OtherwiseFunc | R):{ [k in keyof M]: M[k] extends Func ? O : never }[keyof M] | R +{ + const otw = valueToFunction(otherwise) + + const c = uCase as UnionToIntersection + + const ks = keys(c) + + if (ks.length != 1) + return otw(c) + + const key = ks[0] + const arg = c[key] + + const matchFunc = matchFuncs[key] + + if (isUndefined(matchFunc)) + return otw(c); + + return matchFunc(arg as any) as any; +} + + +export function dispatch>() +{ + // type Intersection = UnionToIntersection; + // + // type MapFuncs = { [k in keyof Intersection]: Func } + // type OtherwiseKeys = Exclude; + // + // type OtherwiseArg = { + // [k in keyof Intersection]: Record + // }[OtherwiseKeys] + // + // type OtherwiseFunc, R> = Func extends never ? unknown : OtherwiseArg, R>; + + return >, R>(matchFuncs: M, otherwise: OtherwiseFunc | R) => + { + const otw = valueToFunction(otherwise) + + return (uCase: U): { [k in keyof M]: M[k] extends Func ? O : never }[keyof M] | R => + { + const c = uCase as UnionToIntersection + + const ks = keys(c) + + if (ks.length != 1) + return otw(c) + + const key = ks[0] + const arg = c[key] + + const matchFunc = matchFuncs[key] + + if (isUndefined(matchFunc)) + return otw(c); + + return matchFunc(arg as any) as any; + } + }; +} + + +export function concat, T extends Dictionary>(rec: R, t:T) +{ + + const result = {} as { + [k in keyof UnionToIntersection]: Record[k] & T> + }[keyof UnionToIntersection] + + for (const k in rec) + { + + // @ts-ignore + result[k] = { ...rec[k], ...t} + } + + return result +} + + + + + + diff --git a/typescript/Frontend/src/dataCache/utils/maybe.ts b/typescript/Frontend/src/dataCache/utils/maybe.ts new file mode 100644 index 000000000..7ab47fdb9 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/maybe.ts @@ -0,0 +1,16 @@ +export type Maybe = T | undefined | null; + +export function isDefined(e: Maybe): e is T +{ + return e != undefined // != by design to include null +} + +export function isUndefined(e: Maybe): e is undefined | null +{ + return e == undefined // == by design to include null +} + +export function toArray(e: Maybe): T[] +{ + return isDefined(e) ? [e] : [] +} diff --git a/typescript/Frontend/src/dataCache/utils/milliseconds.ts b/typescript/Frontend/src/dataCache/utils/milliseconds.ts new file mode 100644 index 000000000..7d0daf94a --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/milliseconds.ts @@ -0,0 +1,18 @@ + +export type Milliseconds = number + +export const Milliseconds = +{ + fromSeconds: (count: number): Milliseconds => count * 1000, + fromMinutes: (count: number): Milliseconds => count * 1000 * 60, + fromHours : (count: number): Milliseconds => count * 1000 * 60 * 60, + fromDays : (count: number): Milliseconds => count * 1000 * 60 * 60 * 24, + fromWeeks : (count: number): Milliseconds => count * 1000 * 60 * 60 * 24 * 7, + + toSeconds: (count: Milliseconds): number => count / 1000, + toMinutes: (count: Milliseconds): number => count / 1000 / 60, + toHours : (count: Milliseconds): number => count / 1000 / 60 / 60, + toDays : (count: Milliseconds): number => count / 1000 / 60 / 60 / 24, + toWeeks : (count: Milliseconds): number => count / 1000 / 60 / 60 / 24 / 7, +} as const + diff --git a/typescript/Frontend/src/dataCache/utils/mime.ts b/typescript/Frontend/src/dataCache/utils/mime.ts new file mode 100644 index 000000000..87dc60150 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/mime.ts @@ -0,0 +1,32 @@ +import PlatformPath from "path"; +import { isDefined } from "./maybe"; + + +function guessFromPath(path: string) : string +{ + const ext = PlatformPath.parse(path).ext?.substring(1) as keyof typeof MimeType; + + const mimeType = MimeType[ext] + + return isDefined(mimeType) && typeof mimeType === "string" + ? mimeType + : 'application/octet-stream' +} + +const MimeType = +{ + ico : 'image/x-icon', + html: 'text/html; charset=UTF-8', + js : 'text/javascript', + json: 'application/json; charset=UTF-8', + css : 'text/css; charset=UTF-8', + png : 'image/png', + jpg : 'image/jpeg', + wav : 'audio/wav', + mp3 : 'audio/mpeg', + svg : 'image/svg+xml; charset=UTF-8', + pdf : 'application/pdf', + guessFromPath +}; + +export default MimeType \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/utils/path.ts b/typescript/Frontend/src/dataCache/utils/path.ts new file mode 100644 index 000000000..6d82809b2 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/path.ts @@ -0,0 +1,81 @@ +import {isUndefined} from "./maybe"; +import {from, IEnumerable} from "linq-to-typescript"; +import {isBoolean, isNumber, isPlainObject, isString} from "./runtimeTypeChecking"; + +function getAt(root: any, path: (keyof any)[]) +{ + return path.reduce((v, p) => v[p], root) +} + + +function iterate(root: unknown): IEnumerable<{ path: string[]; node: unknown }> +{ + if (isUndefined(root)) + return [] + + return from(iterate(root)) + + function* iterate(node: unknown, path: string[] = []): Generator<{ path: string[]; node: unknown }> + { + if (isString(node) || isNumber(node) || isBoolean(node)) + yield {path, node} + else if (isPlainObject(node)) + for (const key in node) + { + path.push(key) + yield {path, node} + yield* iterate(node[key], path) + path.pop() + } + } +} + +function iterateLeafs(root: unknown): IEnumerable<{ path: string[]; node: unknown }> +{ + if (isUndefined(root)) + return [] + + return from(iterate(root)) + + function* iterate(node: unknown, path: string[] = []): Generator<{ path: string[]; node: unknown }> + { + if (isString(node) || isNumber(node) || isBoolean(node)) + yield {path, node} + else if (isPlainObject(node)) + for (const key in node) + { + path.push(key) + yield* iterate(node[key], path) + path.pop() + } + } +} + + +function iterateBranches(root: unknown): IEnumerable<{ path: string[]; node: unknown }> +{ + if (isUndefined(root)) + return [] + + return from(iterate(root)) + + function* iterate(node: unknown, path: string[] = []): Generator<{ path: string[]; node: unknown }> + { + if (isPlainObject(node)) + for (const key in node) + { + path.push(key) + yield {path, node} + yield* iterate(node[key], path) + path.pop() + } + } +} + +export const Path = +{ + iterate, + iterateLeafs, + iterateBranches, + getAt +} as const diff --git a/typescript/Frontend/src/dataCache/utils/requestUtils.ts b/typescript/Frontend/src/dataCache/utils/requestUtils.ts new file mode 100644 index 000000000..8757cb3cc --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/requestUtils.ts @@ -0,0 +1,45 @@ +import {IncomingMessage} from "http"; +import {firstValueFrom, map, Observable, startWith, toArray} from "rxjs"; + +export function observeData(request: IncomingMessage, maxLength: number = Number.POSITIVE_INFINITY): Observable +{ + let nBytes = 0; + + return new Observable(subscriber => + { + request.on('end', () => subscriber.complete()); + request.on('data', (data: Uint8Array) => + { + nBytes += data.byteLength + + if (nBytes <= maxLength) + subscriber.next(data); + else + { + const error = `too much data: expected ${maxLength} bytes or less, got ${nBytes} bytes.`; + subscriber.error(error); + request.destroy(new Error(error)) + } + }); + }); +} + +export async function getRequestJson(request: IncomingMessage, maxLength = 500000): Promise +{ + const data = await getData(request, maxLength) + return JSON.parse(data.toString()) +} + +const noData = new Uint8Array(0); + +export function getData(request: IncomingMessage, maxLength: number = Number.POSITIVE_INFINITY): Promise +{ + const data = observeData(request, maxLength).pipe + ( + startWith(noData), + toArray(), + map(b => Buffer.concat(b)), // cannot inline! + ) + + return firstValueFrom(data); +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/utils/runtimeTypeChecking.ts b/typescript/Frontend/src/dataCache/utils/runtimeTypeChecking.ts new file mode 100644 index 000000000..c24de1606 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/runtimeTypeChecking.ts @@ -0,0 +1,66 @@ +export type TypeCode = + | "undefined" + | "object" + | "boolean" + | "number" + | "string" + | "function" + | "symbol" + | "bigint"; + +export type PlainObject = Record + +export function isObject(thing: unknown) : thing is object +{ + return typeof thing === "object" +} + +export function isDate(thing: unknown) : thing is Date +{ + return thing instanceof Date +} + +export function isPlainObject(thing: unknown) : thing is PlainObject +{ + return isObject(thing) && !isDate(thing) +} + +export function isArray(thing: unknown) : thing is Array +{ + return Array.isArray(thing) +} + +export function isNumber(thing: unknown) : thing is number +{ + return typeof thing === "number" +} + +export function isBoolean(thing: unknown) : thing is boolean +{ + return typeof thing === "boolean" +} + +export function isString(thing: unknown) : thing is string +{ + return typeof thing === "string" +} + +// export function isFunction(thing: unknown): thing is (...args: unknown[]) => unknown +// { +// return typeof thing === "function" +// } + +export function isFunction(obj: unknown): obj is (...args: any[]) => any +{ + return obj instanceof Function; +} + +export function isSymbol(thing: unknown) : thing is symbol +{ + return typeof thing === "symbol" +} + +export function isBigint(thing: unknown) : thing is bigint +{ + return typeof thing === "bigint" +} diff --git a/typescript/Frontend/src/dataCache/utils/stringUtils.ts b/typescript/Frontend/src/dataCache/utils/stringUtils.ts new file mode 100644 index 000000000..b377c01fe --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/stringUtils.ts @@ -0,0 +1,22 @@ +export function toLowercaseAscii(string: string) +{ + return string + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, "") + .toLowerCase(); +} + +export function containsIgnoringAccents(string: string, substring: string) +{ + if (substring === "") return true; + if (string === "") return false; + + substring = "" + substring; + + if (substring.length > string.length) + return false; + + return toLowercaseAscii(string).includes(toLowercaseAscii(substring)); +} + + diff --git a/typescript/Frontend/src/dataCache/utils/tree.ts b/typescript/Frontend/src/dataCache/utils/tree.ts new file mode 100644 index 000000000..88a83f785 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/tree.ts @@ -0,0 +1,71 @@ +import {from, IEnumerable} from "linq-to-typescript"; +import {isDefined, isUndefined, Maybe} from "./maybe"; + + +export function Tree(getChildren: (t: T) => IEnumerable) +{ + function iterate(root: Maybe): IEnumerable + { + if (isUndefined(root)) + return [] + + return from(iterateTree()) + + function* iterateTree() + { + const queue: T[] = [root!] + + do + { + const element = queue.shift()! + yield element + for (const child of getChildren(element)) + queue.push(child) + } + while (queue.length > 0) + } + } + + + function iterateWithPath(root: Maybe): IEnumerable + { + return isDefined(root) + ? from(iterateTreeWithPath()) + : []; + + function* iterateTreeWithPath() + { + const stack: Array> = [[root!]] + + while (true) + { + const head = stack[0]; + + if (head.length > 0) + { + yield stack + .select(l => l[0]) + .toArray() + + const children = getChildren(head[0]).toArray() + stack.unshift(children) + } + else + { + stack.shift() // remove empty array in front + if(stack.length > 0) + stack[0].shift() + else + break; + } + } + + } + } + + return { + iterate, + iterateWithPath + } as const +} + diff --git a/typescript/Frontend/src/dataCache/utils/type.ts b/typescript/Frontend/src/dataCache/utils/type.ts new file mode 100644 index 000000000..da775fb19 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/type.ts @@ -0,0 +1,115 @@ +export {} +// export type Type = +// | "number" +// | "object" +// | "string" +// | "never" +// | "any" +// | "unknown" +// | "undefined" +// | "boolean" +// | "bigint" +// | "symbol" +// | Property[] +// | Func +// +// export type Key = "string" | "number" | "symbol" +// +// export type Property = Func | +// { +// key: Key, +// type: Type, +// readonly? : boolean, +// nullable? : boolean +// } +// +// export type Arg = +// { +// name: string, +// type: Type, +// nullable? : boolean +// } +// +// export type Func = +// { +// args: Arg[], +// returnType: Type, +// } +// +// +// type X = Partial +// +// export function render(t: Type, indent = 0) +// { +// if (typeof t === "string") +// return t +// +// +// return "ERROR" +// } +// + + +type DeviceType = + | "Pv" + | "Load" + | "Battery" + | "Grid" + | "Inverter" + | "AcInToAcOut" + | "DcDc" + | "AcInBus" + | "AcOutBus" + | "DcBus" + | "Dc48Bus" // low voltage DC Bus, to be eliminated in later versions + + +type Phase = +{ + voltage : number // U, non-negative + current : number // I, sign depends on device type, see sign convention below +} + +type AcPhase = Phase & +{ + phi : number // [0,2pi) +} + +type Device = +{ + Type: DeviceType, + Name?: string, +} + +type Stack = +{ + Top? : Device[], // 0 to N + Right? : Device // 0 or 1 + Bottom? : Device[] // 0 to N + Disconnected?: boolean // not present = false +} + +/// A DC device must have a field denoting its DC connection +type DcDevice = Device & +{ + Dc : Phase +} + + +/// An AC device can have 1 to 3 AC phases +/// An AC device also needs a Frequency measurement +/// Total power can be obtained by summing the power of the phases +type AcDevice = Device & +{ + Ac: AcPhase[] + Frequency: number +} + + +/// A low voltage 48V DC device +/// Needed to distinguish the two sides of the DCDC +/// Will be dropped once we get HV batteries +type Dc48Device = Device & +{ + dc48 : Phase +} diff --git a/typescript/Frontend/src/dataCache/utils/utilityTypes.ts b/typescript/Frontend/src/dataCache/utils/utilityTypes.ts new file mode 100644 index 000000000..66f57ee96 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/utilityTypes.ts @@ -0,0 +1,52 @@ +import {UnionToIntersection} from "simplytyped"; + +export type Dictionary = Record +export type Nothing = Dictionary + +export type IsUnion = [T] extends [UnionToIntersection] ? false : true; + +export type UnionToDeepPartialIntersection = DeepPartial> +export type UnionToPartialIntersection = Partial> + +export type Func = (arg: T) => R +export type AsyncFunc = (arg: T) => Promise + +export type SyncAction = (arg: T) => void +export type AsyncAction = (arg: T) => Promise +export type Action = SyncAction | AsyncAction + +export type Lazy = () => T +export type Base64 = string +export type ValueOf = T[keyof T]; + +export type DeepPartial = T extends object ? { [P in keyof T]?: DeepPartial; } : T; +export type DeepMutable = { -readonly [P in keyof T]: DeepMutable }; +export type Mutable = { -readonly [P in keyof T]: T[P] }; +export type NumberLiteralToStringLiteral = T extends number ? `${T}` : T + +export type KeyedChildren = { children?: Dictionary } + +export type Union = { [S in K] : V} + +export type IntersectionToUnion = { [Prop in keyof T]: Record }[keyof T] // not sure if this is aptly named + +// helper to flatten (instantiate) types in editor popups +// eslint-disable-next-line @typescript-eslint/ban-types + +export type Normalize = T extends (...args: infer A) => infer R ? (...args: Normalize) => Normalize + : [T] extends [any] ? { [K in keyof T]: Normalize } + : never + +export type Normalize1 = T extends (...args: infer A) => infer R ? (...args: A) => R + : [T] extends [any] ? { [K in keyof T]: T[K] } + : T + +export type Normalize2 = T extends (...args: infer A) => infer R ? (...args: Normalize1) => Normalize1 + : [T] extends [any] ? { [K in keyof T]: Normalize1 } + : never + +export function mutable(t: T) +{ + return t as Mutable +} + diff --git a/typescript/Frontend/src/dataCache/utils/utils.ts b/typescript/Frontend/src/dataCache/utils/utils.ts new file mode 100644 index 000000000..8e922df8a --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/utils.ts @@ -0,0 +1,55 @@ + +import {IncomingMessage} from "http"; +import {from} from "linq-to-typescript"; +import { Maybe, isUndefined } from "./maybe"; +import { Dictionary, Func } from "./utilityTypes"; + +type StringValued = +{ + [Key in keyof T]: T[Key] extends number ? Maybe + : T[Key] extends string ? Maybe + : T[Key] extends boolean ? Maybe + : never +} + +export function getQueryParams(request: IncomingMessage): Maybe> +{ + if (isUndefined(request.url)) + return undefined + + const url = new URL(request.url, `https://${request.headers.host}/`); + + const query: Dictionary = {} + const urlSearchParams = new URLSearchParams(url.search); + + if (!from(urlSearchParams.entries()).any()) + return undefined + + for (const [key, value] of urlSearchParams.entries()) + query[key] = value; + + return query as StringValued; +} + +export function getPath(req: IncomingMessage) +{ + return new URL(req.url!, `https://${req.headers.host}/`).pathname; +} + +export function entries(t: T) +{ + return Object.entries(t as Dictionary) +} + +export function keys(t: T): (keyof T)[] +{ + return Object.keys(t as object) as (keyof T)[] +} + +export function valueToFunction(tr: Func | R) : Func +{ + if (typeof tr === "function") + return tr as Func + + return (_: T) => tr +} \ No newline at end of file diff --git a/typescript/Frontend/src/util/graph.util.tsx b/typescript/Frontend/src/util/graph.util.tsx new file mode 100644 index 000000000..8bbf435c8 --- /dev/null +++ b/typescript/Frontend/src/util/graph.util.tsx @@ -0,0 +1,68 @@ +import { Datum, TypedArray } from "plotly.js"; +import { RecordSeries } from "../dataCache/data"; +import { isDefined } from "../dataCache/utils/maybe"; + +export const mergeDeep = (...objects: any[]) => { + const isObject = (obj: GraphCoordinates) => obj && typeof obj === "object"; + return objects.reduce((prev, obj) => { + Object.keys(obj).forEach((key) => { + const pVal = prev[key]; + const oVal = obj[key]; + + if (Array.isArray(pVal) && Array.isArray(oVal)) { + prev[key] = pVal.concat(...oVal); + } else if (isObject(pVal) && isObject(oVal)) { + prev[key] = mergeDeep(pVal, oVal); + } else { + prev[key] = oVal; + } + }); + + return prev; + }, {} as GraphData); +}; + +export const transformToGraphData = (timeStampData: RecordSeries) => { + return timeStampData.reduce((acc, curr) => { + if (isDefined(curr.value)) { + const timeStampObj = Object.keys(curr.value).reduce( + (pathAcc, currPath) => { + if (currPath) { + return { + ...pathAcc, + [currPath]: { + x: [curr.time.ticks], + y: [curr.value ? curr.value[currPath] : 0], + }, + }; + } + return pathAcc; + }, + {} as GraphData + ); + return mergeDeep(acc, timeStampObj); + } + return acc; + }, {} as GraphData); +}; + +export interface GraphCoordinates { + x: Datum[] | Datum[][] | TypedArray; + y: Datum[] | Datum[][] | TypedArray; +} + +export interface GraphData { + [path: string]: GraphCoordinates; +} + +export const parseCsv = (text: string) => { + const y = text + .split(/\r?\n/) + .map((l) => l.split(";")) + .filter((fields) => !isNaN(parseFloat(fields[1]))); + + const x = y + .map((fields) => ({ [fields[0]]: parseFloat(fields[1]) })) + .reduce((acc, current) => ({ ...acc, ...current }), {}); + return x; +}; diff --git a/typescript/Frontend/tsconfig.json b/typescript/Frontend/tsconfig.json index f199ca8fd..423cb75a1 100644 --- a/typescript/Frontend/tsconfig.json +++ b/typescript/Frontend/tsconfig.json @@ -7,6 +7,7 @@ "esnext" ], "allowJs": true, + "downlevelIteration": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true,