AUDIT / src /hooks /useBootEffects.ts
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 2)
cc11e77 verified
Raw
History Blame Contribute Delete
6.83 kB
/**
* useBootEffects.ts — S-BE1: Tutti gli effetti di bootstrap dell'app
*
* Estratto da MainWorkspace.tsx (S-BE1):
* - hydrateKnowledgeFromCloud (cloud context)
* - prewarmProviders (TCP/TLS pre-connect S316)
* - autoRecoverTasks (S696 task recovery)
* - agentLoop chunk preload (S439 requestIdleCallback)
* - agent:stream-chunk SSE listener (S422)
* - useStorageAutoPrune / useSafariLifecycle / useSafariMemory
* - selfDiag (autodiagnosi critica)
* - VFS → previewHtml + setTab("preview")
* - agente:preview_open event listener
* - sessionStorage cleanup on unload
* - ghListCommits fire-and-forget
*
* Nessun parametro, nessun valore di ritorno — puri side-effects.
* Usa getState() per setPreviewHtml/setActiveTab → zero stale-closure deps.
*/
import { useEffect } from "react";
import { useStorageAutoPrune } from "@/hooks/useStorageQuota";
import { useSafariLifecycle } from "@/hooks/useSafariLifecycle";
import { useSafariMemory } from "@/hooks/useSafariMemory";
import { hydrateKnowledgeFromCloud } from "@/lib/contextBridge";
import { prewarmProviders } from "@/lib/providerChain";
import { ghListCommits, getRepoConfig } from "@/lib/github";
import { vfsAsync, onVfsChanged } from "@/lib/vfsDb";
import { selfDiag } from "@/lib/selfDiag";
import { type Message } from "@/lib/storage";
import { useConversationStore } from "@/store/conversationStore";
import { useWorkspaceStore } from "@/store/workspaceStore";
import { useUIStore } from "@/store/uiStore";
import { useChatStore } from "@/store/chatStore";
// ── agentLoop lazy cache (modulo-level, condiviso con gli effetti del hook) ──
let _agentLoopPromise: Promise<typeof import("@/lib/agentLoop")> | null = null;
const _loadAgentLoop = (): Promise<typeof import("@/lib/agentLoop")> =>
(_agentLoopPromise ??= import("@/lib/agentLoop"));
// ─────────────────────────────────────────────────────────────────────────────
export function useBootEffects(): void {
// ── Cloud context hydration ───────────────────────────────────────────────
useEffect(() => { hydrateKnowledgeFromCloud(); }, []);
// ── S316: prewarm TCP/TLS verso Groq — riduce cold-start ~300ms su mobile/Safari
useEffect(() => { prewarmProviders(); }, []);
// ── S696: auto-recovery — drena task interrotti < 10min fa (max 3 task) ──
useEffect(() => {
import("@/lib/taskRecovery").then(m => m.autoRecoverTasks()).catch(() => {});
}, []); // eslint-disable-line react-hooks/exhaustive-deps
// ── S439: preload agentLoop chunk via requestIdleCallback ─────────────────
// Garantisce chunk in memoria prima del primo invio messaggio.
// Fallback setTimeout 2s se rIC non supportato.
useEffect(() => {
const load = () => { _loadAgentLoop().catch(() => {}); };
if (typeof requestIdleCallback !== "undefined") {
const id = requestIdleCallback(load, { timeout: 3_000 });
return () => cancelIdleCallback(id);
}
const t = setTimeout(load, 2_000);
return () => clearTimeout(t);
}, []);
// ── S422: SSE token listener → chatStore (live stream senza re-render completo)
useEffect(() => {
const handler = (e: Event) => {
const ev = e as CustomEvent<{ taskId: string; token: string }>;
useChatStore.getState().appendStreamChunk(ev.detail.taskId, ev.detail.token);
};
window.addEventListener("agent:stream-chunk", handler);
return () => window.removeEventListener("agent:stream-chunk", handler);
}, []);
// ── Storage / Safari lifecycle hooks ─────────────────────────────────────
useStorageAutoPrune();
useSafariLifecycle();
useSafariMemory();
// ── selfDiag — inietta messaggio critico in conversazione attiva ──────────
useEffect(() => {
selfDiag.start();
return selfDiag.onCritical(report => {
const sysMsg: Message = {
id: crypto.randomUUID(), role: "assistant",
content: `🔍 **Autodiagnosi:** ${report}`,
createdAt: Date.now(),
};
useConversationStore.getState().setMessages(prev => [...prev, sysMsg]);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// ── VFS → previewHtml: apre tab preview quando VFS produce un .html ───────
// Usa getState() — i setter Zustand sono stabili, zero stale-closure deps.
useEffect(() => {
const check = () => {
vfsAsync.list()
.then(files => {
const html = files
.filter(f => f.name.endsWith(".html") || f.path.endsWith(".html"))
.sort((a, b) => b.updatedAt - a.updatedAt)[0];
if (html?.content) {
useWorkspaceStore.getState().setPreviewHtml({ content: html.content, name: html.name });
useUIStore.getState().setActiveTab("preview");
}
})
.catch(() => {});
};
return onVfsChanged(check);
}, []);
// ── agente:preview_open — preview HTML inline da tool output ─────────────
useEffect(() => {
const handler = (e: Event) => {
const detail = (e as CustomEvent<{ path?: string; html?: string; name?: string }>).detail;
if (detail?.html) {
const fname = detail.name ?? detail.path?.split("/").pop() ?? "preview.html";
useWorkspaceStore.getState().setPreviewHtml({ content: detail.html, name: fname });
useUIStore.getState().setActiveTab("preview");
}
};
window.addEventListener("agente:preview_open", handler);
return () => window.removeEventListener("agente:preview_open", handler);
}, []);
// ── sessionStorage cleanup on unload ─────────────────────────────────────
useEffect(() => {
const cleanup = () => { try { sessionStorage.removeItem("agente_active_id"); } catch { /**/ } };
window.addEventListener("beforeunload", cleanup);
return () => window.removeEventListener("beforeunload", cleanup);
}, []);
// ── GH recent commit (fire-and-forget) ───────────────────────────────────
useEffect(() => {
const { owner, repo } = getRepoConfig();
ghListCommits(owner, repo, 1).catch(() => {});
}, []);
}