From 8ce429507b9c627a19ff4ff235bd2cf1dd7401aa Mon Sep 17 00:00:00 2001 From: Sacha Al Himdani Date: Fri, 17 Apr 2026 15:08:13 +0200 Subject: [PATCH] Bump langsmith to 0.5.20 and override @langchain/classic to fix CVE langsmith <=0.5.18 leaks streamed LLM output past redaction controls (GHSA-rr7j-v2q5-chgv). The transitive pin via @n8n/ai-utilities held @langchain/classic at 1.0.5 which required langsmith ^0.3.64, blocking the upgrade. Add npm overrides for @langchain/classic (^1.0.27) and langsmith (^0.5.19) to resolve all three langsmith advisories. The lockfile refresh bumps eslint-plugin-react-hooks from 7.0.1 to 7.1.0 which enables the react-hooks/refs and set-state-in-effect rules. Fix the resulting lint errors: - PDFPreview (console + trust): read currentPage through a ref synced via useEffect, keeping the setState updater pure and ref access outside of render - MeasuresPage: remove redundant categoryFilter state that mirrored urlCategory, use a ref to detect URL changes and trigger refetch as the single source of category refetches - WebhooksSettingsPage: defer loadEvents via requestAnimationFrame to avoid synchronous setState within the effect body Signed-off-by: Sacha Al Himdani --- .../src/components/documents/PDFPreview.tsx | 27 +- .../organizations/measures/MeasuresPage.tsx | 20 +- .../settings/WebhooksSettingsPage.tsx | 3 +- apps/trust/src/components/PDFPreview.tsx | 27 +- package-lock.json | 468 +++++++++--------- package.json | 3 +- 6 files changed, 287 insertions(+), 261 deletions(-) diff --git a/apps/console/src/components/documents/PDFPreview.tsx b/apps/console/src/components/documents/PDFPreview.tsx index bc3224551..6cdfcdd3c 100644 --- a/apps/console/src/components/documents/PDFPreview.tsx +++ b/apps/console/src/components/documents/PDFPreview.tsx @@ -21,7 +21,7 @@ import { Spinner, } from "@probo/ui"; import { IconMinusLarge } from "@probo/ui/src/Atoms/Icons/IconMinusLarge"; -import { type ComponentProps, useRef, useState } from "react"; +import { type ComponentProps, useCallback, useEffect, useRef, useState } from "react"; import { Document, Page, pdfjs } from "react-pdf"; import "react-pdf/dist/Page/TextLayer.css"; @@ -51,11 +51,17 @@ export function PDFPreview({ src, name }: { src: string; name?: string }) { setScale(scale * factor); }; - const movePage = (direction: 1 | -1) => () => { - if (currentPage === 1 && direction === -1) { + const currentPageRef = useRef(currentPage); + useEffect(() => { + currentPageRef.current = currentPage; + }, [currentPage]); + + const movePage = useCallback((direction: 1 | -1) => { + const prev = currentPageRef.current; + if (prev === 1 && direction === -1) { return; } - const newPage = currentPage + direction; + const newPage = prev + direction; const page = documentRef.current?.pages.current[newPage - 1]; if (!page) { return; @@ -66,9 +72,9 @@ export function PDFPreview({ src, name }: { src: string; name?: string }) { inline: "center", }); setCurrentPage(newPage); - }; + }, []); - const resolveCurrentPage = () => { + const resolveCurrentPage = useCallback(() => { if (!wrapperRef.current) { return; } @@ -81,10 +87,11 @@ export function PDFPreview({ src, name }: { src: string; name?: string }) { for (let i = 0; i < pages.length; i++) { const childRect = pages[i].getBoundingClientRect(); if (childRect.top <= parentMiddleY && childRect.bottom >= parentMiddleY) { - return setCurrentPage(i + 1); + setCurrentPage(i + 1); + return; } } - }; + }, []); const handleDownload = () => { const link = document.createElement("a"); @@ -100,7 +107,7 @@ export function PDFPreview({ src, name }: { src: string; name?: string }) {
{name}
- diff --git a/apps/console/src/pages/organizations/measures/MeasuresPage.tsx b/apps/console/src/pages/organizations/measures/MeasuresPage.tsx index 3c8059527..7eddf8e71 100644 --- a/apps/console/src/pages/organizations/measures/MeasuresPage.tsx +++ b/apps/console/src/pages/organizations/measures/MeasuresPage.tsx @@ -185,8 +185,6 @@ export default function MeasuresPage({ queryRef }: MeasuresPageProps) { const [isPending, startTransition] = useTransition(); const [queryFilter, setQueryFilter] = useState(null); const [stateFilter, setStateFilter] = useState(null); - const [categoryFilter, setCategoryFilter] = useState(urlCategory); - const { data, loadNext, hasNext, isLoadingNext, refetch } = usePaginationFragment( measuresPageFragment, @@ -199,7 +197,7 @@ export default function MeasuresPage({ queryRef }: MeasuresPageProps) { { query: queryFilter, state: stateFilter, - category: categoryFilter, + category: urlCategory, ...overrides, }, { fetchPolicy: "network-only" }, @@ -208,6 +206,7 @@ export default function MeasuresPage({ queryRef }: MeasuresPageProps) { }; const initialUrlCategory = useRef(urlCategory); + const prevUrlCategory = useRef(urlCategory); useEffect(() => { if (initialUrlCategory.current) { startTransition(() => { @@ -224,12 +223,11 @@ export default function MeasuresPage({ queryRef }: MeasuresPageProps) { }, [refetch, startTransition]); useEffect(() => { - if (urlCategory !== categoryFilter) { - setCategoryFilter(urlCategory); + if (urlCategory !== prevUrlCategory.current) { + prevUrlCategory.current = urlCategory; refetchFilters({ category: urlCategory }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [urlCategory]); + }); const handleQueryFilterChange = (value: string) => { const newQuery = value === "" ? null : value; @@ -245,7 +243,6 @@ export default function MeasuresPage({ queryRef }: MeasuresPageProps) { const handleCategoryFilterChange = (value: string) => { const newCategory = value === "ALL" ? null : value; - setCategoryFilter(newCategory); setSearchParams((prev) => { const next = new URLSearchParams(prev); if (newCategory) { @@ -255,13 +252,12 @@ export default function MeasuresPage({ queryRef }: MeasuresPageProps) { } return next; }, { replace: true }); - refetchFilters({ category: newCategory }); }; const currentFilter = { query: queryFilter, state: stateFilter, - category: categoryFilter, + category: urlCategory, }; const connectionId = ConnectionHandler.getConnectionID( @@ -274,7 +270,7 @@ export default function MeasuresPage({ queryRef }: MeasuresPageProps) { MeasuresConnectionKey, { filter: { query: null, state: null, category: null } }, ); - const hasActiveFilter = queryFilter || stateFilter || categoryFilter; + const hasActiveFilter = queryFilter || stateFilter || urlCategory; const createConnectionIds = hasActiveFilter ? [allFiltersNullConnectionId, connectionId] : [connectionId]; @@ -362,7 +358,7 @@ export default function MeasuresPage({ queryRef }: MeasuresPageProps) {