diff --git a/packages/react-hooks/src/hooks/useRealtime.ts b/packages/react-hooks/src/hooks/useRealtime.ts index 9492c085de..f154fb0b40 100644 --- a/packages/react-hooks/src/hooks/useRealtime.ts +++ b/packages/react-hooks/src/hooks/useRealtime.ts @@ -9,6 +9,7 @@ import { } from "@trigger.dev/core/v3"; import { useCallback, useEffect, useId, useRef, useState } from "react"; import { KeyedMutator, useSWR } from "../utils/trigger-swr.js"; +import { logIfFetcherPresent } from "../utils/swrMiddleware.js"; import { useApiClient, UseApiClientOptions } from "./useApiClient.js"; import { createThrottledQueue } from "../utils/throttle.js"; @@ -78,17 +79,23 @@ export function useRealtimeRun( const idKey = options?.id ?? hookId; // Store the streams state in SWR, using the idKey as the key to share states. - const { data: run, mutate: mutateRun } = useSWR>([idKey, "run"], null); + const { data: run, mutate: mutateRun } = useSWR>( + [idKey, "run"], + null, + { use: [logIfFetcherPresent("realtime:run")] } + ); const { data: error = undefined, mutate: setError } = useSWR( [idKey, "error"], - null + null, + { use: [logIfFetcherPresent("realtime:error")] } ); // Add state to track when the subscription is complete const { data: isComplete = false, mutate: setIsComplete } = useSWR( [idKey, "complete"], - null + null, + { use: [logIfFetcherPresent("realtime:complete")] } ); // Abort controller to cancel the current API call. @@ -229,6 +236,7 @@ export function useRealtimeRunWithStreams< null, { fallbackData: initialStreamsFallback, + use: [logIfFetcherPresent("realtime:streams")], } ); @@ -239,17 +247,23 @@ export function useRealtimeRunWithStreams< }, [streams]); // Store the streams state in SWR, using the idKey as the key to share states. - const { data: run, mutate: mutateRun } = useSWR>([idKey, "run"], null); + const { data: run, mutate: mutateRun } = useSWR>( + [idKey, "run"], + null, + { use: [logIfFetcherPresent("realtime:run")] } + ); // Add state to track when the subscription is complete const { data: isComplete = false, mutate: setIsComplete } = useSWR( [idKey, "complete"], - null + null, + { use: [logIfFetcherPresent("realtime:complete")] } ); const { data: error = undefined, mutate: setError } = useSWR( [idKey, "error"], - null + null, + { use: [logIfFetcherPresent("realtime:error")] } ); // Abort controller to cancel the current API call. @@ -403,6 +417,7 @@ export function useRealtimeRunsWithTag( // Store the streams state in SWR, using the idKey as the key to share states. const { data: runs, mutate: mutateRuns } = useSWR[]>([idKey, "run"], null, { fallbackData: [], + use: [logIfFetcherPresent("realtime:runs")], }); // Keep the latest streams in a ref. @@ -413,7 +428,8 @@ export function useRealtimeRunsWithTag( const { data: error = undefined, mutate: setError } = useSWR( [idKey, "error"], - null + null, + { use: [logIfFetcherPresent("realtime:error")] } ); // Abort controller to cancel the current API call. @@ -501,6 +517,7 @@ export function useRealtimeBatch( // Store the streams state in SWR, using the idKey as the key to share states. const { data: runs, mutate: mutateRuns } = useSWR[]>([idKey, "run"], null, { fallbackData: [], + use: [logIfFetcherPresent("realtime:runs")], }); // Keep the latest streams in a ref. @@ -511,7 +528,8 @@ export function useRealtimeBatch( const { data: error = undefined, mutate: setError } = useSWR( [idKey, "error"], - null + null, + { use: [logIfFetcherPresent("realtime:error")] } ); // Abort controller to cancel the current API call. diff --git a/packages/react-hooks/src/utils/swrMiddleware.ts b/packages/react-hooks/src/utils/swrMiddleware.ts new file mode 100644 index 0000000000..a61a0532ba --- /dev/null +++ b/packages/react-hooks/src/utils/swrMiddleware.ts @@ -0,0 +1,27 @@ +"use client"; + +import type { Middleware } from "swr"; + +/** + * Middleware that logs if a fetcher is present. This helps detect cases where a global SWR fetcher + * might be injected into hooks that are intended to manage their own data (e.g. realtime hooks). + * + * This middleware is non-invasive: it does not modify the fetcher or behavior, it only logs in dev. + */ +export function logIfFetcherPresent(label: string): Middleware { + return (useSWRNext) => { + return (key, fetcher, config) => { + if (typeof fetcher === "function" && process.env.NODE_ENV !== "production") { + // eslint-disable-next-line no-console + console.warn( + `[trigger.dev][${label}] Detected a fetcher for SWR key. This hook is intended to manage its own data; an inherited global SWR fetcher may cause unintended requests. key:`, + key + ); + } + + return useSWRNext(key, fetcher, config); + }; + }; +} + +