@datarecce/ui 1.52.0 → 1.53.0-nightly.20260611
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.d.ts +3 -3
- package/dist/advanced.js +1 -1
- package/dist/advanced.js.map +1 -1
- package/dist/api-DTKI1Y_n.js +3 -0
- package/dist/{api-S5ho3Qs3.js.map → api-DTKI1Y_n.js.map} +1 -1
- package/dist/api.js +1 -1
- package/dist/components-C1oqsBU3.js +3 -0
- package/dist/components-C1oqsBU3.js.map +1 -0
- package/dist/components-run.js +1 -1
- package/dist/components.d.ts +3 -3
- package/dist/components.js +1 -1
- package/dist/contexts.js +1 -1
- package/dist/{fetchClient-D0p358nB.js → fetchClient-Bf9Q3QKq.js} +2 -2
- package/dist/{fetchClient-D0p358nB.js.map → fetchClient-Bf9Q3QKq.js.map} +1 -1
- package/dist/hooks.js +1 -1
- package/dist/{index-DjuwVxKT.d.ts → index-Cl16UDPD.d.ts} +2 -2
- package/dist/{index-DjuwVxKT.d.ts.map → index-Cl16UDPD.d.ts.map} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/{keepAlive-bjIHulj-.js → keepAlive-Bowms1oa.js} +2 -2
- package/dist/{keepAlive-bjIHulj-.js.map → keepAlive-Bowms1oa.js.map} +1 -1
- package/dist/lib/api/user.js +1 -1
- package/dist/lineage-C6YQCnhM.js +7 -0
- package/dist/lineage-C6YQCnhM.js.map +1 -0
- package/dist/{lineage-2nc0YlpN.d.ts → lineage-DZl7z5RZ.d.ts} +4 -4
- package/dist/{lineage-2nc0YlpN.d.ts.map → lineage-DZl7z5RZ.d.ts.map} +1 -1
- package/dist/{primitives-Dvljxc6u.d.ts → primitives-DEcmeDKq.d.ts} +3 -3
- package/dist/{primitives-Dvljxc6u.d.ts.map → primitives-DEcmeDKq.d.ts.map} +1 -1
- package/dist/primitives.d.ts +1 -1
- package/dist/primitives.js +1 -1
- package/dist/result.js +1 -1
- package/dist/src-D9arJm8F.js +3 -0
- package/dist/src-D9arJm8F.js.map +1 -0
- package/dist/style.css +23 -23
- package/dist/theme.js +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/utils-BzZEjJAS.js +3 -0
- package/dist/{utils-FOrjYCpW.js.map → utils-BzZEjJAS.js.map} +1 -1
- package/dist/utils-D1bvitEj.js +6 -0
- package/dist/utils-D1bvitEj.js.map +1 -0
- package/dist/utils.js +1 -1
- package/package.json +1 -1
- package/dist/api-S5ho3Qs3.js +0 -3
- package/dist/hooks-CrJq15DU.js +0 -7
- package/dist/hooks-CrJq15DU.js.map +0 -1
- package/dist/src-DI-sLY25.js +0 -12
- package/dist/src-DI-sLY25.js.map +0 -1
- package/dist/utils-Dl2dMzK8.js +0 -6
- package/dist/utils-Dl2dMzK8.js.map +0 -1
- package/dist/utils-FOrjYCpW.js +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils-FOrjYCpW.js","names":["defaultApiClient","defaultApiClient"],"sources":["../src/providers/contexts/RouteConfigContext.tsx","../src/contexts/action/RecceActionContext.tsx","../src/contexts/instance/types.ts","../src/contexts/instance/useRecceInstanceInfo.ts","../src/contexts/instance/RecceInstanceContext.tsx","../src/contexts/idle/types.ts","../src/contexts/idle/useIdleDetection.ts","../src/contexts/idle/IdleTimeoutContext.tsx","../src/contexts/lineage/LineageGraphContext.tsx","../src/contexts/lineage/LineageViewContext.tsx","../src/contexts/lineage/types.ts","../src/contexts/lineage/useRecceServerFlag.ts","../src/components/lineage/computeColumnLineage.ts","../src/contexts/lineage/utils.ts"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n} from \"react\";\n\n/**\n * Route Configuration for path prefix customization.\n *\n * This context allows cloud consumers to configure a base path prefix\n * for all navigation within OSS components.\n *\n * Default behavior (OSS):\n * - basePath: \"\" (uses absolute paths like /query, /checks)\n *\n * Cloud usage example:\n * - basePath: \"/oss/abc123\" or \"/preview/abc123\"\n * - Navigation to \"/query\" becomes \"/oss/abc123/query\"\n */\n\n/**\n * Route configuration interface\n */\nexport interface RouteConfig {\n /**\n * Base path prefix for navigation.\n * For OSS: \"\" (empty string, uses absolute paths like /query)\n * For Cloud: \"/oss/<sessionId>\" or \"/preview/<sessionId>\"\n */\n basePath: string;\n}\n\n/**\n * Context value with path resolution utility\n */\nexport interface RouteConfigContextType extends RouteConfig {\n /**\n * Resolves a path with the base path prefix.\n * @param path - The path to resolve (e.g., \"/query\")\n * @returns The resolved path (e.g., \"/oss/abc123/query\")\n */\n resolvePath: (path: string) => string;\n}\n\nconst defaultConfig: RouteConfig = {\n basePath: \"\",\n};\n\nconst RouteConfigContext = createContext<RouteConfigContextType | null>(null);\nRouteConfigContext.displayName = \"RouteConfigContext\";\n\n/**\n * Props for RouteConfigProvider\n */\nexport interface RouteConfigProviderProps extends Partial<RouteConfig> {\n children: ReactNode;\n}\n\n/**\n * Provider for route configuration.\n *\n * Wrap your application (or RecceContextProvider) with this provider\n * to configure path prefixes for navigation.\n *\n * @example\n * ```tsx\n * // In cloud application\n * <RouteConfigProvider basePath={`/oss/${sessionId}`}>\n * <RecceContextProvider>\n * {children}\n * </RecceContextProvider>\n * </RouteConfigProvider>\n * ```\n */\nexport function RouteConfigProvider({\n children,\n basePath = defaultConfig.basePath,\n}: RouteConfigProviderProps) {\n const resolvePath = useCallback(\n (path: string): string => {\n // If no basePath configured, return path as-is (OSS mode)\n if (!basePath) {\n return path;\n }\n\n // Handle paths that already start with the basePath (avoid double-prefixing)\n if (path.startsWith(basePath)) {\n return path;\n }\n\n // Handle absolute URLs (http://, https://, etc.) - don't prefix\n if (path.match(/^https?:\\/\\//)) {\n return path;\n }\n\n // Handle hash-only paths - don't prefix\n if (path.startsWith(\"#\")) {\n return path;\n }\n\n // Ensure proper joining (no double slashes)\n const cleanBasePath = basePath.endsWith(\"/\")\n ? basePath.slice(0, -1)\n : basePath;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n\n return `${cleanBasePath}${cleanPath}`;\n },\n [basePath],\n );\n\n const contextValue: RouteConfigContextType = useMemo(\n () => ({\n basePath,\n resolvePath,\n }),\n [basePath, resolvePath],\n );\n\n return (\n <RouteConfigContext.Provider value={contextValue}>\n {children}\n </RouteConfigContext.Provider>\n );\n}\n\n// Default context for OSS mode (no prefix)\nconst defaultRouteConfigContext: RouteConfigContextType = {\n basePath: \"\",\n resolvePath: (path: string) => path,\n};\n\n/**\n * Hook to access route configuration.\n *\n * When used outside RouteConfigProvider, returns default config\n * (for OSS backward compatibility).\n *\n * @returns RouteConfigContextType with basePath and resolvePath function\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { basePath, resolvePath } = useRouteConfig();\n * const fullPath = resolvePath('/checks');\n * // In cloud with basePath=\"/oss/abc123\": \"/oss/abc123/checks\"\n * // In OSS with basePath=\"\": \"/checks\"\n * }\n * ```\n */\nexport function useRouteConfig(): RouteConfigContextType {\n const context = useContext(RouteConfigContext);\n // Return default config if outside provider (OSS mode)\n return context ?? defaultRouteConfigContext;\n}\n\n/**\n * Safe hook that returns null if context not available.\n * Useful for components that need to detect if RouteConfigProvider is present.\n */\nexport function useRouteConfigSafe(): RouteConfigContextType | null {\n return useContext(RouteConfigContext);\n}\n","\"use client\";\n\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from \"react\";\nimport type {\n AxiosQueryParams,\n RecceActionContextType,\n RecceActionOptions,\n} from \"./types\";\n\n/**\n * Props for RecceActionProvider\n *\n * This is a props-driven provider - pass your own run execution handler.\n * The provider manages UI state (result pane, history panel) internally.\n */\nexport interface RecceActionProviderProps {\n children: ReactNode;\n\n /**\n * Handler called when a run action is requested.\n * Implement this to submit runs to your backend.\n *\n * @param type - The run type (e.g., \"row_count_diff\", \"value_diff\")\n * @param params - Query parameters for the run\n * @param options - Action options (showForm, trackProps)\n * @returns Promise that resolves to the run ID, or void if handled differently\n */\n onRunAction?: (\n type: string,\n params?: AxiosQueryParams,\n options?: RecceActionOptions,\n ) => Promise<string | undefined> | string | undefined;\n\n /**\n * Handler called when a run result should be shown.\n * Called by showRunId when displaying a specific run.\n *\n * @param runId - The run ID to display\n * @param refreshHistory - Whether to refresh the history list\n */\n onShowRunId?: (runId: string, refreshHistory?: boolean) => void;\n\n /** Initial run ID to display (optional) */\n initialRunId?: string;\n\n /** Initial state for history panel (default: false) */\n initialHistoryOpen?: boolean;\n}\n\nconst defaultContext: RecceActionContextType = {\n runAction: () => void 0,\n showRunId: () => void 0,\n isRunResultOpen: false,\n closeRunResult: () => void 0,\n isHistoryOpen: false,\n closeHistory: () => void 0,\n showHistory: () => void 0,\n setHistoryOpen: () => void 0,\n clearRunResult: () => void 0,\n};\n\nconst RecceActionContext =\n createContext<RecceActionContextType>(defaultContext);\nRecceActionContext.displayName = \"RecceActionContext\";\n\n/**\n * Provider for run action context.\n *\n * This is a props-driven provider designed for library consumers.\n * Pass your own `onRunAction` handler to execute runs.\n *\n * @example\n * ```tsx\n * const handleRunAction = async (type, params, options) => {\n * const response = await api.submitRun(type, params);\n * return response.run_id;\n * };\n *\n * <RecceActionProvider onRunAction={handleRunAction}>\n * <LineageView />\n * </RecceActionProvider>\n * ```\n */\nexport function RecceActionProvider({\n children,\n onRunAction,\n onShowRunId,\n initialRunId,\n initialHistoryOpen = false,\n}: RecceActionProviderProps) {\n // Run result pane state\n const [isRunResultOpen, setRunResultOpen] = useState(!!initialRunId);\n const [runId, setRunId] = useState<string | undefined>(initialRunId);\n\n // History panel state\n const [isHistoryOpen, setHistoryOpen] = useState(initialHistoryOpen);\n\n // Close run result pane\n const closeRunResult = useCallback(() => {\n setRunResultOpen(false);\n }, []);\n\n // Clear run result and close pane\n const clearRunResult = useCallback(() => {\n setRunId(undefined);\n setRunResultOpen(false);\n }, []);\n\n // History panel controls\n const showHistory = useCallback(() => {\n setHistoryOpen(true);\n }, []);\n\n const closeHistory = useCallback(() => {\n setHistoryOpen(false);\n }, []);\n\n // Show a specific run result\n const showRunId = useCallback(\n (newRunId: string, refreshHistory?: boolean) => {\n setRunId(newRunId);\n setRunResultOpen(true);\n onShowRunId?.(newRunId, refreshHistory);\n },\n [onShowRunId],\n );\n\n // Execute a run action\n const runAction = useCallback(\n async (\n type: string,\n params?: AxiosQueryParams,\n options?: RecceActionOptions,\n ) => {\n if (!onRunAction) {\n console.warn(\n \"RecceActionProvider: onRunAction not provided, cannot execute run\",\n );\n return;\n }\n\n const result = await onRunAction(type, params, options);\n\n // If the handler returns a run ID, show the result\n if (typeof result === \"string\") {\n showRunId(result);\n }\n },\n [onRunAction, showRunId],\n );\n\n const contextValue = useMemo<RecceActionContextType>(\n () => ({\n runAction,\n runId,\n showRunId,\n isRunResultOpen,\n closeRunResult,\n isHistoryOpen,\n closeHistory,\n showHistory,\n setHistoryOpen,\n clearRunResult,\n }),\n [\n runAction,\n runId,\n showRunId,\n isRunResultOpen,\n closeRunResult,\n isHistoryOpen,\n closeHistory,\n showHistory,\n clearRunResult,\n ],\n );\n\n return (\n <RecceActionContext.Provider value={contextValue}>\n {children}\n </RecceActionContext.Provider>\n );\n}\n\n/**\n * Hook to access the RecceAction context.\n *\n * @returns RecceActionContextType with run action methods and state\n */\nexport function useRecceActionContext(): RecceActionContextType {\n return useContext(RecceActionContext);\n}\n","\"use client\";\n\n/**\n * Feature mode for the Recce instance.\n * - \"read only\": No modifications allowed\n * - \"metadata only\": Database queries disabled\n * - null: Full functionality enabled\n */\nexport type RecceFeatureMode = \"read only\" | \"metadata only\" | null;\n\n/**\n * Feature toggles that control what actions are available in the UI.\n * These are derived from the server's instance info based on server mode.\n */\nexport interface RecceFeatureToggles {\n /** Current feature mode based on server_mode */\n mode: RecceFeatureMode;\n /** Disable saving state to file */\n disableSaveToFile: boolean;\n /** Disable exporting state file */\n disableExportStateFile: boolean;\n /** Disable importing state file */\n disableImportStateFile: boolean;\n /** Disable updating checklist */\n disableUpdateChecklist: boolean;\n /** Disable database query execution */\n disableDatabaseQuery: boolean;\n /** Disable view action dropdown menu */\n disableViewActionDropdown: boolean;\n /** Disable node action dropdown menu */\n disableNodeActionDropdown: boolean;\n /** Disable share functionality */\n disableShare: boolean;\n /** Checklist disabled due to insufficient permissions (viewer role) — show disabled button with tooltip instead of hiding */\n checklistPermissionDenied: boolean;\n}\n\n/**\n * Instance information exposed through the RecceInstanceContext.\n * Contains feature toggles and session information.\n */\nexport interface InstanceInfoType {\n /** Whether running in single environment mode */\n singleEnv: boolean;\n /** Whether user is authenticated */\n authed: boolean;\n /** Feature toggles based on server mode */\n featureToggles: RecceFeatureToggles;\n /** When the instance lifetime expires */\n lifetimeExpiredAt?: Date;\n /** URL for sharing the instance */\n shareUrl?: string;\n /** Current session ID */\n sessionId?: string;\n /** Python runtime version (e.g., \"3.9.18\") */\n pythonVersion?: string;\n}\n\n/**\n * Default feature toggles with all features enabled.\n */\nexport const defaultFeatureToggles: RecceFeatureToggles = {\n mode: null,\n disableSaveToFile: false,\n disableExportStateFile: false,\n disableImportStateFile: false,\n disableUpdateChecklist: false,\n disableDatabaseQuery: false,\n disableViewActionDropdown: false,\n disableNodeActionDropdown: false,\n disableShare: false,\n checklistPermissionDenied: false,\n};\n\n/**\n * Default instance info values.\n */\nexport const defaultInstanceInfo: InstanceInfoType = {\n singleEnv: false,\n authed: false,\n lifetimeExpiredAt: undefined,\n featureToggles: defaultFeatureToggles,\n shareUrl: undefined,\n sessionId: undefined,\n pythonVersion: undefined,\n};\n","\"use client\";\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport { cacheKeys } from \"../../api/cacheKeys\";\nimport {\n getRecceInstanceInfo,\n type RecceInstanceInfo,\n} from \"../../api/instanceInfo\";\nimport { createFetchClient } from \"../../lib/fetchClient\";\nimport { useApiConfigOptional } from \"../../providers/contexts/ApiContext\";\n\n// Default API client for use outside RecceProvider (OSS mode)\nconst defaultApiClient = createFetchClient({ baseURL: \"\" });\n\n/**\n * Hook to fetch Recce instance information from the server.\n *\n * Uses TanStack Query to cache the response and the configured API client.\n * Works both inside RecceProvider (uses configured client) and outside (uses default API client).\n *\n * @returns Query result with RecceInstanceInfo data\n *\n * @example\n * ```tsx\n * function InstanceStatus() {\n * const { data, isLoading, error } = useRecceInstanceInfo();\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error loading instance info</div>;\n *\n * return <div>Server mode: {data?.server_mode}</div>;\n * }\n * ```\n */\nexport function useRecceInstanceInfo() {\n const apiConfig = useApiConfigOptional();\n const apiClient = apiConfig?.apiClient ?? defaultApiClient;\n\n return useQuery<RecceInstanceInfo>({\n queryKey: cacheKeys.instanceInfo(),\n queryFn: () => getRecceInstanceInfo(apiClient),\n });\n}\n","\"use client\";\n\nimport { createContext, type ReactNode, useContext, useState } from \"react\";\n\nimport {\n defaultFeatureToggles,\n defaultInstanceInfo,\n type InstanceInfoType,\n type RecceFeatureToggles,\n} from \"./types\";\nimport { useRecceInstanceInfo } from \"./useRecceInstanceInfo\";\n\nconst InstanceInfoContext =\n createContext<InstanceInfoType>(defaultInstanceInfo);\nInstanceInfoContext.displayName = \"RecceInstanceInfoContext\";\n\n/**\n * Provider that fetches and processes Recce instance information.\n *\n * This provider:\n * 1. Fetches instance info from the server using useRecceInstanceInfo\n * 2. Computes feature toggles based on server_mode\n * 3. Provides the processed data through context\n *\n * Feature toggles are computed based on:\n * - server_mode: \"read-only\" disables all modifications\n * - server_mode: \"preview\" disables database queries but allows metadata operations\n * - single_env: disables checklist updates and sharing\n * - cloud_instance: disables sharing\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <RecceProvider api={{ baseUrl: \"/api\" }}>\n * <RecceInstanceInfoProvider>\n * <MyComponent />\n * </RecceInstanceInfoProvider>\n * </RecceProvider>\n * );\n * }\n * ```\n */\nexport function RecceInstanceInfoProvider({\n children,\n}: {\n children: ReactNode;\n}) {\n const { data: instanceInfo, isLoading } = useRecceInstanceInfo();\n const [featureToggles, setFeatureToggles] = useState<RecceFeatureToggles>(\n defaultFeatureToggles,\n );\n const [singleEnv, setSingleEnv] = useState<boolean>(false);\n const [authed, setAuthed] = useState<boolean>(false);\n const [lifetimeExpiredAt, setLifetimeExpiredAt] = useState<Date>();\n const [shareUrl, setShareUrl] = useState<string>();\n const [sessionId, setSessionId] = useState<string>();\n const [pythonVersion, setPythonVersion] = useState<string>();\n const [prevInstanceInfo, setPrevInstanceInfo] = useState(instanceInfo);\n\n // Adjust state during render when instanceInfo changes\n if (!isLoading && instanceInfo && instanceInfo !== prevInstanceInfo) {\n setPrevInstanceInfo(instanceInfo);\n\n setSingleEnv(instanceInfo.single_env);\n setAuthed(instanceInfo.authed);\n setShareUrl(instanceInfo.share_url);\n setSessionId(instanceInfo.session_id);\n setPythonVersion(instanceInfo.python_version);\n\n if (instanceInfo.lifetime_expired_at) {\n setLifetimeExpiredAt(new Date(instanceInfo.lifetime_expired_at));\n console.log(\"lifetime expired at\", instanceInfo.lifetime_expired_at);\n }\n\n // Set feature toggles based on instanceInfo\n const toggles = { ...defaultFeatureToggles };\n if (instanceInfo.server_mode === \"read-only\") {\n toggles.mode = \"read only\";\n toggles.disableSaveToFile = true;\n toggles.disableExportStateFile = true;\n toggles.disableImportStateFile = true;\n toggles.disableUpdateChecklist = true;\n toggles.disableDatabaseQuery = true;\n toggles.disableViewActionDropdown = true;\n toggles.disableNodeActionDropdown = true;\n toggles.disableShare = true;\n } else if (instanceInfo.server_mode === \"preview\") {\n toggles.mode = \"metadata only\";\n toggles.disableSaveToFile = true;\n toggles.disableExportStateFile = true;\n toggles.disableImportStateFile = true;\n toggles.disableUpdateChecklist = false;\n toggles.disableDatabaseQuery = true;\n toggles.disableViewActionDropdown = false;\n toggles.disableNodeActionDropdown = false;\n toggles.disableShare = true;\n }\n if (instanceInfo.single_env) {\n toggles.disableUpdateChecklist = true;\n toggles.disableShare = true;\n }\n if (instanceInfo.cloud_instance) {\n toggles.disableShare = true;\n }\n if (instanceInfo.user_role === \"viewer\") {\n // Only show permission-denied tooltip when checklist isn't already disabled\n // by mode (read-only) or env (single_env) — in those cases, hide entirely\n if (!toggles.disableUpdateChecklist) {\n toggles.checklistPermissionDenied = true;\n }\n toggles.disableUpdateChecklist = true;\n }\n setFeatureToggles(toggles);\n }\n\n return (\n <InstanceInfoContext.Provider\n value={{\n featureToggles,\n singleEnv,\n authed,\n lifetimeExpiredAt,\n shareUrl,\n sessionId,\n pythonVersion,\n }}\n >\n {children}\n </InstanceInfoContext.Provider>\n );\n}\n\n/**\n * Hook to access the Recce instance context.\n *\n * Returns the current instance information including feature toggles,\n * authentication status, and session information.\n *\n * @returns InstanceInfoType with feature toggles and session info\n *\n * @example\n * ```tsx\n * function FeatureGate() {\n * const { featureToggles } = useRecceInstanceContext();\n *\n * if (featureToggles.disableDatabaseQuery) {\n * return <div>Database queries are disabled</div>;\n * }\n *\n * return <QueryEditor />;\n * }\n * ```\n */\nexport function useRecceInstanceContext(): InstanceInfoType {\n return useContext(InstanceInfoContext);\n}\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\n\n/**\n * Context for sharing idle timeout state across components\n *\n * IMPORTANT: The countdown is based on the last SUCCESSFUL keep-alive API call,\n * NOT on user activity. This ensures the countdown accurately reflects the\n * server's idle timeout state.\n */\nexport interface IdleTimeoutContextType {\n /** Remaining seconds until timeout (null if idle timeout not enabled) */\n remainingSeconds: number | null;\n /** Idle timeout value from server in seconds (null if not configured) */\n idleTimeout: number | null;\n /** Whether idle timeout is enabled */\n isEnabled: boolean;\n /** Mark as disconnected - stops countdown and keep-alive */\n setDisconnected: () => void;\n /** Reset connection state - restarts countdown and keep-alive after successful reconnect */\n resetConnection: () => void;\n /** Whether the connection is disconnected */\n isDisconnected: boolean;\n}\n\nexport const IdleTimeoutContext = createContext<\n IdleTimeoutContextType | undefined\n>(undefined);\n\n/**\n * Hook to access idle timeout context, returns null if outside provider\n * Used internally by useIdleDetection to avoid circular dependency\n */\nexport function useIdleTimeoutSafe(): IdleTimeoutContextType | null {\n return useContext(IdleTimeoutContext) ?? null;\n}\n","\"use client\";\n\nimport throttle from \"lodash/throttle\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport { sendKeepAlive } from \"../../api/keepAlive\";\nimport { createFetchClient } from \"../../lib/fetchClient\";\nimport { useApiConfigOptional } from \"../../providers/contexts/ApiContext\";\n\nimport { useRecceInstanceInfo } from \"../instance\";\nimport { useIdleTimeoutSafe } from \"./types\";\n\n// Default API client for use outside RecceProvider (OSS mode)\nconst defaultApiClient = createFetchClient({ baseURL: \"\" });\n\n/**\n * Check if debug logging is enabled via window.RECCE_DEBUG_IDLE\n * Enable in browser console: window.RECCE_DEBUG_IDLE = true\n * Disable: delete window.RECCE_DEBUG_IDLE\n */\nfunction isDebugEnabled(): boolean {\n // biome-ignore lint/suspicious/noExplicitAny: window flag for debug logging\n return typeof window !== \"undefined\" && !!(window as any).RECCE_DEBUG_IDLE;\n}\n\n/**\n * Log function that only outputs when debug is enabled\n */\nfunction debugLog(message: string, data?: Record<string, unknown>) {\n if (isDebugEnabled()) {\n if (data) {\n console.log(message, data);\n } else {\n console.log(message);\n }\n }\n}\n\n/**\n * Configuration for idle detection behavior\n */\nconst IDLE_DETECTION_CONFIG = {\n /** Events to listen for user activity */\n ACTIVITY_EVENTS: [\"focus\", \"mousemove\", \"keydown\", \"scroll\"] as const,\n /**\n * Throttle event handler execution to reduce JS overhead (150ms).\n * Uses lodash.throttle with { leading: true, trailing: true } to ensure\n * immediate response on first activity (leading) and also capture the final\n * event in a burst (trailing), which is important for user experience.\n */\n EVENT_THROTTLE_MS: 150,\n} as const;\n\n/**\n * Hook to detect user activity and send keep-alive signals to prevent server idle timeout.\n *\n * This hook:\n * - Listens for user activities (focus, mouse, keyboard, scroll)\n * - Sends keep-alive requests (throttled at API layer to minimum 3 seconds)\n * - Pauses when the tab is inactive (using Page Visibility API)\n * - Immediately sends a keep-alive when tab becomes active\n * - Only activates when idle_timeout is configured on the server\n *\n * Note: The countdown in IdleTimeoutContext is based on successful keep-alive\n * API calls, not on user activity. This ensures accurate server state tracking.\n */\nexport function useIdleDetection() {\n const { data: instanceInfo, isLoading, isError } = useRecceInstanceInfo();\n const idleTimeoutContext = useIdleTimeoutSafe();\n const isDisconnected = idleTimeoutContext?.isDisconnected ?? false;\n const apiConfig = useApiConfigOptional();\n const apiClient = apiConfig?.apiClient ?? defaultApiClient;\n\n // Only enable idle detection if idle_timeout is configured and not disconnected\n const idleTimeout = instanceInfo?.idle_timeout;\n const isEnabled =\n idleTimeout !== undefined && idleTimeout > 0 && !isDisconnected;\n\n // Debug: Log instance info state when debug is enabled\n debugLog(\"[Idle Detection] Instance info\", {\n isLoading,\n isError,\n hasIdleTimeout: idleTimeout !== undefined,\n idleTimeout:\n idleTimeout !== undefined ? `${idleTimeout}s` : \"not configured\",\n isDisconnected,\n isEnabled,\n });\n\n /**\n * Send keep-alive signal to server\n * Throttling is handled at the API layer (minimum 3 seconds between API calls)\n * The successful send will notify IdleTimeoutContext to reset countdown\n */\n const sendKeepAliveNow = useCallback(async () => {\n if (document.hidden) return;\n\n const sent = await sendKeepAlive(apiClient);\n\n if (sent) {\n debugLog(\"[Idle Detection] Keep-alive sent successfully\", {\n timestamp: new Date().toISOString(),\n });\n }\n }, [apiClient]);\n\n /**\n * Handle any user activity event\n * Attempts to send keep-alive (API layer handles throttling)\n */\n const handleActivity = useCallback(\n (event: Event) => {\n if (isEnabled && !document.hidden) {\n debugLog(\"[Idle Detection] Activity detected\", {\n event: event.type,\n tabActive: !document.hidden,\n });\n\n // Send keep-alive API call (API layer handles throttling)\n void sendKeepAliveNow();\n }\n },\n [isEnabled, sendKeepAliveNow],\n );\n\n /**\n * Handle tab visibility changes\n * When tab becomes active, attempt to send keep-alive\n */\n const handleVisibilityChange = useCallback(() => {\n if (!isEnabled) return;\n\n if (!document.hidden) {\n debugLog(\"[Idle Detection] Tab became active\", {\n timestamp: new Date().toISOString(),\n });\n\n // Send keep-alive (API layer handles throttling)\n void sendKeepAliveNow();\n }\n }, [isEnabled, sendKeepAliveNow]);\n\n // Create throttled handler using lodash to reduce JS overhead from high-frequency events\n // useMemo ensures stable reference and proper cleanup\n const throttledHandler = useMemo(\n () =>\n throttle(handleActivity, IDLE_DETECTION_CONFIG.EVENT_THROTTLE_MS, {\n leading: true,\n trailing: true,\n }),\n [handleActivity],\n );\n\n useEffect(() => {\n if (!isEnabled) {\n debugLog(\"[Idle Detection] Disabled\", {\n idleTimeout: idleTimeout,\n reason:\n idleTimeout === undefined\n ? \"idle_timeout not configured on server\"\n : idleTimeout === 0\n ? \"idle_timeout is 0\"\n : \"disconnected\",\n });\n return;\n }\n\n debugLog(\"[Idle Detection] Initialized\", {\n enabled: true,\n idleTimeout: `${idleTimeout}s`,\n eventThrottle: `${IDLE_DETECTION_CONFIG.EVENT_THROTTLE_MS}ms`,\n apiThrottle: \"3s (API layer)\",\n monitoredEvents: IDLE_DETECTION_CONFIG.ACTIVITY_EVENTS.join(\", \"),\n });\n\n // Register activity event listeners with throttled handler\n IDLE_DETECTION_CONFIG.ACTIVITY_EVENTS.forEach((eventType) => {\n window.addEventListener(eventType, throttledHandler, { passive: true });\n });\n\n // Register visibility change listener (not throttled - immediate response needed)\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n // Cleanup function\n return () => {\n debugLog(\"[Idle Detection] Cleanup - removing event listeners\");\n IDLE_DETECTION_CONFIG.ACTIVITY_EVENTS.forEach((eventType) => {\n window.removeEventListener(eventType, throttledHandler);\n });\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n throttledHandler.cancel(); // Cancel any pending throttled calls\n };\n }, [isEnabled, throttledHandler, handleVisibilityChange, idleTimeout]);\n}\n","\"use client\";\n\nimport {\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport {\n getLastKeepAliveTime,\n setKeepAliveCallback,\n} from \"../../api/keepAlive\";\nimport { useRecceInstanceInfo } from \"../instance\";\nimport { IdleTimeoutContext, type IdleTimeoutContextType } from \"./types\";\nimport { useIdleDetection } from \"./useIdleDetection\";\n\n/**\n * Provider for idle timeout state\n *\n * The countdown is based on lastServerSyncTime (when keep-alive API was last\n * successfully sent), not on user activity. This provides accurate server state.\n */\nexport function IdleTimeoutProvider({ children }: { children: ReactNode }) {\n const { data: instanceInfo } = useRecceInstanceInfo();\n // Track the last time we successfully synced with server (keep-alive sent)\n const lastServerSyncRef = useRef<number>(Date.now());\n const [remainingSeconds, setRemainingSeconds] = useState<number | null>(null);\n const [isDisconnected, setIsDisconnected] = useState(false);\n\n const idleTimeout = instanceInfo?.idle_timeout ?? null;\n const isEnabled = idleTimeout !== null && idleTimeout > 0;\n\n // Register callback to receive keep-alive success notifications\n // Use ref to track enabled state to avoid race condition in callback\n const isEnabledRef = useRef(isEnabled);\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n if (!isEnabled) {\n setKeepAliveCallback(null);\n return;\n }\n\n setKeepAliveCallback((timestamp: number) => {\n // Check current enabled state to avoid updating after disabled\n if (isEnabledRef.current) {\n lastServerSyncRef.current = timestamp;\n }\n });\n\n // Initialize with current keep-alive time if available\n const currentTime = getLastKeepAliveTime();\n if (currentTime > 0) {\n lastServerSyncRef.current = currentTime;\n }\n\n return () => {\n setKeepAliveCallback(null);\n };\n }, [isEnabled]);\n\n const setDisconnected = useCallback(() => {\n setIsDisconnected(true);\n }, []);\n\n const resetConnection = useCallback(() => {\n // Reset disconnected state\n setIsDisconnected(false);\n // Reset server sync time to now (server just restarted, timer reset)\n lastServerSyncRef.current = Date.now();\n }, []);\n\n // Update remaining seconds every second based on server sync time\n useEffect(() => {\n if (!isEnabled || idleTimeout === null) {\n setRemainingSeconds(null);\n return;\n }\n\n // Stop updating countdown if disconnected\n if (isDisconnected) {\n return;\n }\n\n const updateCountdown = () => {\n const now = Date.now();\n const elapsedSeconds = (now - lastServerSyncRef.current) / 1000;\n const remaining = Math.max(0, idleTimeout - elapsedSeconds);\n setRemainingSeconds(remaining);\n };\n\n // Initial update\n updateCountdown();\n\n // Set up interval for updates\n const intervalId = setInterval(updateCountdown, 1000);\n\n return () => {\n clearInterval(intervalId);\n };\n }, [isEnabled, idleTimeout, isDisconnected]);\n\n return (\n <IdleTimeoutContext.Provider\n value={{\n remainingSeconds,\n idleTimeout,\n isEnabled,\n setDisconnected,\n resetConnection,\n isDisconnected,\n }}\n >\n <IdleDetector />\n {children}\n </IdleTimeoutContext.Provider>\n );\n}\n\n/**\n * Internal component that activates idle detection\n * Placed inside provider so it has access to context\n */\nfunction IdleDetector() {\n useIdleDetection();\n return null;\n}\n\n/**\n * Hook to access idle timeout context\n * @throws Error if used outside IdleTimeoutProvider\n */\nexport function useIdleTimeout() {\n const context = useContext(IdleTimeoutContext);\n if (!context) {\n throw new Error(\"useIdleTimeout must be used within IdleTimeoutProvider\");\n }\n return context;\n}\n\n// Re-export type and safe hook for backwards compatibility\nexport type { IdleTimeoutContextType } from \"./types\";\nexport { useIdleTimeoutSafe } from \"./types\";\n","\"use client\";\n\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n} from \"react\";\nimport type { RunsAggregated } from \"../../api/runs\";\nimport type { EnvInfo, LineageGraph, LineageGraphContextType } from \"./types\";\n\n/**\n * Props for LineageGraphProvider\n *\n * This is a props-driven provider - pass data from your data fetching layer.\n * The provider does NOT fetch data internally; consumers handle data fetching\n * and pass the results via props.\n */\nexport interface LineageGraphProviderProps {\n children: ReactNode;\n\n /** The processed lineage graph data */\n lineageGraph?: LineageGraph;\n\n /** Environment information (git, dbt, sqlmesh metadata) */\n envInfo?: EnvInfo;\n\n /** Whether in review mode (read-only checks) */\n reviewMode?: boolean;\n\n /** Whether in cloud mode (recce cloud) */\n cloudMode?: boolean;\n\n /** Whether in file mode (loading from file) */\n fileMode?: boolean;\n\n /** The state file name if in file mode */\n fileName?: string;\n\n /** Whether this is the demo site */\n isDemoSite?: boolean;\n\n /** Whether running in GitHub Codespace */\n isCodespace?: boolean;\n\n /** Loading state */\n isLoading?: boolean;\n\n /** Error message if loading failed */\n error?: string;\n\n /** Supported task types from server */\n supportTasks?: Record<string, boolean>;\n\n /** Callback to refetch the lineage graph */\n onRefetchLineageGraph?: () => void;\n\n /** Pre-aggregated run results by model */\n runsAggregated?: RunsAggregated;\n\n /** Callback to refetch aggregated runs */\n onRefetchRunsAggregated?: () => void;\n}\n\nconst defaultContext: LineageGraphContextType = {\n isActionAvailable: () => true,\n isDemoSite: false,\n isLoading: true, // Default to loading state for SSR hydration safety\n};\n\nconst LineageGraphContext =\n createContext<LineageGraphContextType>(defaultContext);\nLineageGraphContext.displayName = \"RecceLineageGraphContext\";\n\n/**\n * Provider for LineageGraph context.\n *\n * This is a props-driven provider designed for library consumers.\n * Pass data from your data fetching layer (e.g., TanStack Query).\n *\n * @example\n * ```tsx\n * const { data, isLoading, error, refetch } = useQuery({\n * queryKey: ['lineage'],\n * queryFn: fetchLineageData,\n * });\n *\n * <LineageGraphProvider\n * lineageGraph={data?.lineageGraph}\n * envInfo={data?.envInfo}\n * isLoading={isLoading}\n * error={error?.message}\n * onRefetchLineageGraph={refetch}\n * >\n * <LineageView />\n * </LineageGraphProvider>\n * ```\n */\nexport function LineageGraphProvider({\n children,\n lineageGraph,\n envInfo,\n reviewMode,\n cloudMode,\n fileMode,\n fileName,\n isDemoSite = false,\n isCodespace,\n isLoading,\n error,\n supportTasks,\n onRefetchLineageGraph,\n runsAggregated,\n onRefetchRunsAggregated,\n}: LineageGraphProviderProps) {\n // Create stable isActionAvailable function\n const isActionAvailable = useCallback(\n (name: string) => {\n if (supportTasks) {\n // Default to true if action not found in supportTasks\n return supportTasks[name] ?? true;\n }\n // If supportTasks not provided, all actions are available\n return true;\n },\n [supportTasks],\n );\n\n // Memoize the context value to prevent unnecessary re-renders\n const contextValue = useMemo<LineageGraphContextType>(\n () => ({\n lineageGraph,\n envInfo,\n reviewMode,\n cloudMode,\n fileMode,\n fileName,\n isDemoSite,\n isCodespace,\n isLoading,\n error,\n supportTasks,\n refetchLineageGraph: onRefetchLineageGraph,\n isActionAvailable,\n runsAggregated,\n refetchRunsAggregated: onRefetchRunsAggregated,\n }),\n [\n lineageGraph,\n envInfo,\n reviewMode,\n cloudMode,\n fileMode,\n fileName,\n isDemoSite,\n isCodespace,\n isLoading,\n error,\n supportTasks,\n onRefetchLineageGraph,\n isActionAvailable,\n runsAggregated,\n onRefetchRunsAggregated,\n ],\n );\n\n return (\n <LineageGraphContext.Provider value={contextValue}>\n {children}\n </LineageGraphContext.Provider>\n );\n}\n\n/**\n * Hook to access the LineageGraph context.\n *\n * @returns LineageGraphContextType with lineage data and utilities\n * @throws Warning in dev mode if used outside provider (returns default context)\n */\nexport function useLineageGraphContext(): LineageGraphContextType {\n const context = useContext(LineageGraphContext);\n return context;\n}\n\n/**\n * Hook to access aggregated runs data.\n * Convenience wrapper around useLineageGraphContext.\n *\n * @returns Tuple of [runsAggregated, refetchRunsAggregated]\n */\nexport function useRunsAggregated(): [\n RunsAggregated | undefined,\n (() => void) | undefined,\n] {\n const { runsAggregated, refetchRunsAggregated } = useLineageGraphContext();\n return [runsAggregated, refetchRunsAggregated];\n}\n","import { createContext, useContext } from \"react\";\nimport type { LineageViewContextType } from \"./types\";\n\nexport const LineageViewContext = createContext<\n LineageViewContextType | undefined\n>(undefined);\n\nexport const useLineageViewContextSafe = (): LineageViewContextType => {\n const context = useContext(LineageViewContext);\n if (!context) {\n throw new Error(\n \"useLineageViewContext must be used within a LineageViewProvider\",\n );\n }\n return context;\n};\n\nexport const useLineageViewContext = (): LineageViewContextType | undefined => {\n return useContext(LineageViewContext);\n};\n","import type { Edge, Node } from \"@xyflow/react\";\nimport type React from \"react\";\nimport type { CllInput, ColumnLineageData } from \"../../api/cll\";\nimport type {\n CatalogMetadata,\n GitInfo,\n ManifestMetadata,\n PullRequestInfo,\n SQLMeshInfo,\n StateMetadata,\n} from \"../../api/info\";\nimport type { LineageDiffViewOptions } from \"../../api/lineagecheck\";\nimport type { RunsAggregated } from \"../../api/runs\";\nimport type { Run } from \"../../api/types\";\n\n/**\n * Environment information derived from server info\n */\nexport interface EnvInfo {\n stateMetadata?: StateMetadata;\n adapterType?: string;\n git?: GitInfo;\n pullRequest?: PullRequestInfo;\n dbt?: {\n base: ManifestMetadata | undefined | null;\n current: ManifestMetadata | undefined | null;\n };\n sqlmesh?: SQLMeshInfo | null;\n}\n\n/**\n * Lineage graph node type for React Flow\n */\nexport type LineageGraphNode = Node<\n {\n id: string;\n name: string;\n resourceType?: string;\n packageName?: string;\n schema?: string;\n materialized?: string;\n changeStatus?: \"added\" | \"removed\" | \"modified\";\n change?: {\n category: \"breaking\" | \"non_breaking\" | \"partial_breaking\" | \"unknown\";\n columns?: Record<string, \"added\" | \"removed\" | \"modified\" | \"unknown\">;\n };\n parents: Record<string, LineageGraphEdge>;\n children: Record<string, LineageGraphEdge>;\n },\n \"lineageGraphNode\"\n>;\n\n/**\n * Column-level lineage node type\n */\nexport type LineageGraphColumnNode = Node<\n {\n node: LineageGraphNode[\"data\"];\n column: string;\n type: string;\n transformationType?: string;\n changeStatus?: \"added\" | \"removed\" | \"modified\";\n isImpacted?: boolean;\n },\n \"lineageGraphColumnNode\"\n>;\n\n/**\n * Lineage graph edge type for React Flow\n */\nexport type LineageGraphEdge = Edge<\n {\n changeStatus?: \"added\" | \"removed\";\n },\n \"lineageGraphEdge\"\n>;\n\n/**\n * Union type for all lineage graph node types\n */\nexport type LineageGraphNodes = LineageGraphNode | LineageGraphColumnNode;\n\n/**\n * The main lineage graph data structure\n */\nexport interface LineageGraph {\n nodes: Record<string, LineageGraphNode>;\n edges: Record<string, LineageGraphEdge>;\n modifiedSet: string[];\n manifestMetadata: {\n base?: ManifestMetadata;\n current?: ManifestMetadata;\n };\n catalogMetadata: {\n base?: CatalogMetadata;\n current?: CatalogMetadata;\n };\n}\n\n/**\n * Type guard for LineageGraphNode\n */\nexport function isLineageGraphNode(\n node: LineageGraphNodes,\n): node is LineageGraphNode {\n return node.type === \"lineageGraphNode\";\n}\n\n/**\n * Type guard for LineageGraphColumnNode\n */\nexport function isLineageGraphColumnNode(\n node: LineageGraphNodes,\n): node is LineageGraphColumnNode {\n return node.type === \"lineageGraphColumnNode\";\n}\n\n/**\n * Context value exposed by LineageGraphContext\n */\nexport interface LineageGraphContextType {\n /** The processed lineage graph */\n lineageGraph?: LineageGraph;\n /** Environment metadata */\n envInfo?: EnvInfo;\n /** Whether in review mode (read-only checks) */\n reviewMode?: boolean;\n /** Whether in cloud mode (recce cloud) */\n cloudMode?: boolean;\n /** Whether in file mode (loading from file) */\n fileMode?: boolean;\n /** The state file name if in file mode */\n fileName?: string;\n /** Whether this is the demo site */\n isDemoSite: boolean;\n /** Whether running in GitHub Codespace */\n isCodespace?: boolean;\n /** Loading state */\n isLoading?: boolean;\n /** Error message if loading failed */\n error?: string;\n /** Supported task types from server */\n supportTasks?: Record<string, boolean>;\n /** Refetch the lineage graph data */\n refetchLineageGraph?: () => void;\n /** Check if an action is available */\n isActionAvailable: (actionName: string) => boolean;\n /** Pre-aggregated run results by model */\n runsAggregated?: RunsAggregated;\n /** Refetch the aggregated runs */\n refetchRunsAggregated?: () => void;\n}\n\n// ============================================================================\n// LineageViewContext Types\n// ============================================================================\n\n/**\n * Action mode for node operations - single or multiple nodes\n */\nexport type ActionMode = \"per_node\" | \"multi_nodes\";\n\n/**\n * Select mode for multi-node selection\n */\nexport type SelectMode = \"selecting\" | \"action_result\" | undefined;\n\n/**\n * Action state for individual node operations\n */\nexport interface NodeAction {\n /** Whether this is a per-node or multi-node action */\n mode: ActionMode;\n /** Current status of this node's action */\n status?: \"pending\" | \"running\" | \"success\" | \"failure\" | \"skipped\";\n /** Reason why this node was skipped (if applicable) */\n skipReason?: string;\n /** The run associated with this action */\n run?: Run;\n}\n\n/**\n * Overall action state for batch operations\n */\nexport interface ActionState {\n /** Whether this is a per-node or multi-node action */\n mode: ActionMode;\n /** Overall status of the action batch */\n status: \"pending\" | \"running\" | \"canceling\" | \"canceled\" | \"completed\";\n /** Current run being executed */\n currentRun?: Partial<Run>;\n /** Number of completed actions */\n completed: number;\n /** Total number of actions */\n total: number;\n /** Per-node action state */\n actions: Record<string, NodeAction>;\n}\n\n/**\n * Context value exposed by LineageViewContext.\n * Provides state and methods for interacting with the lineage view.\n */\nexport interface LineageViewContextType {\n /** Whether the view is interactive (vs read-only) */\n interactive: boolean;\n /** All nodes in the current view */\n nodes: LineageGraphNodes[];\n /** Currently focused node (hovered/highlighted) */\n focusedNode?: LineageGraphNode;\n /** Currently selected nodes for batch operations */\n selectedNodes: LineageGraphNode[];\n /** Column-level lineage data if showing CLL */\n cll: ColumnLineageData | undefined;\n\n // Context menu\n /** Show the context menu for a node */\n showContextMenu: (event: React.MouseEvent, node: LineageGraphNodes) => void;\n\n // View options\n /** Current view options (filters, display settings) */\n viewOptions: LineageDiffViewOptions;\n /** Update view options */\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n\n // Multi-node selection\n /** Current selection mode */\n selectMode: SelectMode;\n /** Select/deselect a single node */\n selectNode: (nodeId: string) => void;\n /** Select all parent nodes up to a degree */\n selectParentNodes: (nodeId: string, degree?: number) => void;\n /** Select all child nodes up to a degree */\n selectChildNodes: (nodeId: string, degree?: number) => void;\n /** Clear selection */\n deselect: () => void;\n\n // Node state queries\n /** Check if a node is highlighted */\n isNodeHighlighted: (nodeId: string) => boolean;\n /** Check if a node is selected */\n isNodeSelected: (nodeId: string) => boolean;\n /** Check if an edge is highlighted */\n isEdgeHighlighted: (source: string, target: string) => boolean;\n /** Get the action state for a node */\n getNodeAction: (nodeId: string) => NodeAction;\n /** Get the columns visible for a node in CLL mode */\n getNodeColumnSet: (nodeId: string) => Set<string>;\n /** Check if a node is showing change analysis */\n isNodeShowingChangeAnalysis: (nodeId: string) => boolean;\n /** Whether impact radius (change analysis) mode is active */\n changeAnalysisMode: boolean;\n /** Whether the new CLL experience flag is enabled on the server */\n newCllExperience: boolean;\n /**\n * Whether the `--whole-model-impact` server flag is on. Implies\n * `newCllExperience`. Gates two surfaces (NodeView title chip + left\n * stripe for whole-model kinds; LineageNode COLUMN / ADD graph badge\n * for per-column kinds) AND the suppression of the \"Breaking /\n * Non Breaking / Partial Breaking\" text labels on the graph node.\n * When false, lineage nodes render the original category text labels\n * and no whole-model UI.\n */\n wholeModelImpact: boolean;\n // TODO: Move isImpacted to be a per-model state on node data instead of\n // a lookup set, so impact status is part of the graph model rather than\n // a side-channel computed separately.\n /** Frozen set of node IDs that are impacted, computed once during impact\n * analysis and stable across column selections in new CLL experience. */\n impactedNodeIds: Set<string>;\n /** Frozen set of column IDs that are impacted, same lifecycle as impactedNodeIds. */\n impactedColumnIds: Set<string>;\n /**\n * Models that themselves have a whole-model change (row-shape edit at the\n * CLL classifier level — `change_category === \"breaking\"`). Drives the\n * brown title chip + brown left stripe in NodeView. The LineageNode graph\n * badge is intentionally suppressed for whole-model kinds; the chip +\n * stripe carry the signal. Empty Set when the `--whole-model-impact`\n * flag is off.\n */\n wholeModelChangedNodeIds: Set<string>;\n /**\n * Models that are BFS-downstream of one or more whole-model-changed\n * models (the set includes the changed models themselves — they are\n * trivially \"impacted\" by their own change). Drives the amber title chip\n * + amber left stripe in NodeView. The LineageNode graph badge is\n * intentionally suppressed for whole-model kinds. Changed-wins: a model\n * may appear in both this set AND in `wholeModelChangedNodeIds`;\n * consumers must consult `wholeModelChangedNodeIds` first (use\n * `pickWholeModelFlags`). Empty Set when the `--whole-model-impact` flag\n * is off.\n */\n wholeModelImpactedNodeIds: Set<string>;\n /** Set change analysis mode on/off */\n setChangeAnalysisMode: (active: boolean) => void;\n\n // Actions\n /** Run row count on selected nodes */\n runRowCount: () => Promise<void>;\n /** Run row count diff on selected nodes */\n runRowCountDiff: () => Promise<void>;\n /** Run value diff on selected nodes */\n runValueDiff: () => Promise<void>;\n /** Add a lineage diff check */\n addLineageDiffCheck: (viewMode?: string) => void;\n /** Add a schema diff check */\n addSchemaDiffCheck: () => void;\n /** Cancel the current action */\n cancel: () => void;\n /** Current action state */\n actionState: ActionState;\n\n // Column-level lineage\n /** Center the view on a specific node */\n centerNode: (nodeId: string) => void;\n /** Show column-level lineage for a node/column */\n showColumnLevelLineage: (cll?: CllInput) => Promise<void>;\n /** Reset column-level lineage view */\n resetColumnLevelLineage: (previous?: boolean) => Promise<void>;\n}\n","\"use client\";\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport { cacheKeys } from \"../../api/cacheKeys\";\nimport { getServerFlag, type RecceServerFlags } from \"../../api/flag\";\nimport { createFetchClient } from \"../../lib/fetchClient\";\nimport { useApiConfigOptional } from \"../../providers/contexts/ApiContext\";\n\n// Default API client for use outside RecceProvider (OSS mode)\nconst defaultApiClient = createFetchClient({ baseURL: \"\" });\n\n/**\n * Hook to fetch server-side feature flags.\n *\n * Uses TanStack Query to cache and manage flag state.\n * Works both inside RecceProvider (uses configured client) and outside (uses default API client).\n *\n * @returns TanStack Query result with RecceServerFlags data\n */\nexport function useRecceServerFlag() {\n const apiConfig = useApiConfigOptional();\n const apiClient = apiConfig?.apiClient ?? defaultApiClient;\n\n return useQuery<RecceServerFlags>({\n queryKey: cacheKeys.flag(),\n queryFn: () => getServerFlag(apiClient),\n });\n}\n","import type { ColumnLineageData } from \"../../api\";\n\nexport interface ColumnAnnotation {\n column: string;\n isImpacted: boolean;\n transformationType?: string;\n changeStatus?: \"added\" | \"removed\" | \"modified\";\n}\n\n/**\n * Coerce a wire-format `change_status` (which may include `\"unknown\"` since\n * DRC-3409's loud-fail fallback) into the CLL renderer's narrower\n * `ColumnAnnotation[\"changeStatus\"]`. `\"unknown\"` columns are tagged as\n * \"we know something upstream changed but couldn't attribute it\" — surface\n * those as `\"modified\"` so the renderer shows the ~ amber indicator rather\n * than rendering a silent \"no change\" row (the DRC-3409 symptom, one layer\n * deeper on the CLL graph view).\n *\n * Returns `undefined` for `undefined` / unknown-shape inputs so callers can\n * pass through optional fields without losing the \"no change\" case.\n */\nexport function coerceCllChangeStatus(\n status: \"added\" | \"removed\" | \"modified\" | \"unknown\" | undefined,\n): ColumnAnnotation[\"changeStatus\"] | undefined {\n if (status === \"unknown\") {\n return \"modified\";\n }\n return status;\n}\n\n/**\n * Walk both parent_map and child_map from a selected column to collect every\n * column in its lineage chain — upstream ancestors and downstream descendants.\n *\n * Returns a Map keyed by node ID, with an array of columns and their impact\n * status for each model that participates in the lineage chain. A model can\n * contribute multiple columns (e.g., several upstream columns feeding a\n * downstream one). The selected column itself is included exactly once.\n *\n * @param impactedColumns - Pre-computed set of impacted column IDs.\n */\nexport function computeColumnLineage(\n cll: ColumnLineageData,\n nodeId: string,\n column: string,\n impactedColumns: Set<string>,\n): Map<string, ColumnAnnotation[]> {\n const { parent_map, child_map, columns } = cll.current;\n\n const result = new Map<string, ColumnAnnotation[]>();\n const visited = new Set<string>();\n\n function walk(columnId: string, adjacency: Record<string, string[]>) {\n if (visited.has(columnId)) return;\n visited.add(columnId);\n\n const col = columns[columnId];\n if (col) {\n const modelId = columnId.slice(0, -(col.name.length + 1));\n const annotations = result.get(modelId) ?? [];\n annotations.push({\n column: col.name,\n isImpacted: impactedColumns.has(columnId),\n transformationType: col.transformation_type,\n changeStatus: coerceCllChangeStatus(col.change_status),\n });\n result.set(modelId, annotations);\n }\n\n const next = adjacency[columnId] ?? [];\n for (const neighbor of next) {\n walk(neighbor, adjacency);\n }\n }\n\n const startId = `${nodeId}_${column}`;\n\n // Seed the start column once so the shared `visited` set doesn't block the\n // second walk from exploring the start's neighbors in the opposite direction.\n const startCol = columns[startId];\n if (startCol) {\n const modelId = startId.slice(0, -(startCol.name.length + 1));\n result.set(modelId, [\n {\n column: startCol.name,\n isImpacted: impactedColumns.has(startId),\n transformationType: startCol.transformation_type,\n changeStatus: coerceCllChangeStatus(startCol.change_status),\n },\n ]);\n }\n visited.add(startId);\n\n for (const parent of parent_map[startId] ?? []) {\n walk(parent, parent_map);\n }\n for (const child of child_map[startId] ?? []) {\n walk(child, child_map);\n }\n\n return result;\n}\n","import type { Position } from \"@xyflow/react\";\nimport type { MergedLineageResponse } from \"../../api/info\";\nimport { coerceCllChangeStatus } from \"../../components/lineage/computeColumnLineage\";\nimport type {\n LineageGraph,\n LineageGraphColumnNode,\n LineageGraphEdge,\n LineageGraphNode,\n LineageGraphNodes,\n} from \"./types\";\nimport { isLineageGraphNode } from \"./types\";\n\n/**\n * Height of a column node in pixels\n * Must match COLUMN_NODE_HEIGHT in columns/LineageColumnNode.tsx\n */\nexport const COLUMN_HEIGHT = 24;\n\n/**\n * Map of node IDs to their column sets\n */\nexport type NodeColumnSetMap = Record<string, Set<string>>;\n\n// =============================================================================\n// Set Utilities\n// =============================================================================\n\n/**\n * Get the union of multiple sets\n */\nexport function union<T>(...sets: Set<T>[]): Set<T> {\n const unionSet = new Set<T>();\n for (const set of sets) {\n for (const key of set) {\n unionSet.add(key);\n }\n }\n return unionSet;\n}\n\n/**\n * Get the intersection of multiple sets\n */\nexport function intersect<T>(...sets: Set<T>[]): Set<T> {\n if (sets.length === 0) {\n return new Set<T>();\n }\n\n let intersection = new Set<T>(sets[0]);\n\n for (const set of sets) {\n intersection = new Set([...intersection].filter((x) => set.has(x)));\n }\n\n return intersection;\n}\n\n/**\n * Get a set of neighbor nodes up to a certain degree\n */\nexport function getNeighborSet(\n nodeIds: string[],\n getNeighbors: (id: string) => string[],\n degree = 1000,\n): Set<string> {\n const neighborSet = new Set<string>();\n const visited: Record<string, number | undefined> = {};\n\n const dfs = (id: string, currentDegree: number) => {\n if (currentDegree < 0) {\n return;\n }\n if (visited[id] != null && visited[id] >= currentDegree) {\n return;\n }\n visited[id] = currentDegree;\n\n const neighbors = getNeighbors(id);\n\n for (const neighborId of neighbors) {\n dfs(neighborId, currentDegree - 1);\n }\n\n neighborSet.add(id);\n };\n\n for (const nodeId of nodeIds) {\n dfs(nodeId, degree);\n }\n\n return neighborSet;\n}\n\n// =============================================================================\n// Lineage Graph Building\n// =============================================================================\n\n/**\n * Build a LineageGraph from a merged lineage response.\n *\n * The server merges base + current and bakes in diff data (DRC-3258),\n * so this is now a trivial mapping — no client-side diffing.\n *\n * @param lineage - Merged lineage from /api/info\n * @returns Processed LineageGraph structure\n */\nexport function buildLineageGraph(\n lineage: MergedLineageResponse,\n): LineageGraph {\n const nodes: Record<string, LineageGraphNode> = {};\n const edges: Record<string, LineageGraphEdge> = {};\n const modifiedSet: string[] = [];\n\n // 1. Map nodes\n for (const [id, merged] of Object.entries(lineage.nodes)) {\n nodes[id] = {\n id,\n data: {\n id,\n name: merged.name,\n resourceType: merged.resource_type,\n packageName: merged.package_name,\n schema: merged.schema,\n materialized: merged.materialized,\n changeStatus: coerceCllChangeStatus(merged.change_status),\n change: merged.change ?? undefined,\n parents: {},\n children: {},\n },\n type: \"lineageGraphNode\",\n } as LineageGraphNode;\n\n if (merged.change_status) {\n modifiedSet.push(id);\n }\n }\n\n // 2. Map edges and build bidirectional parent/child refs\n for (const edge of lineage.edges) {\n const id = `${edge.source}_${edge.target}`;\n const parentNode = nodes[edge.source];\n const childNode = nodes[edge.target];\n\n if (!parentNode || !childNode) continue;\n\n edges[id] = {\n id,\n source: edge.source,\n target: edge.target,\n data: {\n changeStatus: edge.change_status ?? undefined,\n },\n };\n\n childNode.data.parents[edge.source] = edges[id];\n parentNode.data.children[edge.target] = edges[id];\n }\n\n // 3. Extract metadata\n const baseMeta = lineage.metadata?.base;\n const currentMeta = lineage.metadata?.current;\n\n return {\n nodes,\n edges,\n modifiedSet,\n manifestMetadata: {\n base: baseMeta?.manifest_metadata ?? undefined,\n current: currentMeta?.manifest_metadata ?? undefined,\n },\n catalogMetadata: {\n base: baseMeta?.catalog_metadata ?? undefined,\n current: currentMeta?.catalog_metadata ?? undefined,\n },\n };\n}\n\n// =============================================================================\n// Selection Utilities\n// =============================================================================\n\n/**\n * Select upstream nodes from the given node IDs\n *\n * @param lineageGraph - The lineage graph\n * @param nodeIds - Starting node IDs\n * @param degree - Maximum degree of separation (default: 1000)\n * @returns Set of upstream node IDs\n */\nexport function selectUpstream(\n lineageGraph: LineageGraph,\n nodeIds: string[],\n degree = 1000,\n): Set<string> {\n return getNeighborSet(\n nodeIds,\n (key) => {\n const maybeNodes = lineageGraph.nodes as unknown as Record<\n string,\n LineageGraphNode | undefined\n >;\n if (maybeNodes[key] === undefined) {\n return [];\n }\n return Object.keys(lineageGraph.nodes[key].data.parents);\n },\n degree,\n );\n}\n\n/**\n * Select downstream nodes from the given node IDs\n *\n * @param lineageGraph - The lineage graph\n * @param nodeIds - Starting node IDs\n * @param degree - Maximum degree of separation (default: 1000)\n * @returns Set of downstream node IDs\n */\nexport function selectDownstream(\n lineageGraph: LineageGraph,\n nodeIds: string[],\n degree = 1000,\n): Set<string> {\n return getNeighborSet(\n nodeIds,\n (key) => {\n if (\n (lineageGraph.nodes[key] as LineageGraphNode | undefined) === undefined\n ) {\n return [];\n }\n return Object.keys(lineageGraph.nodes[key].data.children);\n },\n degree,\n );\n}\n\n// =============================================================================\n// React Flow Conversion\n// =============================================================================\n\n/**\n * Convert a LineageGraph to React Flow nodes and edges\n *\n * This is a simplified version that converts the graph structure.\n * For full column-level lineage support, use the OSS implementation.\n *\n * @param lineageGraph - The lineage graph to convert\n * @param selectedNodes - Optional filter for which nodes to include\n * @returns Tuple of [nodes, edges]\n */\nexport function toReactFlowBasic(\n lineageGraph: LineageGraph,\n selectedNodes?: string[],\n): [LineageGraphNode[], LineageGraphEdge[]] {\n const nodes: LineageGraphNode[] = [];\n const edges: LineageGraphEdge[] = [];\n\n function getWeight(changeStatus?: string) {\n if (changeStatus === \"removed\") {\n return 0;\n } else if (changeStatus === \"added\") {\n return 2;\n }\n return 1;\n }\n\n function compareFn(\n a: LineageGraphNode | LineageGraphEdge,\n b: LineageGraphNode | LineageGraphEdge,\n ) {\n const weightA = getWeight(a.data?.changeStatus);\n const weightB = getWeight(b.data?.changeStatus);\n return weightA - weightB;\n }\n\n const filterSet =\n selectedNodes !== undefined ? new Set(selectedNodes) : undefined;\n const sortedNodes = Object.values(lineageGraph.nodes).sort(compareFn);\n\n for (const node of sortedNodes) {\n if (filterSet && !filterSet.has(node.id)) {\n continue;\n }\n\n nodes.push({\n id: node.id,\n position: { x: 0, y: 0 },\n width: 300,\n height: 60,\n className: \"no-track-pii-safe\",\n data: {\n ...node.data,\n },\n type: \"lineageGraphNode\",\n targetPosition: \"left\" as Position,\n sourcePosition: \"right\" as Position,\n style: {\n width: 300,\n height: 60,\n },\n } as LineageGraphNode);\n }\n\n const sortedEdges = Object.values(lineageGraph.edges).sort(compareFn);\n for (const edge of sortedEdges) {\n if (\n filterSet &&\n (!filterSet.has(edge.source) || !filterSet.has(edge.target))\n ) {\n continue;\n }\n\n edges.push({\n id: edge.id,\n type: \"lineageGraphEdge\",\n source: edge.source,\n target: edge.target,\n data: {\n ...edge.data,\n },\n } as LineageGraphEdge);\n }\n\n return [nodes, edges];\n}\n\n/**\n * Apply dagre layout to nodes and edges\n *\n * NOTE: This function requires @dagrejs/dagre to be installed.\n * Import and call it as follows:\n *\n * @example\n * ```tsx\n * import dagre from '@dagrejs/dagre';\n * import { layoutWithDagre } from '@datarecce/ui';\n *\n * const [nodes, edges] = toReactFlowBasic(lineageGraph);\n * layoutWithDagre(dagre, nodes, edges);\n * ```\n *\n * @param dagre - The dagre library instance\n * @param nodes - Nodes to layout\n * @param edges - Edges for layout\n * @param direction - Layout direction (default: \"LR\" for left-to-right)\n */\nexport function layoutWithDagre(\n // biome-ignore lint/suspicious/noExplicitAny: dagre library is external, consumers provide their own instance\n dagre: any,\n nodes: LineageGraphNodes[],\n edges: LineageGraphEdge[],\n direction = \"LR\",\n): void {\n const dagreGraph = new dagre.graphlib.Graph();\n dagreGraph.setDefaultEdgeLabel(() => ({}));\n dagreGraph.setGraph({ rankdir: direction, ranksep: 50, nodesep: 30 });\n\n for (const node of nodes) {\n if (!isLineageGraphNode(node)) {\n continue;\n }\n let width = 300;\n let height = 60;\n if (node.style?.height && node.style.width) {\n width = parseInt(String(node.style.width), 10);\n height = parseInt(String(node.style.height), 10);\n }\n dagreGraph.setNode(node.id, { width, height });\n }\n\n for (const edge of edges) {\n dagreGraph.setEdge(edge.source, edge.target);\n }\n\n dagre.layout(dagreGraph);\n\n for (const node of nodes) {\n if (!isLineageGraphNode(node)) {\n continue;\n }\n\n const nodeWidth = node.style?.width ?? 300;\n const nodeHeight = node.style?.height ?? 60;\n const nodeWithPosition = dagreGraph.node(node.id);\n\n // Shift from center anchor to top-left anchor\n node.position = {\n x: nodeWithPosition.x - Number(nodeWidth) / 2,\n y: nodeWithPosition.y - Number(nodeHeight) / 2,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils-BzZEjJAS.js","names":["defaultApiClient","defaultApiClient"],"sources":["../src/providers/contexts/RouteConfigContext.tsx","../src/contexts/action/RecceActionContext.tsx","../src/contexts/instance/types.ts","../src/contexts/instance/useRecceInstanceInfo.ts","../src/contexts/instance/RecceInstanceContext.tsx","../src/contexts/idle/types.ts","../src/contexts/idle/useIdleDetection.ts","../src/contexts/idle/IdleTimeoutContext.tsx","../src/contexts/lineage/LineageGraphContext.tsx","../src/contexts/lineage/LineageViewContext.tsx","../src/contexts/lineage/types.ts","../src/contexts/lineage/useRecceServerFlag.ts","../src/components/lineage/computeColumnLineage.ts","../src/contexts/lineage/utils.ts"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n} from \"react\";\n\n/**\n * Route Configuration for path prefix customization.\n *\n * This context allows cloud consumers to configure a base path prefix\n * for all navigation within OSS components.\n *\n * Default behavior (OSS):\n * - basePath: \"\" (uses absolute paths like /query, /checks)\n *\n * Cloud usage example:\n * - basePath: \"/oss/abc123\" or \"/preview/abc123\"\n * - Navigation to \"/query\" becomes \"/oss/abc123/query\"\n */\n\n/**\n * Route configuration interface\n */\nexport interface RouteConfig {\n /**\n * Base path prefix for navigation.\n * For OSS: \"\" (empty string, uses absolute paths like /query)\n * For Cloud: \"/oss/<sessionId>\" or \"/preview/<sessionId>\"\n */\n basePath: string;\n}\n\n/**\n * Context value with path resolution utility\n */\nexport interface RouteConfigContextType extends RouteConfig {\n /**\n * Resolves a path with the base path prefix.\n * @param path - The path to resolve (e.g., \"/query\")\n * @returns The resolved path (e.g., \"/oss/abc123/query\")\n */\n resolvePath: (path: string) => string;\n}\n\nconst defaultConfig: RouteConfig = {\n basePath: \"\",\n};\n\nconst RouteConfigContext = createContext<RouteConfigContextType | null>(null);\nRouteConfigContext.displayName = \"RouteConfigContext\";\n\n/**\n * Props for RouteConfigProvider\n */\nexport interface RouteConfigProviderProps extends Partial<RouteConfig> {\n children: ReactNode;\n}\n\n/**\n * Provider for route configuration.\n *\n * Wrap your application (or RecceContextProvider) with this provider\n * to configure path prefixes for navigation.\n *\n * @example\n * ```tsx\n * // In cloud application\n * <RouteConfigProvider basePath={`/oss/${sessionId}`}>\n * <RecceContextProvider>\n * {children}\n * </RecceContextProvider>\n * </RouteConfigProvider>\n * ```\n */\nexport function RouteConfigProvider({\n children,\n basePath = defaultConfig.basePath,\n}: RouteConfigProviderProps) {\n const resolvePath = useCallback(\n (path: string): string => {\n // If no basePath configured, return path as-is (OSS mode)\n if (!basePath) {\n return path;\n }\n\n // Handle paths that already start with the basePath (avoid double-prefixing)\n if (path.startsWith(basePath)) {\n return path;\n }\n\n // Handle absolute URLs (http://, https://, etc.) - don't prefix\n if (path.match(/^https?:\\/\\//)) {\n return path;\n }\n\n // Handle hash-only paths - don't prefix\n if (path.startsWith(\"#\")) {\n return path;\n }\n\n // Ensure proper joining (no double slashes)\n const cleanBasePath = basePath.endsWith(\"/\")\n ? basePath.slice(0, -1)\n : basePath;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n\n return `${cleanBasePath}${cleanPath}`;\n },\n [basePath],\n );\n\n const contextValue: RouteConfigContextType = useMemo(\n () => ({\n basePath,\n resolvePath,\n }),\n [basePath, resolvePath],\n );\n\n return (\n <RouteConfigContext.Provider value={contextValue}>\n {children}\n </RouteConfigContext.Provider>\n );\n}\n\n// Default context for OSS mode (no prefix)\nconst defaultRouteConfigContext: RouteConfigContextType = {\n basePath: \"\",\n resolvePath: (path: string) => path,\n};\n\n/**\n * Hook to access route configuration.\n *\n * When used outside RouteConfigProvider, returns default config\n * (for OSS backward compatibility).\n *\n * @returns RouteConfigContextType with basePath and resolvePath function\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { basePath, resolvePath } = useRouteConfig();\n * const fullPath = resolvePath('/checks');\n * // In cloud with basePath=\"/oss/abc123\": \"/oss/abc123/checks\"\n * // In OSS with basePath=\"\": \"/checks\"\n * }\n * ```\n */\nexport function useRouteConfig(): RouteConfigContextType {\n const context = useContext(RouteConfigContext);\n // Return default config if outside provider (OSS mode)\n return context ?? defaultRouteConfigContext;\n}\n\n/**\n * Safe hook that returns null if context not available.\n * Useful for components that need to detect if RouteConfigProvider is present.\n */\nexport function useRouteConfigSafe(): RouteConfigContextType | null {\n return useContext(RouteConfigContext);\n}\n","\"use client\";\n\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from \"react\";\nimport type {\n AxiosQueryParams,\n RecceActionContextType,\n RecceActionOptions,\n} from \"./types\";\n\n/**\n * Props for RecceActionProvider\n *\n * This is a props-driven provider - pass your own run execution handler.\n * The provider manages UI state (result pane, history panel) internally.\n */\nexport interface RecceActionProviderProps {\n children: ReactNode;\n\n /**\n * Handler called when a run action is requested.\n * Implement this to submit runs to your backend.\n *\n * @param type - The run type (e.g., \"row_count_diff\", \"value_diff\")\n * @param params - Query parameters for the run\n * @param options - Action options (showForm, trackProps)\n * @returns Promise that resolves to the run ID, or void if handled differently\n */\n onRunAction?: (\n type: string,\n params?: AxiosQueryParams,\n options?: RecceActionOptions,\n ) => Promise<string | undefined> | string | undefined;\n\n /**\n * Handler called when a run result should be shown.\n * Called by showRunId when displaying a specific run.\n *\n * @param runId - The run ID to display\n * @param refreshHistory - Whether to refresh the history list\n */\n onShowRunId?: (runId: string, refreshHistory?: boolean) => void;\n\n /** Initial run ID to display (optional) */\n initialRunId?: string;\n\n /** Initial state for history panel (default: false) */\n initialHistoryOpen?: boolean;\n}\n\nconst defaultContext: RecceActionContextType = {\n runAction: () => void 0,\n showRunId: () => void 0,\n isRunResultOpen: false,\n closeRunResult: () => void 0,\n isHistoryOpen: false,\n closeHistory: () => void 0,\n showHistory: () => void 0,\n setHistoryOpen: () => void 0,\n clearRunResult: () => void 0,\n};\n\nconst RecceActionContext =\n createContext<RecceActionContextType>(defaultContext);\nRecceActionContext.displayName = \"RecceActionContext\";\n\n/**\n * Provider for run action context.\n *\n * This is a props-driven provider designed for library consumers.\n * Pass your own `onRunAction` handler to execute runs.\n *\n * @example\n * ```tsx\n * const handleRunAction = async (type, params, options) => {\n * const response = await api.submitRun(type, params);\n * return response.run_id;\n * };\n *\n * <RecceActionProvider onRunAction={handleRunAction}>\n * <LineageView />\n * </RecceActionProvider>\n * ```\n */\nexport function RecceActionProvider({\n children,\n onRunAction,\n onShowRunId,\n initialRunId,\n initialHistoryOpen = false,\n}: RecceActionProviderProps) {\n // Run result pane state\n const [isRunResultOpen, setRunResultOpen] = useState(!!initialRunId);\n const [runId, setRunId] = useState<string | undefined>(initialRunId);\n\n // History panel state\n const [isHistoryOpen, setHistoryOpen] = useState(initialHistoryOpen);\n\n // Close run result pane\n const closeRunResult = useCallback(() => {\n setRunResultOpen(false);\n }, []);\n\n // Clear run result and close pane\n const clearRunResult = useCallback(() => {\n setRunId(undefined);\n setRunResultOpen(false);\n }, []);\n\n // History panel controls\n const showHistory = useCallback(() => {\n setHistoryOpen(true);\n }, []);\n\n const closeHistory = useCallback(() => {\n setHistoryOpen(false);\n }, []);\n\n // Show a specific run result\n const showRunId = useCallback(\n (newRunId: string, refreshHistory?: boolean) => {\n setRunId(newRunId);\n setRunResultOpen(true);\n onShowRunId?.(newRunId, refreshHistory);\n },\n [onShowRunId],\n );\n\n // Execute a run action\n const runAction = useCallback(\n async (\n type: string,\n params?: AxiosQueryParams,\n options?: RecceActionOptions,\n ) => {\n if (!onRunAction) {\n console.warn(\n \"RecceActionProvider: onRunAction not provided, cannot execute run\",\n );\n return;\n }\n\n const result = await onRunAction(type, params, options);\n\n // If the handler returns a run ID, show the result\n if (typeof result === \"string\") {\n showRunId(result);\n }\n },\n [onRunAction, showRunId],\n );\n\n const contextValue = useMemo<RecceActionContextType>(\n () => ({\n runAction,\n runId,\n showRunId,\n isRunResultOpen,\n closeRunResult,\n isHistoryOpen,\n closeHistory,\n showHistory,\n setHistoryOpen,\n clearRunResult,\n }),\n [\n runAction,\n runId,\n showRunId,\n isRunResultOpen,\n closeRunResult,\n isHistoryOpen,\n closeHistory,\n showHistory,\n clearRunResult,\n ],\n );\n\n return (\n <RecceActionContext.Provider value={contextValue}>\n {children}\n </RecceActionContext.Provider>\n );\n}\n\n/**\n * Hook to access the RecceAction context.\n *\n * @returns RecceActionContextType with run action methods and state\n */\nexport function useRecceActionContext(): RecceActionContextType {\n return useContext(RecceActionContext);\n}\n","\"use client\";\n\n/**\n * Feature mode for the Recce instance.\n * - \"read only\": No modifications allowed\n * - \"metadata only\": Database queries disabled\n * - null: Full functionality enabled\n */\nexport type RecceFeatureMode = \"read only\" | \"metadata only\" | null;\n\n/**\n * Feature toggles that control what actions are available in the UI.\n * These are derived from the server's instance info based on server mode.\n */\nexport interface RecceFeatureToggles {\n /** Current feature mode based on server_mode */\n mode: RecceFeatureMode;\n /** Disable saving state to file */\n disableSaveToFile: boolean;\n /** Disable exporting state file */\n disableExportStateFile: boolean;\n /** Disable importing state file */\n disableImportStateFile: boolean;\n /** Disable updating checklist */\n disableUpdateChecklist: boolean;\n /** Disable database query execution */\n disableDatabaseQuery: boolean;\n /** Disable view action dropdown menu */\n disableViewActionDropdown: boolean;\n /** Disable node action dropdown menu */\n disableNodeActionDropdown: boolean;\n /** Disable share functionality */\n disableShare: boolean;\n /** Checklist disabled due to insufficient permissions (viewer role) — show disabled button with tooltip instead of hiding */\n checklistPermissionDenied: boolean;\n}\n\n/**\n * Instance information exposed through the RecceInstanceContext.\n * Contains feature toggles and session information.\n */\nexport interface InstanceInfoType {\n /** Whether running in single environment mode */\n singleEnv: boolean;\n /** Whether user is authenticated */\n authed: boolean;\n /** Feature toggles based on server mode */\n featureToggles: RecceFeatureToggles;\n /** When the instance lifetime expires */\n lifetimeExpiredAt?: Date;\n /** URL for sharing the instance */\n shareUrl?: string;\n /** Current session ID */\n sessionId?: string;\n /** Python runtime version (e.g., \"3.9.18\") */\n pythonVersion?: string;\n}\n\n/**\n * Default feature toggles with all features enabled.\n */\nexport const defaultFeatureToggles: RecceFeatureToggles = {\n mode: null,\n disableSaveToFile: false,\n disableExportStateFile: false,\n disableImportStateFile: false,\n disableUpdateChecklist: false,\n disableDatabaseQuery: false,\n disableViewActionDropdown: false,\n disableNodeActionDropdown: false,\n disableShare: false,\n checklistPermissionDenied: false,\n};\n\n/**\n * Default instance info values.\n */\nexport const defaultInstanceInfo: InstanceInfoType = {\n singleEnv: false,\n authed: false,\n lifetimeExpiredAt: undefined,\n featureToggles: defaultFeatureToggles,\n shareUrl: undefined,\n sessionId: undefined,\n pythonVersion: undefined,\n};\n","\"use client\";\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport { cacheKeys } from \"../../api/cacheKeys\";\nimport {\n getRecceInstanceInfo,\n type RecceInstanceInfo,\n} from \"../../api/instanceInfo\";\nimport { createFetchClient } from \"../../lib/fetchClient\";\nimport { useApiConfigOptional } from \"../../providers/contexts/ApiContext\";\n\n// Default API client for use outside RecceProvider (OSS mode)\nconst defaultApiClient = createFetchClient({ baseURL: \"\" });\n\n/**\n * Hook to fetch Recce instance information from the server.\n *\n * Uses TanStack Query to cache the response and the configured API client.\n * Works both inside RecceProvider (uses configured client) and outside (uses default API client).\n *\n * @returns Query result with RecceInstanceInfo data\n *\n * @example\n * ```tsx\n * function InstanceStatus() {\n * const { data, isLoading, error } = useRecceInstanceInfo();\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error loading instance info</div>;\n *\n * return <div>Server mode: {data?.server_mode}</div>;\n * }\n * ```\n */\nexport function useRecceInstanceInfo() {\n const apiConfig = useApiConfigOptional();\n const apiClient = apiConfig?.apiClient ?? defaultApiClient;\n\n return useQuery<RecceInstanceInfo>({\n queryKey: cacheKeys.instanceInfo(),\n queryFn: () => getRecceInstanceInfo(apiClient),\n });\n}\n","\"use client\";\n\nimport { createContext, type ReactNode, useContext, useState } from \"react\";\n\nimport {\n defaultFeatureToggles,\n defaultInstanceInfo,\n type InstanceInfoType,\n type RecceFeatureToggles,\n} from \"./types\";\nimport { useRecceInstanceInfo } from \"./useRecceInstanceInfo\";\n\nconst InstanceInfoContext =\n createContext<InstanceInfoType>(defaultInstanceInfo);\nInstanceInfoContext.displayName = \"RecceInstanceInfoContext\";\n\n/**\n * Provider that fetches and processes Recce instance information.\n *\n * This provider:\n * 1. Fetches instance info from the server using useRecceInstanceInfo\n * 2. Computes feature toggles based on server_mode\n * 3. Provides the processed data through context\n *\n * Feature toggles are computed based on:\n * - server_mode: \"read-only\" disables all modifications\n * - server_mode: \"preview\" disables database queries but allows metadata operations\n * - single_env: disables checklist updates and sharing\n * - cloud_instance: disables sharing\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <RecceProvider api={{ baseUrl: \"/api\" }}>\n * <RecceInstanceInfoProvider>\n * <MyComponent />\n * </RecceInstanceInfoProvider>\n * </RecceProvider>\n * );\n * }\n * ```\n */\nexport function RecceInstanceInfoProvider({\n children,\n}: {\n children: ReactNode;\n}) {\n const { data: instanceInfo, isLoading } = useRecceInstanceInfo();\n const [featureToggles, setFeatureToggles] = useState<RecceFeatureToggles>(\n defaultFeatureToggles,\n );\n const [singleEnv, setSingleEnv] = useState<boolean>(false);\n const [authed, setAuthed] = useState<boolean>(false);\n const [lifetimeExpiredAt, setLifetimeExpiredAt] = useState<Date>();\n const [shareUrl, setShareUrl] = useState<string>();\n const [sessionId, setSessionId] = useState<string>();\n const [pythonVersion, setPythonVersion] = useState<string>();\n const [prevInstanceInfo, setPrevInstanceInfo] = useState(instanceInfo);\n\n // Adjust state during render when instanceInfo changes\n if (!isLoading && instanceInfo && instanceInfo !== prevInstanceInfo) {\n setPrevInstanceInfo(instanceInfo);\n\n setSingleEnv(instanceInfo.single_env);\n setAuthed(instanceInfo.authed);\n setShareUrl(instanceInfo.share_url);\n setSessionId(instanceInfo.session_id);\n setPythonVersion(instanceInfo.python_version);\n\n if (instanceInfo.lifetime_expired_at) {\n setLifetimeExpiredAt(new Date(instanceInfo.lifetime_expired_at));\n console.log(\"lifetime expired at\", instanceInfo.lifetime_expired_at);\n }\n\n // Set feature toggles based on instanceInfo\n const toggles = { ...defaultFeatureToggles };\n if (instanceInfo.server_mode === \"read-only\") {\n toggles.mode = \"read only\";\n toggles.disableSaveToFile = true;\n toggles.disableExportStateFile = true;\n toggles.disableImportStateFile = true;\n toggles.disableUpdateChecklist = true;\n toggles.disableDatabaseQuery = true;\n toggles.disableViewActionDropdown = true;\n toggles.disableNodeActionDropdown = true;\n toggles.disableShare = true;\n } else if (instanceInfo.server_mode === \"preview\") {\n toggles.mode = \"metadata only\";\n toggles.disableSaveToFile = true;\n toggles.disableExportStateFile = true;\n toggles.disableImportStateFile = true;\n toggles.disableUpdateChecklist = false;\n toggles.disableDatabaseQuery = true;\n toggles.disableViewActionDropdown = false;\n toggles.disableNodeActionDropdown = false;\n toggles.disableShare = true;\n }\n if (instanceInfo.single_env) {\n toggles.disableUpdateChecklist = true;\n toggles.disableShare = true;\n }\n if (instanceInfo.cloud_instance) {\n toggles.disableShare = true;\n }\n if (instanceInfo.user_role === \"viewer\") {\n // Only show permission-denied tooltip when checklist isn't already disabled\n // by mode (read-only) or env (single_env) — in those cases, hide entirely\n if (!toggles.disableUpdateChecklist) {\n toggles.checklistPermissionDenied = true;\n }\n toggles.disableUpdateChecklist = true;\n }\n setFeatureToggles(toggles);\n }\n\n return (\n <InstanceInfoContext.Provider\n value={{\n featureToggles,\n singleEnv,\n authed,\n lifetimeExpiredAt,\n shareUrl,\n sessionId,\n pythonVersion,\n }}\n >\n {children}\n </InstanceInfoContext.Provider>\n );\n}\n\n/**\n * Hook to access the Recce instance context.\n *\n * Returns the current instance information including feature toggles,\n * authentication status, and session information.\n *\n * @returns InstanceInfoType with feature toggles and session info\n *\n * @example\n * ```tsx\n * function FeatureGate() {\n * const { featureToggles } = useRecceInstanceContext();\n *\n * if (featureToggles.disableDatabaseQuery) {\n * return <div>Database queries are disabled</div>;\n * }\n *\n * return <QueryEditor />;\n * }\n * ```\n */\nexport function useRecceInstanceContext(): InstanceInfoType {\n return useContext(InstanceInfoContext);\n}\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\n\n/**\n * Context for sharing idle timeout state across components\n *\n * IMPORTANT: The countdown is based on the last SUCCESSFUL keep-alive API call,\n * NOT on user activity. This ensures the countdown accurately reflects the\n * server's idle timeout state.\n */\nexport interface IdleTimeoutContextType {\n /** Remaining seconds until timeout (null if idle timeout not enabled) */\n remainingSeconds: number | null;\n /** Idle timeout value from server in seconds (null if not configured) */\n idleTimeout: number | null;\n /** Whether idle timeout is enabled */\n isEnabled: boolean;\n /** Mark as disconnected - stops countdown and keep-alive */\n setDisconnected: () => void;\n /** Reset connection state - restarts countdown and keep-alive after successful reconnect */\n resetConnection: () => void;\n /** Whether the connection is disconnected */\n isDisconnected: boolean;\n}\n\nexport const IdleTimeoutContext = createContext<\n IdleTimeoutContextType | undefined\n>(undefined);\n\n/**\n * Hook to access idle timeout context, returns null if outside provider\n * Used internally by useIdleDetection to avoid circular dependency\n */\nexport function useIdleTimeoutSafe(): IdleTimeoutContextType | null {\n return useContext(IdleTimeoutContext) ?? null;\n}\n","\"use client\";\n\nimport throttle from \"lodash/throttle\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport { sendKeepAlive } from \"../../api/keepAlive\";\nimport { createFetchClient } from \"../../lib/fetchClient\";\nimport { useApiConfigOptional } from \"../../providers/contexts/ApiContext\";\n\nimport { useRecceInstanceInfo } from \"../instance\";\nimport { useIdleTimeoutSafe } from \"./types\";\n\n// Default API client for use outside RecceProvider (OSS mode)\nconst defaultApiClient = createFetchClient({ baseURL: \"\" });\n\n/**\n * Check if debug logging is enabled via window.RECCE_DEBUG_IDLE\n * Enable in browser console: window.RECCE_DEBUG_IDLE = true\n * Disable: delete window.RECCE_DEBUG_IDLE\n */\nfunction isDebugEnabled(): boolean {\n // biome-ignore lint/suspicious/noExplicitAny: window flag for debug logging\n return typeof window !== \"undefined\" && !!(window as any).RECCE_DEBUG_IDLE;\n}\n\n/**\n * Log function that only outputs when debug is enabled\n */\nfunction debugLog(message: string, data?: Record<string, unknown>) {\n if (isDebugEnabled()) {\n if (data) {\n console.log(message, data);\n } else {\n console.log(message);\n }\n }\n}\n\n/**\n * Configuration for idle detection behavior\n */\nconst IDLE_DETECTION_CONFIG = {\n /** Events to listen for user activity */\n ACTIVITY_EVENTS: [\"focus\", \"mousemove\", \"keydown\", \"scroll\"] as const,\n /**\n * Throttle event handler execution to reduce JS overhead (150ms).\n * Uses lodash.throttle with { leading: true, trailing: true } to ensure\n * immediate response on first activity (leading) and also capture the final\n * event in a burst (trailing), which is important for user experience.\n */\n EVENT_THROTTLE_MS: 150,\n} as const;\n\n/**\n * Hook to detect user activity and send keep-alive signals to prevent server idle timeout.\n *\n * This hook:\n * - Listens for user activities (focus, mouse, keyboard, scroll)\n * - Sends keep-alive requests (throttled at API layer to minimum 3 seconds)\n * - Pauses when the tab is inactive (using Page Visibility API)\n * - Immediately sends a keep-alive when tab becomes active\n * - Only activates when idle_timeout is configured on the server\n *\n * Note: The countdown in IdleTimeoutContext is based on successful keep-alive\n * API calls, not on user activity. This ensures accurate server state tracking.\n */\nexport function useIdleDetection() {\n const { data: instanceInfo, isLoading, isError } = useRecceInstanceInfo();\n const idleTimeoutContext = useIdleTimeoutSafe();\n const isDisconnected = idleTimeoutContext?.isDisconnected ?? false;\n const apiConfig = useApiConfigOptional();\n const apiClient = apiConfig?.apiClient ?? defaultApiClient;\n\n // Only enable idle detection if idle_timeout is configured and not disconnected\n const idleTimeout = instanceInfo?.idle_timeout;\n const isEnabled =\n idleTimeout !== undefined && idleTimeout > 0 && !isDisconnected;\n\n // Debug: Log instance info state when debug is enabled\n debugLog(\"[Idle Detection] Instance info\", {\n isLoading,\n isError,\n hasIdleTimeout: idleTimeout !== undefined,\n idleTimeout:\n idleTimeout !== undefined ? `${idleTimeout}s` : \"not configured\",\n isDisconnected,\n isEnabled,\n });\n\n /**\n * Send keep-alive signal to server\n * Throttling is handled at the API layer (minimum 3 seconds between API calls)\n * The successful send will notify IdleTimeoutContext to reset countdown\n */\n const sendKeepAliveNow = useCallback(async () => {\n if (document.hidden) return;\n\n const sent = await sendKeepAlive(apiClient);\n\n if (sent) {\n debugLog(\"[Idle Detection] Keep-alive sent successfully\", {\n timestamp: new Date().toISOString(),\n });\n }\n }, [apiClient]);\n\n /**\n * Handle any user activity event\n * Attempts to send keep-alive (API layer handles throttling)\n */\n const handleActivity = useCallback(\n (event: Event) => {\n if (isEnabled && !document.hidden) {\n debugLog(\"[Idle Detection] Activity detected\", {\n event: event.type,\n tabActive: !document.hidden,\n });\n\n // Send keep-alive API call (API layer handles throttling)\n void sendKeepAliveNow();\n }\n },\n [isEnabled, sendKeepAliveNow],\n );\n\n /**\n * Handle tab visibility changes\n * When tab becomes active, attempt to send keep-alive\n */\n const handleVisibilityChange = useCallback(() => {\n if (!isEnabled) return;\n\n if (!document.hidden) {\n debugLog(\"[Idle Detection] Tab became active\", {\n timestamp: new Date().toISOString(),\n });\n\n // Send keep-alive (API layer handles throttling)\n void sendKeepAliveNow();\n }\n }, [isEnabled, sendKeepAliveNow]);\n\n // Create throttled handler using lodash to reduce JS overhead from high-frequency events\n // useMemo ensures stable reference and proper cleanup\n const throttledHandler = useMemo(\n () =>\n throttle(handleActivity, IDLE_DETECTION_CONFIG.EVENT_THROTTLE_MS, {\n leading: true,\n trailing: true,\n }),\n [handleActivity],\n );\n\n useEffect(() => {\n if (!isEnabled) {\n debugLog(\"[Idle Detection] Disabled\", {\n idleTimeout: idleTimeout,\n reason:\n idleTimeout === undefined\n ? \"idle_timeout not configured on server\"\n : idleTimeout === 0\n ? \"idle_timeout is 0\"\n : \"disconnected\",\n });\n return;\n }\n\n debugLog(\"[Idle Detection] Initialized\", {\n enabled: true,\n idleTimeout: `${idleTimeout}s`,\n eventThrottle: `${IDLE_DETECTION_CONFIG.EVENT_THROTTLE_MS}ms`,\n apiThrottle: \"3s (API layer)\",\n monitoredEvents: IDLE_DETECTION_CONFIG.ACTIVITY_EVENTS.join(\", \"),\n });\n\n // Register activity event listeners with throttled handler\n IDLE_DETECTION_CONFIG.ACTIVITY_EVENTS.forEach((eventType) => {\n window.addEventListener(eventType, throttledHandler, { passive: true });\n });\n\n // Register visibility change listener (not throttled - immediate response needed)\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n // Cleanup function\n return () => {\n debugLog(\"[Idle Detection] Cleanup - removing event listeners\");\n IDLE_DETECTION_CONFIG.ACTIVITY_EVENTS.forEach((eventType) => {\n window.removeEventListener(eventType, throttledHandler);\n });\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n throttledHandler.cancel(); // Cancel any pending throttled calls\n };\n }, [isEnabled, throttledHandler, handleVisibilityChange, idleTimeout]);\n}\n","\"use client\";\n\nimport {\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport {\n getLastKeepAliveTime,\n setKeepAliveCallback,\n} from \"../../api/keepAlive\";\nimport { useRecceInstanceInfo } from \"../instance\";\nimport { IdleTimeoutContext, type IdleTimeoutContextType } from \"./types\";\nimport { useIdleDetection } from \"./useIdleDetection\";\n\n/**\n * Provider for idle timeout state\n *\n * The countdown is based on lastServerSyncTime (when keep-alive API was last\n * successfully sent), not on user activity. This provides accurate server state.\n */\nexport function IdleTimeoutProvider({ children }: { children: ReactNode }) {\n const { data: instanceInfo } = useRecceInstanceInfo();\n // Track the last time we successfully synced with server (keep-alive sent)\n const lastServerSyncRef = useRef<number>(Date.now());\n const [remainingSeconds, setRemainingSeconds] = useState<number | null>(null);\n const [isDisconnected, setIsDisconnected] = useState(false);\n\n const idleTimeout = instanceInfo?.idle_timeout ?? null;\n const isEnabled = idleTimeout !== null && idleTimeout > 0;\n\n // Register callback to receive keep-alive success notifications\n // Use ref to track enabled state to avoid race condition in callback\n const isEnabledRef = useRef(isEnabled);\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n if (!isEnabled) {\n setKeepAliveCallback(null);\n return;\n }\n\n setKeepAliveCallback((timestamp: number) => {\n // Check current enabled state to avoid updating after disabled\n if (isEnabledRef.current) {\n lastServerSyncRef.current = timestamp;\n }\n });\n\n // Initialize with current keep-alive time if available\n const currentTime = getLastKeepAliveTime();\n if (currentTime > 0) {\n lastServerSyncRef.current = currentTime;\n }\n\n return () => {\n setKeepAliveCallback(null);\n };\n }, [isEnabled]);\n\n const setDisconnected = useCallback(() => {\n setIsDisconnected(true);\n }, []);\n\n const resetConnection = useCallback(() => {\n // Reset disconnected state\n setIsDisconnected(false);\n // Reset server sync time to now (server just restarted, timer reset)\n lastServerSyncRef.current = Date.now();\n }, []);\n\n // Update remaining seconds every second based on server sync time\n useEffect(() => {\n if (!isEnabled || idleTimeout === null) {\n setRemainingSeconds(null);\n return;\n }\n\n // Stop updating countdown if disconnected\n if (isDisconnected) {\n return;\n }\n\n const updateCountdown = () => {\n const now = Date.now();\n const elapsedSeconds = (now - lastServerSyncRef.current) / 1000;\n const remaining = Math.max(0, idleTimeout - elapsedSeconds);\n setRemainingSeconds(remaining);\n };\n\n // Initial update\n updateCountdown();\n\n // Set up interval for updates\n const intervalId = setInterval(updateCountdown, 1000);\n\n return () => {\n clearInterval(intervalId);\n };\n }, [isEnabled, idleTimeout, isDisconnected]);\n\n return (\n <IdleTimeoutContext.Provider\n value={{\n remainingSeconds,\n idleTimeout,\n isEnabled,\n setDisconnected,\n resetConnection,\n isDisconnected,\n }}\n >\n <IdleDetector />\n {children}\n </IdleTimeoutContext.Provider>\n );\n}\n\n/**\n * Internal component that activates idle detection\n * Placed inside provider so it has access to context\n */\nfunction IdleDetector() {\n useIdleDetection();\n return null;\n}\n\n/**\n * Hook to access idle timeout context\n * @throws Error if used outside IdleTimeoutProvider\n */\nexport function useIdleTimeout() {\n const context = useContext(IdleTimeoutContext);\n if (!context) {\n throw new Error(\"useIdleTimeout must be used within IdleTimeoutProvider\");\n }\n return context;\n}\n\n// Re-export type and safe hook for backwards compatibility\nexport type { IdleTimeoutContextType } from \"./types\";\nexport { useIdleTimeoutSafe } from \"./types\";\n","\"use client\";\n\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n} from \"react\";\nimport type { RunsAggregated } from \"../../api/runs\";\nimport type { EnvInfo, LineageGraph, LineageGraphContextType } from \"./types\";\n\n/**\n * Props for LineageGraphProvider\n *\n * This is a props-driven provider - pass data from your data fetching layer.\n * The provider does NOT fetch data internally; consumers handle data fetching\n * and pass the results via props.\n */\nexport interface LineageGraphProviderProps {\n children: ReactNode;\n\n /** The processed lineage graph data */\n lineageGraph?: LineageGraph;\n\n /** Environment information (git, dbt, sqlmesh metadata) */\n envInfo?: EnvInfo;\n\n /** Whether in review mode (read-only checks) */\n reviewMode?: boolean;\n\n /** Whether in cloud mode (recce cloud) */\n cloudMode?: boolean;\n\n /** Whether in file mode (loading from file) */\n fileMode?: boolean;\n\n /** The state file name if in file mode */\n fileName?: string;\n\n /** Whether this is the demo site */\n isDemoSite?: boolean;\n\n /** Whether running in GitHub Codespace */\n isCodespace?: boolean;\n\n /** Loading state */\n isLoading?: boolean;\n\n /** Error message if loading failed */\n error?: string;\n\n /** Supported task types from server */\n supportTasks?: Record<string, boolean>;\n\n /** Callback to refetch the lineage graph */\n onRefetchLineageGraph?: () => void;\n\n /** Pre-aggregated run results by model */\n runsAggregated?: RunsAggregated;\n\n /** Callback to refetch aggregated runs */\n onRefetchRunsAggregated?: () => void;\n}\n\nconst defaultContext: LineageGraphContextType = {\n isActionAvailable: () => true,\n isDemoSite: false,\n isLoading: true, // Default to loading state for SSR hydration safety\n};\n\nconst LineageGraphContext =\n createContext<LineageGraphContextType>(defaultContext);\nLineageGraphContext.displayName = \"RecceLineageGraphContext\";\n\n/**\n * Provider for LineageGraph context.\n *\n * This is a props-driven provider designed for library consumers.\n * Pass data from your data fetching layer (e.g., TanStack Query).\n *\n * @example\n * ```tsx\n * const { data, isLoading, error, refetch } = useQuery({\n * queryKey: ['lineage'],\n * queryFn: fetchLineageData,\n * });\n *\n * <LineageGraphProvider\n * lineageGraph={data?.lineageGraph}\n * envInfo={data?.envInfo}\n * isLoading={isLoading}\n * error={error?.message}\n * onRefetchLineageGraph={refetch}\n * >\n * <LineageView />\n * </LineageGraphProvider>\n * ```\n */\nexport function LineageGraphProvider({\n children,\n lineageGraph,\n envInfo,\n reviewMode,\n cloudMode,\n fileMode,\n fileName,\n isDemoSite = false,\n isCodespace,\n isLoading,\n error,\n supportTasks,\n onRefetchLineageGraph,\n runsAggregated,\n onRefetchRunsAggregated,\n}: LineageGraphProviderProps) {\n // Create stable isActionAvailable function\n const isActionAvailable = useCallback(\n (name: string) => {\n if (supportTasks) {\n // Default to true if action not found in supportTasks\n return supportTasks[name] ?? true;\n }\n // If supportTasks not provided, all actions are available\n return true;\n },\n [supportTasks],\n );\n\n // Memoize the context value to prevent unnecessary re-renders\n const contextValue = useMemo<LineageGraphContextType>(\n () => ({\n lineageGraph,\n envInfo,\n reviewMode,\n cloudMode,\n fileMode,\n fileName,\n isDemoSite,\n isCodespace,\n isLoading,\n error,\n supportTasks,\n refetchLineageGraph: onRefetchLineageGraph,\n isActionAvailable,\n runsAggregated,\n refetchRunsAggregated: onRefetchRunsAggregated,\n }),\n [\n lineageGraph,\n envInfo,\n reviewMode,\n cloudMode,\n fileMode,\n fileName,\n isDemoSite,\n isCodespace,\n isLoading,\n error,\n supportTasks,\n onRefetchLineageGraph,\n isActionAvailable,\n runsAggregated,\n onRefetchRunsAggregated,\n ],\n );\n\n return (\n <LineageGraphContext.Provider value={contextValue}>\n {children}\n </LineageGraphContext.Provider>\n );\n}\n\n/**\n * Hook to access the LineageGraph context.\n *\n * @returns LineageGraphContextType with lineage data and utilities\n * @throws Warning in dev mode if used outside provider (returns default context)\n */\nexport function useLineageGraphContext(): LineageGraphContextType {\n const context = useContext(LineageGraphContext);\n return context;\n}\n\n/**\n * Hook to access aggregated runs data.\n * Convenience wrapper around useLineageGraphContext.\n *\n * @returns Tuple of [runsAggregated, refetchRunsAggregated]\n */\nexport function useRunsAggregated(): [\n RunsAggregated | undefined,\n (() => void) | undefined,\n] {\n const { runsAggregated, refetchRunsAggregated } = useLineageGraphContext();\n return [runsAggregated, refetchRunsAggregated];\n}\n","import { createContext, useContext } from \"react\";\nimport type { LineageViewContextType } from \"./types\";\n\nexport const LineageViewContext = createContext<\n LineageViewContextType | undefined\n>(undefined);\n\nexport const useLineageViewContextSafe = (): LineageViewContextType => {\n const context = useContext(LineageViewContext);\n if (!context) {\n throw new Error(\n \"useLineageViewContext must be used within a LineageViewProvider\",\n );\n }\n return context;\n};\n\nexport const useLineageViewContext = (): LineageViewContextType | undefined => {\n return useContext(LineageViewContext);\n};\n","import type { Edge, Node } from \"@xyflow/react\";\nimport type React from \"react\";\nimport type { CllInput, ColumnLineageData } from \"../../api/cll\";\nimport type {\n CatalogMetadata,\n GitInfo,\n ManifestMetadata,\n PullRequestInfo,\n SQLMeshInfo,\n StateMetadata,\n} from \"../../api/info\";\nimport type { LineageDiffViewOptions } from \"../../api/lineagecheck\";\nimport type { RunsAggregated } from \"../../api/runs\";\nimport type { Run } from \"../../api/types\";\n\n/**\n * Environment information derived from server info\n */\nexport interface EnvInfo {\n stateMetadata?: StateMetadata;\n adapterType?: string;\n git?: GitInfo;\n pullRequest?: PullRequestInfo;\n dbt?: {\n base: ManifestMetadata | undefined | null;\n current: ManifestMetadata | undefined | null;\n };\n sqlmesh?: SQLMeshInfo | null;\n}\n\n/**\n * Lineage graph node type for React Flow\n */\nexport type LineageGraphNode = Node<\n {\n id: string;\n name: string;\n resourceType?: string;\n packageName?: string;\n schema?: string;\n materialized?: string;\n changeStatus?: \"added\" | \"removed\" | \"modified\";\n change?: {\n category: \"breaking\" | \"non_breaking\" | \"partial_breaking\" | \"unknown\";\n columns?: Record<string, \"added\" | \"removed\" | \"modified\" | \"unknown\">;\n };\n parents: Record<string, LineageGraphEdge>;\n children: Record<string, LineageGraphEdge>;\n },\n \"lineageGraphNode\"\n>;\n\n/**\n * Column-level lineage node type\n */\nexport type LineageGraphColumnNode = Node<\n {\n node: LineageGraphNode[\"data\"];\n column: string;\n type: string;\n transformationType?: string;\n changeStatus?: \"added\" | \"removed\" | \"modified\";\n isImpacted?: boolean;\n },\n \"lineageGraphColumnNode\"\n>;\n\n/**\n * Lineage graph edge type for React Flow\n */\nexport type LineageGraphEdge = Edge<\n {\n changeStatus?: \"added\" | \"removed\";\n },\n \"lineageGraphEdge\"\n>;\n\n/**\n * Union type for all lineage graph node types\n */\nexport type LineageGraphNodes = LineageGraphNode | LineageGraphColumnNode;\n\n/**\n * The main lineage graph data structure\n */\nexport interface LineageGraph {\n nodes: Record<string, LineageGraphNode>;\n edges: Record<string, LineageGraphEdge>;\n modifiedSet: string[];\n manifestMetadata: {\n base?: ManifestMetadata;\n current?: ManifestMetadata;\n };\n catalogMetadata: {\n base?: CatalogMetadata;\n current?: CatalogMetadata;\n };\n}\n\n/**\n * Type guard for LineageGraphNode\n */\nexport function isLineageGraphNode(\n node: LineageGraphNodes,\n): node is LineageGraphNode {\n return node.type === \"lineageGraphNode\";\n}\n\n/**\n * Type guard for LineageGraphColumnNode\n */\nexport function isLineageGraphColumnNode(\n node: LineageGraphNodes,\n): node is LineageGraphColumnNode {\n return node.type === \"lineageGraphColumnNode\";\n}\n\n/**\n * Context value exposed by LineageGraphContext\n */\nexport interface LineageGraphContextType {\n /** The processed lineage graph */\n lineageGraph?: LineageGraph;\n /** Environment metadata */\n envInfo?: EnvInfo;\n /** Whether in review mode (read-only checks) */\n reviewMode?: boolean;\n /** Whether in cloud mode (recce cloud) */\n cloudMode?: boolean;\n /** Whether in file mode (loading from file) */\n fileMode?: boolean;\n /** The state file name if in file mode */\n fileName?: string;\n /** Whether this is the demo site */\n isDemoSite: boolean;\n /** Whether running in GitHub Codespace */\n isCodespace?: boolean;\n /** Loading state */\n isLoading?: boolean;\n /** Error message if loading failed */\n error?: string;\n /** Supported task types from server */\n supportTasks?: Record<string, boolean>;\n /** Refetch the lineage graph data */\n refetchLineageGraph?: () => void;\n /** Check if an action is available */\n isActionAvailable: (actionName: string) => boolean;\n /** Pre-aggregated run results by model */\n runsAggregated?: RunsAggregated;\n /** Refetch the aggregated runs */\n refetchRunsAggregated?: () => void;\n}\n\n// ============================================================================\n// LineageViewContext Types\n// ============================================================================\n\n/**\n * Action mode for node operations - single or multiple nodes\n */\nexport type ActionMode = \"per_node\" | \"multi_nodes\";\n\n/**\n * Select mode for multi-node selection\n */\nexport type SelectMode = \"selecting\" | \"action_result\" | undefined;\n\n/**\n * Action state for individual node operations\n */\nexport interface NodeAction {\n /** Whether this is a per-node or multi-node action */\n mode: ActionMode;\n /** Current status of this node's action */\n status?: \"pending\" | \"running\" | \"success\" | \"failure\" | \"skipped\";\n /** Reason why this node was skipped (if applicable) */\n skipReason?: string;\n /** The run associated with this action */\n run?: Run;\n}\n\n/**\n * Overall action state for batch operations\n */\nexport interface ActionState {\n /** Whether this is a per-node or multi-node action */\n mode: ActionMode;\n /** Overall status of the action batch */\n status: \"pending\" | \"running\" | \"canceling\" | \"canceled\" | \"completed\";\n /** Current run being executed */\n currentRun?: Partial<Run>;\n /** Number of completed actions */\n completed: number;\n /** Total number of actions */\n total: number;\n /** Per-node action state */\n actions: Record<string, NodeAction>;\n}\n\n/**\n * Context value exposed by LineageViewContext.\n * Provides state and methods for interacting with the lineage view.\n */\nexport interface LineageViewContextType {\n /** Whether the view is interactive (vs read-only) */\n interactive: boolean;\n /** All nodes in the current view */\n nodes: LineageGraphNodes[];\n /** Currently focused node (hovered/highlighted) */\n focusedNode?: LineageGraphNode;\n /** Currently selected nodes for batch operations */\n selectedNodes: LineageGraphNode[];\n /** Column-level lineage data if showing CLL */\n cll: ColumnLineageData | undefined;\n\n // Context menu\n /** Show the context menu for a node */\n showContextMenu: (event: React.MouseEvent, node: LineageGraphNodes) => void;\n\n // View options\n /** Current view options (filters, display settings) */\n viewOptions: LineageDiffViewOptions;\n /** Update view options */\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n\n // Multi-node selection\n /** Current selection mode */\n selectMode: SelectMode;\n /** Select/deselect a single node */\n selectNode: (nodeId: string) => void;\n /** Select all parent nodes up to a degree */\n selectParentNodes: (nodeId: string, degree?: number) => void;\n /** Select all child nodes up to a degree */\n selectChildNodes: (nodeId: string, degree?: number) => void;\n /** Clear selection */\n deselect: () => void;\n\n // Node state queries\n /** Check if a node is highlighted */\n isNodeHighlighted: (nodeId: string) => boolean;\n /** Check if a node is selected */\n isNodeSelected: (nodeId: string) => boolean;\n /** Check if an edge is highlighted */\n isEdgeHighlighted: (source: string, target: string) => boolean;\n /** Get the action state for a node */\n getNodeAction: (nodeId: string) => NodeAction;\n /** Get the columns visible for a node in CLL mode */\n getNodeColumnSet: (nodeId: string) => Set<string>;\n /** Check if a node is showing change analysis */\n isNodeShowingChangeAnalysis: (nodeId: string) => boolean;\n /** Whether impact radius (change analysis) mode is active */\n changeAnalysisMode: boolean;\n /** Whether the new CLL experience flag is enabled on the server */\n newCllExperience: boolean;\n /**\n * Whether the `--whole-model-impact` server flag is on. Implies\n * `newCllExperience`. Gates two surfaces (NodeView title chip + left\n * stripe for whole-model kinds; LineageNode COLUMN / ADD graph badge\n * for per-column kinds) AND the suppression of the \"Breaking /\n * Non Breaking / Partial Breaking\" text labels on the graph node.\n * When false, lineage nodes render the original category text labels\n * and no whole-model UI.\n */\n wholeModelImpact: boolean;\n // TODO: Move isImpacted to be a per-model state on node data instead of\n // a lookup set, so impact status is part of the graph model rather than\n // a side-channel computed separately.\n /** Frozen set of node IDs that are impacted, computed once during impact\n * analysis and stable across column selections in new CLL experience. */\n impactedNodeIds: Set<string>;\n /** Frozen set of column IDs that are impacted, same lifecycle as impactedNodeIds. */\n impactedColumnIds: Set<string>;\n /**\n * Models that themselves have a whole-model change (row-shape edit at the\n * CLL classifier level — `change_category === \"breaking\"`). Drives the\n * brown title chip + brown left stripe in NodeView. The LineageNode graph\n * badge is intentionally suppressed for whole-model kinds; the chip +\n * stripe carry the signal. Empty Set when the `--whole-model-impact`\n * flag is off.\n */\n wholeModelChangedNodeIds: Set<string>;\n /**\n * Models that are BFS-downstream of one or more whole-model-changed\n * models (the set includes the changed models themselves — they are\n * trivially \"impacted\" by their own change). Drives the amber title chip\n * + amber left stripe in NodeView. The LineageNode graph badge is\n * intentionally suppressed for whole-model kinds. Changed-wins: a model\n * may appear in both this set AND in `wholeModelChangedNodeIds`;\n * consumers must consult `wholeModelChangedNodeIds` first (use\n * `pickWholeModelFlags`). Empty Set when the `--whole-model-impact` flag\n * is off.\n */\n wholeModelImpactedNodeIds: Set<string>;\n /** Set change analysis mode on/off */\n setChangeAnalysisMode: (active: boolean) => void;\n\n // Actions\n /** Run row count on selected nodes */\n runRowCount: () => Promise<void>;\n /** Run row count diff on selected nodes */\n runRowCountDiff: () => Promise<void>;\n /** Run value diff on selected nodes */\n runValueDiff: () => Promise<void>;\n /** Add a lineage diff check */\n addLineageDiffCheck: (viewMode?: string) => void;\n /** Add a schema diff check */\n addSchemaDiffCheck: () => void;\n /** Cancel the current action */\n cancel: () => void;\n /** Current action state */\n actionState: ActionState;\n\n // Column-level lineage\n /** Center the view on a specific node */\n centerNode: (nodeId: string) => void;\n /** Show column-level lineage for a node/column */\n showColumnLevelLineage: (cll?: CllInput) => Promise<void>;\n /** Reset column-level lineage view */\n resetColumnLevelLineage: (previous?: boolean) => Promise<void>;\n}\n","\"use client\";\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport { cacheKeys } from \"../../api/cacheKeys\";\nimport { getServerFlag, type RecceServerFlags } from \"../../api/flag\";\nimport { createFetchClient } from \"../../lib/fetchClient\";\nimport { useApiConfigOptional } from \"../../providers/contexts/ApiContext\";\n\n// Default API client for use outside RecceProvider (OSS mode)\nconst defaultApiClient = createFetchClient({ baseURL: \"\" });\n\n/**\n * Hook to fetch server-side feature flags.\n *\n * Uses TanStack Query to cache and manage flag state.\n * Works both inside RecceProvider (uses configured client) and outside (uses default API client).\n *\n * @returns TanStack Query result with RecceServerFlags data\n */\nexport function useRecceServerFlag() {\n const apiConfig = useApiConfigOptional();\n const apiClient = apiConfig?.apiClient ?? defaultApiClient;\n\n return useQuery<RecceServerFlags>({\n queryKey: cacheKeys.flag(),\n queryFn: () => getServerFlag(apiClient),\n });\n}\n","import type { ColumnLineageData } from \"../../api\";\n\nexport interface ColumnAnnotation {\n column: string;\n isImpacted: boolean;\n transformationType?: string;\n changeStatus?: \"added\" | \"removed\" | \"modified\";\n}\n\n/**\n * Coerce a wire-format `change_status` (which may include `\"unknown\"` since\n * DRC-3409's loud-fail fallback) into the CLL renderer's narrower\n * `ColumnAnnotation[\"changeStatus\"]`. `\"unknown\"` columns are tagged as\n * \"we know something upstream changed but couldn't attribute it\" — surface\n * those as `\"modified\"` so the renderer shows the ~ amber indicator rather\n * than rendering a silent \"no change\" row (the DRC-3409 symptom, one layer\n * deeper on the CLL graph view).\n *\n * Returns `undefined` for `undefined` / unknown-shape inputs so callers can\n * pass through optional fields without losing the \"no change\" case.\n */\nexport function coerceCllChangeStatus(\n status: \"added\" | \"removed\" | \"modified\" | \"unknown\" | undefined,\n): ColumnAnnotation[\"changeStatus\"] | undefined {\n if (status === \"unknown\") {\n return \"modified\";\n }\n return status;\n}\n\n/**\n * Walk both parent_map and child_map from a selected column to collect every\n * column in its lineage chain — upstream ancestors and downstream descendants.\n *\n * Returns a Map keyed by node ID, with an array of columns and their impact\n * status for each model that participates in the lineage chain. A model can\n * contribute multiple columns (e.g., several upstream columns feeding a\n * downstream one). The selected column itself is included exactly once.\n *\n * @param impactedColumns - Pre-computed set of impacted column IDs.\n */\nexport function computeColumnLineage(\n cll: ColumnLineageData,\n nodeId: string,\n column: string,\n impactedColumns: Set<string>,\n): Map<string, ColumnAnnotation[]> {\n const { parent_map, child_map, columns } = cll.current;\n\n const result = new Map<string, ColumnAnnotation[]>();\n const visited = new Set<string>();\n\n function walk(columnId: string, adjacency: Record<string, string[]>) {\n if (visited.has(columnId)) return;\n visited.add(columnId);\n\n const col = columns[columnId];\n if (col) {\n const modelId = columnId.slice(0, -(col.name.length + 1));\n const annotations = result.get(modelId) ?? [];\n annotations.push({\n column: col.name,\n isImpacted: impactedColumns.has(columnId),\n transformationType: col.transformation_type,\n changeStatus: coerceCllChangeStatus(col.change_status),\n });\n result.set(modelId, annotations);\n }\n\n const next = adjacency[columnId] ?? [];\n for (const neighbor of next) {\n walk(neighbor, adjacency);\n }\n }\n\n const startId = `${nodeId}_${column}`;\n\n // Seed the start column once so the shared `visited` set doesn't block the\n // second walk from exploring the start's neighbors in the opposite direction.\n const startCol = columns[startId];\n if (startCol) {\n const modelId = startId.slice(0, -(startCol.name.length + 1));\n result.set(modelId, [\n {\n column: startCol.name,\n isImpacted: impactedColumns.has(startId),\n transformationType: startCol.transformation_type,\n changeStatus: coerceCllChangeStatus(startCol.change_status),\n },\n ]);\n }\n visited.add(startId);\n\n for (const parent of parent_map[startId] ?? []) {\n walk(parent, parent_map);\n }\n for (const child of child_map[startId] ?? []) {\n walk(child, child_map);\n }\n\n return result;\n}\n","import type { Position } from \"@xyflow/react\";\nimport type { MergedLineageResponse } from \"../../api/info\";\nimport { coerceCllChangeStatus } from \"../../components/lineage/computeColumnLineage\";\nimport type {\n LineageGraph,\n LineageGraphColumnNode,\n LineageGraphEdge,\n LineageGraphNode,\n LineageGraphNodes,\n} from \"./types\";\nimport { isLineageGraphNode } from \"./types\";\n\n/**\n * Height of a column node in pixels\n * Must match COLUMN_NODE_HEIGHT in columns/LineageColumnNode.tsx\n */\nexport const COLUMN_HEIGHT = 24;\n\n/**\n * Map of node IDs to their column sets\n */\nexport type NodeColumnSetMap = Record<string, Set<string>>;\n\n// =============================================================================\n// Set Utilities\n// =============================================================================\n\n/**\n * Get the union of multiple sets\n */\nexport function union<T>(...sets: Set<T>[]): Set<T> {\n const unionSet = new Set<T>();\n for (const set of sets) {\n for (const key of set) {\n unionSet.add(key);\n }\n }\n return unionSet;\n}\n\n/**\n * Get the intersection of multiple sets\n */\nexport function intersect<T>(...sets: Set<T>[]): Set<T> {\n if (sets.length === 0) {\n return new Set<T>();\n }\n\n let intersection = new Set<T>(sets[0]);\n\n for (const set of sets) {\n intersection = new Set([...intersection].filter((x) => set.has(x)));\n }\n\n return intersection;\n}\n\n/**\n * Get a set of neighbor nodes up to a certain degree\n */\nexport function getNeighborSet(\n nodeIds: string[],\n getNeighbors: (id: string) => string[],\n degree = 1000,\n): Set<string> {\n const neighborSet = new Set<string>();\n const visited: Record<string, number | undefined> = {};\n\n const dfs = (id: string, currentDegree: number) => {\n if (currentDegree < 0) {\n return;\n }\n if (visited[id] != null && visited[id] >= currentDegree) {\n return;\n }\n visited[id] = currentDegree;\n\n const neighbors = getNeighbors(id);\n\n for (const neighborId of neighbors) {\n dfs(neighborId, currentDegree - 1);\n }\n\n neighborSet.add(id);\n };\n\n for (const nodeId of nodeIds) {\n dfs(nodeId, degree);\n }\n\n return neighborSet;\n}\n\n// =============================================================================\n// Lineage Graph Building\n// =============================================================================\n\n/**\n * Build a LineageGraph from a merged lineage response.\n *\n * The server merges base + current and bakes in diff data (DRC-3258),\n * so this is now a trivial mapping — no client-side diffing.\n *\n * @param lineage - Merged lineage from /api/info\n * @returns Processed LineageGraph structure\n */\nexport function buildLineageGraph(\n lineage: MergedLineageResponse,\n): LineageGraph {\n const nodes: Record<string, LineageGraphNode> = {};\n const edges: Record<string, LineageGraphEdge> = {};\n const modifiedSet: string[] = [];\n\n // 1. Map nodes\n for (const [id, merged] of Object.entries(lineage.nodes)) {\n nodes[id] = {\n id,\n data: {\n id,\n name: merged.name,\n resourceType: merged.resource_type,\n packageName: merged.package_name,\n schema: merged.schema,\n materialized: merged.materialized,\n changeStatus: coerceCllChangeStatus(merged.change_status),\n change: merged.change ?? undefined,\n parents: {},\n children: {},\n },\n type: \"lineageGraphNode\",\n } as LineageGraphNode;\n\n if (merged.change_status) {\n modifiedSet.push(id);\n }\n }\n\n // 2. Map edges and build bidirectional parent/child refs\n for (const edge of lineage.edges) {\n const id = `${edge.source}_${edge.target}`;\n const parentNode = nodes[edge.source];\n const childNode = nodes[edge.target];\n\n if (!parentNode || !childNode) continue;\n\n edges[id] = {\n id,\n source: edge.source,\n target: edge.target,\n data: {\n changeStatus: edge.change_status ?? undefined,\n },\n };\n\n childNode.data.parents[edge.source] = edges[id];\n parentNode.data.children[edge.target] = edges[id];\n }\n\n // 3. Extract metadata\n const baseMeta = lineage.metadata?.base;\n const currentMeta = lineage.metadata?.current;\n\n return {\n nodes,\n edges,\n modifiedSet,\n manifestMetadata: {\n base: baseMeta?.manifest_metadata ?? undefined,\n current: currentMeta?.manifest_metadata ?? undefined,\n },\n catalogMetadata: {\n base: baseMeta?.catalog_metadata ?? undefined,\n current: currentMeta?.catalog_metadata ?? undefined,\n },\n };\n}\n\n// =============================================================================\n// Selection Utilities\n// =============================================================================\n\n/**\n * Select upstream nodes from the given node IDs\n *\n * @param lineageGraph - The lineage graph\n * @param nodeIds - Starting node IDs\n * @param degree - Maximum degree of separation (default: 1000)\n * @returns Set of upstream node IDs\n */\nexport function selectUpstream(\n lineageGraph: LineageGraph,\n nodeIds: string[],\n degree = 1000,\n): Set<string> {\n return getNeighborSet(\n nodeIds,\n (key) => {\n const maybeNodes = lineageGraph.nodes as unknown as Record<\n string,\n LineageGraphNode | undefined\n >;\n if (maybeNodes[key] === undefined) {\n return [];\n }\n return Object.keys(lineageGraph.nodes[key].data.parents);\n },\n degree,\n );\n}\n\n/**\n * Select downstream nodes from the given node IDs\n *\n * @param lineageGraph - The lineage graph\n * @param nodeIds - Starting node IDs\n * @param degree - Maximum degree of separation (default: 1000)\n * @returns Set of downstream node IDs\n */\nexport function selectDownstream(\n lineageGraph: LineageGraph,\n nodeIds: string[],\n degree = 1000,\n): Set<string> {\n return getNeighborSet(\n nodeIds,\n (key) => {\n if (\n (lineageGraph.nodes[key] as LineageGraphNode | undefined) === undefined\n ) {\n return [];\n }\n return Object.keys(lineageGraph.nodes[key].data.children);\n },\n degree,\n );\n}\n\n// =============================================================================\n// React Flow Conversion\n// =============================================================================\n\n/**\n * Convert a LineageGraph to React Flow nodes and edges\n *\n * This is a simplified version that converts the graph structure.\n * For full column-level lineage support, use the OSS implementation.\n *\n * @param lineageGraph - The lineage graph to convert\n * @param selectedNodes - Optional filter for which nodes to include\n * @returns Tuple of [nodes, edges]\n */\nexport function toReactFlowBasic(\n lineageGraph: LineageGraph,\n selectedNodes?: string[],\n): [LineageGraphNode[], LineageGraphEdge[]] {\n const nodes: LineageGraphNode[] = [];\n const edges: LineageGraphEdge[] = [];\n\n function getWeight(changeStatus?: string) {\n if (changeStatus === \"removed\") {\n return 0;\n } else if (changeStatus === \"added\") {\n return 2;\n }\n return 1;\n }\n\n function compareFn(\n a: LineageGraphNode | LineageGraphEdge,\n b: LineageGraphNode | LineageGraphEdge,\n ) {\n const weightA = getWeight(a.data?.changeStatus);\n const weightB = getWeight(b.data?.changeStatus);\n return weightA - weightB;\n }\n\n const filterSet =\n selectedNodes !== undefined ? new Set(selectedNodes) : undefined;\n const sortedNodes = Object.values(lineageGraph.nodes).sort(compareFn);\n\n for (const node of sortedNodes) {\n if (filterSet && !filterSet.has(node.id)) {\n continue;\n }\n\n nodes.push({\n id: node.id,\n position: { x: 0, y: 0 },\n width: 300,\n height: 60,\n className: \"no-track-pii-safe\",\n data: {\n ...node.data,\n },\n type: \"lineageGraphNode\",\n targetPosition: \"left\" as Position,\n sourcePosition: \"right\" as Position,\n style: {\n width: 300,\n height: 60,\n },\n } as LineageGraphNode);\n }\n\n const sortedEdges = Object.values(lineageGraph.edges).sort(compareFn);\n for (const edge of sortedEdges) {\n if (\n filterSet &&\n (!filterSet.has(edge.source) || !filterSet.has(edge.target))\n ) {\n continue;\n }\n\n edges.push({\n id: edge.id,\n type: \"lineageGraphEdge\",\n source: edge.source,\n target: edge.target,\n data: {\n ...edge.data,\n },\n } as LineageGraphEdge);\n }\n\n return [nodes, edges];\n}\n\n/**\n * Apply dagre layout to nodes and edges\n *\n * NOTE: This function requires @dagrejs/dagre to be installed.\n * Import and call it as follows:\n *\n * @example\n * ```tsx\n * import dagre from '@dagrejs/dagre';\n * import { layoutWithDagre } from '@datarecce/ui';\n *\n * const [nodes, edges] = toReactFlowBasic(lineageGraph);\n * layoutWithDagre(dagre, nodes, edges);\n * ```\n *\n * @param dagre - The dagre library instance\n * @param nodes - Nodes to layout\n * @param edges - Edges for layout\n * @param direction - Layout direction (default: \"LR\" for left-to-right)\n */\nexport function layoutWithDagre(\n // biome-ignore lint/suspicious/noExplicitAny: dagre library is external, consumers provide their own instance\n dagre: any,\n nodes: LineageGraphNodes[],\n edges: LineageGraphEdge[],\n direction = \"LR\",\n): void {\n const dagreGraph = new dagre.graphlib.Graph();\n dagreGraph.setDefaultEdgeLabel(() => ({}));\n dagreGraph.setGraph({ rankdir: direction, ranksep: 50, nodesep: 30 });\n\n for (const node of nodes) {\n if (!isLineageGraphNode(node)) {\n continue;\n }\n let width = 300;\n let height = 60;\n if (node.style?.height && node.style.width) {\n width = parseInt(String(node.style.width), 10);\n height = parseInt(String(node.style.height), 10);\n }\n dagreGraph.setNode(node.id, { width, height });\n }\n\n for (const edge of edges) {\n dagreGraph.setEdge(edge.source, edge.target);\n }\n\n dagre.layout(dagreGraph);\n\n for (const node of nodes) {\n if (!isLineageGraphNode(node)) {\n continue;\n }\n\n const nodeWidth = node.style?.width ?? 300;\n const nodeHeight = node.style?.height ?? 60;\n const nodeWithPosition = dagreGraph.node(node.id);\n\n // Shift from center anchor to top-left anchor\n node.position = {\n x: nodeWithPosition.x - Number(nodeWidth) / 2,\n y: nodeWithPosition.y - Number(nodeHeight) / 2,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import{n as e}from"./colors-4aGxuAVw.js";import{createContext as t,memo as n,useCallback as r,useContext as i,useEffect as a,useId as o,useState as s}from"react";import{Fragment as c,jsx as l,jsxs as u}from"react/jsx-runtime";import d from"@mui/material/Box";import f from"@mui/material/Tooltip";import p from"@mui/material/IconButton";import m from"@mui/material/Typography";import h from"@mui/material/Alert";import g from"@mui/material/CircularProgress";import _ from"@mui/material/Snackbar";import v from"@mui/material/Stack";import{PiCopy as y,PiDotsThreeVertical as b}from"react-icons/pi";import ee from"@mui/material/Menu";import te from"@mui/material/MenuItem";import{format as ne,formatDistance as x,parseISO as S}from"date-fns";import{VscClose as re,VscKebabVertical as ie,VscKey as ae,VscPin as oe,VscPinned as se}from"react-icons/vsc";import C from"file-saver";import ce from"write-excel-file/universal";import w from"lodash";import{useCopyToClipboard as le}from"usehooks-ts";import ue from"react-split";const de={INTEGER:`integer`,INT:`integer`,BIGINT:`integer`,SMALLINT:`integer`,TINYINT:`integer`,INT64:`integer`,INT32:`integer`,INT16:`integer`,INT8:`integer`,INT4:`integer`,INT2:`integer`,MEDIUMINT:`integer`,SERIAL:`integer`,BIGSERIAL:`integer`,SMALLSERIAL:`integer`,DOUBLE:`number`,FLOAT:`number`,REAL:`number`,NUMERIC:`number`,DECIMAL:`number`,NUMBER:`number`,FLOAT64:`number`,FLOAT32:`number`,"DOUBLE PRECISION":`number`,VARCHAR:`text`,TEXT:`text`,STRING:`text`,CHAR:`text`,"CHARACTER VARYING":`text`,CHARACTER:`text`,NCHAR:`text`,NVARCHAR:`text`,VARCHAR2:`text`,NVARCHAR2:`text`,CLOB:`text`,NCLOB:`text`,TINYTEXT:`text`,MEDIUMTEXT:`text`,LONGTEXT:`text`,BOOLEAN:`boolean`,BOOL:`boolean`,DATE:`date`,TIMESTAMP:`datetime`,DATETIME:`datetime`,TIMESTAMP_NTZ:`datetime`,TIMESTAMP_LTZ:`datetime`,TIMESTAMP_TZ:`datetime`,TIMESTAMPTZ:`datetime`,"TIMESTAMP WITH TIME ZONE":`datetime`,"TIMESTAMP WITHOUT TIME ZONE":`datetime`,"TIMESTAMP WITH LOCAL TIME ZONE":`datetime`,DATETIME2:`datetime`,SMALLDATETIME:`datetime`,DATETIMEOFFSET:`datetime`,TIME:`time`,TIMETZ:`time`,"TIME WITH TIME ZONE":`time`,"TIME WITHOUT TIME ZONE":`time`,BINARY:`binary`,VARBINARY:`binary`,BYTES:`binary`,BLOB:`binary`,BYTEA:`binary`,TINYBLOB:`binary`,MEDIUMBLOB:`binary`,LONGBLOB:`binary`,JSON:`json`,JSONB:`json`,VARIANT:`json`,OBJECT:`json`,STRUCT:`json`,MAP:`json`,ARRAY:`array`,LIST:`array`,GEOGRAPHY:`geography`,GEOMETRY:`geography`,POINT:`geography`,LINESTRING:`geography`,POLYGON:`geography`,MULTIPOINT:`geography`,MULTILINESTRING:`geography`,MULTIPOLYGON:`geography`,GEOMETRYCOLLECTION:`geography`,SDO_GEOMETRY:`geography`};function fe(e){let t=e.trim().toUpperCase();if(!t)return`unknown`;if(/^TINYINT\s*\(\s*1\s*\)$/.test(t))return`boolean`;let n=t.indexOf(`(`);return de[n===-1?t:t.slice(0,n).trimEnd()]??`unknown`}const T=.6,pe=18/30;function E({size:e=`1em`,style:t,className:n,children:r}){return u(`svg`,{viewBox:`0 0 30 18`,width:e,height:typeof e==`number`?e*pe:`${pe}em`,style:t,className:n,"aria-hidden":`true`,children:[l(`rect`,{x:T,y:T,width:30-T*2,height:18-T*2,rx:3,fill:`none`,stroke:`currentColor`,strokeWidth:1.2}),r]})}function D({text:e,size:t,style:n,className:r}){return l(E,{size:t,style:n,className:r,children:l(`text`,{x:30/2,y:18/2,textAnchor:`middle`,dominantBaseline:`central`,fontSize:10.5,fontFamily:`monospace`,fontWeight:500,fill:`currentColor`,children:e})})}function me(e){return l(D,{text:`123`,...e})}function he(e){return l(D,{text:`1.2`,...e})}function ge(e){return l(D,{text:`abc`,...e})}function _e(e){return l(D,{text:`T/F`,...e})}function ve({size:e,style:t,className:n}){let r=o(),i=T,a=T;return u(E,{size:e,style:t,className:n,children:[u(`mask`,{id:r,children:[l(`rect`,{x:i,y:a,width:15-i,height:16.8,fill:`white`}),l(`text`,{x:15/2,y:18/2,textAnchor:`middle`,dominantBaseline:`central`,fontSize:10,fontFamily:`monospace`,fontWeight:700,fill:`black`,children:`0`})]}),l(`path`,{d:`M15 ${a} H3.6 Q${i} ${a} ${i} 3.6 V14.400000000000002 Q${i} 17.400000000000002 3.6 17.400000000000002 H15 Z`,fill:`currentColor`,mask:`url(#${r})`}),l(`text`,{x:22.5,y:18/2,textAnchor:`middle`,dominantBaseline:`central`,fontSize:10,fontFamily:`monospace`,fontWeight:700,fill:`currentColor`,children:`1`})]})}function ye(e){return l(D,{text:`{ }`,...e})}function be({size:e,style:t,className:n}){return u(E,{size:e,style:t,className:n,children:[u(`g`,{stroke:`currentColor`,fill:`none`,strokeWidth:1.2,strokeLinecap:`round`,children:[l(`line`,{x1:6,y1:5,x2:4,y2:5}),l(`line`,{x1:4,y1:5,x2:4,y2:13}),l(`line`,{x1:4,y1:13,x2:6,y2:13}),l(`line`,{x1:24,y1:5,x2:26,y2:5}),l(`line`,{x1:26,y1:5,x2:26,y2:13}),l(`line`,{x1:26,y1:13,x2:24,y2:13})]}),l(`text`,{x:30/2,y:18/2,textAnchor:`middle`,dominantBaseline:`central`,fontSize:9,fontFamily:`monospace`,fontWeight:500,fill:`currentColor`,children:`1,2`})]})}function xe(e){return l(D,{text:`···`,...e})}function Se({size:e,style:t,className:n}){return l(E,{size:e,style:t,className:n,children:u(`g`,{stroke:`currentColor`,fill:`none`,strokeWidth:1,strokeLinecap:`round`,strokeLinejoin:`round`,transform:`translate(9, 2.5)`,children:[l(`rect`,{x:0,y:2,width:11,height:9.5,rx:1.2}),l(`line`,{x1:3,y1:.5,x2:3,y2:3.5}),l(`line`,{x1:8,y1:.5,x2:8,y2:3.5}),l(`line`,{x1:0,y1:5.5,x2:11,y2:5.5})]})})}function Ce({size:e,style:t,className:n}){return l(E,{size:e,style:t,className:n,children:u(`g`,{stroke:`currentColor`,fill:`none`,strokeWidth:1,strokeLinecap:`round`,strokeLinejoin:`round`,transform:`translate(4, 2.5)`,children:[l(`rect`,{x:0,y:2,width:9.5,height:8.5,rx:1.2}),l(`line`,{x1:2.5,y1:.5,x2:2.5,y2:3.5}),l(`line`,{x1:7,y1:.5,x2:7,y2:3.5}),l(`line`,{x1:0,y1:5,x2:9.5,y2:5}),l(`circle`,{cx:17,cy:8.5,r:3.2}),l(`line`,{x1:17,y1:6.8,x2:17,y2:8.5}),l(`line`,{x1:17,y1:8.5,x2:18.3,y2:9.5})]})})}function we({size:e,style:t,className:n}){return l(E,{size:e,style:t,className:n,children:u(`g`,{stroke:`currentColor`,fill:`none`,strokeWidth:1,strokeLinecap:`round`,strokeLinejoin:`round`,transform:`translate(10, 3.5)`,children:[l(`circle`,{cx:5,cy:5.5,r:5}),l(`line`,{x1:5,y1:3,x2:5,y2:5.5}),l(`line`,{x1:5,y1:5.5,x2:7,y2:6.8})]})})}function Te({size:e,style:t,className:n}){return l(E,{size:e,style:t,className:n,children:u(`g`,{stroke:`currentColor`,fill:`none`,strokeWidth:1,strokeLinecap:`round`,strokeLinejoin:`round`,children:[l(`path`,{d:`M15 14.5 C15 14.5 10.5 11 10.5 8 A4.5 4.5 0 0 1 19.5 8 C19.5 11 15 14.5 15 14.5 Z`,strokeWidth:1.2}),l(`circle`,{cx:15,cy:8,r:1.5,fill:`currentColor`,stroke:`none`})]})})}function Ee(e){let{name:t,status:n,baseType:r,currentType:i,cllAvailable:a,impacted:o}=e,s;switch(n){case`added`:s=i?`${t} added ${i}`:`${t} added`;break;case`removed`:return`deleted ${t}`;case`type_changed`:s=`${t}, was ${r} now ${i}`;break;case`definition_changed`:s=i?`${t} ${i} changed definition`:`${t} changed definition`;break;case`definition_unknown`:s=i?`${t} ${i} change status unknown`:`${t} change status unknown`;break;case`unchanged`:s=i?`${t} ${i}`:t;break;default:s=r&&i&&r!==i?`${t}, was ${r} now ${i}`:i?`${t} ${i}`:t;break}return o&&(s+=` · impacted`),a&&(s+=` · Click for column lineage`),s}const De={integer:me,number:he,text:ge,boolean:_e,date:Se,datetime:Ce,time:we,binary:ve,json:ye,array:be,geography:Te,unknown:xe};function Oe({type:e,size:t,style:n,className:r,disableTooltip:i}){let a=De[fe(e)],o=l(`span`,{"data-testid":`data-type-icon`,style:{display:`inline-flex`,alignItems:`center`,lineHeight:0},children:l(a,{size:t,style:n,className:r})});return i?o:l(f,{title:e,placement:`top`,arrow:!0,children:o})}const O=t(null);O.displayName=`RecceThemeContext`;function ke(){let e=i(O);if(!e)throw Error(`useRecceTheme must be used within RecceProvider`);return e}function Ae(){return i(O)}function je({children:e,defaultMode:t=`system`}){let[n,r]=s(t),[i,o]=s(()=>typeof window>`u`?`light`:t===`system`?window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`:t);return a(()=>{if(n===`system`){let e=window.matchMedia(`(prefers-color-scheme: dark)`);o(e.matches?`dark`:`light`);let t=e=>{o(e.matches?`dark`:`light`)};return e.addEventListener(`change`,t),()=>e.removeEventListener(`change`,t)}else o(n)},[n]),a(()=>{typeof window>`u`||document.documentElement.classList.toggle(`dark`,i===`dark`)},[i]),l(O.Provider,{value:{mode:n,resolvedMode:i,setMode:r},children:e})}const Me=t(null);let k=0;function Ne({children:e}){let[t,n]=s([]),i=r(e=>{let t=e.id||`toast-${++k}`,r={id:t,open:!0,duration:e.type===`loading`?null:e.duration??5e3,closable:e.closable??!0,...e};return n(e=>[...e.filter(e=>e.id!==t),r]),t},[]),a=r(e=>{n(t=>t.map(t=>t.id===e?{...t,open:!1}:t)),setTimeout(()=>{n(t=>t.filter(t=>t.id!==e))},300)},[]),o={toast:i,success:e=>i({...e,type:`success`}),error:e=>i({...e,type:`error`}),warning:e=>i({...e,type:`warning`}),info:e=>i({...e,type:`info`}),loading:e=>i({...e,type:`loading`}),dismiss:a,update:r((e,t)=>{n(n=>n.map(n=>n.id===e?{...n,...t}:n))},[])};return u(Me.Provider,{value:o,children:[e,t.map(e=>l(_,{open:e.open,autoHideDuration:e.duration,onClose:()=>e.closable&&a(e.id),anchorOrigin:{vertical:`bottom`,horizontal:`right`},children:l(h,{severity:e.type===`loading`?`info`:e.type||`info`,onClose:e.closable?()=>a(e.id):void 0,icon:e.type===`loading`?l(g,{size:20,color:`inherit`}):void 0,sx:{width:`100%`,minWidth:300},children:u(v,{spacing:.5,children:[e.title&&l(m,{variant:`subtitle2`,sx:{fontWeight:`bold`},children:e.title}),e.description&&l(m,{variant:`body2`,component:`div`,children:e.description})]})})},e.id))]})}function Pe(){let e=i(Me);if(!e)throw Error(`useToaster must be used within ToasterProvider`);return e}const A=new Set,j={create:e=>{let t=e.id||`toast-${++k}`;return A.forEach(n=>n({type:`create`,options:{...e,id:t}})),t},success:e=>j.create({...e,type:`success`}),error:e=>j.create({...e,type:`error`}),warning:e=>j.create({...e,type:`warning`}),info:e=>j.create({...e,type:`info`}),loading:e=>j.create({...e,type:`loading`}),dismiss:e=>{A.forEach(t=>t({type:`dismiss`,id:e}))},remove:e=>{A.forEach(t=>t({type:`dismiss`,id:e}))},update:(e,t)=>{A.forEach(n=>n({type:`update`,id:e,options:t}))},subscribe:e=>(A.add(e),()=>A.delete(e))};function Fe(){let[e,t]=s([]);s(()=>j.subscribe(e=>{if(e.type===`create`&&e.options){let n={id:e.options.id||`toast-${++k}`,open:!0,duration:e.options.type===`loading`?void 0:e.options.duration??5e3,closable:e.options.closable??!0,...e.options};t(e=>[...e.filter(e=>e.id!==n.id),n])}else if(e.type===`dismiss`&&e.id){let n=e.id;t(e=>e.map(e=>e.id===n?{...e,open:!1}:e)),setTimeout(()=>{t(e=>e.filter(e=>e.id!==n))},300)}else e.type===`update`&&e.id&&e.options&&t(t=>t.map(t=>t.id===e.id?{...t,...e.options}:t))}));let n=e=>{t(t=>t.map(t=>t.id===e?{...t,open:!1}:t)),setTimeout(()=>{t(t=>t.filter(t=>t.id!==e))},300)};return l(c,{children:e.map(e=>l(_,{open:e.open,autoHideDuration:e.duration,onClose:()=>e.closable&&n(e.id),anchorOrigin:{vertical:`bottom`,horizontal:`right`},children:l(h,{severity:e.type===`loading`?`info`:e.type||`info`,onClose:e.closable?()=>n(e.id):void 0,icon:e.type===`loading`?l(g,{size:20,color:`inherit`}):void 0,sx:{width:`100%`,minWidth:300},children:u(v,{spacing:.5,children:[e.title&&l(m,{variant:`subtitle2`,sx:{fontWeight:`bold`},children:e.title}),e.description&&l(m,{variant:`body2`,component:`div`,children:e.description})]})})},e.id))})}function M(){let e=Ae(),[t,n]=s(!1),[r,i]=s(!1);return a(()=>{if(n(!0),!e){let e=()=>{i(document.documentElement.classList.contains(`dark`))};e();let t=new MutationObserver(e);return t.observe(document.documentElement,{attributes:!0,attributeFilter:[`class`]}),()=>t.disconnect()}},[e]),t?e?e.resolvedMode===`dark`:r:!1}function N(e,t=`en-US`,n){return typeof e==`number`?new Intl.NumberFormat(t,n).format(e):e}function Ie(e){let t=e>0&&e<=.001,n=e<1&&e>=.999,r=(t=e)=>N(t,`en-US`,{style:`percent`,minimumFractionDigits:1});return t?`<${r(.001)}`:n?`>${r(.999)}`:r()}function Le(e){if(typeof e!=`number`)return e;{let t=Math.abs(e),n=10**-2,r=10**3,i=10**6,a=10**9,o=10**12,s=10**15,c=t>=n,l=t>=1,u=t>=r,d=t>=i,f=t>=a,p=t>=o,m=t>=s;if(m||p)return new Intl.NumberFormat(`en-US`,{style:`unit`,unit:`liter`,unitDisplay:`narrow`,maximumFractionDigits:m?0:2}).format(e/0xe8d4a51000).replace(`L`,`T`);if(f||d||u){let t={base:f?a:d?i:r,unit:f?`B`:d?`M`:`K`};return new Intl.NumberFormat(`en-US`,{style:`unit`,unit:`liter`,unitDisplay:`narrow`,maximumFractionDigits:1}).format(e/t.base).replace(`L`,t.unit)}else if(l)return new Intl.NumberFormat(`en-US`,{maximumFractionDigits:2}).format(e);else return new Intl.NumberFormat(`en-US`,{maximumFractionDigits:c?3:2,notation:c||t===0?`standard`:`scientific`}).format(e)}}function Re(e,t){return`${e}_${t}`}function ze(e,t,n){return n?.has(Re(e,t))??!1}function Be(e,t){C(new Blob([e],{type:`text/csv;charset=utf-8`}),t)}function Ve(e,t){C(new Blob([e],{type:`text/tab-separated-values;charset=utf-8`}),t)}function He(e,t){C(e,t)}async function Ue(e){if(typeof navigator>`u`||!navigator.clipboard?.writeText)throw Error(`Clipboard API not available. Ensure you're using HTTPS or localhost.`);await navigator.clipboard.writeText(e)}function We(e){return e==null?null:typeof e==`object`?{value:JSON.stringify(e),type:String}:typeof e==`number`?{value:e,type:Number}:typeof e==`boolean`?{value:e,type:Boolean}:{value:String(e),type:String}}function Ge(e,t){return ce([e.map(e=>({value:e,type:String})),...t.map(e=>e.map(We))]).toBlob()}function Ke(e,t){return(e==null?``:String(e))===(t==null?``:String(t))?e:`${e==null?``:`(${e})`} ${t==null?``:`(${t})`}`.trim()}function P(e){return!e?.columns||!e.data?null:{columns:e.columns.map(e=>e.name),rows:e.data.map(e=>[...e])}}function qe(e){return P(e)}function Je(e){return P(e)}function Ye(e,t){let n=e,r=t?.displayMode??`inline`,i=t?.primaryKeys??[];return n?.diff?Xe(n.diff,r,i):Ze(n,r)}function Xe(e,t,n){if(!e?.columns||!e?.data)return null;let r=e.columns.findIndex(e=>e.key.toLowerCase()===`in_a`),i=e.columns.findIndex(e=>e.key.toLowerCase()===`in_b`),a=e.columns.filter(e=>e.key.toLowerCase()!==`in_a`&&e.key.toLowerCase()!==`in_b`),o=a.map(e=>e.name),s=a.map(t=>e.columns.findIndex(e=>e.key===t.key)),c=n.map(t=>e.columns.findIndex(e=>e.key===t)).filter(e=>e>=0),l=e=>s.map(t=>e[t]),u=e=>c.length===0?``:c.map(t=>String(e[t]??``)).join(`|||`),d=new Map,f=[];if(e.data.forEach((e,t)=>{let n=r>=0?e[r]:!0,a=i>=0?e[i]:!0,o=u(e);o===``&&(o=String(t)),d.has(o)||(d.set(o,{base:null,current:null}),f.push(o));let s=d.get(o);if(!s)return;let c=l(e);n&&(s.base=c),a&&(s.current=c)}),t===`side_by_side`){let e=[];o.forEach(t=>{e.push(`base__${t}`,`current__${t}`)});let t=[];for(let e of f){let n=d.get(e);if(!n)continue;let r=n.base,i=n.current,a=[];o.forEach((e,t)=>{a.push(r?r[t]:null),a.push(i?i[t]:null)}),t.push(a)}return{columns:e,rows:t}}let p=[...o],m=[];for(let e of f){let t=d.get(e);if(!t)continue;let n=t.base,r=t.current,i=[];o.forEach((e,t)=>{let a=n?n[t]:null,o=r?r[t]:null;i.push(Ke(a,o))}),m.push(i)}return{columns:p,rows:m}}function Ze(e,t){let n=e?.current||e?.base;if(!n)return null;if(!e?.base||!e?.current)return P(n);let r=e.current.columns.map(e=>e.name);if(t===`side_by_side`){let t=[];r.forEach(e=>{t.push(`base__${e}`,`current__${e}`)});let n=[],i=Math.max(e.base.data.length,e.current.data.length);for(let t=0;t<i;t++){let i=[],a=t<e.base.data.length?e.base.data[t]:null,o=t<e.current.data.length?e.current.data[t]:null;r.forEach((e,t)=>{i.push(a?a[t]:null),i.push(o?o[t]:null)}),n.push(i)}return{columns:t,rows:n}}let i=[...r],a=[],o=Math.max(e.base.data.length,e.current.data.length);for(let t=0;t<o;t++){let n=t<e.base.data.length?e.base.data[t]:null,i=t<e.current.data.length?e.current.data[t]:null,o=[];r.forEach((e,t)=>{let r=n?n[t]:null,a=i?i[t]:null;o.push(Ke(r,a))}),a.push(o)}return{columns:i,rows:a}}function Qe(e){let t=e,n=t?.current||t?.base;if(!n)return null;if(t?.base&&t?.current){let e=[`_source`,...t.current.columns.map(e=>e.name)],n=[];return t.base.data.forEach(e=>{n.push([`base`,...e])}),t.current.data.forEach(e=>{n.push([`current`,...e])}),{columns:e,rows:n}}return P(n)}function $e(e){let t=e;if(!t||typeof t!=`object`)return null;let n=[`node`,`base_count`,`current_count`,`diff`,`diff_percent`],r=[];for(let[e,n]of Object.entries(t))if(n&&typeof n==`object`){let t=n.base,i=n.curr,a=t!=null&&i!=null?i-t:null,o=t&&a!==null?(a/t*100).toFixed(2)+`%`:null;r.push([e,t,i,a,o])}return{columns:n,rows:r}}function et(e){let t=e;return t?.data?P(t.data):null}function tt(e){return P(e)}function nt(e){let t=e,n=!!t?.base?.values,r=!!t?.current?.values;if(!n&&!r)return null;let i=[`_source`,`value`,`count`],a=[];return t?.base?.values&&t.base.values.forEach((e,n)=>{a.push([`base`,e,t.base.counts[n]])}),t?.current?.values&&t.current.values.forEach((e,n)=>{a.push([`current`,e,t.current.counts[n]])}),{columns:i,rows:a}}const rt={query:qe,query_base:Je,query_diff:Ye,profile:Qe,profile_diff:Qe,row_count:$e,row_count_diff:$e,value_diff:et,value_diff_detail:tt,top_k_diff:nt};function it(e,t,n){let r=rt[e];if(!r)return null;try{return r(t,n)}catch(t){return console.error(`Failed to extract CSV data for run type "${e}":`,t),null}}function at(e){return e in rt}function ot(e){if(e==null)return``;let t=typeof e==`object`?JSON.stringify(e):String(e);return t.includes(`,`)||t.includes(`"`)||t.includes(`
|
|
3
|
+
`)||t.includes(`\r`)?`"${t.replace(/"/g,`""`)}"`:t}function st(e,t){return``+[e.map(ot).join(`,`),...t.map(e=>e.map(ot).join(`,`))].join(`\r
|
|
4
|
+
`)}function F(e){return e==null?``:(typeof e==`object`?JSON.stringify(e):String(e)).replace(/[\t\r\n]+/g,` `)}function ct(e,t){return[e.map(F).join(` `),...t.map(e=>e.map(F).join(` `))].join(`\r
|
|
5
|
+
`)}function lt(e){let t=e===1?`row`:`rows`;if(e<1e3)return`${e} ${t}`;if(e<1e6){let n=e/1e3;return n>=999.95?`1M ${t}`:`${n%1==0?String(n):n.toFixed(1)}k ${t}`}let n=e/1e6;return`${n%1==0?String(n):n.toFixed(1)}M ${t}`}function I(){let e=new Date;return`${e.getFullYear()}${String(e.getMonth()+1).padStart(2,`0`)}${String(e.getDate()).padStart(2,`0`)}-${String(e.getHours()).padStart(2,`0`)}${String(e.getMinutes()).padStart(2,`0`)}${String(e.getSeconds()).padStart(2,`0`)}`}function ut(e,t){let n=I(),r=e.replace(/_/g,`-`),i;return t?.node_names&&Array.isArray(t.node_names)&&t.node_names.length===1?i=String(t.node_names[0]):t?.model&&typeof t.model==`string`&&(i=t.model),i?(i=i.replace(/[^a-zA-Z0-9_.-]/g,`-`).toLowerCase(),`${r}-${i}-${n}.csv`):`${r}-result-${n}.csv`}function L({value:t,colorPalette:n,grayOut:r,noCopy:i,fontSize:a,onCopy:o}){let[c,f]=s(!1),p=M();return u(d,{sx:{display:`flex`,p:`2px 5px`,minWidth:`30px`,maxWidth:`200px`,overflow:`hidden`,textOverflow:`ellipsis`,color:p?e[n][300]:e[n][800],bgcolor:p?e[n][900]:e[n][100],alignItems:`center`,gap:`2px`,borderRadius:`8px`,fontSize:a,flexShrink:i?0:`inherit`},onMouseEnter:()=>{f(!0)},onMouseLeave:()=>{f(!1)},children:[l(d,{sx:{overflow:`hidden`,textOverflow:`ellipsis`,color:r?`text.disabled`:`inherit`},children:t}),l(dt,{value:t,noCopy:i,grayOut:r,isHovered:c,onCopy:o})]})}function dt({value:e,noCopy:t,grayOut:n,isHovered:r,onCopy:i}){return t||n||!r?null:l(f,{title:`Copy Value`,children:l(p,{"aria-label":`Copy`,size:`small`,onClick:()=>{i?i(e):typeof navigator<`u`&&navigator.clipboard&&navigator.clipboard.writeText(e)},sx:{minWidth:`0.625rem`,height:`0.625rem`,display:`flex`,alignItems:`center`,justifyContent:`center`,p:0,color:`inherit`},children:l(y,{size:`0.625rem`})})})}function R(e,t){return[{value:`Show raw value`,onClick:()=>{t({[e]:`raw`})}},{value:`Show 2 decimal points`,onClick:()=>{t({[e]:2})}},{value:`Show as percentage`,onClick:()=>{t({[e]:`percent`})}},{value:`Show with net change`,onClick:()=>{t({[e]:`delta`})}}]}function z({name:e,columnStatus:t,columnType:n,primaryKeys:r=[],onPrimaryKeyChange:i,pinnedColumns:a=[],onPinnedColumnsChange:o,onColumnsRenderModeChanged:f}){let[m,h]=s(null),g=!!m,_=e=>{h(e.currentTarget)},v=()=>{h(null)};if(e===`index`)return l(c,{});let y=r.includes(e),b=a.includes(e),ne=t!==`added`&&t!==`removed`,x=[];return f&&(x=R(e,f)),u(d,{sx:{display:`flex`,alignItems:`center`,gap:`10px`,width:`100%`},className:`grid-header`,children:[y&&l(ae,{}),l(d,{sx:{flex:1,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:e}),ne&&i&&l(d,{component:y?re:ae,className:y?`close-icon`:`key-icon`,sx:{display:y?`block`:`none`,cursor:`pointer`},onClick:y?()=>{i&&i(r.filter(t=>t!==e))}:()=>{i&&i([...r.filter(e=>e!==`index`),e])}}),!y&&o&&l(d,{component:b?se:oe,className:b?`unpin-icon`:`pin-icon`,sx:{display:b?`block`:`none`,cursor:`pointer`},onClick:b?()=>{o&&o(a.filter(t=>t!==e))}:()=>{o&&o([...a,e])}}),!y&&n===`number`&&x.length>0&&u(c,{children:[l(p,{"aria-label":`Options`,size:`small`,className:`!size-4 !min-w-4`,onClick:_,children:l(ie,{})}),l(ee,{anchorEl:m,open:g,onClose:v,children:x.map(e=>l(te,{onClick:()=>{e.onClick(),v()},children:e.value},e.value))})]})]})}function ft({name:e,pinnedColumns:t=[],onPinnedColumnsChange:n=()=>{},columnType:r,onColumnsRenderModeChanged:i}){let[a,o]=s(null),f=!!a,m=e=>{o(e.currentTarget)},h=()=>{o(null)},g=[];i&&(g=R(e,i));let _=t.includes(e);return u(d,{sx:{display:`flex`,alignItems:`center`,width:`100%`},className:`grid-header`,children:[l(d,{sx:{flex:1},children:e}),l(d,{component:_?se:oe,className:_?`unpin-icon`:`pin-icon`,sx:{display:_?`block`:`none`,cursor:`pointer`},onClick:_?()=>{n(t.filter(t=>t!==e))}:()=>{n([...t,e])}}),r===`number`&&u(c,{children:[l(p,{"aria-label":`Options`,size:`small`,className:`size-6!`,sx:{p:0},onClick:m,children:l(b,{})}),l(ee,{anchorEl:a,open:f,onClose:h,children:g.map(e=>l(te,{onClick:()=>{e.onClick(),h()},children:e.value},e.value))})]})]})}function B(e){return e.data.map((t,n)=>({...e.columns.reduce((e,n,r)=>(e[n.key]=t[r],e),{}),__status:void 0,_index:n+1}))}function pt(e){let t=Number(e);return!Number.isNaN(t)&&Number.isFinite(t)?t:mt(e)}function mt(e){let t=0;for(let n=0;n<e.length;n++){let r=e.charCodeAt(n);t=(t<<5)-t+r,t&=t}return Math.abs(t)}function ht(e,t){let n=t.toLowerCase();if(n in e)return e[n];let r=Object.keys(e).find(e=>e.toLowerCase()===n);return r?e[r]:void 0}function V(e,t){let n=[...e],r=[...t],i=[];for(;n.length>0&&r.length>0;)if(i.includes(n[0]))n.shift();else if(i.includes(r[0]))r.shift();else if(n[0]===r[0])i.push(n[0]),n.shift(),r.shift();else if(r.includes(n[0])){let e=r.indexOf(n[0]);for(let t=0;t<e;t++)i.includes(r[t])||i.push(r[t]);i.push(n[0]),n.shift(),r.splice(0,e+1)}else i.push(n[0]),n.shift();return n.forEach(e=>{i.includes(e)||i.push(e)}),r.forEach(e=>{i.includes(e)||i.push(e)}),i}function H(e,t){let n=V(e,t),r={};for(let i of n)e.includes(i)?t.includes(i)?r[i]=void 0:r[i]=`removed`:r[i]=`added`;let i={};e.forEach((e,t)=>{i[e]=t});let a=-1;for(let e of n){let t=i[e];t!=null&&(t>a?a=t:r[e]=`reordered`)}return r}function gt(e){let t={};return e.columns.forEach((e,n)=>{t[e.name]={key:e.key,index:n,colType:e.type}}),t}function _t(e){let t={};if(e.columns.forEach((e,n)=>{t[e.key]={key:e.key,index:n,colType:e.type}}),!t.in_a)throw Error(`Joined DataFrame missing required 'in_a' column`);if(!t.in_b)throw Error(`Joined DataFrame missing required 'in_b' column`);return t}function vt(e,t){let n={},r=H(e.columns.map(e=>e.key),t.columns.map(e=>e.key));return Object.entries(r).forEach(([r,i])=>{let a=e.columns.find(e=>e.key===r),o=t.columns.find(e=>e.key===r);n[r]={status:i,baseColumnKey:a?.key??`unknown`,currentColumnKey:o?.key??`unknown`,colType:a?.type??o?.type??`unknown`,key:a?.key??o?.key??`unknown`}}),n}function U(e,t){let n=[];for(let r of t){let t=e.find(e=>e.key===r);if(!t)throw Error(`Column ${r} not found`);n.push(t.key)}return n}function W(e,t,n){if(t.length===0)return String(n._index);let r=[];for(let i of t){let t=e.find(e=>e.key===i);if(!t)throw Error(`Primary Column ${i} not found`);let a=n[i];r.push(`${t.name}=${a}`)}return r.join(`|`)}function G(e,t=2){let n=N(Object.is(e,-0)?0:e,`en-US`,{maximumFractionDigits:t,roundingMode:`halfEven`})??String(e);return n===`-0`?`0`:n}function yt(e,t){return Number.isNaN(e)?`NaN`:Number.isFinite(e)?t===`raw`?String(e):typeof t==`number`?G(e,t):t===`percent`?N(e,`en-US`,{style:`percent`,maximumFractionDigits:2})??String(e):G(e,2):e>0?`∞`:`-∞`}function K(e,t,n,r){let i=ht(e,t);if(i==null)return[`-`,!0];let a,o=!1;return typeof i==`boolean`?a=i.toString():i===``?(a=`(empty)`,o=!0):a=typeof i==`number`?yt(i,r):n===`number`?yt(parseFloat(i),r):String(i),[a,o]}function bt(e){if(e===`added`)return`diff-header-added`;if(e===`removed`)return`diff-header-removed`}const xt=e=>{let t=e.colDef,n=t?.context?.columnType,r=t?.context?.columnRenderMode,i=t?.field??``;if(!e.data)return null;let[a,o]=K(e.data,i,n,r);return l(m,{component:`span`,style:{color:o?`gray`:`inherit`},children:a})};function St(){function e(e){j.create({description:e,type:`info`,duration:5e3,closable:!0})}function t(e,t){j.create({title:e,description:String(t),type:`error`,duration:5e3,closable:!0})}return{successToast:e,failToast:t}}function Ct(e){let[,t]=le(),{successToast:n}=St(),r=e=>{t(e),n(`${e} copied`)};return l(L,{...e,onCopy:r})}function wt(e={}){let t=e.DiffTextComponent??L;return e=>{let n=e.colDef,r=n?.context?.columnType,i=n?.context?.columnRenderMode,a=n?.field??``;if(!e.data)return null;let o=e.data,s=`base__${a}`.toLowerCase(),c=`current__${a}`.toLowerCase();if(!Object.hasOwn(o,s)&&!Object.hasOwn(o,c))return`-`;let p=Object.hasOwn(o,s),h=Object.hasOwn(o,c),[g,_]=K(o,`base__${a}`.toLowerCase(),r,i),[v,y]=K(o,`current__${a}`.toLowerCase(),r,i);if(o[s]===o[c])return l(m,{component:`span`,style:{color:y?`gray`:`inherit`},children:v});if(i===`delta`&&(r===`number`||r===`integer`)&&p&&h){let e=Et(o[s]),n=Et(o[c]);if(Number.isFinite(e)&&Number.isFinite(n)){let r=n-e,i=e===0?0:r/e*100,a=G(n),o=G(r),s=`(${r>=0?`+`:``}${o})`;return l(f,{title:`Base: ${e}\nCurrent: ${n}\nChange: ${r>=0?`+`:``}${r} (${i>=0?`+`:``}${i.toFixed(2)}%)`,slotProps:{tooltip:{sx:{whiteSpace:`pre-line`}}},enterDelay:300,placement:`top`,children:u(d,{sx:{gap:`5px`,display:`flex`,alignItems:`center`,lineHeight:`normal`,height:`100%`},children:[l(t,{value:a,colorPalette:`green`,grayOut:y}),l(m,{sx:{color:r>=0?`green.600`:`red.600`,fontSize:`0.75rem`},children:s})]})})}}return u(d,{sx:{display:`flex`,gap:`5px`,alignItems:`center`,lineHeight:`normal`,height:`100%`},children:[p&&l(t,{value:g,colorPalette:`red`,grayOut:_}),h&&l(t,{value:v,colorPalette:`green`,grayOut:y})]})}}const Tt=wt({DiffTextComponent:Ct});function Et(e){if(typeof e==`number`)return e;if(typeof e==`string`){let t=Number.parseFloat(e);return Number.isNaN(t)?0:t}return 0}function Dt({children:e,direction:t=`horizontal`,sizes:n,minSizes:r=0,maxSizes:i,gutterSize:a=5,snapOffset:o=30,dragInterval:s=1,onDragEnd:c,onDrag:u,theme:f,style:p,className:m}){let h=M(),g=f?f===`dark`:h,_={display:`flex`,flexDirection:t===`horizontal`?`row`:`column`,height:`100%`,width:`100%`,...p};return l(d,{className:m,sx:{height:`100%`,width:`100%`,"& .gutter":{backgroundColor:`divider`,backgroundRepeat:`no-repeat`,backgroundPosition:`50%`,transition:`background-color 0.15s ease`,"&:hover":{backgroundColor:g?`grey.600`:`grey.300`}},"& .gutter.gutter-horizontal":{cursor:`col-resize`},"& .gutter.gutter-vertical":{cursor:`row-resize`}},children:l(ue,{style:_,direction:t,sizes:n,minSize:r,maxSize:i,gutterSize:a,snapOffset:o,dragInterval:s,onDragEnd:c,onDrag:u,children:e})})}const q=n(Dt);q.displayName=`SplitPane`;function Ot(e){let{style:t,children:n,gutterSize:r=5,minSize:i,maxSize:a,sizes:o,snapOffset:s,dragInterval:c,onDragEnd:u,onDrag:d,className:f}=e;return l(q,{direction:`horizontal`,gutterSize:r,minSizes:i,maxSizes:a,sizes:o,snapOffset:typeof s==`number`?s:void 0,dragInterval:c,onDragEnd:u,onDrag:d,style:t,className:f,children:n})}function kt(e){let{style:t,children:n,gutterSize:r=5,minSize:i,maxSize:a,sizes:o,snapOffset:s,dragInterval:c,onDragEnd:u,onDrag:d,className:f}=e;return l(q,{direction:`vertical`,gutterSize:r,minSizes:i,maxSizes:a,sizes:o,snapOffset:typeof s==`number`?s:void 0,dragInterval:c,onDragEnd:u,onDrag:d,style:t,className:f,children:n})}function At({color:e}){return l(d,{component:`span`,sx:{display:`inline-block`,width:`10px`,height:`10px`,bgcolor:e,mr:1,borderRadius:`4px`}})}function jt(e,t){return n=>{let r=n.data;if(!r)return;let i=r.__status;if(i===`removed`)return`diff-cell-removed`;if(i===`added`)return`diff-cell-added`;if(t===`added`||t===`removed`)return;let a=`base__${e}`.toLowerCase(),o=`current__${e}`.toLowerCase();if(!w.isEqual(r[a],r[o]))return`diff-cell-removed`}}function Mt(e,t){return n=>{let r=n.data;if(!r)return;let i=r.__status;if(i===`removed`)return`diff-cell-removed`;if(i===`added`)return`diff-cell-added`;if(t===`added`||t===`removed`)return;let a=`base__${e}`.toLowerCase(),o=`current__${e}`.toLowerCase();if(!w.isEqual(r[a],r[o]))return`diff-cell-added`}}function Nt(e){let{name:t,columnStatus:n,columnType:r,columnRenderMode:i,displayMode:a,baseTitle:o=`Base`,currentTitle:s=`Current`,headerProps:c={},renderComponents:u}=e,{DataFrameColumnGroupHeader:d,defaultRenderCell:f,inlineRenderCell:p}=u,m=bt(n),h=()=>l(d,{name:t,columnStatus:n,columnType:r,...c});if(a===`inline`)return{field:t,headerName:t,headerClass:m,headerComponent:h,cellRenderer:p,context:{columnType:r,columnRenderMode:i}};let g=jt(t,n),_=Mt(t,n);return{headerName:t,headerClass:m,headerGroupComponent:h,context:{columnType:r,columnRenderMode:i},children:[{field:`base__${t}`,headerName:o,headerClass:m,cellClass:g,cellRenderer:f,context:{columnType:r,columnRenderMode:i}},{field:`current__${t}`,headerName:s,headerClass:m,cellClass:_,cellRenderer:f,context:{columnType:r,columnRenderMode:i}}]}}function Pt(){return{field:`_index`,headerName:``,width:50,maxWidth:100,cellClass:`index-column`,resizable:!1,pinned:`left`}}function Ft(e,t,n){let{key:r,name:i,columnType:a,columnStatus:o,columnRenderMode:s}=e,{DataFrameColumnGroupHeader:c,defaultRenderCell:u}=n;return{field:r,headerName:i,headerComponent:()=>l(c,{name:i,columnStatus:o??``,columnType:a,...t}),pinned:`left`,cellClass:e=>{if(e.data?.__status)return`diff-header-${e.data.__status}`},cellRenderer:u,context:{columnType:a,columnRenderMode:s}}}function It(e,t,n,r,i,a){let{name:o,columnType:s,columnStatus:c,columnRenderMode:l}=e;return Nt({name:o,columnStatus:c??``,columnType:s,columnRenderMode:l,displayMode:t,baseTitle:i,currentTitle:a,headerProps:n,renderComponents:r})}function J(e){let{columns:t,displayMode:n,headerProps:r,baseTitle:i,currentTitle:a,allowIndexFallback:o=!1,renderComponents:s}=e,c=[],l=!1;!t.some(e=>e.isPrimaryKey)&&o&&(c.push(Pt()),l=!0);for(let e of t)if(e.isPrimaryKey)c.push(Ft(e,r,s));else{let t=It(e,n,r,s,i,a);c.push({...t,pinned:e.frozen?`left`:void 0})}return{columns:c,usedIndexFallback:l}}function Lt(e,t,n){return!t||!n?!0:e===`added`||e===`removed`||e===`modified`}function Rt(e,t){return t.includes(e)}function zt(e,t){return t.includes(e)}function Bt(e,t){return t.includes(e)}function Vt(e,t){return e[t]}function Ht(e){let{columnMap:t,primaryKeys:n,pinnedColumns:r,columnsRenderMode:i,changedOnly:a=!1,rowStats:o,excludeColumns:s=[],strictMode:c=!1}=e,l=(o?.modified??0)>0,u=[];return n.forEach(e=>{let n=Vt(t,e);if(!n){if(c)throw Error(`Primary key column "${e}" not found in columnMap`);return}u.push({key:e,name:e,columnType:n.colType,columnStatus:n.status,columnRenderMode:i[e],frozen:!0,isPrimaryKey:!0})}),r.forEach(e=>{if(Rt(e,n)||Bt(e,s))return;let r=Vt(t,e);if(!r){if(c)throw Error(`Pinned column "${e}" not found in columnMap`);return}u.push({key:e,name:e,columnType:r.colType,columnStatus:r.status,columnRenderMode:i[e],frozen:!0})}),Object.entries(t).forEach(([e,t])=>{Rt(e,n)||zt(e,r)||Bt(e,s)||Lt(t.status,a,l)&&u.push({key:e,name:e,columnType:t.colType,columnStatus:t.status,columnRenderMode:i[e]})}),u}function Ut(e){let{columnMap:t,primaryKeys:n,pinnedColumns:r,columnsRenderMode:i,excludeColumns:a=[],strictMode:o=!1}=e,s=[];return n.forEach(e=>{let n=t[e];if(!n){if(o)throw Error(`Primary key column "${e}" not found in columnMap`);return}s.push({key:e,name:e,columnType:n.colType,columnRenderMode:i[e],frozen:!0,isPrimaryKey:!0})}),r.forEach(e=>{if(n.includes(e)||a.includes(e))return;let r=t[e];if(!r){if(o)throw Error(`Pinned column "${e}" not found in columnMap`);return}s.push({key:r.key,name:e,columnType:r.colType,columnRenderMode:i[e],frozen:!0})}),Object.entries(t).forEach(([e,t])=>{n.includes(e)||r.includes(e)||a.includes(e)||s.push({key:t.key,name:e,columnType:t.colType,columnRenderMode:i[e]})}),s}function Wt(e){return`baseColumnKey`in e&&`currentColumnKey`in e}function Gt(e,t){return t.includes(e)}function Kt(e){return Wt(e)?{baseKey:e.baseColumnKey,currentKey:e.currentColumnKey}:{baseKey:e.key,currentKey:e.key}}function qt(e,t,n,r,i){n.forEach(n=>{let a=n.key;Gt(a,i)?e[String(a).toLowerCase()]=t[a]:e[`${r}${a}`.toLowerCase()]=t[a]})}function Jt(e,t,n,r){let i=!1;for(let[a,o]of Object.entries(n)){if(a===`index`||Gt(a,r))continue;let{baseKey:n,currentKey:s}=Kt(o);if(n===`unknown`||s===`unknown`)continue;let c=e[n],l=t[s];w.isEqual(c,l)||(i=!0,o.status=`modified`)}return i}function Y(e){let{baseMap:t,currentMap:n,baseColumns:r,currentColumns:i,columnMap:a,primaryKeys:o,changedOnly:s=!1}=e,c=H(Object.keys(t),Object.keys(n)),l={added:0,removed:0,modified:0},u=Object.entries(c).map(([e])=>{let s=t[e],c=n[e],u={_index:pt(e),__status:void 0};return s&&qt(u,s,r,`base__`,o),c&&qt(u,c,i,`current__`,o),s?c?Jt(s,c,a,o)&&(u.__status=`modified`,l.modified++):(u.__status=`removed`,l.removed++):(u.__status=`added`,l.added++),u});return s&&(u=u.filter(e=>e.__status===`added`||e.__status===`removed`||e.__status===`modified`)),{rows:u,rowStats:l}}var X=class extends Error{context;details;constructor(e,t,n){super(t?`[${t}] ${e}`:e),this.context=t,this.details=n,this.name=`DataGridValidationError`}};function Z(e,t=`DataFrame`){if(e!=null){if(typeof e!=`object`)throw new X(`Expected an object, got ${typeof e}`,t);if(!(`columns`in e))throw new X(`Missing 'columns' property`,t,{receivedKeys:Object.keys(e)});if(!Array.isArray(e.columns))throw new X(`'columns' must be an array, got ${typeof e.columns}`,t);if(!(`data`in e))throw new X(`Missing 'data' property`,t,{receivedKeys:Object.keys(e)});if(!Array.isArray(e.data))throw new X(`'data' must be an array, got ${typeof e.data}`,t);Yt(e.columns,t),e.data.length>0&&Xt(e,t)}}function Yt(e,t=`DataFrame`){e.forEach((e,n)=>{if(!e||typeof e!=`object`)throw new X(`Column at index ${n} is not an object`,t,{column:e});if(typeof e.key!=`string`||e.key===``)throw new X(`Column at index ${n} has invalid 'key': expected non-empty string, got ${JSON.stringify(e.key)}`,t,{column:e});if(typeof e.name!=`string`)throw new X(`Column '${e.key}' has invalid 'name': expected string, got ${typeof e.name}`,t,{column:e});if(typeof e.type!=`string`)throw new X(`Column '${e.key}' has invalid 'type': expected string, got ${typeof e.type}`,t,{column:e})})}function Xt(e,t=`DataFrame`){let n=e.columns.length;for(let r=0;r<e.data.length;r++){let i=e.data[r];if(!Array.isArray(i))throw new X(`Row at index ${r} is not an array`,t,{row:i,rowType:typeof i});if(i.length!==n)throw new X(`Row ${r} has ${i.length} values but expected ${n} (column count)`,t,{rowIndex:r,rowLength:i.length,columnCount:n,columns:e.columns.map(e=>e.key)})}}function Zt(e,t,n={}){let{required:r=!1,caseInsensitive:i=!1,context:a=`primaryKeys`}=n;if(r&&(!e||e.length===0))throw new X(`Primary keys are required but none were provided`,a);if(!e||e.length===0)return;let o=t.map(e=>e.key),s=i?o.map(e=>e.toLowerCase()):o;for(let t of e){let e=i?t.toLowerCase():t;if(!(i?s.includes(e):o.includes(t)))throw new X(`Primary key column '${t}' not found in columns`,a,{requestedKey:t,availableColumns:o,caseInsensitive:i})}let c=new Set;for(let t of e){let n=i?t.toLowerCase():t;if(c.has(n))throw new X(`Duplicate primary key: '${t}'`,a,{primaryKeys:e});c.add(n)}}function Qt(e,t){Z(e,`dataframe`),e&&t?.primaryKeys&&Zt(t.primaryKeys,e.columns,{context:`toDataGrid`})}function $t(e,t,n){if(Z(e,`base`),Z(t,`current`),n?.primaryKeys&&n.primaryKeys.length>0){let r=e?.columns??[],i=t?.columns??[],a=new Set([...r.map(e=>e.key),...i.map(e=>e.key)]);for(let e of n.primaryKeys)if(!a.has(e))throw new X(`Primary key column '${e}' not found in either base or current DataFrame`,`toDataDiffGrid`,{requestedKey:e,baseColumns:r.map(e=>e.key),currentColumns:i.map(e=>e.key)})}}function en(e,t){if(!e)throw new X(`DataFrame is required for value diff`);if(Z(e),!t||t.length===0)throw new X(`Primary keys are required for value diff`);Zt(t,e.columns,{required:!0,context:`toValueDiffGrid`});let n=e.columns.map(e=>e.key);if(!n.includes(`in_a`))throw new X(`Value diff DataFrame must include lowercase 'in_a' column`);if(!n.includes(`in_b`))throw new X(`Value diff DataFrame must include lowercase 'in_b' column`)}function tn(e,t,n,r){$t(e,t,n);let i=e??{columns:[],data:[]},a=t??{columns:[],data:[]},o=n?.primaryKeys??[],s=n?.pinnedColumns??[],c=n?.changedOnly??!1,l=n?.displayMode??`side_by_side`,u=n?.columnsRenderMode??{},d=B(i),f=B(a),p=vt(i,a),m={},h={},g=!1,_=!1;if(o.length===0)d.forEach(e=>{m[String(e._index)]=e}),f.forEach(e=>{h[String(e._index)]=e});else{let e=U(i.columns,o);d.forEach(t=>{let n=W(i.columns,e,t);n in m&&(g=!0),m[n]=t});let t=U(a.columns,o);f.forEach(e=>{let n=W(a.columns,t,e);n in h&&(_=!0),h[n]=e})}let{rows:v,rowStats:y}=Y({baseMap:m,currentMap:h,baseColumns:i.columns,currentColumns:a.columns,columnMap:p,primaryKeys:o,changedOnly:c}),{columns:b}=J({columns:Ht({columnMap:p,primaryKeys:o,pinnedColumns:s,columnsRenderMode:u,changedOnly:c,rowStats:y,excludeColumns:[`index`],strictMode:!1}),displayMode:l,allowIndexFallback:!0,baseTitle:n?.baseTitle,currentTitle:n?.currentTitle,headerProps:{primaryKeys:o,pinnedColumns:s,onPrimaryKeyChange:n?.onPrimaryKeyChange,onPinnedColumnsChange:n?.onPinnedColumnsChange,onColumnsRenderModeChanged:n?.onColumnsRenderModeChanged},renderComponents:r?.renderComponents??{DataFrameColumnGroupHeader:()=>null,defaultRenderCell:()=>null,inlineRenderCell:()=>null}});return{columns:b,rows:v,invalidPKeyBase:g,invalidPKeyCurrent:_}}function nn(){return{field:`_index`,headerName:``,width:50,cellClass:`index-column`,resizable:!1,pinned:`left`}}function rn(e,t,n){let{key:r,name:i,columnType:a,columnRenderMode:o}=e,{DataFrameColumnGroupHeader:s,defaultRenderCell:c}=n;return{field:r,headerName:i,headerComponent:()=>l(s,{name:i,columnStatus:``,columnType:a,pinnedColumns:t.pinnedColumns,onPinnedColumnsChange:t.onPinnedColumnsChange,onColumnsRenderModeChanged:t.onColumnsRenderModeChanged}),pinned:`left`,cellRenderer:c,context:{columnType:a,columnRenderMode:o}}}function an(e,t,n){let{key:r,name:i,columnType:a,columnRenderMode:o}=e,{DataFrameColumnHeader:s,defaultRenderCell:c}=n;return{field:r,headerName:i,headerComponent:()=>l(s,{name:i,columnType:a,pinnedColumns:t.pinnedColumns,onPinnedColumnsChange:t.onPinnedColumnsChange,onColumnsRenderModeChanged:t.onColumnsRenderModeChanged}),cellRenderer:c,context:{columnType:a,columnRenderMode:o}}}function on(e){let{columns:t,headerProps:n,allowIndexFallback:r=!0,renderComponents:i}=e,a=[],o=!1;!t.some(e=>e.isPrimaryKey)&&r&&(a.push(nn()),o=!0);for(let e of t)if(e.isPrimaryKey)a.push(rn(e,n,i));else{let t=an(e,n,i);a.push({...t,pinned:e.frozen?`left`:void 0})}return{columns:a,usedIndexFallback:o}}function sn(e,t,n){Qt(e,t);let r=t.primaryKeys??[],i=t.pinnedColumns??[],a=t.columnsRenderMode??{},{columns:o}=on({columns:Ut({columnMap:gt(e),primaryKeys:r,pinnedColumns:i,columnsRenderMode:a}),headerProps:{pinnedColumns:i,onPinnedColumnsChange:t.onPinnedColumnsChange,onColumnsRenderModeChanged:t.onColumnsRenderModeChanged},allowIndexFallback:!0,renderComponents:n.renderComponents});return{columns:o,rows:B(e)}}function cn(e,t,n,r){en(e,t);let i=n?.pinnedColumns??[],a=n?.changedOnly??!1,o=n?.displayMode??`inline`,s=n?.columnsRenderMode??{},c=B(e),l=_t(e),u={},d={},f=U(e.columns,t),p=l.in_a.key,m=l.in_b.key;c.forEach(t=>{let n=W(e.columns,f,t);t[p]&&(u[n.toLowerCase()]=t),t[m]&&(d[n.toLowerCase()]=t)});let{rows:h,rowStats:g}=Y({baseMap:u,currentMap:d,baseColumns:e.columns,currentColumns:e.columns,columnMap:l,primaryKeys:t,changedOnly:a}),{columns:_}=J({columns:Ht({columnMap:l,primaryKeys:t,pinnedColumns:i,columnsRenderMode:s,changedOnly:a,rowStats:g,excludeColumns:[`in_a`,`in_b`],strictMode:!0}),displayMode:o,allowIndexFallback:!1,baseTitle:n?.baseTitle,currentTitle:n?.currentTitle,headerProps:{primaryKeys:t,pinnedColumns:i,onPinnedColumnsChange:n?.onPinnedColumnsChange,onColumnsRenderModeChanged:n?.onColumnsRenderModeChanged},renderComponents:r?.renderComponents??{DataFrameColumnGroupHeader:()=>null,defaultRenderCell:()=>null,inlineRenderCell:()=>null}});return{columns:_,rows:h}}const Q={DataFrameColumnGroupHeader:z,defaultRenderCell:xt,inlineRenderCell:Tt},$={DataFrameColumnGroupHeader:z,DataFrameColumnHeader:ft,defaultRenderCell:xt};function ln(e){return Nt({...e,renderComponents:Q})}function un(e){return J({...e,renderComponents:Q})}function dn(e,t,n){return tn(e,t,n,{renderComponents:Q})}function fn(e,t,n){return cn(e,t,n,{renderComponents:Q})}function pn(e,t){return sn(e,t,{renderComponents:$})}function mn(e){return on({...e,renderComponents:$})}function hn(e,t){if(e===0&&t!==0)return`N/A`;if(e<t){let n=(t-e)/e*100;return`+${n>=.1?n.toFixed(1):` <0.1 `}%`}else if(e>t){let n=(e-t)/e*100;return`-${n>=.1?n.toFixed(1):` <0.1 `}%`}else return`0`}function gn(e,t){return e!==null&&t!==null?e===t?`0`:hn(e,t):e===t?`N/A`:e===null?`Added`:t===null?`Removed`:`N/A`}function _n(e){return{columns:[{key:`name`,name:`Name`,type:`text`},{key:`base`,name:`Base Rows`,type:`number`},{key:`current`,name:`Current Rows`,type:`number`},{key:`delta`,name:`Delta`,type:`text`}],data:Object.entries(e).map(([e,t])=>{let n=typeof t.base==`number`?t.base:null,r=typeof t.curr==`number`?t.curr:null;return[e,n,r,gn(n,r)]})}}function vn(e){return{columns:[{key:`name`,name:`Name`,type:`text`},{key:`current`,name:`Row Count`,type:`number`}],data:Object.entries(e).map(([e,t])=>[e,typeof t.curr==`number`?t.curr:null])}}function yn(e,t){if(e===null&&t!==null)return`added`;if(e!==null&&t===null)return`removed`;if(e!==null&&t!==null&&e!==t)return`modified`}function bn(e){return{columns:[{field:`name`,headerName:`Name`,resizable:!0},{field:`current`,headerName:`Row Count`,resizable:!0}],rows:B(vn(e)).map(e=>({...e,current:e.current??`N/A`,__status:void 0}))}}function xn(){return e=>{let t=e.data;if(!t)return;let n=t.base,r=t.current,i=n===`N/A`?null:n,a=r===`N/A`?null:r;if(i!==a){if(i===null||typeof i==`number`&&typeof a==`number`&&i<a)return`diff-cell-added`;if(a===null||typeof i==`number`&&typeof a==`number`&&i>a)return`diff-cell-removed`}}}function Sn(e){let t=B(_n(e)).map(e=>{let t=e.base,n=e.current;return{...e,base:t??`N/A`,current:n??`N/A`,__status:yn(typeof t==`number`?t:null,typeof n==`number`?n:null)}}),n=xn();return{columns:[{field:`name`,headerName:`Name`,resizable:!0,cellClass:n},{field:`base`,headerName:`Base Rows`,resizable:!0,cellClass:n},{field:`current`,headerName:`Current Rows`,resizable:!0,cellClass:n},{field:`delta`,headerName:`Delta`,resizable:!0,cellClass:n}],rows:t}}function Cn(e){let t=new Set,n=new Set;if(e?.nodes){let r=Object.values(e.nodes);for(let e of r)e.data.schema&&(e.data.changeStatus!==`added`&&t.add(e.data.schema),e.data.changeStatus!==`removed`&&n.add(e.data.schema))}return[t,n]}function wn(e,t){let n=V(e,t);if(n.length===0)return[];if(e.length===0||t.length===0)return n.map((e,t)=>t===n.length-1?e:e+`,`);let r=``;return n.forEach(n=>{e.includes(n)&&t.includes(n)&&(r=n)}),n.map((i,a)=>{let o;return o=e.includes(i)?t.includes(i)?i:`--- ${i} (Removed)`:`--- ${i} (Added)`,i===r||a===n.length-1?o:o+`,`})}function Tn(e){let t=Math.floor(e);return{hours:Math.floor(t/3600),minutes:Math.floor(t%3600/60),seconds:t%60}}function En({hours:e,minutes:t,seconds:n}){return e>0?`${e}:${t.toString().padStart(2,`0`)}:${n.toString().padStart(2,`0`)}`:`${t}:${n.toString().padStart(2,`0`)}`}function Dn({hours:e,minutes:t,seconds:n}){let r=[];return e>0&&r.push(`${e} hour${e===1?``:`s`}`),t>0&&r.push(`${t} min${t===1?``:`s`}`),e===0&&(r.length===0||n>0)&&r.push(`${n} second${n===1?``:`s`}`),r.join(` `)}function On(e,t=`verbose`){let n=Tn(e);return t===`compact`?En(n):Dn(n)}function kn(e){if(!Number.isFinite(e))return String(e);let{hours:t,minutes:n,seconds:r}=Tn((Math.floor(e)%86400+86400)%86400),i=e=>e.toString().padStart(2,`0`);return`${i(t)}:${i(n)}:${i(r)}`}function An(e){let t=new Date(e*1e3);return Number.isNaN(t.getTime())?String(e):t.toLocaleDateString(void 0,{year:`numeric`,month:`short`,day:`numeric`,timeZone:`UTC`})}function jn(e){return ne(S(e),`yyyy-MM-dd'T'HH:mm:ss`)}function Mn(e){return x(S(e),new Date,{addSuffix:!0})}function Nn(e,t){if(!e||!t)return;let n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!0;for(let e=0;e<n.length;e++)if(n[e]!==r[e])return!0;for(let n of r)if(!e[n]||e[n].type!==t[n]?.type)return!0;return!1}function Pn(e,t){return e?.organization_name&&e.web_url?`${e.web_url}/organizations/${e.organization_name}/settings`:t}export{validateToDataGridInputs as $,Oe as $t,createCellClassCurrent as A,I as At,isPinnedColumn as B,Ve as Bt,buildJoinedColumnMap as C,St as Ct,columnPrecisionSelectOptions as D,ht as Dt,calculateDelta as E,B as Et,getHeaderCellClass as F,at as Ft,toDiffColumn as G,M as Gt,rowCountDiffResultToDataFrame as H,ze as Ht,getPrimaryKeyValue as I,Ge as It,validateColumns as J,j as Jt,toRenderedValue as K,Fe as Kt,getRowCountDiffStatus as L,Ue as Lt,formatSmartDecimal as M,st as Mt,getCellClass as N,ct as Nt,columnRenderedValue as O,L as Ot,getDisplayColumns as P,it as Pt,validateToDataDiffGridInputs as Q,Ae as Qt,getSimpleDisplayColumns as R,Be as Rt,buildDiffRows as S,Ct as St,buildSimpleColumnDefinitions as T,H as Tt,rowCountResultToDataFrame as U,Le as Ut,isPrimaryKeyColumn as V,Re as Vt,shouldIncludeColumn as W,Ie as Wt,validatePrimaryKeyConfig as X,je as Xt,validateDataFrame as Y,Pe as Yt,validatePrimaryKeys as Z,ke as Zt,Cn as _,ToggleSwitch as _t,getCaseInsensitive as a,bn as at,buildColumnOrder as b,kt as bt,keyToNumber as c,mn as ct,On as d,dn as dt,Ee as en,validateToValueDiffGridInputs as et,An as f,pn as ft,wn as g,DiffDisplayModeSwitch as gt,jn as h,ChangedOnlyCheckbox as ht,formatNumber as i,Sn as it,determineRowStatus as j,lt as jt,createCellClassBase as k,ut as kt,Pn as l,Q as lt,Mn as m,fn as mt,formatAsAbbreviatedNumber as n,toDataGrid as nt,getValueAtPath as o,hn as ot,kn as p,ln as pt,validateColumnDataAlignment as q,Ne as qt,formatIntervalMinMax as r,toValueDiffGrid as rt,hashStringToNumber as s,un as st,dataFrameToRowObjects as t,fe as tn,toDataDiffGrid as tt,Nn as u,$ as ut,DataGridValidationError as v,At as vt,buildMergedColumnMap as w,V as wt,buildDiffColumnDefinitions as x,q as xt,buildColumnMap as y,Ot as yt,isExcludedColumn as z,He as zt};
|
|
6
|
+
//# sourceMappingURL=utils-D1bvitEj.js.map
|