Files
noteflow/.hygeine/biome.json
Travis Vasceannie 0a18f2d23d chore: update linting artifacts
- Updated basedpyright linting results (705 files analyzed, analysis time reduced from 22.928s to 13.105s).
- Updated biome linting artifact with warning about unnecessary hook dependency (preferencesVersion) in MeetingDetail.tsx.
2026-01-08 21:45:05 -05:00

2 lines
24 KiB
JSON

{"summary":{"changed":0,"unchanged":410,"matches":0,"duration":{"secs":0,"nanos":143328432},"scannerDuration":{"secs":0,"nanos":11027925},"errors":0,"warnings":2,"infos":0,"skipped":0,"suggestedFixesSkipped":0,"diagnosticsNotPrinted":0},"diagnostics":[{"category":"lint/correctness/useExhaustiveDependencies","severity":"warning","description":"This hook does not specify its dependency on fetcher.","message":[{"elements":[],"content":"This hook "},{"elements":["Emphasis"],"content":"does not specify"},{"elements":[],"content":" its dependency on "},{"elements":["Emphasis"],"content":"fetcher"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"This dependency is being used here, but is not specified in the hook dependency list."}]]},{"frame":{"path":null,"span":[3112,3119],"sourceCode":"/**\n * Generic async data fetching hook with loading and error states.\n *\n * Consolidates the common pattern of:\n * - Fetching data from an async source\n * - Tracking loading state\n * - Handling errors consistently\n * - Providing refetch capability\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { extractErrorMessage } from '@/api/helpers';\n\n/**\n * Result of an async data fetch operation.\n */\nexport interface AsyncDataResult<T> {\n /** The fetched data, or undefined if not yet loaded or on error */\n data: T | undefined;\n /** Whether a fetch is currently in progress */\n isLoading: boolean;\n /** Error message if the last fetch failed */\n error: string | null;\n /** Manually trigger a refetch */\n refetch: () => void;\n /** Reset to initial state (clear data, error, and loading) */\n reset: () => void;\n}\n\n/**\n * Options for useAsyncData hook.\n */\nexport interface UseAsyncDataOptions<T> {\n /** Initial data value (optional) */\n initialData?: T;\n /** Whether to skip the initial fetch (useful for conditional fetching) */\n skip?: boolean;\n /** Callback when fetch succeeds */\n onSuccess?: (data: T) => void;\n /** Callback when fetch fails */\n onError?: (error: string) => void;\n}\n\n/**\n * Hook for fetching async data with loading and error states.\n *\n * @param fetcher - Async function that returns the data\n * @param deps - Dependencies that trigger a refetch when changed\n * @param options - Optional configuration\n * @returns AsyncDataResult with data, loading state, error, and control functions\n *\n * @example\n * ```typescript\n * // Basic usage\n * const { data: meetings, isLoading, error } = useAsyncData(\n * () => api.listMeetings(),\n * []\n * );\n *\n * // With dependencies (refetch when meetingId changes)\n * const { data: meeting, isLoading } = useAsyncData(\n * () => api.getMeeting(meetingId),\n * [meetingId]\n * );\n *\n * // With options\n * const { data, refetch } = useAsyncData(\n * () => api.getSettings(),\n * [],\n * {\n * initialData: defaultSettings,\n * onSuccess: (data) => console.log('Loaded:', data),\n * onError: (err) => showToast(err),\n * }\n * );\n *\n * // Conditional fetching\n * const { data } = useAsyncData(\n * () => api.getMeeting(meetingId),\n * [meetingId],\n * { skip: !meetingId }\n * );\n * ```\n */\nexport function useAsyncData<T>(\n fetcher: () => Promise<T>,\n deps: readonly unknown[],\n options: UseAsyncDataOptions<T> = {}\n): AsyncDataResult<T> {\n const { initialData, skip = false, onSuccess, onError } = options;\n\n const [data, setData] = useState<T | undefined>(initialData);\n const [isLoading, setIsLoading] = useState(!skip);\n const [error, setError] = useState<string | null>(null);\n\n // Track if component is mounted to avoid state updates after unmount\n const isMountedRef = useRef(true);\n // Track current fetch to handle race conditions\n const fetchIdRef = useRef(0);\n\n const doFetch = useCallback(async () => {\n if (skip) {\n return;\n }\n\n const currentFetchId = ++fetchIdRef.current;\n\n setIsLoading(true);\n setError(null);\n\n try {\n const result = await fetcher();\n\n // Only update state if this is still the most recent fetch and component is mounted\n if (isMountedRef.current && currentFetchId === fetchIdRef.current) {\n setData(result);\n setIsLoading(false);\n onSuccess?.(result);\n }\n } catch (err) {\n if (isMountedRef.current && currentFetchId === fetchIdRef.current) {\n const errorMessage = extractErrorMessage(err);\n setError(errorMessage);\n setIsLoading(false);\n onError?.(errorMessage);\n }\n }\n // The fetcher is intentionally excluded - deps are controlled by caller\n // biome-ignore lint/correctness/useExhaustiveDependencies: fetcher is intentionally excluded, user controls deps\n }, [skip, onSuccess, onError, ...deps]);\n\n const refetch = useCallback(() => {\n void doFetch();\n }, [doFetch]);\n\n const reset = useCallback(() => {\n fetchIdRef.current++;\n setData(initialData);\n setIsLoading(false);\n setError(null);\n }, [initialData]);\n\n // Fetch on mount and when deps change\n useEffect(() => {\n void doFetch();\n }, [doFetch]);\n\n // Track mounted state\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n return { data, isLoading, error, refetch, reset };\n}\n\n/**\n * Result type for mutation operations.\n */\nexport interface MutationResult<TData, TVariables> {\n /** Execute the mutation */\n mutate: (variables: TVariables) => Promise<TData | undefined>;\n /** The result data from the last successful mutation */\n data: TData | undefined;\n /** Whether a mutation is currently in progress */\n isLoading: boolean;\n /** Error message if the last mutation failed */\n error: string | null;\n /** Reset the mutation state */\n reset: () => void;\n}\n\n/**\n * Options for useMutation hook.\n */\nexport interface UseMutationOptions<TData> {\n /** Callback when mutation succeeds */\n onSuccess?: (data: TData) => void;\n /** Callback when mutation fails */\n onError?: (error: string) => void;\n}\n\n/**\n * Hook for handling async mutations with loading and error states.\n *\n * @param mutationFn - Async function that performs the mutation\n * @param options - Optional callbacks\n * @returns MutationResult with mutate function, data, loading state, and error\n *\n * @example\n * ```typescript\n * const { mutate: createMeeting, isLoading } = useMutation(\n * (title: string) => api.createMeeting({ title }),\n * {\n * onSuccess: (meeting) => navigate(`/meeting/${meeting.id}`),\n * onError: (err) => showToast(err),\n * }\n * );\n *\n * // Usage\n * await createMeeting('My Meeting');\n * ```\n */\nexport function useMutation<TData, TVariables>(\n mutationFn: (variables: TVariables) => Promise<TData>,\n options: UseMutationOptions<TData> = {}\n): MutationResult<TData, TVariables> {\n const { onSuccess, onError } = options;\n\n const [data, setData] = useState<TData | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const isMountedRef = useRef(true);\n\n const mutate = useCallback(\n async (variables: TVariables): Promise<TData | undefined> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const result = await mutationFn(variables);\n\n if (isMountedRef.current) {\n setData(result);\n setIsLoading(false);\n onSuccess?.(result);\n }\n return result;\n } catch (err) {\n if (isMountedRef.current) {\n const errorMessage = extractErrorMessage(err);\n setError(errorMessage);\n setIsLoading(false);\n onError?.(errorMessage);\n }\n return undefined;\n }\n },\n [mutationFn, onSuccess, onError]\n );\n\n const reset = useCallback(() => {\n setData(undefined);\n setIsLoading(false);\n setError(null);\n }, []);\n\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n return { mutate, data, isLoading, error, reset };\n}\n"}},{"log":["info",[{"elements":[],"content":"React relies on hook dependencies to determine when to re-compute Effects.\nFailing to specify dependencies can result in Effects "},{"elements":["Emphasis"],"content":"not updating correctly"},{"elements":[],"content":" when state changes.\nThese \"stale closures\" are a common source of surprising bugs."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Add the missing dependency to the list."}]]},{"diff":{"dictionary":"/**\n * Generic async data fetching hook with loading and error states.\n * // The fetcher is intentionally excluded - deps are controlled by caller\n // biome-ignore lint/correctness/useExhaustiveDependencies: fetcher is intentionally excluded, user controls deps\n }, [skip, onSuccess, onError, ...deps, fetcher]);\n\n const refetch = useCallback(() => { return { mutate, data, isLoading, error, reset };\n}\n","ops":[{"diffOp":{"equal":{"range":[0,73]}}},{"equalLines":{"line_count":124}},{"diffOp":{"equal":{"range":[73,269]}}},{"diffOp":{"equal":{"range":[269,303]}}},{"diffOp":{"insert":{"range":[303,312]}}},{"diffOp":{"equal":{"range":[312,313]}}},{"diffOp":{"equal":{"range":[313,354]}}},{"equalLines":{"line_count":125}},{"diffOp":{"equal":{"range":[354,408]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/hooks/use-async-data.ts"},"span":[2917,2928],"sourceCode":"/**\n * Generic async data fetching hook with loading and error states.\n *\n * Consolidates the common pattern of:\n * - Fetching data from an async source\n * - Tracking loading state\n * - Handling errors consistently\n * - Providing refetch capability\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { extractErrorMessage } from '@/api/helpers';\n\n/**\n * Result of an async data fetch operation.\n */\nexport interface AsyncDataResult<T> {\n /** The fetched data, or undefined if not yet loaded or on error */\n data: T | undefined;\n /** Whether a fetch is currently in progress */\n isLoading: boolean;\n /** Error message if the last fetch failed */\n error: string | null;\n /** Manually trigger a refetch */\n refetch: () => void;\n /** Reset to initial state (clear data, error, and loading) */\n reset: () => void;\n}\n\n/**\n * Options for useAsyncData hook.\n */\nexport interface UseAsyncDataOptions<T> {\n /** Initial data value (optional) */\n initialData?: T;\n /** Whether to skip the initial fetch (useful for conditional fetching) */\n skip?: boolean;\n /** Callback when fetch succeeds */\n onSuccess?: (data: T) => void;\n /** Callback when fetch fails */\n onError?: (error: string) => void;\n}\n\n/**\n * Hook for fetching async data with loading and error states.\n *\n * @param fetcher - Async function that returns the data\n * @param deps - Dependencies that trigger a refetch when changed\n * @param options - Optional configuration\n * @returns AsyncDataResult with data, loading state, error, and control functions\n *\n * @example\n * ```typescript\n * // Basic usage\n * const { data: meetings, isLoading, error } = useAsyncData(\n * () => api.listMeetings(),\n * []\n * );\n *\n * // With dependencies (refetch when meetingId changes)\n * const { data: meeting, isLoading } = useAsyncData(\n * () => api.getMeeting(meetingId),\n * [meetingId]\n * );\n *\n * // With options\n * const { data, refetch } = useAsyncData(\n * () => api.getSettings(),\n * [],\n * {\n * initialData: defaultSettings,\n * onSuccess: (data) => console.log('Loaded:', data),\n * onError: (err) => showToast(err),\n * }\n * );\n *\n * // Conditional fetching\n * const { data } = useAsyncData(\n * () => api.getMeeting(meetingId),\n * [meetingId],\n * { skip: !meetingId }\n * );\n * ```\n */\nexport function useAsyncData<T>(\n fetcher: () => Promise<T>,\n deps: readonly unknown[],\n options: UseAsyncDataOptions<T> = {}\n): AsyncDataResult<T> {\n const { initialData, skip = false, onSuccess, onError } = options;\n\n const [data, setData] = useState<T | undefined>(initialData);\n const [isLoading, setIsLoading] = useState(!skip);\n const [error, setError] = useState<string | null>(null);\n\n // Track if component is mounted to avoid state updates after unmount\n const isMountedRef = useRef(true);\n // Track current fetch to handle race conditions\n const fetchIdRef = useRef(0);\n\n const doFetch = useCallback(async () => {\n if (skip) {\n return;\n }\n\n const currentFetchId = ++fetchIdRef.current;\n\n setIsLoading(true);\n setError(null);\n\n try {\n const result = await fetcher();\n\n // Only update state if this is still the most recent fetch and component is mounted\n if (isMountedRef.current && currentFetchId === fetchIdRef.current) {\n setData(result);\n setIsLoading(false);\n onSuccess?.(result);\n }\n } catch (err) {\n if (isMountedRef.current && currentFetchId === fetchIdRef.current) {\n const errorMessage = extractErrorMessage(err);\n setError(errorMessage);\n setIsLoading(false);\n onError?.(errorMessage);\n }\n }\n // The fetcher is intentionally excluded - deps are controlled by caller\n // biome-ignore lint/correctness/useExhaustiveDependencies: fetcher is intentionally excluded, user controls deps\n }, [skip, onSuccess, onError, ...deps]);\n\n const refetch = useCallback(() => {\n void doFetch();\n }, [doFetch]);\n\n const reset = useCallback(() => {\n fetchIdRef.current++;\n setData(initialData);\n setIsLoading(false);\n setError(null);\n }, [initialData]);\n\n // Fetch on mount and when deps change\n useEffect(() => {\n void doFetch();\n }, [doFetch]);\n\n // Track mounted state\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n return { data, isLoading, error, refetch, reset };\n}\n\n/**\n * Result type for mutation operations.\n */\nexport interface MutationResult<TData, TVariables> {\n /** Execute the mutation */\n mutate: (variables: TVariables) => Promise<TData | undefined>;\n /** The result data from the last successful mutation */\n data: TData | undefined;\n /** Whether a mutation is currently in progress */\n isLoading: boolean;\n /** Error message if the last mutation failed */\n error: string | null;\n /** Reset the mutation state */\n reset: () => void;\n}\n\n/**\n * Options for useMutation hook.\n */\nexport interface UseMutationOptions<TData> {\n /** Callback when mutation succeeds */\n onSuccess?: (data: TData) => void;\n /** Callback when mutation fails */\n onError?: (error: string) => void;\n}\n\n/**\n * Hook for handling async mutations with loading and error states.\n *\n * @param mutationFn - Async function that performs the mutation\n * @param options - Optional callbacks\n * @returns MutationResult with mutate function, data, loading state, and error\n *\n * @example\n * ```typescript\n * const { mutate: createMeeting, isLoading } = useMutation(\n * (title: string) => api.createMeeting({ title }),\n * {\n * onSuccess: (meeting) => navigate(`/meeting/${meeting.id}`),\n * onError: (err) => showToast(err),\n * }\n * );\n *\n * // Usage\n * await createMeeting('My Meeting');\n * ```\n */\nexport function useMutation<TData, TVariables>(\n mutationFn: (variables: TVariables) => Promise<TData>,\n options: UseMutationOptions<TData> = {}\n): MutationResult<TData, TVariables> {\n const { onSuccess, onError } = options;\n\n const [data, setData] = useState<TData | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const isMountedRef = useRef(true);\n\n const mutate = useCallback(\n async (variables: TVariables): Promise<TData | undefined> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const result = await mutationFn(variables);\n\n if (isMountedRef.current) {\n setData(result);\n setIsLoading(false);\n onSuccess?.(result);\n }\n return result;\n } catch (err) {\n if (isMountedRef.current) {\n const errorMessage = extractErrorMessage(err);\n setError(errorMessage);\n setIsLoading(false);\n onError?.(errorMessage);\n }\n return undefined;\n }\n },\n [mutationFn, onSuccess, onError]\n );\n\n const reset = useCallback(() => {\n setData(undefined);\n setIsLoading(false);\n setError(null);\n }, []);\n\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n return { mutate, data, isLoading, error, reset };\n}\n"},"tags":["fixable"],"source":null},{"category":"suppressions/unused","severity":"warning","description":"Suppression comment has no effect. Remove the suppression or make sure you are suppressing the correct rule.","message":[{"elements":[],"content":"Suppression comment has no effect. Remove the suppression or make sure you are suppressing the correct rule."}],"advices":{"advices":[]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/hooks/use-async-data.ts"},"span":[3716,3829],"sourceCode":"/**\n * Generic async data fetching hook with loading and error states.\n *\n * Consolidates the common pattern of:\n * - Fetching data from an async source\n * - Tracking loading state\n * - Handling errors consistently\n * - Providing refetch capability\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { extractErrorMessage } from '@/api/helpers';\n\n/**\n * Result of an async data fetch operation.\n */\nexport interface AsyncDataResult<T> {\n /** The fetched data, or undefined if not yet loaded or on error */\n data: T | undefined;\n /** Whether a fetch is currently in progress */\n isLoading: boolean;\n /** Error message if the last fetch failed */\n error: string | null;\n /** Manually trigger a refetch */\n refetch: () => void;\n /** Reset to initial state (clear data, error, and loading) */\n reset: () => void;\n}\n\n/**\n * Options for useAsyncData hook.\n */\nexport interface UseAsyncDataOptions<T> {\n /** Initial data value (optional) */\n initialData?: T;\n /** Whether to skip the initial fetch (useful for conditional fetching) */\n skip?: boolean;\n /** Callback when fetch succeeds */\n onSuccess?: (data: T) => void;\n /** Callback when fetch fails */\n onError?: (error: string) => void;\n}\n\n/**\n * Hook for fetching async data with loading and error states.\n *\n * @param fetcher - Async function that returns the data\n * @param deps - Dependencies that trigger a refetch when changed\n * @param options - Optional configuration\n * @returns AsyncDataResult with data, loading state, error, and control functions\n *\n * @example\n * ```typescript\n * // Basic usage\n * const { data: meetings, isLoading, error } = useAsyncData(\n * () => api.listMeetings(),\n * []\n * );\n *\n * // With dependencies (refetch when meetingId changes)\n * const { data: meeting, isLoading } = useAsyncData(\n * () => api.getMeeting(meetingId),\n * [meetingId]\n * );\n *\n * // With options\n * const { data, refetch } = useAsyncData(\n * () => api.getSettings(),\n * [],\n * {\n * initialData: defaultSettings,\n * onSuccess: (data) => console.log('Loaded:', data),\n * onError: (err) => showToast(err),\n * }\n * );\n *\n * // Conditional fetching\n * const { data } = useAsyncData(\n * () => api.getMeeting(meetingId),\n * [meetingId],\n * { skip: !meetingId }\n * );\n * ```\n */\nexport function useAsyncData<T>(\n fetcher: () => Promise<T>,\n deps: readonly unknown[],\n options: UseAsyncDataOptions<T> = {}\n): AsyncDataResult<T> {\n const { initialData, skip = false, onSuccess, onError } = options;\n\n const [data, setData] = useState<T | undefined>(initialData);\n const [isLoading, setIsLoading] = useState(!skip);\n const [error, setError] = useState<string | null>(null);\n\n // Track if component is mounted to avoid state updates after unmount\n const isMountedRef = useRef(true);\n // Track current fetch to handle race conditions\n const fetchIdRef = useRef(0);\n\n const doFetch = useCallback(async () => {\n if (skip) {\n return;\n }\n\n const currentFetchId = ++fetchIdRef.current;\n\n setIsLoading(true);\n setError(null);\n\n try {\n const result = await fetcher();\n\n // Only update state if this is still the most recent fetch and component is mounted\n if (isMountedRef.current && currentFetchId === fetchIdRef.current) {\n setData(result);\n setIsLoading(false);\n onSuccess?.(result);\n }\n } catch (err) {\n if (isMountedRef.current && currentFetchId === fetchIdRef.current) {\n const errorMessage = extractErrorMessage(err);\n setError(errorMessage);\n setIsLoading(false);\n onError?.(errorMessage);\n }\n }\n // The fetcher is intentionally excluded - deps are controlled by caller\n // biome-ignore lint/correctness/useExhaustiveDependencies: fetcher is intentionally excluded, user controls deps\n }, [skip, onSuccess, onError, ...deps]);\n\n const refetch = useCallback(() => {\n void doFetch();\n }, [doFetch]);\n\n const reset = useCallback(() => {\n fetchIdRef.current++;\n setData(initialData);\n setIsLoading(false);\n setError(null);\n }, [initialData]);\n\n // Fetch on mount and when deps change\n useEffect(() => {\n void doFetch();\n }, [doFetch]);\n\n // Track mounted state\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n return { data, isLoading, error, refetch, reset };\n}\n\n/**\n * Result type for mutation operations.\n */\nexport interface MutationResult<TData, TVariables> {\n /** Execute the mutation */\n mutate: (variables: TVariables) => Promise<TData | undefined>;\n /** The result data from the last successful mutation */\n data: TData | undefined;\n /** Whether a mutation is currently in progress */\n isLoading: boolean;\n /** Error message if the last mutation failed */\n error: string | null;\n /** Reset the mutation state */\n reset: () => void;\n}\n\n/**\n * Options for useMutation hook.\n */\nexport interface UseMutationOptions<TData> {\n /** Callback when mutation succeeds */\n onSuccess?: (data: TData) => void;\n /** Callback when mutation fails */\n onError?: (error: string) => void;\n}\n\n/**\n * Hook for handling async mutations with loading and error states.\n *\n * @param mutationFn - Async function that performs the mutation\n * @param options - Optional callbacks\n * @returns MutationResult with mutate function, data, loading state, and error\n *\n * @example\n * ```typescript\n * const { mutate: createMeeting, isLoading } = useMutation(\n * (title: string) => api.createMeeting({ title }),\n * {\n * onSuccess: (meeting) => navigate(`/meeting/${meeting.id}`),\n * onError: (err) => showToast(err),\n * }\n * );\n *\n * // Usage\n * await createMeeting('My Meeting');\n * ```\n */\nexport function useMutation<TData, TVariables>(\n mutationFn: (variables: TVariables) => Promise<TData>,\n options: UseMutationOptions<TData> = {}\n): MutationResult<TData, TVariables> {\n const { onSuccess, onError } = options;\n\n const [data, setData] = useState<TData | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const isMountedRef = useRef(true);\n\n const mutate = useCallback(\n async (variables: TVariables): Promise<TData | undefined> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const result = await mutationFn(variables);\n\n if (isMountedRef.current) {\n setData(result);\n setIsLoading(false);\n onSuccess?.(result);\n }\n return result;\n } catch (err) {\n if (isMountedRef.current) {\n const errorMessage = extractErrorMessage(err);\n setError(errorMessage);\n setIsLoading(false);\n onError?.(errorMessage);\n }\n return undefined;\n }\n },\n [mutationFn, onSuccess, onError]\n );\n\n const reset = useCallback(() => {\n setData(undefined);\n setIsLoading(false);\n setError(null);\n }, []);\n\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n return { mutate, data, isLoading, error, reset };\n}\n"},"tags":[],"source":null}],"command":"lint"}