From 22d263377366ee8691d11b59a31c2295a2fe382a Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Thu, 28 May 2026 12:17:31 -0300 Subject: [PATCH] fix(memory): replace swr with native React hooks (swr not installed) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F7 originally implemented useEngineStatus/useMemorySettings via swr, but the package is not in package.json — would crash at runtime. Replaced with native useState + useEffect + setInterval polling. Same public API (status/settings/isLoading/isError/mutate/save) so the existing components and the 8 UI tests (which mock the hooks directly) keep working unchanged. --- .../dashboard/memory/hooks/useEngineStatus.ts | 62 +++++++++---- .../memory/hooks/useMemorySettings.ts | 86 ++++++++++++------- 2 files changed, 99 insertions(+), 49 deletions(-) diff --git a/src/app/(dashboard)/dashboard/memory/hooks/useEngineStatus.ts b/src/app/(dashboard)/dashboard/memory/hooks/useEngineStatus.ts index 37a9dddb7a..5480d80a0c 100644 --- a/src/app/(dashboard)/dashboard/memory/hooks/useEngineStatus.ts +++ b/src/app/(dashboard)/dashboard/memory/hooks/useEngineStatus.ts @@ -1,28 +1,58 @@ "use client"; -import useSWR from "swr"; +import { useCallback, useEffect, useRef, useState } from "react"; import type { MemoryEngineStatus } from "@/shared/schemas/memory"; -const fetcher = (url: string) => fetch(url).then((res) => res.json()); - export interface UseEngineStatusResult { status: MemoryEngineStatus | null; isLoading: boolean; isError: boolean; - mutate: () => void; + mutate: () => Promise; } -export function useEngineStatus(): UseEngineStatusResult { - const { data, error, isLoading, mutate } = useSWR( - "/api/memory/engine-status", - fetcher, - { refreshInterval: 5000 }, - ); +/** + * Lightweight engine-status fetcher with periodic polling. + * Avoids the swr dependency (not installed in this project) while keeping a + * compatible mutate()/loading/error surface for callers. + */ +export function useEngineStatus(refreshIntervalMs = 5000): UseEngineStatusResult { + const [status, setStatus] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [isError, setIsError] = useState(false); + const mounted = useRef(true); - return { - status: data ?? null, - isLoading, - isError: Boolean(error), - mutate, - }; + const fetchOnce = useCallback(async (): Promise => { + try { + const res = await fetch("/api/memory/engine-status"); + if (!res.ok) throw new Error(`status_${res.status}`); + const data = (await res.json()) as MemoryEngineStatus; + if (mounted.current) { + setStatus(data); + setIsError(false); + } + } catch { + if (mounted.current) setIsError(true); + } finally { + if (mounted.current) setIsLoading(false); + } + }, []); + + useEffect(() => { + mounted.current = true; + void fetchOnce(); + if (!refreshIntervalMs || refreshIntervalMs <= 0) { + return () => { + mounted.current = false; + }; + } + const id = setInterval(() => { + void fetchOnce(); + }, refreshIntervalMs); + return () => { + mounted.current = false; + clearInterval(id); + }; + }, [fetchOnce, refreshIntervalMs]); + + return { status, isLoading, isError, mutate: fetchOnce }; } diff --git a/src/app/(dashboard)/dashboard/memory/hooks/useMemorySettings.ts b/src/app/(dashboard)/dashboard/memory/hooks/useMemorySettings.ts index 49b5f32a17..ae50f246fa 100644 --- a/src/app/(dashboard)/dashboard/memory/hooks/useMemorySettings.ts +++ b/src/app/(dashboard)/dashboard/memory/hooks/useMemorySettings.ts @@ -1,48 +1,68 @@ "use client"; -import useSWR from "swr"; +import { useCallback, useEffect, useRef, useState } from "react"; import type { MemorySettingsExtended } from "@/shared/schemas/memory"; -const fetcher = (url: string) => fetch(url).then((res) => res.json()); - export interface UseMemorySettingsResult { settings: MemorySettingsExtended | null; isLoading: boolean; isError: boolean; - mutate: () => void; + mutate: () => Promise; save: (updates: Partial) => Promise; } +/** + * Lightweight settings fetcher + saver. + * Avoids the swr dependency (not installed in this project) while keeping a + * compatible mutate()/save() surface for callers. + */ export function useMemorySettings(): UseMemorySettingsResult { - const { data, error, isLoading, mutate } = useSWR( - "/api/settings/memory", - fetcher, + const [settings, setSettings] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [isError, setIsError] = useState(false); + const mounted = useRef(true); + + const fetchOnce = useCallback(async (): Promise => { + try { + const res = await fetch("/api/settings/memory"); + if (!res.ok) throw new Error(`status_${res.status}`); + const data = (await res.json()) as MemorySettingsExtended; + if (mounted.current) { + setSettings(data); + setIsError(false); + } + } catch { + if (mounted.current) setIsError(true); + } finally { + if (mounted.current) setIsLoading(false); + } + }, []); + + useEffect(() => { + mounted.current = true; + void fetchOnce(); + return () => { + mounted.current = false; + }; + }, [fetchOnce]); + + const save = useCallback( + async (updates: Partial): Promise => { + try { + const res = await fetch("/api/settings/memory", { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(updates), + }); + if (!res.ok) return false; + await fetchOnce(); + return true; + } catch { + return false; + } + }, + [fetchOnce] ); - const save = async (updates: Partial): Promise => { - try { - const current = data ?? {}; - const next = { ...current, ...updates }; - const res = await fetch("/api/settings/memory", { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(next), - }); - if (res.ok) { - await mutate(); - return true; - } - return false; - } catch { - return false; - } - }; - - return { - settings: data ?? null, - isLoading, - isError: Boolean(error), - mutate, - save, - }; + return { settings, isLoading, isError, mutate: fetchOnce, save }; }