From 33b5a85d1cd94237b159e5f88943706c89136c37 Mon Sep 17 00:00:00 2001
From: SeDemal <demal.sebastien@bluewin.ch>
Date: Wed, 31 Jul 2024 18:58:06 +0200
Subject: [PATCH] fix: javacript errors and mistakes

---
 packages/common/src/number.ts                 |  2 +-
 .../deluge/deluge-integration.ts              |  6 +-
 .../nzbget/nzbget-integration.ts              |  8 +--
 .../qbittorrent/qbittorrent-integration.ts    |  6 +-
 .../sabnzbd/sabnzbd-integration.ts            | 12 ++--
 .../transmission/transmission-integration.ts  |  6 +-
 packages/widgets/src/downloads/component.tsx  | 56 +++++++++++--------
 packages/widgets/src/downloads/index.ts       |  3 +-
 .../widgets/src/modals/widget-edit-modal.tsx  |  4 +-
 pnpm-lock.yaml                                | 43 +++-----------
 10 files changed, 64 insertions(+), 82 deletions(-)

diff --git a/packages/common/src/number.ts b/packages/common/src/number.ts
index 66e4daab84..d381e2d50c 100644
--- a/packages/common/src/number.ts
+++ b/packages/common/src/number.ts
@@ -30,7 +30,7 @@ export const randomInt = (min: number, max: number) => {
 export const humanFileSize = (size: number) => {
   if (!Number.isInteger(size)) return NaN;
   let count = 0;
-  while (true) {
+  while (count < siRanges.length) {
     const tempSize = size / Math.pow(1024, count);
     if (tempSize < 1024 || count === siRanges.length - 1) {
       return tempSize.toFixed(Math.min(count, 1)) + siRanges[count];
diff --git a/packages/integrations/src/download-client/deluge/deluge-integration.ts b/packages/integrations/src/download-client/deluge/deluge-integration.ts
index ea3961fde3..cef3ea44b0 100644
--- a/packages/integrations/src/download-client/deluge/deluge-integration.ts
+++ b/packages/integrations/src/download-client/deluge/deluge-integration.ts
@@ -23,7 +23,7 @@ export class DelugeIntegration extends DownloadClientIntegration {
       ...(torrent as { completed_time: number } & typeof torrent),
       id,
     }));
-    const paused = torrents.find(({ state }) => this.getTorrentState(state) !== "paused") === undefined;
+    const paused = torrents.find(({ state }) => DelugeIntegration.getTorrentState(state) !== "paused") === undefined;
     const status: DownloadClientStatus = {
       paused,
       rates: {
@@ -33,7 +33,7 @@ export class DelugeIntegration extends DownloadClientIntegration {
       type,
     };
     const items = torrents.map((torrent): DownloadClientItem => {
-      const state = this.getTorrentState(torrent.state);
+      const state = DelugeIntegration.getTorrentState(torrent.state);
       return {
         type,
         id: torrent.id,
@@ -96,7 +96,7 @@ export class DelugeIntegration extends DownloadClientIntegration {
     });
   }
 
-  private getTorrentState(state: string): DownloadClientItem["state"] {
+  private static getTorrentState(state: string): DownloadClientItem["state"] {
     switch (state) {
       case "Queued":
       case "Checking":
diff --git a/packages/integrations/src/download-client/nzbget/nzbget-integration.ts b/packages/integrations/src/download-client/nzbget/nzbget-integration.ts
index 8b8d9a784b..3b0f282b2d 100644
--- a/packages/integrations/src/download-client/nzbget/nzbget-integration.ts
+++ b/packages/integrations/src/download-client/nzbget/nzbget-integration.ts
@@ -25,7 +25,7 @@ export class NzbGetIntegration extends DownloadClientIntegration {
     };
     const items = queue
       .map((file): DownloadClientItem => {
-        const state = this.getNzbQueueState(file.Status);
+        const state = NzbGetIntegration.getNzbQueueState(file.Status);
         return {
           type,
           id: file.NZBID.toString(),
@@ -42,7 +42,7 @@ export class NzbGetIntegration extends DownloadClientIntegration {
       })
       .concat(
         history.map((file, index): DownloadClientItem => {
-          const state = this.getNzbHistoryState(file.ScriptStatus);
+          const state = NzbGetIntegration.getNzbHistoryState(file.ScriptStatus);
           return {
             type,
             id: file.NZBID.toString(),
@@ -98,7 +98,7 @@ export class NzbGetIntegration extends DownloadClientIntegration {
     return new NzbGetClient(url);
   }
 
-  private getNzbQueueState(status: string): DownloadClientItem["state"] {
+  private static getNzbQueueState(status: string): DownloadClientItem["state"] {
     switch (status) {
       case "QUEUED":
         return "queued";
@@ -109,7 +109,7 @@ export class NzbGetIntegration extends DownloadClientIntegration {
     }
   }
 
-  private getNzbHistoryState(status: string): DownloadClientItem["state"] {
+  private static getNzbHistoryState(status: string): DownloadClientItem["state"] {
     switch (status) {
       case "FAILURE":
         return "failed";
diff --git a/packages/integrations/src/download-client/qbittorrent/qbittorrent-integration.ts b/packages/integrations/src/download-client/qbittorrent/qbittorrent-integration.ts
index 6d633abf1f..3530604050 100644
--- a/packages/integrations/src/download-client/qbittorrent/qbittorrent-integration.ts
+++ b/packages/integrations/src/download-client/qbittorrent/qbittorrent-integration.ts
@@ -20,10 +20,10 @@ export class QBitTorrentIntegration extends DownloadClientIntegration {
       ({ down, up }, { dlspeed, upspeed }) => ({ down: down + dlspeed, up: up + upspeed }),
       { down: 0, up: 0 },
     );
-    const paused = torrents.find(({ state }) => this.getTorrentState(state) !== "paused") === undefined;
+    const paused = torrents.find(({ state }) => QBitTorrentIntegration.getTorrentState(state) !== "paused") === undefined;
     const status: DownloadClientStatus = { paused, rates, type };
     const items = torrents.map((torrent): DownloadClientItem => {
-      const state = this.getTorrentState(torrent.state);
+      const state = QBitTorrentIntegration.getTorrentState(torrent.state);
       return {
         type,
         id: torrent.hash,
@@ -77,7 +77,7 @@ export class QBitTorrentIntegration extends DownloadClientIntegration {
     });
   }
 
-  private getTorrentState(state: string): DownloadClientItem["state"] {
+  private static getTorrentState(state: string): DownloadClientItem["state"] {
     switch (state) {
       case "allocating":
       case "checkingDL":
diff --git a/packages/integrations/src/download-client/sabnzbd/sabnzbd-integration.ts b/packages/integrations/src/download-client/sabnzbd/sabnzbd-integration.ts
index 5fefebc2c0..508fcc21ca 100644
--- a/packages/integrations/src/download-client/sabnzbd/sabnzbd-integration.ts
+++ b/packages/integrations/src/download-client/sabnzbd/sabnzbd-integration.ts
@@ -26,7 +26,7 @@ export class SabnzbdIntegration extends DownloadClientIntegration {
     };
     const items = queue.slots
       .map((slot): DownloadClientItem => {
-        const state = this.getNzbQueueState(slot.status);
+        const state = SabnzbdIntegration.getNzbQueueState(slot.status);
         const times = slot.timeleft.split(":").reverse();
         const time = dayjs
           .duration({
@@ -52,7 +52,7 @@ export class SabnzbdIntegration extends DownloadClientIntegration {
       })
       .concat(
         history.slots.map((slot, index): DownloadClientItem => {
-          const state = this.getNzbHistoryState(slot.status);
+          const state = SabnzbdIntegration.getNzbHistoryState(slot.status);
           return {
             type,
             id: slot.nzo_id,
@@ -109,12 +109,12 @@ export class SabnzbdIntegration extends DownloadClientIntegration {
       url.searchParams.append(key, value);
     });
     url.searchParams.append("apikey", this.getSecretValue("apiKey"));
-    return fetch(url)
+    return await fetch(url)
       .then((response) => {
         if (!response.ok) {
           throw new Error(response.statusText);
         }
-        return response.json();
+        return response.json() as Promise<unknown>;
       })
       .catch((error) => {
         if (error instanceof Error) {
@@ -125,7 +125,7 @@ export class SabnzbdIntegration extends DownloadClientIntegration {
       });
   }
 
-  private getNzbQueueState(status: string): DownloadClientItem["state"] {
+  private static getNzbQueueState(status: string): DownloadClientItem["state"] {
     switch (status) {
       case "Queued":
         return "queued";
@@ -136,7 +136,7 @@ export class SabnzbdIntegration extends DownloadClientIntegration {
     }
   }
 
-  private getNzbHistoryState(status: string): DownloadClientItem["state"] {
+  private static getNzbHistoryState(status: string): DownloadClientItem["state"] {
     switch (status) {
       case "Completed":
         return "completed";
diff --git a/packages/integrations/src/download-client/transmission/transmission-integration.ts b/packages/integrations/src/download-client/transmission/transmission-integration.ts
index f99ffdfab2..0d2e2aa8a7 100644
--- a/packages/integrations/src/download-client/transmission/transmission-integration.ts
+++ b/packages/integrations/src/download-client/transmission/transmission-integration.ts
@@ -19,10 +19,10 @@ export class TransmissionIntegration extends DownloadClientIntegration {
       ({ down, up }, { rateDownload, rateUpload }) => ({ down: down + rateDownload, up: up + rateUpload }),
       { down: 0, up: 0 },
     );
-    const paused = torrents.find(({ status }) => this.getTorrentState(status) !== "paused") === undefined;
+    const paused = torrents.find(({ status }) => TransmissionIntegration.getTorrentState(status) !== "paused") === undefined;
     const status: DownloadClientStatus = { paused, rates, type };
     const items = torrents.map((torrent): DownloadClientItem => {
-      const state = this.getTorrentState(torrent.status);
+      const state = TransmissionIntegration.getTorrentState(torrent.status);
       return {
         type,
         id: torrent.hashString,
@@ -78,7 +78,7 @@ export class TransmissionIntegration extends DownloadClientIntegration {
     });
   }
 
-  private getTorrentState(status: number): DownloadClientItem["state"] {
+  private static getTorrentState(status: number): DownloadClientItem["state"] {
     switch (status) {
       case 0:
         return "paused";
diff --git a/packages/widgets/src/downloads/component.tsx b/packages/widgets/src/downloads/component.tsx
index cabdc5c60f..3d62dcaab1 100644
--- a/packages/widgets/src/downloads/component.tsx
+++ b/packages/widgets/src/downloads/component.tsx
@@ -49,6 +49,7 @@ import { useScopedI18n } from "@homarr/translation/client";
 import type { WidgetComponentProps } from "../definition";
 
 //TODO:
+// - NzbGet API not working I think
 // - Data Subscription permission issues                    <- Need help
 // - table tbody hide under thead and keep transparency     <- Need help
 // - Add integrations to shouldHide options                 <- Potential help needed
@@ -156,7 +157,7 @@ export default function DownloadClientsWidget({
                 (type === "torrent" &&
                   ((progress === 1 &&
                     options.showCompletedTorrent &&
-                    upSpeed! >= Number(options.activeTorrentThreshold) * 1024) ||
+                    (upSpeed ?? 0) >= Number(options.activeTorrentThreshold) * 1024) ||
                     progress !== 1)) ||
                 (type === "usenet" && ((progress === 1 && options.showCompletedUsenet) || progress !== 1)),
             )
@@ -199,7 +200,7 @@ export default function DownloadClientsWidget({
             )
             .reduce(
               ({ totalUp, totalDown }, { sent, size, progress }) => ({
-                totalUp: isTorrent ? totalUp! + sent! : undefined,
+                totalUp: isTorrent ? (totalUp ?? 0) + (sent ?? 0) : undefined,
                 totalDown: totalDown + size * progress,
               }),
               { totalDown: 0, totalUp: isTorrent ? 0 : undefined },
@@ -211,7 +212,8 @@ export default function DownloadClientsWidget({
             ratio: totalUp === undefined ? undefined : totalUp / totalDown,
             ...pair.data.status,
           };
-        }).sort(({type: typeA},{type: typeB}) => typeA.length - typeB.length),
+        })
+        .sort(({ type: typeA }, { type: typeB }) => typeA.length - typeB.length),
     [currentItems, integrationIds, options],
   );
 
@@ -383,7 +385,7 @@ export default function DownloadClientsWidget({
         sortUndefined: "last",
         Cell: ({ cell }) => {
           const downSpeed = cell.getValue<ExtendedDownloadClientItem["downSpeed"]>();
-          return <Text>{downSpeed !== undefined && humanFileSize(downSpeed) + "/s"}</Text>;
+          return <Text>{downSpeed !== undefined && humanFileSize(downSpeed)?.toString().concat("/s")}</Text>;
         },
       },
       {
@@ -510,7 +512,7 @@ export default function DownloadClientsWidget({
         sortUndefined: "last",
         Cell: ({ cell }) => {
           const upSpeed = cell.getValue<ExtendedDownloadClientItem["upSpeed"]>();
-          return upSpeed !== undefined && <Text>{humanFileSize(upSpeed) + "/s"}</Text>;
+          return upSpeed !== undefined && <Text>{humanFileSize(upSpeed)?.toString().concat("/s")}</Text>;
         },
       },
     ],
@@ -584,8 +586,8 @@ export default function DownloadClientsWidget({
     .filter(({ integration: { kind } }) => ["qBittorrent", "deluge", "transmission"].includes(kind))
     .reduce(
       ({ up, down }, { totalUp, totalDown }) => ({
-        up: up + totalUp!,
-        down: down + totalDown!,
+        up: up + (totalUp ?? 0),
+        down: down + (totalDown ?? 0),
       }),
       { up: 0, down: 0 },
     );
@@ -620,9 +622,9 @@ interface ItemInfoModalProps {
 }
 
 const ItemInfoModal = ({ items, currentIndex, opened, onClose }: ItemInfoModalProps) => {
-  const item = useMemo<ExtendedDownloadClientItem | undefined>(() => items[currentIndex], [currentIndex, opened]);
+  const item = useMemo<ExtendedDownloadClientItem | undefined>(() => items[currentIndex], [items, currentIndex, opened]);
   const t = useScopedI18n("widget.downloads.states");
-  if (item === undefined) return;
+  if (item === undefined) return <></>;
   return (
     <Modal opened={opened} onClose={onClose} centered title={item.id} size="auto">
       <Stack align="center">
@@ -636,11 +638,11 @@ const ItemInfoModal = ({ items, currentIndex, opened, onClose }: ItemInfoModalPr
         <NormalizedLine itemKey="state" values={t(item.state)} />
         <NormalizedLine
           itemKey="upSpeed"
-          values={item.upSpeed === undefined ? undefined : humanFileSize(item.upSpeed) + "/s"}
+          values={item.upSpeed === undefined ? undefined : humanFileSize(item.upSpeed)?.toString().concat("/s")}
         />
         <NormalizedLine
           itemKey="downSpeed"
-          values={item.downSpeed === undefined ? undefined : humanFileSize(item.downSpeed) + "/s"}
+          values={item.downSpeed === undefined ? undefined : humanFileSize(item.downSpeed)?.toString().concat("/s")}
         />
         <NormalizedLine itemKey="sent" values={item.sent === undefined ? undefined : humanFileSize(item.sent)} />
         <NormalizedLine itemKey="received" values={humanFileSize(item.received)} />
@@ -652,7 +654,7 @@ const ItemInfoModal = ({ items, currentIndex, opened, onClose }: ItemInfoModalPr
           )}
         />
         <NormalizedLine itemKey="ratio" values={item.ratio} />
-        <NormalizedLine itemKey="added" values={item.added == undefined ? "unknown" : dayjs(item.added).format()} />
+        <NormalizedLine itemKey="added" values={item.added === undefined ? "unknown" : dayjs(item.added).format()} />
         <NormalizedLine itemKey="time" values={dayjs().add(item.time).format()} />
         <NormalizedLine itemKey="category" values={item.category} />
       </Stack>
@@ -667,7 +669,7 @@ const NormalizedLine = ({
   itemKey: Exclude<keyof ExtendedDownloadClientItem, "integration" | "actions" | "name" | "id">;
   values?: number | string | string[];
 }) => {
-  if (typeof values !== "number" && (values === undefined || values.length === 0)) return;
+  if (typeof values !== "number" && (values === undefined || values.length === 0)) return <></>;
   const t = useScopedI18n("widget.downloads.items");
   const tCommon = useScopedI18n("common");
   const translatedKey = t(`${itemKey}.detailsTitle`);
@@ -686,7 +688,7 @@ const NormalizedLine = ({
       {Array.isArray(values) ? (
         <Stack>
           {values.map((value) => (
-            <Text>{value}</Text>
+            <Text key={value}>{value}</Text>
           ))}
         </Stack>
       ) : (
@@ -704,10 +706,10 @@ interface ClientsControlProps {
 const ClientsControl = ({ clients, style }: ClientsControlProps) => {
   const pausedIntegrations: string[] = [];
   const activeIntegrations: string[] = [];
-  clients.map((client) =>
+  clients.forEach((client) =>
     client.paused ? pausedIntegrations.push(client.integration.id) : activeIntegrations.push(client.integration.id),
   );
-  const totalSpeed = humanFileSize(clients.reduce((count, { rates: { down } }) => count + down, 0)) + "/s";
+  const totalSpeed = humanFileSize(clients.reduce((count, { rates: { down } }) => count + down, 0))?.toString().concat("/s");
   const { mutate: mutateResumeQueue } = clientApi.widget.downloads.resume.useMutation();
   const { mutate: mutatePauseQueue } = clientApi.widget.downloads.pause.useMutation();
   const [opened, { open, close }] = useDisclosure(false);
@@ -742,18 +744,24 @@ const ClientsControl = ({ clients, style }: ClientsControlProps) => {
                     <Stack gap={0} pt={5} h={60} justify="center" flex={1}>
                       {client.rates.up !== undefined ? (
                         <Group display="flex" justify="center" c="green" w="100%" gap={5}>
-                          <Text flex={1} ta="right">{"↑ " + humanFileSize(client.rates.up) + "/s"}</Text>
+                          <Text flex={1} ta="right">
+                            {`↑ ${humanFileSize(client.rates.up)}/s`}
+                          </Text>
                           <Text>{"-"}</Text>
-                          <Text flex={1} ta="left">{humanFileSize(client.totalUp ?? 0)}</Text>
+                          <Text flex={1} ta="left">
+                            {humanFileSize(client.totalUp ?? 0)}
+                          </Text>
                         </Group>
                       ) : undefined}
-                      <Text c="blue">
                       <Group display="flex" justify="center" c="blue" w="100%" gap={5}>
-                          <Text flex={1} ta="right">{"↓ " + humanFileSize(client.rates.down) + "/s"}</Text>
-                          <Text>{"-"}</Text>
-                          <Text flex={1} ta="left">{humanFileSize(Math.floor(client.totalDown ?? 0))}</Text>
-                        </Group>
-                      </Text>
+                        <Text flex={1} ta="right">
+                          {`↓ ${humanFileSize(client.rates.down)}/s`}
+                        </Text>
+                        <Text>{"-"}</Text>
+                        <Text flex={1} ta="left">
+                          {humanFileSize(Math.floor(client.totalDown ?? 0))}
+                        </Text>
+                      </Group>
                     </Stack>
                   </Group>
                 </Paper>
diff --git a/packages/widgets/src/downloads/index.ts b/packages/widgets/src/downloads/index.ts
index 392c794200..9016767d1d 100644
--- a/packages/widgets/src/downloads/index.ts
+++ b/packages/widgets/src/downloads/index.ts
@@ -47,7 +47,8 @@ export const { definition, componentLoader, serverDataLoader } = createWidgetDef
         step: 1,
       }),
       categoryFilter: factory.multiText({
-        //defaultValue: [] as string[];
+        defaultValue: [] as string[],
+        validate: z.string(),
       }),
       filterIsWhitelist: factory.switch({
         defaultValue: false,
diff --git a/packages/widgets/src/modals/widget-edit-modal.tsx b/packages/widgets/src/modals/widget-edit-modal.tsx
index 7a6f920420..005597dc20 100644
--- a/packages/widgets/src/modals/widget-edit-modal.tsx
+++ b/packages/widgets/src/modals/widget-edit-modal.tsx
@@ -47,9 +47,9 @@ export const WidgetEditModal = createModal<ModalProps<WidgetKind>>(({ actions, i
       z.object({
         options: z.object(
           objectEntries(widgetImports[innerProps.kind].definition.options).reduce(
-            (acc, [key, value]: [string, { validate?: z.ZodType<unknown> }]) => {
+            (acc, [key, value]: [string, { type: string, validate?: z.ZodType<unknown> }]) => {
               if (value.validate) {
-                acc[key] = value.validate;
+                acc[key] = value.type === "multiText" ? z.array(value.validate).optional() : value.validate;
               }
 
               return acc;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 811a3dfb9c..095a6c88cd 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -673,7 +673,7 @@ importers:
     dependencies:
       '@extractus/feed-extractor':
         specifier: ^7.1.3
-        version: 7.1.3
+        version: 7.1.3(encoding@0.1.13)
       '@homarr/analytics':
         specifier: workspace:^0.1.0
         version: link:../analytics
@@ -1271,7 +1271,7 @@ importers:
     dependencies:
       '@extractus/feed-extractor':
         specifier: ^7.1.3
-        version: 7.1.3
+        version: 7.1.3(encoding@0.1.13)
       '@homarr/api':
         specifier: workspace:^0.1.0
         version: link:../api
@@ -2449,13 +2449,8 @@ packages:
   '@swc/helpers@0.5.5':
     resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==}
 
-  '@szmarczak/http-timer@4.0.6':
-    resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
-    engines: {node: '>=10'}
-
   '@t3-oss/env-core@0.11.0':
     resolution: {integrity: sha512-PSalC5bG0a7XbyoLydiQdAnx3gICX6IQNctvh+TyLrdFxsxgocdj9Ui7sd061UlBzi+z4aIGjnem1kZx9QtUgQ==}
-
     peerDependencies:
       typescript: '>=5.0.0'
       zod: ^3.0.0
@@ -6548,10 +6543,6 @@ packages:
     resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
     hasBin: true
 
-  uuid@9.0.1:
-    resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
-    hasBin: true
-
   uuidjs@4.2.14:
     resolution: {integrity: sha512-Z4iL8AWHlTWeAmi6v1TCPKBF5QkTxpdLDS2yrAm9cA9GvEwWFxnRm7uEEcDY5TrZuO2A/cLQyeuXjlohAMcCIQ==}
     hasBin: true
@@ -7295,10 +7286,10 @@ snapshots:
 
   '@eslint/object-schema@2.1.4': {}
 
-  '@extractus/feed-extractor@7.1.3':
+  '@extractus/feed-extractor@7.1.3(encoding@0.1.13)':
     dependencies:
       bellajs: 11.2.0
-      cross-fetch: 4.0.0
+      cross-fetch: 4.0.0(encoding@0.1.13)
       fast-xml-parser: 4.4.0
       html-entities: 2.5.2
     transitivePeerDependencies:
@@ -7658,10 +7649,6 @@ snapshots:
       '@swc/counter': 0.1.3
       tslib: 2.6.2
 
-  '@szmarczak/http-timer@4.0.6':
-    dependencies:
-      defer-to-connect: 2.0.1
-
   '@t3-oss/env-core@0.11.0(typescript@5.5.4)(zod@3.23.8)':
     dependencies:
       zod: 3.23.8
@@ -8035,14 +8022,7 @@ snapshots:
   '@types/body-parser@1.19.5':
     dependencies:
       '@types/connect': 3.4.38
-      '@types/node': 20.14.11
-
-  '@types/cacheable-request@6.0.3':
-    dependencies:
-      '@types/http-cache-semantics': 4.0.4
-      '@types/keyv': 3.1.4
-      '@types/node': 20.14.11
-      '@types/responselike': 1.0.3
+      '@types/node': 20.14.13
 
   '@types/chroma-js@2.4.4': {}
 
@@ -8101,9 +8081,7 @@ snapshots:
   '@types/glob@7.2.0':
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.14.11
-
-  '@types/http-cache-semantics@4.0.4': {}
+      '@types/node': 20.14.13
 
   '@types/http-errors@2.0.4': {}
 
@@ -9075,9 +9053,9 @@ snapshots:
     dependencies:
       cross-spawn: 7.0.3
 
-  cross-fetch@4.0.0:
+  cross-fetch@4.0.0(encoding@0.1.13):
     dependencies:
-      node-fetch: 2.7.0
+      node-fetch: 2.7.0(encoding@0.1.13)
     transitivePeerDependencies:
       - encoding
 
@@ -12038,7 +12016,6 @@ snapshots:
       docker-compose: 0.24.8
       dockerode: 3.3.5
       get-port: 5.1.1
-      node-fetch: 2.7.0(encoding@0.1.13)
       proper-lockfile: 4.1.2
       properties-reader: 2.3.0
       ssh-remote-port-forward: 1.0.4
@@ -12380,8 +12357,6 @@ snapshots:
 
   uuid@8.3.2: {}
 
-  uuid@9.0.1: {}
-
   uuidjs@4.2.14: {}
 
   v8-compile-cache-lib@3.0.1: {}
@@ -12700,8 +12675,6 @@ snapshots:
       compress-commons: 6.0.2
       readable-stream: 4.5.2
 
-
-
   zipcodes-regex@1.0.3: {}
 
   zod@3.23.8: {}