diff --git a/containers/clusterManagement/index.tsx b/containers/clusterManagement/index.tsx index 22701ca8..ca035548 100644 --- a/containers/clusterManagement/index.tsx +++ b/containers/clusterManagement/index.tsx @@ -22,13 +22,11 @@ import { Container, Content, Description, Header, LearnMoreLink } from './cluste import { getClusterManagementColumns, getClusterState } from './columnDefinition'; export interface ClusterManagementProps { - apiUrl: string; useTelemetry: boolean; kubefirstVersion: string; } const ClusterManagement: FunctionComponent = ({ - apiUrl, kubefirstVersion, useTelemetry, }) => { @@ -62,7 +60,7 @@ const ClusterManagement: FunctionComponent = ({ }; const handleDeleteCluster = () => { - dispatch(deleteCluster({ apiUrl, clusterName: selectedCluster?.clusterName })).unwrap(); + dispatch(deleteCluster({ clusterName: selectedCluster?.clusterName })).unwrap(); handleGetClusters(); closeDeleteModal(); }; @@ -73,8 +71,8 @@ const ClusterManagement: FunctionComponent = ({ }; const handleGetClusters = useCallback(async (): Promise => { - await dispatch(getClusters({ apiUrl })); - }, [apiUrl, dispatch]); + await dispatch(getClusters()); + }, [dispatch]); const getClusterInterval = (params: ClusterRequestProps) => { return setInterval(async () => { @@ -85,7 +83,6 @@ const ClusterManagement: FunctionComponent = ({ useEffect(() => { if (isDeleting && !isDeleted && selectedCluster) { interval.current = getClusterInterval({ - apiUrl, clusterName: selectedCluster?.clusterName as string, }); handleGetClusters(); @@ -106,11 +103,11 @@ const ClusterManagement: FunctionComponent = ({ useEffect(() => { handleGetClusters(); - }, [apiUrl, dispatch, handleGetClusters]); + }, [dispatch, handleGetClusters]); useEffect(() => { - dispatch(setConfigValues({ isTelemetryEnabled: useTelemetry, apiUrl, kubefirstVersion })); - }, [dispatch, useTelemetry, apiUrl, kubefirstVersion]); + dispatch(setConfigValues({ isTelemetryEnabled: useTelemetry, kubefirstVersion })); + }, [dispatch, useTelemetry, kubefirstVersion]); return ( diff --git a/containers/header/index.tsx b/containers/header/index.tsx index d21a111f..6b268672 100644 --- a/containers/header/index.tsx +++ b/containers/header/index.tsx @@ -13,9 +13,8 @@ import { ClusterIndicator, ClusterMenu, Container } from './header.styled'; const Header: FunctionComponent = () => { const dispatch = useAppDispatch(); - const { apiUrl, clusters, selectedCluster } = useAppSelector(({ api, cluster, config }) => ({ + const { clusters, selectedCluster } = useAppSelector(({ api, cluster }) => ({ clusters: api.clusters, - apiUrl: config.apiUrl, selectedCluster: cluster.selectedCluster, })); @@ -32,10 +31,8 @@ const Header: FunctionComponent = () => { }; useEffect(() => { - if (apiUrl) { - dispatch(getClusters({ apiUrl })); - } - }, [apiUrl, dispatch]); + dispatch(getClusters()); + }, [dispatch]); useEffect(() => { if (clusters.length && !selectedCluster) { diff --git a/containers/provision/index.tsx b/containers/provision/index.tsx index b72a2429..c41337a6 100644 --- a/containers/provision/index.tsx +++ b/containers/provision/index.tsx @@ -24,16 +24,11 @@ import Button from '../../components/button'; import { AdvancedOptionsContainer, ErrorContainer, Form, FormContent } from './provision.styled'; export interface ProvisionProps { - apiUrl: string; kubefirstVersion: string; useTelemetry: boolean; } -const Provision: FunctionComponent = ({ - apiUrl, - kubefirstVersion, - useTelemetry, -}) => { +const Provision: FunctionComponent = ({ kubefirstVersion, useTelemetry }) => { const dispatch = useAppDispatch(); const { installType, gitProvider, installationStep, values, error, authErrors } = useAppSelector( ({ git, installation }) => ({ @@ -137,8 +132,8 @@ const Provision: FunctionComponent = ({ await dispatch(clearError()); await dispatch(clearClusterState()); - await dispatch(createCluster({ apiUrl })).unwrap(); - }, [apiUrl, dispatch, error]); + await dispatch(createCluster()).unwrap(); + }, [dispatch, error]); const form = useMemo(() => { if (installationStep === 0) { @@ -205,12 +200,12 @@ const Provision: FunctionComponent = ({ ]); useEffect(() => { - dispatch(setConfigValues({ isTelemetryEnabled: useTelemetry, apiUrl, kubefirstVersion })); + dispatch(setConfigValues({ isTelemetryEnabled: useTelemetry, kubefirstVersion })); return () => { dispatch(resetInstallState()); }; - }, [dispatch, useTelemetry, apiUrl, kubefirstVersion]); + }, [dispatch, useTelemetry, kubefirstVersion]); useEffect(() => { if (isSetupStep) { diff --git a/containers/services/index.tsx b/containers/services/index.tsx index d989fa16..8330c927 100644 --- a/containers/services/index.tsx +++ b/containers/services/index.tsx @@ -22,7 +22,6 @@ enum SERVICES_TABS { } export interface ServicesProps { - apiUrl: string; domainName: string; k3dDomain: string; kubefirstVersion: string; @@ -30,7 +29,6 @@ export interface ServicesProps { } const Services: FunctionComponent = ({ - apiUrl, domainName, k3dDomain, kubefirstVersion, @@ -66,11 +64,9 @@ const Services: FunctionComponent = ({ }; useEffect(() => { - dispatch( - setConfigValues({ isTelemetryEnabled: useTelemetry, kubefirstVersion, k3dDomain, apiUrl }), - ); + dispatch(setConfigValues({ isTelemetryEnabled: useTelemetry, kubefirstVersion, k3dDomain })); dispatch(getMarketplaceApps()); - }, [dispatch, useTelemetry, kubefirstVersion, k3dDomain, apiUrl]); + }, [dispatch, useTelemetry, kubefirstVersion, k3dDomain]); useEffect(() => { if (!selectedCluster?.clusterName) { diff --git a/containers/terminalLogs/terminalLogs.tsx b/containers/terminalLogs/terminalLogs.tsx index 34e378cd..6bc7d881 100644 --- a/containers/terminalLogs/terminalLogs.tsx +++ b/containers/terminalLogs/terminalLogs.tsx @@ -48,7 +48,6 @@ const TerminalLogs: FunctionComponent = () => { const interval = useRef(); const dispatch = useAppDispatch(); const { - config: { apiUrl = '' }, api: { isProvisioned, isProvisioning, @@ -94,7 +93,7 @@ const TerminalLogs: FunctionComponent = () => { useEffect(() => { const clusterName = values?.clusterName; if (isProvisioning && !isProvisioned && clusterName) { - interval.current = getClusterInterval({ apiUrl, clusterName }); + interval.current = getClusterInterval({ clusterName }); } if (isProvisioned) { @@ -130,7 +129,7 @@ const TerminalLogs: FunctionComponent = () => { terminal.open(terminalRef.current); - const emitter = createLogStream(`${apiUrl}/stream`); + const emitter = createLogStream(`api/stream`); emitter.on('log', (log) => { if ( log.message.includes('time=') && @@ -156,19 +155,12 @@ const TerminalLogs: FunctionComponent = () => { emitter.startLogStream(); - // const tempLogs = []; - // setInterval(() => { - // tempLogs.push(`\x1b[0;37m 2023-05-05 \x1b[0;34m INFO:\x1b[1;37m Hey Kubefirst \n`); - // setLogs(tempLogs); - // terminal.write(`\x1b[0;37m 2023-05-05 \x1b[0;34m INFO:\x1b[1;37m Hey Kubefirst \n`); - // }, 5000); - return () => { terminal.dispose(); emitter.stopLogStream(); }; } - }, [apiUrl, loadAddons]); + }, [loadAddons]); useEffect(() => { Object.keys(CLUSTER_CHECKS).forEach((checkKey) => { diff --git a/package.json b/package.json index b5ee7b68..7f7b32e7 100644 --- a/package.json +++ b/package.json @@ -35,11 +35,13 @@ "axios": "^1.3.3", "axios-mock-adapter": "^1.21.4", "babel-preset-next": "^1.4.0", + "eventsource": "^2.0.2", "lodash": "^4.17.21", "moment": "^2.29.4", "next": "13.0.3", "next-redux-wrapper": "^8.0.0", "posthog-node": "^2.6.0", + "query-string": "^8.1.0", "react": "18.2.0", "react-copy-to-clipboard": "^5.1.0", "react-dom": "18.2.0", @@ -74,6 +76,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.3.0", "@types/analytics-node": "^3.1.9", + "@types/eventsource": "^1.1.11", "@types/react": "^18.0.24", "@types/react-copy-to-clipboard": "^5.0.4", "@types/styled-components": "^5.1.26", diff --git a/pages/api/config.ts b/pages/api/config.ts index 3b3fac27..307505b8 100644 --- a/pages/api/config.ts +++ b/pages/api/config.ts @@ -5,43 +5,11 @@ type Configs = { }; export default function handler(req: NextApiRequest, res: NextApiResponse) { - const { - API_URL = '', - ARGO_CD_URL = '', - ARGO_WORKFLOWS_URL = '', - ATLANTIS_URL = '', - CLOUD = '', - CLUSTER_ID = '', - CLUSTER_TYPE = '', - DOMAIN_NAME = '', - GIT_PROVIDER = '', - GITHUB_OWNER = '', - GITLAB_OWNER = '', - KUBEFIRST_VERSION = '', - METAPHOR_DEVELOPMENT_URL = '', - METAPHOR_STAGING_URL = '', - METAPHOR_PRODUCTION_URL = '', - USE_TELEMETRY = '', - VAULT_URL = '', - } = process.env; + const { API_URL = '', KUBEFIRST_VERSION = '', USE_TELEMETRY = '' } = process.env; res.status(200).json({ API_URL, - ARGO_CD_URL, - ARGO_WORKFLOWS_URL, - ATLANTIS_URL, - CLOUD, - CLUSTER_ID, - CLUSTER_TYPE, - DOMAIN_NAME, - GIT_PROVIDER, - GITHUB_OWNER, - GITLAB_OWNER, KUBEFIRST_VERSION, - VAULT_URL, - METAPHOR_DEVELOPMENT_URL, - METAPHOR_STAGING_URL, - METAPHOR_PRODUCTION_URL, USE_TELEMETRY, }); } diff --git a/pages/api/proxy.ts b/pages/api/proxy.ts new file mode 100644 index 00000000..b61a56a0 --- /dev/null +++ b/pages/api/proxy.ts @@ -0,0 +1,24 @@ +import axios from 'axios'; +import type { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const { API_URL = '' } = process.env; + const { body, url } = req.body; + const { url: queryUrl } = req.query; + + if (!API_URL) { + return res.status(200).json('API_URL not provided'); + } + + const kubefirstEndpointUrl = `${API_URL}${queryUrl || url}`; + + // eslint-disable-next-line no-console + console.log(`METHOD: ${req.method} URL: ${kubefirstEndpointUrl}`); + try { + const response = await axios({ url: kubefirstEndpointUrl, data: body, method: req.method }); + res.status(200).json(response.data); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + res.status(500).send(error?.response?.data || error?.message); + } +} diff --git a/pages/api/proxyHealth.ts b/pages/api/proxyHealth.ts new file mode 100644 index 00000000..10c6f0ef --- /dev/null +++ b/pages/api/proxyHealth.ts @@ -0,0 +1,13 @@ +import axios from 'axios'; +import type { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const { API_URL = '' } = process.env; + + if (!API_URL) { + return res.status(200).json('API_URL not provided'); + } + + const response = await axios.get(`${API_URL}/health`); + res.status(200).json(response.data); +} diff --git a/pages/api/stream.ts b/pages/api/stream.ts new file mode 100644 index 00000000..1e3385b2 --- /dev/null +++ b/pages/api/stream.ts @@ -0,0 +1,24 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import EventSource from 'eventsource'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const { API_URL = '' } = process.env; + + res.writeHead(200, { + 'Connection': 'keep-alive', + 'Content-Encoding': 'none', + 'Cache-Control': 'no-cache', + 'Content-Type': 'text/event-stream', + }); + res.flushHeaders(); + + const eventSource = new EventSource(`${API_URL}/stream`); + + eventSource.addEventListener('open', () => { + // eslint-disable-next-line no-console + console.log('connection established from console api'); + }); + eventSource.addEventListener('message', (e) => { + res.write(`data: ${e.data}\n\n`); + }); +} diff --git a/pages/cluster-management.tsx b/pages/cluster-management.tsx index ff635c73..8825a98f 100644 --- a/pages/cluster-management.tsx +++ b/pages/cluster-management.tsx @@ -23,11 +23,10 @@ const ClusterManagementPage: FunctionComponent = (props) }; export async function getServerSideProps() { - const { API_URL = '', KUBEFIRST_VERSION = '', USE_TELEMETRY = '' } = process.env; + const { KUBEFIRST_VERSION = '', USE_TELEMETRY = '' } = process.env; return { props: { - apiUrl: API_URL, kubefirstVersion: KUBEFIRST_VERSION, useTelemetry: USE_TELEMETRY === 'true', }, diff --git a/pages/index.ts b/pages/index.ts index 57570e7f..ae9866ae 100644 --- a/pages/index.ts +++ b/pages/index.ts @@ -34,7 +34,7 @@ const MainPage: FunctionComponent = ({ flags }) => { }; export const getServerSideProps: GetServerSideProps = async () => { - const { API_URL = '', POSTHOG_KEY = '', USE_TELEMETRY = '' } = process.env; + const { POSTHOG_KEY = '', USE_TELEMETRY = '' } = process.env; let flags; try { @@ -49,7 +49,6 @@ export const getServerSideProps: GetServerSideProps = async () => { return { props: { flags, - apiUrl: API_URL, useTelemetry: USE_TELEMETRY === 'true', }, }; diff --git a/pages/provision.tsx b/pages/provision.tsx index 928f4763..edc42a70 100644 --- a/pages/provision.tsx +++ b/pages/provision.tsx @@ -30,11 +30,10 @@ const ProvisionPage: FunctionComponent = (props) => { }; export async function getServerSideProps() { - const { API_URL = '', KUBEFIRST_VERSION = '', USE_TELEMETRY = '' } = process.env; + const { KUBEFIRST_VERSION = '', USE_TELEMETRY = '' } = process.env; return { props: { - apiUrl: API_URL, kubefirstVersion: KUBEFIRST_VERSION, useTelemetry: USE_TELEMETRY === 'true', }, diff --git a/pages/services.tsx b/pages/services.tsx index 43293152..dce7b2f9 100644 --- a/pages/services.tsx +++ b/pages/services.tsx @@ -3,7 +3,6 @@ import React, { FunctionComponent } from 'react'; import Services from '../containers/services'; interface ServicesPageProps { - apiUrl: string; domainName: string; k3dDomain: string; kubefirstVersion: string; @@ -14,7 +13,6 @@ const ServicesPage: FunctionComponent = (props) => >) => { state.marketplaceApps = payload; }, - ); + ) + .addCase(getMarketplaceApps.rejected, (state) => { + state.marketplaceApps = []; + }); }, }); diff --git a/redux/slices/config.slice.ts b/redux/slices/config.slice.ts index ce0093a3..e6b0b640 100644 --- a/redux/slices/config.slice.ts +++ b/redux/slices/config.slice.ts @@ -1,7 +1,6 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; export interface ConfigState { - apiUrl?: string; isTelemetryEnabled: boolean; kubefirstVersion?: string; k3dDomain?: string; @@ -9,7 +8,6 @@ export interface ConfigState { export const initialState: ConfigState = { isTelemetryEnabled: false, - apiUrl: undefined, }; const configSlice = createSlice({ @@ -17,11 +15,10 @@ const configSlice = createSlice({ initialState, reducers: { setConfigValues: (state, action: PayloadAction) => { - const { apiUrl, isTelemetryEnabled, kubefirstVersion, k3dDomain } = action.payload; + const { isTelemetryEnabled, kubefirstVersion, k3dDomain } = action.payload; state.isTelemetryEnabled = isTelemetryEnabled; state.kubefirstVersion = kubefirstVersion; state.k3dDomain = k3dDomain; - state.apiUrl = apiUrl; }, }, }); diff --git a/redux/thunks/api.thunk.ts b/redux/thunks/api.thunk.ts index 66e13aa2..b5f4754b 100644 --- a/redux/thunks/api.thunk.ts +++ b/redux/thunks/api.thunk.ts @@ -2,6 +2,7 @@ import axios from 'axios'; import { createAsyncThunk } from '@reduxjs/toolkit'; import { FieldValues } from 'react-hook-form'; import sortBy from 'lodash/sortBy'; +import queryString from 'query-string'; import { AppDispatch, RootState } from '../store'; import { @@ -51,12 +52,12 @@ const mapClusterFromRaw = (cluster: ClusterResponse): Cluster => ({ export const createCluster = createAsyncThunk< Cluster, - { apiUrl: string }, + void, { dispatch: AppDispatch; state: RootState; } ->('api/cluster/provisioning', async ({ apiUrl }, { getState }) => { +>('api/cluster/provisioning', async (_, { getState }) => { const { installation: { installType, gitProvider, values }, } = getState(); @@ -84,7 +85,10 @@ export const createCluster = createAsyncThunk< ...values?.vultr_auth, }, }; - const res = await axios.post(`${apiUrl}/cluster/${values?.clusterName || 'kubefirst'}`, params); + const res = await axios.post('/api/proxy', { + url: `/cluster/${values?.clusterName || 'kubefirst'}`, + body: params, + }); if ('error' in res) { throw res.error; @@ -99,8 +103,10 @@ export const getCluster = createAsyncThunk< dispatch: AppDispatch; state: RootState; } ->('api/cluster/get', async ({ apiUrl, clusterName }) => { - const res = await axios.get(`${apiUrl}/cluster/${clusterName || 'kubefirst'}`); +>('api/cluster/get', async ({ clusterName }) => { + const res = await axios.get( + `/api/proxy?${queryString.stringify({ url: `/cluster/${clusterName || 'kubefirst'}` })}`, + ); if ('error' in res) { throw res.error; @@ -110,13 +116,13 @@ export const getCluster = createAsyncThunk< export const getClusters = createAsyncThunk< Array, - ClusterRequestProps, + void, { dispatch: AppDispatch; state: RootState; } ->('api/cluster/getClusters', async ({ apiUrl }) => { - const res = await axios.get(`${apiUrl}/cluster`); +>('api/cluster/getClusters', async () => { + const res = await axios.get(`/api/proxy?${queryString.stringify({ url: `/cluster` })}`); if ('error' in res) { throw res.error; @@ -132,8 +138,10 @@ export const deleteCluster = createAsyncThunk< dispatch: AppDispatch; state: RootState; } ->('api/cluster/delete', async ({ apiUrl, clusterName }) => { - const res = await axios.delete(`${apiUrl}/cluster/${clusterName || 'kubefirst'}`); +>('api/cluster/delete', async ({ clusterName }) => { + const res = await axios.delete( + `/api/proxy?${queryString.stringify({ url: `/cluster/${clusterName || 'kubefirst'}` })}`, + ); if ('error' in res) { throw res.error; @@ -148,12 +156,10 @@ export const getClusterServices = createAsyncThunk< dispatch: AppDispatch; state: RootState; } ->('api/cluster/getClusterServices', async ({ clusterName }, { getState }) => { - const { - config: { apiUrl }, - } = getState(); - - const res = await axios.get(`${apiUrl}/services/${clusterName}`); +>('api/cluster/getClusterServices', async ({ clusterName }) => { + const res = await axios.get( + `/api/proxy?${queryString.stringify({ url: `/services/${clusterName}` })}`, + ); if ('error' in res) { throw res.error; @@ -168,12 +174,8 @@ export const getMarketplaceApps = createAsyncThunk< dispatch: AppDispatch; state: RootState; } ->('api/getMarketplaceApps', async (_, { getState }) => { - const { - config: { apiUrl }, - } = getState(); - - const res = await axios.get(`${apiUrl}/marketplace/apps`); +>('api/getMarketplaceApps', async () => { + const res = await axios.get(`/api/proxy?${queryString.stringify({ url: `/marketplace/apps` })}`); if ('error' in res) { throw res.error; @@ -188,11 +190,7 @@ export const installMarketplaceApp = createAsyncThunk< dispatch: AppDispatch; state: RootState; } ->('api/installMarketplaceApp', async ({ app, clusterName, values }, { getState }) => { - const { - config: { apiUrl }, - } = getState(); - +>('api/installMarketplaceApp', async ({ app, clusterName, values }) => { const secret_keys = values && Object.keys(values as FieldValues).map((key) => ({ @@ -200,8 +198,11 @@ export const installMarketplaceApp = createAsyncThunk< value: (values as FieldValues)[key], })); - const res = await axios.post(`${apiUrl}/services/${clusterName}/${app.name}`, { - secret_keys, + const res = await axios.post('/api/proxy', { + url: `/services/${clusterName}/${app.name}`, + body: { + secret_keys, + }, }); if ('error' in res) { @@ -219,14 +220,13 @@ export const getCloudRegions = createAsyncThunk< } >('api/getCloudRegions', async (_, { getState }) => { const { - config: { apiUrl }, installation: { values, installType }, } = getState(); - const res = await axios.post<{ regions: Array }>( - `${apiUrl}/region/${installType}`, - installType === InstallationType.AWS ? { ...values, cloud_region: 'us-east-1' } : values, - ); + const res = await axios.post<{ regions: Array }>('/api/proxy', { + url: `/region/${installType}`, + body: installType === InstallationType.AWS ? { ...values, cloud_region: 'us-east-1' } : values, + }); if ('error' in res) { throw res.error; @@ -243,13 +243,15 @@ export const getCloudDomains = createAsyncThunk< } >('api/getCloudDomains', async (cloudRegion, { getState }) => { const { - config: { apiUrl }, installation: { values, installType }, } = getState(); - const res = await axios.post<{ domains: Array }>(`${apiUrl}/domain/${installType}`, { - ...values, - cloud_region: cloudRegion, + const res = await axios.post<{ domains: Array }>('/api/proxy', { + url: `/domain/${installType}`, + body: { + ...values, + cloud_region: cloudRegion, + }, }); if ('error' in res) { @@ -267,13 +269,12 @@ export const resetClusterProgress = createAsyncThunk< } >('api/resetClusterProgress', async (_, { getState }) => { const { - config: { apiUrl }, installation: { values }, } = getState(); - const res = await axios.post<{ regions: Array }>( - `${apiUrl}/cluster/${values?.clusterName}/reset_progress`, - ); + const res = await axios.post<{ regions: Array }>('/api/proxy', { + url: `/cluster/${values?.clusterName}/reset_progress`, + }); if ('error' in res) { throw res.error; diff --git a/services/stream/index.ts b/services/stream/index.ts index b640301a..99e71be9 100644 --- a/services/stream/index.ts +++ b/services/stream/index.ts @@ -17,10 +17,6 @@ export function createLogStream(url: string): LogStreamResponse { // eslint-disable-next-line no-console console.log('connection established'); }); - eventSource.addEventListener('log', (e) => { - const logMessage = JSON.parse(e.data); - logStream.emit('log', logMessage); - }); eventSource.addEventListener('message', (e) => { const logMessage = JSON.parse(e.data); logStream.emit('log', logMessage); diff --git a/types/provision/index.ts b/types/provision/index.ts index 8b4dcc53..f7f4a002 100644 --- a/types/provision/index.ts +++ b/types/provision/index.ts @@ -21,7 +21,6 @@ export interface FormFlowProps { } export interface ClusterRequestProps { - apiUrl?: string; clusterName?: string; } diff --git a/yarn.lock b/yarn.lock index 63500d60..5a0a2301 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3650,6 +3650,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== +"@types/eventsource@^1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@types/eventsource/-/eventsource-1.1.11.tgz#a2c0bfd0436b7db42ed1b2b2117f7ec2e8478dc7" + integrity sha512-L7wLDZlWm5mROzv87W0ofIYeQP5K2UhoFnnUyEWLKM6UBb0ZNRgAqp98qE5DkgfBXdWfc2kYmw9KZm4NLjRbsw== + "@types/geojson@*": version "7946.0.10" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249" @@ -6390,6 +6395,11 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== +decode-uri-component@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz#2ac4859663c704be22bf7db760a1494a49ab2cc5" + integrity sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ== + decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" @@ -7336,6 +7346,11 @@ events@^3.0.0, events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +eventsource@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" + integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== + evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -7617,6 +7632,11 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +filter-obj@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-5.1.0.tgz#5bd89676000a713d7db2e197f660274428e524ed" + integrity sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng== + finalhandler@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" @@ -12132,6 +12152,15 @@ qs@^6.10.0: resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== +query-string@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-8.1.0.tgz#e7f95367737219544cd360a11a4f4ca03836e115" + integrity sha512-BFQeWxJOZxZGix7y+SByG3F36dA0AbTy9o6pSmKFcFz7DAj0re9Frkty3saBn3nHo3D0oZJ/+rx3r8H8r8Jbpw== + dependencies: + decode-uri-component "^0.4.1" + filter-obj "^5.1.0" + split-on-first "^3.0.0" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -13238,6 +13267,11 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5" integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== +split-on-first@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-3.0.0.tgz#f04959c9ea8101b9b0bbf35a61b9ebea784a23e7" + integrity sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"