@unblind/react 0.1.0-alpha.0
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/index.css +3 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +520 -0
- package/dist/index.d.ts +520 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/providers/UnblindClientProvider.tsx","../src/providers/UnblindScopeProvider.tsx","../src/providers/UnblindProvider.tsx","../src/hooks/useMetrics.ts","../src/hooks/useTimeseries.ts","../src/hooks/utils.ts","../src/hooks/useUsage.ts","../src/hooks/useLogs.ts","../src/hooks/useTheme.ts","../src/components/Chart/index.tsx","../src/components/Chart/utils.ts","../src/components/Chart/XAxis.ts","../src/components/Tooltip/plugin.tsx","../src/components/Tooltip/TooltipManager.ts","../src/components/Tooltip/index.tsx","../src/components/Divider.tsx","../src/components/Chart/YAxis.ts","../src/components/TimeseriesChart.tsx"],"sourcesContent":["// Provider\nexport {\n UnblindProvider,\n useUnblindClientConfig,\n useRefresh,\n useScope,\n} from \"./providers/UnblindProvider\";\nexport type {\n UnblindClientConfig,\n UnblindProviderProps,\n UnblindScopeConfig,\n} from \"./providers/UnblindProvider\";\n\n// Separate providers (for advanced use cases)\nexport {\n UnblindClientProvider,\n type UnblindClientProviderProps,\n} from \"./providers/UnblindClientProvider\";\nexport {\n UnblindScope,\n type UnblindScopeProps,\n type UseScopeReturn,\n} from \"./providers/UnblindScopeProvider\";\n\n// Hooks\nexport { useMetrics } from \"./hooks/useMetrics\";\nexport type { UseMetricsReturn } from \"./hooks/useMetrics\";\nexport { useTimeseries } from \"./hooks/useTimeseries\";\nexport type {\n UseTimeseriesParams,\n UseTimeseriesReturn,\n} from \"./hooks/useTimeseries\";\nexport { useUsage } from \"./hooks/useUsage\";\nexport type { UseUsageParams, UseUsageReturn, Usage } from \"./hooks/useUsage\";\nexport { useLogs } from \"./hooks/useLogs\";\nexport type { UseLogsParams, UseLogsReturn } from \"./hooks/useLogs\";\nexport { useTheme } from \"./hooks/useTheme\";\nimport \"./styles.css\";\n\n// Components\nexport { TimeseriesChart } from \"./components/TimeseriesChart\";\nexport type { TimeseriesChartProps } from \"./components/TimeseriesChart\";\nexport type { TooltipProps } from \"./components/Tooltip\";\nexport { Chart } from \"./components/Chart\";\nexport type { ChartProps } from \"./components/Chart\";\nexport { Divider } from \"./components/Divider\";\n\n// Types\nexport type {\n MetricType,\n MetricMetadata,\n MetricMetadataList,\n AggregationOperator,\n AttributeWithValue,\n Serie,\n TimeseriesQuery,\n ChartType,\n TimeRange,\n Log,\n Severity,\n PaginatedResponse,\n Colors,\n TimeConfig,\n TimeseriesProps,\n TimeseriesQueryConfig,\n Interval,\n ChartVisualConfig,\n Appearance,\n} from \"./types\";\n","import {\n QueryClient,\n QueryClientConfig,\n QueryClientProvider,\n useQueryClient,\n} from \"@tanstack/react-query\";\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n type ReactNode,\n} from \"react\";\n\nexport type UnblindClientConfig = {\n /**\n * Base URL that client-side hooks will use.\n * Should point to the host app endpoint that is proxied by middleware.\n * Defaults to `/api/unblind`.\n */\n apiBaseUrl: string;\n\n /**\n * Optional custom fetch implementation (for tests or advanced setups).\n * When not provided, the global `fetch` will be used.\n */\n fetchImpl?: typeof fetch;\n};\n\nconst UnblindClientConfigContext = createContext<\n UnblindClientConfig | undefined\n>(undefined);\n\nexport type UnblindClientProviderProps = {\n children: ReactNode;\n /**\n * Optional QueryClient instance. If not provided, a new one will be created.\n * Useful if you already have a QueryClientProvider in your app and want to reuse it.\n */\n queryClient?: QueryClient;\n\n /**\n * Optional QueryClientConfig. If not provided, a default one will be used.\n * Useful if you already have a QueryClientProvider in your app and want to reuse it.\n */\n queryClientConfig?: QueryClientConfig | undefined;\n\n /**\n * Optional API base URL override. Defaults to `/api/unblind`.\n */\n apiBaseUrl?: string;\n /**\n * Optional custom fetch implementation (for tests or advanced setups).\n */\n fetchImpl?: typeof fetch;\n};\n\n/**\n * UnblindClientProvider sets up the QueryClientProvider (for React Query)\n * and the Unblind client configuration context.\n *\n * @example\n * ```tsx\n * import { UnblindClientProvider } from '@unblind/react';\n *\n * function App() {\n * return (\n * <UnblindClientProvider apiBaseUrl=\"/api/unblind\">\n * <YourComponents />\n * </UnblindClientProvider>\n * );\n * }\n * ```\n */\nexport function UnblindClientProvider({\n children,\n queryClient: providedQueryClient,\n queryClientConfig: providedQueryClientConfig,\n apiBaseUrl = \"/api/unblind\",\n fetchImpl,\n}: UnblindClientProviderProps) {\n // Create a QueryClient if one wasn't provided\n const queryClient = useMemo(() => {\n if (providedQueryClient) {\n return providedQueryClient;\n }\n\n const defaultQueryConfig = {\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n refetchOnMount: false,\n };\n\n const baseConfig = {\n defaultOptions: {\n queries: defaultQueryConfig,\n },\n };\n\n // Merge with provided config, ensuring our defaults aren't overwritten\n if (providedQueryClientConfig) {\n return new QueryClient({\n ...providedQueryClientConfig,\n defaultOptions: {\n ...providedQueryClientConfig.defaultOptions,\n queries: {\n ...defaultQueryConfig,\n ...providedQueryClientConfig.defaultOptions?.queries,\n },\n },\n });\n }\n\n return new QueryClient(baseConfig);\n }, [providedQueryClient, providedQueryClientConfig]);\n\n const configValue: UnblindClientConfig = useMemo(\n () => ({\n apiBaseUrl,\n fetchImpl,\n }),\n [apiBaseUrl, fetchImpl],\n );\n\n return (\n <QueryClientProvider client={queryClient}>\n <UnblindClientConfigContext.Provider value={configValue}>\n {children}\n </UnblindClientConfigContext.Provider>\n </QueryClientProvider>\n );\n}\n\n/**\n * Access the Unblind client configuration.\n *\n * @throws Error if called outside of an UnblindClientProvider.\n * This ensures that QueryClientProvider is always available for hooks.\n */\nexport function useUnblindClientConfig(): UnblindClientConfig {\n const ctx = useContext(UnblindClientConfigContext);\n\n if (!ctx) {\n throw new Error(\n \"useUnblindConfig must be used within an UnblindClientProvider. \" +\n \"Please wrap your app or component tree with <UnblindClientProvider>.\",\n );\n }\n\n return ctx;\n}\n\n/**\n * Hook to refresh all timeseries data.\n * Invalidates all queries with the 'unblind' and 'timeseries' keys,\n * causing them to refetch automatically.\n *\n * @example\n * ```tsx\n * function Dashboard() {\n * const refresh = useRefresh();\n * return (\n * <>\n * <button onClick={() => refresh()}>Refresh</button>\n * <TimeseriesChart metrics={[\"cpu\"]} />\n * </>\n * );\n * }\n * ```\n */\nexport function useRefresh(): () => Promise<void> {\n const queryClient = useQueryClient();\n\n return useCallback(async () => {\n await queryClient.refetchQueries({\n queryKey: [\"unblind\", \"timeseries\"],\n });\n }, [queryClient]);\n}\n","import React, {\n createContext,\n PropsWithChildren,\n useContext,\n useMemo,\n} from \"react\";\nimport type {\n TimeseriesQueryConfig,\n ChartVisualConfig,\n TimeRange,\n Colors,\n Appearance,\n} from \"../types\";\nimport { TooltipProps } from \"../components/Tooltip\";\n\nexport type UnblindScopeConfig = TimeseriesQueryConfig & {\n /**\n * Optional appearance configuration for all components\n * within this scope.\n *\n * Use this to globally override UI components such as loading,\n * or error states.\n *\n * Local overrides passed directly to a component take\n * precedence over this configuration.\n *\n * @example\n * ```tsx\n * <UnblindScope\n * appearance={{\n * components: {\n * Loading: CustomLoading,\n * Error: CustomError,\n * },\n * }}\n * >\n * <App />\n * </UnblindScope>\n * ```\n */\n appearance?: Appearance;\n};\n\nconst UnblindScopeConfigContext = createContext<UnblindScopeConfig | undefined>(\n undefined,\n);\n\nexport type UnblindScopeProps = UnblindScopeConfig & PropsWithChildren;\n\n/**\n * UnblindScope provides scoped configuration for all Unblind components (charts, logs, etc).\n * This includes default time ranges, intervals, attributes, groupBy, operator, appearance, and colors.\n *\n * When nested, child scopes inherit defaults from parent scopes and can override specific values.\n * This allows you to set global defaults (like appearance) at the top level and override\n * settings (like timeRange) in nested scopes.\n *\n * @example\n * ```tsx\n * import { UnblindScope } from '@unblind/react';\n *\n * function App() {\n * return (\n * <UnblindScope\n * appearance={{ components: { Loading: CustomLoading } }}\n * timeRange=\"24h\"\n * >\n * // Components here use 24h and CustomLoading\n *\n * <UnblindScope timeRange=\"1h\">\n * // Components here use 1h but still inherit CustomLoading\n * </UnblindScope>\n * </UnblindScope>\n * );\n * }\n * ```\n */\nexport function UnblindScope({\n children,\n timeRange,\n startTime,\n endTime,\n interval,\n attributes,\n groupBy,\n operator,\n appearance,\n}: UnblindScopeProps) {\n // Read parent context to inherit defaults\n const parentContext = useContext(UnblindScopeConfigContext);\n\n // Extract individual component references to avoid unnecessary re-renders\n // when appearance object is created inline\n const LoadingComponent = appearance?.components?.Loading;\n const ErrorComponent = appearance?.components?.Error;\n const TooltipComponent = appearance?.components?.Tooltip;\n\n // Memoize the appearance object based on individual component references\n const memoizedAppearance = useMemo(() => {\n if (!LoadingComponent && !ErrorComponent && !TooltipComponent) {\n return undefined;\n }\n return {\n components: {\n ...(LoadingComponent && { Loading: LoadingComponent }),\n ...(ErrorComponent && { Error: ErrorComponent }),\n ...(TooltipComponent && { Tooltip: TooltipComponent }),\n },\n } as Appearance;\n }, [LoadingComponent, ErrorComponent, TooltipComponent]);\n\n const scopeConfigValue: UnblindScopeConfig = useMemo(\n () => ({\n // Merge parent context with explicitly provided props\n // Explicitly provided props take precedence over parent defaults\n timeRange: timeRange ?? parentContext?.timeRange,\n startTime: startTime ?? parentContext?.startTime,\n endTime: endTime ?? parentContext?.endTime,\n interval: interval ?? parentContext?.interval,\n attributes: attributes ?? parentContext?.attributes,\n groupBy: groupBy ?? parentContext?.groupBy,\n operator: operator ?? parentContext?.operator,\n appearance: memoizedAppearance ?? parentContext?.appearance,\n }),\n [\n timeRange,\n startTime,\n endTime,\n interval,\n attributes,\n groupBy,\n operator,\n memoizedAppearance,\n parentContext,\n ],\n );\n\n return (\n <UnblindScopeConfigContext.Provider value={scopeConfigValue}>\n {children}\n </UnblindScopeConfigContext.Provider>\n );\n}\n\nconst DefaultLoading = () => (\n <div className=\"flex h-full items-center justify-center text-sm text-gray-500\">\n Loading data...\n </div>\n);\n\nconst DefaultError = ({ error }: { error?: Error }) => (\n <div className=\"flex h-full items-center justify-center text-sm text-red-500\">\n {error?.message ?? \"Something went wrong\"}\n </div>\n);\n\nconst DefaultEmpty = () => (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"text-center\">\n <div className=\"mx-auto flex h-10 w-10 items-center justify-center rounded-full bg-gray-100 dark:bg-zinc-800\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-5 text-gray-400 dark:text-gray-500\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z\"\n />\n </svg>\n </div>\n <p className=\"mt-2 text-sm text-gray-500 dark:text-gray-400\">\n No data available for this time range\n </p>\n </div>\n </div>\n);\n\n/**\n * Default chart color palette.\n *\n * This palette is designed for categorical data in observability charts\n * (e.g. time-series, stacked bars, comparisons).\n *\n * Design principles:\n * - Based on Tailwind CSS default colors\n * - Uses mid-range saturation (`~600`) for balanced emphasis\n * - Ordered to maximize hue contrast between adjacent colors\n * - Safe for reuse across multiple chart types\n *\n * Note:\n * Colors are cycled in order when the number of series exceeds\n * the palette length.\n */\nexport const DEFAULT_COLORS: string[] = [\n \"#7c3aed\", // violet-600\n \"#eab308\", // yellow-500\n \"#2563eb\", // blue-600\n \"#dc2626\", // red-600\n \"#16a34a\", // green-600\n \"#f97316\", // orange-500\n \"#0891b2\", // cyan-600\n \"#9333ea\", // purple-600\n \"#ca8a04\", // yellow-600\n \"#4f46e5\", // indigo-600\n \"#0d9488\", // teal-600\n \"#be185d\", // rose-600\n];\nconst DEFAULT_TIMERANGE = \"6h\" as const;\n\nexport type UseScopeReturn = TimeseriesQueryConfig & {\n // These are always defined in the hook return\n timeRange: TimeRange;\n appearance: {\n components: {\n Loading: React.ComponentType;\n Error: React.ComponentType;\n Empty: React.ComponentType;\n Tooltip?: React.ComponentType<TooltipProps>;\n };\n colors: Colors;\n orderByValues?: boolean;\n };\n};\n\n/**\n * Hook to access the scoped configuration from UnblindScope.\n * Returns all configuration including time range, attributes,\n * groupBy, operator, colors, and UI components.\n *\n * @example\n * ```tsx\n * function MyChart() {\n * const { timeRange, colors, appearance } = useScope();\n * // Use configuration...\n * }\n * ```\n */\nexport function useScope(): UseScopeReturn {\n const ctx = useContext(UnblindScopeConfigContext);\n\n return {\n timeRange: ctx?.timeRange || DEFAULT_TIMERANGE,\n startTime: ctx?.startTime,\n endTime: ctx?.endTime,\n interval: ctx?.interval,\n attributes: ctx?.attributes,\n groupBy: ctx?.groupBy,\n operator: ctx?.operator,\n appearance: {\n components: {\n Loading: ctx?.appearance?.components?.Loading ?? DefaultLoading,\n Error: ctx?.appearance?.components?.Error ?? DefaultError,\n Empty: ctx?.appearance?.components?.Empty ?? DefaultEmpty,\n Tooltip: ctx?.appearance?.components?.Tooltip,\n },\n colors: ctx?.appearance?.colors || DEFAULT_COLORS,\n },\n };\n}\n","import { PropsWithChildren } from \"react\";\nimport {\n UnblindClientProvider,\n UnblindClientProviderProps,\n type UnblindClientConfig,\n} from \"./UnblindClientProvider\";\nimport {\n UnblindScope,\n UnblindScopeProps,\n type UnblindScopeConfig,\n} from \"./UnblindScopeProvider\";\n\n// Re-export types\nexport type { UnblindClientConfig, UnblindScopeConfig };\n\n// Re-export hooks\nexport { useUnblindClientConfig, useRefresh } from \"./UnblindClientProvider\";\nexport { useScope } from \"./UnblindScopeProvider\";\n\nexport type UnblindProviderProps = UnblindClientProviderProps &\n UnblindScopeProps &\n PropsWithChildren;\n\n/**\n * UnblindProvider is required for all Unblind hooks to work.\n * It sets up both the QueryClientProvider (for React Query) and the Unblind configuration context.\n * This is a convenience wrapper around UnblindClientProvider and UnblindScope.\n *\n * @example\n * ```tsx\n * import { UnblindProvider } from '@unblind/react';\n *\n * function App() {\n * return (\n * <UnblindProvider apiBaseUrl=\"/api/unblind\">\n * <YourComponents />\n * </UnblindProvider>\n * );\n * }\n * ```\n */\nexport function UnblindProvider({\n children,\n queryClient,\n apiBaseUrl,\n fetchImpl,\n timeRange,\n startTime,\n endTime,\n interval,\n attributes,\n groupBy,\n operator,\n appearance,\n}: UnblindProviderProps) {\n return (\n <UnblindClientProvider\n queryClient={queryClient}\n apiBaseUrl={apiBaseUrl}\n fetchImpl={fetchImpl}\n >\n <UnblindScope\n timeRange={timeRange}\n startTime={startTime}\n endTime={endTime}\n interval={interval}\n attributes={attributes}\n groupBy={groupBy}\n operator={operator}\n appearance={appearance}\n >\n {children}\n </UnblindScope>\n </UnblindClientProvider>\n );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useUnblindClientConfig } from \"../providers/UnblindProvider\";\nimport type { MetricMetadataList } from \"../types\";\n\nexport interface UseMetricsReturn {\n list: MetricMetadataList | undefined;\n isLoading: boolean;\n hasError: boolean;\n refetch: () => void;\n}\n\n/**\n * Hook to fetch the list of available metrics metadata.\n *\n * @returns Object containing the metrics list, loading state, error state, and refetch function.\n */\nexport function useMetrics(): UseMetricsReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n\n const query = useQuery<MetricMetadataList>({\n queryKey: [\"unblind\", \"metrics\"],\n queryFn: async () => {\n const res = await fetchImpl(`${apiBaseUrl}/metrics`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!res.ok) {\n throw new Error(\"Error loading metrics metadata\");\n }\n\n if (res.status === 200) {\n const { data: metrics } = await res.json();\n return metrics;\n } else {\n throw new Error(\"Unexpected status code\");\n }\n },\n });\n\n return {\n list: query.data,\n isLoading: query.isLoading,\n hasError: query.isError,\n refetch: query.refetch,\n };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useUnblindClientConfig } from \"@/providers/UnblindProvider\";\nimport type {\n MetricMetadata,\n Serie,\n TimeseriesQuery,\n TimeseriesQueryConfig,\n} from \"../types\";\nimport { deduceTimestamp, timeRangeToCalculatedTimestamp } from \"./utils\";\n\n/**\n * Response structure from the timeseries API.\n */\ntype TimeseriesResponse = {\n request: SeriesAPIRequest;\n metadata: Record<string, MetricMetadata>;\n series: Array<Serie>;\n times: Array<number>;\n isEmpty?: boolean;\n};\n\n/**\n * Internal API request structure.\n */\ninterface SeriesAPIRequest {\n queries: Array<TimeseriesQuery>;\n startTime: number;\n endTime: number;\n interval?: number;\n}\n\nexport interface UseTimeseriesParams\n extends Pick<\n TimeseriesQueryConfig,\n \"timeRange\" | \"startTime\" | \"endTime\" | \"interval\"\n > {\n /**\n * Array of queries to execute.\n */\n queries: Array<TimeseriesQuery>;\n}\n\nexport interface UseTimeseriesReturn {\n data: {\n series: Serie[];\n times: number[];\n metadata: Record<string, MetricMetadata>;\n };\n isLoading: boolean;\n isFetching: boolean;\n hasError: boolean;\n refetch: () => void;\n}\n\n/**\n * Hook to fetch timeseries data for metrics.\n *\n * @param params - Configuration object with queries, and either timeRange or startTime/endTime, plus optional interval.\n * @returns Object containing timeseries data, loading states, and error state.\n */\nexport function useTimeseries({\n queries,\n timeRange,\n startTime,\n endTime,\n interval,\n}: UseTimeseriesParams): UseTimeseriesReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n\n const metricNames = useMemo(\n () => queries.map((x) => x.metrics.join(\",\")).join(\",\"),\n [queries],\n );\n const attributes = useMemo(\n () =>\n queries\n .map((x) => {\n const attrs = x.attributes;\n if (!attrs) return \"\";\n\n const attributeKeys = Object.keys(attrs);\n if (attributeKeys.length === 0) return \"\";\n return attributeKeys\n .map((key: string) => key + \":\" + attrs[key]?.join(\",\"))\n .join(\",\");\n })\n .join(\",\"),\n [queries],\n );\n const operators = useMemo(() => queries.map((x) => x.operator), [queries]);\n const groupBy = useMemo(\n () => queries.map((x) => x.groupBy).join(\", \"),\n [queries],\n );\n\n const hasValidTimeConfig =\n (typeof startTime === \"number\" && typeof endTime === \"number\") ||\n !!timeRange;\n\n // Query all widget metrics concurrently\n const query = useQuery<TimeseriesResponse>({\n queryKey: [\n \"unblind\",\n \"timeseries\",\n metricNames,\n attributes,\n // Include explicit times in queryKey for proper caching\n startTime,\n endTime,\n timeRange,\n interval,\n operators,\n groupBy,\n ],\n queryFn: async () => {\n if (!metricNames) {\n throw new Error(\"Missing required parameters\");\n }\n if (metricNames.length === 0) {\n throw new Error(\"No series provided\");\n }\n\n // Calculate times inside queryFn so they're fresh when explicitly refetched\n const [calculatedStartTime, calculatedEndTime] = deduceTimestamp(\n timeRange,\n startTime,\n endTime,\n );\n\n const apiRequestBody: SeriesAPIRequest = {\n queries,\n startTime: calculatedStartTime,\n endTime: calculatedEndTime,\n interval,\n };\n const res = await fetchImpl(`${apiBaseUrl}/tenants/timeseries`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(apiRequestBody),\n });\n if (!res.ok) throw new Error(\"Error fetching metric\");\n const { series, times, metadata } = await res.json();\n if (!series) {\n console.error(\"Series not found\");\n throw new Error(\"Series not found\");\n }\n return { series, times, metadata } as TimeseriesResponse;\n },\n enabled: !!metricNames && hasValidTimeConfig,\n });\n\n const {\n metadata,\n series,\n times,\n }: {\n series: Array<Serie>;\n times: Array<number>;\n metadata: Record<string, MetricMetadata>;\n } = useMemo(() => {\n if (!query.data) return { series: [], times: [], metadata: {} };\n return {\n series: query.data.series,\n times: query.data.times,\n metadata: query.data.metadata,\n };\n }, [query]);\n\n const isLoading = query.isLoading;\n const isFetching = query.isFetching;\n const hasError = query.isError;\n\n return {\n data: { series, times, metadata },\n isLoading,\n isFetching,\n hasError,\n refetch: query.refetch,\n };\n}\n","import { TimeRange } from \"@/types\";\nimport ms from \"ms\";\n\nexport function timeRangeToCalculatedTimestamp(\n timeRange: TimeRange,\n): [number, number] {\n const now = Date.now();\n const calculatedStartTime = now - ms(timeRange);\n const calculatedEndTime = now;\n\n return [calculatedStartTime, calculatedEndTime];\n}\n\nexport function deduceTimestamp(\n timeRange?: TimeRange,\n startTime?: number,\n endTime?: number,\n): [number, number] {\n let calculatedStartTime: number;\n let calculatedEndTime: number;\n\n if (typeof startTime === \"number\" && typeof endTime === \"number\") {\n calculatedStartTime = startTime!;\n calculatedEndTime = endTime!;\n } else if (timeRange) {\n const [startTime, endTime] = timeRangeToCalculatedTimestamp(timeRange);\n calculatedStartTime = startTime;\n calculatedEndTime = endTime;\n } else {\n throw new Error(\n \"Either timeRange or both startTime and endTime must be provided\",\n );\n }\n\n return [calculatedStartTime, calculatedEndTime];\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useUnblindClientConfig } from \"@/providers/UnblindProvider\";\nimport { TimeConfig } from \"@/types\";\nimport { deduceTimestamp } from \"./utils\";\n\nexport interface Usage {\n date: string;\n metrics: {\n units: number;\n };\n logs: {\n bytes: number;\n units: number;\n };\n}\n\ntype UsageResponse = {\n data: Array<Usage>;\n};\n\nexport type UseUsageParams = TimeConfig;\n\nexport interface UseUsageReturn {\n usage: Array<Usage>;\n isLoading: boolean;\n hasError: boolean;\n refetch: () => void;\n}\n\n/**\n * Hook to fetch usage data.\n *\n * @param params - Configuration object with optional timeRange.\n * @returns Object containing usage data, loading state, and error state.\n */\nexport function useUsage({\n timeRange,\n startTime,\n endTime,\n}: UseUsageParams): UseUsageReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n const hasValidTimeConfig =\n (typeof startTime === \"number\" && typeof endTime === \"number\") ||\n !!timeRange;\n\n const query = useQuery<Array<Usage>>({\n queryKey: [\"unblind\", \"usage\", timeRange, startTime, endTime],\n queryFn: async () => {\n const [calculatedStartTime, calculatedEndTime] = deduceTimestamp(\n timeRange,\n startTime,\n endTime,\n );\n\n const endpoint = `${apiBaseUrl}/tenants/usage`;\n\n const res = await fetchImpl(endpoint, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n startTime: calculatedStartTime,\n endTime: calculatedEndTime,\n }),\n });\n if (!res.ok) throw new Error(\"Error fetching usage\");\n const { data: usage } = (await res.json()) as UsageResponse;\n if (!usage) {\n throw new Error(\"usage not found\");\n }\n\n return usage;\n },\n enabled: hasValidTimeConfig,\n });\n\n const usage: Array<Usage> = useMemo(() => {\n if (!query.data) return [];\n return query.data || [];\n }, [query]);\n\n const isLoading = query.isLoading || query.isRefetching;\n const hasError = query.isError;\n\n return {\n usage,\n isLoading,\n hasError,\n refetch: query.refetch,\n };\n}\n","import { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useUnblindClientConfig } from \"@/providers/UnblindProvider\";\nimport type {\n AttributeWithValue,\n Log,\n PaginatedResponse,\n TimeConfig,\n} from \"../types\";\nimport { deduceTimestamp } from \"./utils\";\n\nexport type UseLogsParams = {\n /**\n * Array of filters to apply to the logs query.\n */\n filters: Array<AttributeWithValue>;\n} & TimeConfig;\n\nexport interface UseLogsReturn {\n logs: Array<Log>;\n isLoading: boolean;\n hasError: boolean;\n hasNextPage: boolean;\n fetchNextPage: () => void;\n isFetchingNextPage: boolean;\n refetch: () => void;\n}\n\nexport type PaginatedLogsResponse = PaginatedResponse<Log>;\n\n/**\n * Hook to fetch logs data with infinite scroll pagination.\n *\n * @param params - Configuration object with timeRange and filters.\n * @returns Object containing logs data, loading states, and pagination controls.\n */\nexport function useLogs({\n timeRange,\n filters,\n startTime,\n endTime,\n}: UseLogsParams): UseLogsReturn {\n const { apiBaseUrl, fetchImpl = fetch } = useUnblindClientConfig();\n const hasValidTimeConfig =\n (typeof startTime === \"number\" && typeof endTime === \"number\") ||\n !!timeRange;\n\n const query = useInfiniteQuery<PaginatedLogsResponse>({\n queryKey: [\n \"unblind\",\n \"logs\",\n timeRange,\n filters\n .map((x) => x.name + \":\" + x.value)\n .sort()\n .join(\",\"),\n ],\n queryFn: async ({ pageParam }) => {\n const filtersByName = filters.reduce<Record<string, string[]>>(\n (acc, filter) => {\n if (!acc[filter.name]) {\n acc[filter.name] = [];\n }\n acc[filter.name]!.push(filter.value);\n return acc;\n },\n {},\n );\n\n const {\n body = [],\n severity = [],\n \"service.name\": service = [],\n \"trace.id\": traceId = [],\n \"span.id\": spanId = [],\n ...attributes\n } = filtersByName;\n\n const [deducedStartTime, deducedEndTime] = deduceTimestamp(\n timeRange,\n startTime,\n endTime,\n );\n\n const now = Date.now();\n const res = await fetchImpl(`${apiBaseUrl}/tenants/logs`, {\n method: \"POST\",\n body: JSON.stringify({\n filter: {\n attributes,\n body,\n severity,\n traceId,\n spanId,\n service,\n },\n startTime: deducedStartTime,\n endTime: deducedEndTime,\n pagination: {\n page: pageParam,\n },\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!res.ok) throw new Error(\"Error fetching logs\");\n\n const { data, next_page } = await res.json();\n\n if (!data) {\n throw new Error(\"logs not found\");\n }\n\n return { data, next_page } as PaginatedLogsResponse;\n },\n enabled: hasValidTimeConfig,\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => lastPage.next_page,\n });\n\n const logs = useMemo(() => {\n if (!query.data) return [];\n return query.data.pages.flatMap((page) => page.data || []);\n }, [query.data]);\n\n return {\n logs,\n isLoading: query.isLoading,\n hasError: query.isError,\n hasNextPage: query.hasNextPage ?? false,\n fetchNextPage: query.fetchNextPage,\n isFetchingNextPage: query.isFetchingNextPage,\n refetch: query.refetch,\n };\n}\n","import { useEffect, useState } from \"react\";\n\nexport function useTheme() {\n const [isDark, setIsDark] = useState(false);\n\n useEffect(() => {\n const update = () =>\n setIsDark(document.documentElement.classList.contains(\"dark\"));\n update();\n window.addEventListener(\"storage\", update);\n window.addEventListener(\"theme-change\", update);\n\n return () => {\n window.removeEventListener(\"storage\", update);\n window.removeEventListener(\"theme-change\", update);\n };\n }, []);\n\n return isDark;\n}\n","import { useEffect, useRef } from \"react\";\nimport uPlot from \"uplot\";\nimport type { MetricMetadata, Serie, ChartVisualConfig } from \"../../types\";\nimport { TooltipProps } from \"../Tooltip\";\nimport { useScope } from \"@/providers/UnblindProvider\";\nimport { useTheme } from \"@/hooks/useTheme\";\nimport { stack } from \"./YAxis\";\nimport { compareAndResolveUnit, createChartOptions } from \"./utils\";\n\nexport interface ChartProps extends ChartVisualConfig {\n times: Array<number>;\n series: Serie[];\n metadata: Record<string, MetricMetadata>;\n type: \"bar\" | \"line\" | \"area\" | \"step\";\n className?: string;\n timeZone?: string;\n options?: uPlot.Options;\n tooltipAppearance?: React.ComponentType<TooltipProps>;\n}\n\n/**\n * Renders a chart for time series data\n */\nexport function Chart(props: ChartProps) {\n const {\n times,\n series,\n metadata,\n type,\n className,\n timeZone,\n options: propsOptions,\n tooltipAppearance,\n colors: propsColors,\n orderByValues,\n } = props;\n const chartRef = useRef<HTMLDivElement>(null);\n const isDark = useTheme();\n const scope = useScope();\n const colors = propsColors || scope.appearance.colors;\n\n useEffect(() => {\n if (!series || series.length === 0) {\n console.warn(\"No series provided\");\n return;\n }\n\n // uPlot aligned data works in the following way:\n // - First array of values is for X-axis\n // - The rest arrays are values for Y-axis\n let data: uPlot.AlignedData = [times];\n let unit: string | undefined = undefined;\n series.forEach((serie) => {\n const metricMetadata = metadata[serie.metric];\n unit = compareAndResolveUnit(unit, metricMetadata);\n\n data.push(serie.values);\n });\n\n const stacked = type === \"bar\" || type === \"area\";\n const stackedData = stack(data, !stacked);\n const container = chartRef.current;\n\n const opts = createChartOptions(\n container,\n unit,\n series,\n type,\n stacked,\n isDark,\n colors,\n timeZone,\n tooltipAppearance,\n orderByValues,\n );\n\n let u: uPlot | null = null;\n\n if (container) {\n opts.bands = stackedData.bands;\n u = new uPlot({ ...opts, ...propsOptions }, stackedData.data, container);\n const resizeObserver = new ResizeObserver(() => {\n u?.setSize({\n width: container.clientWidth,\n height: container.clientHeight,\n });\n });\n\n resizeObserver.observe(container);\n\n return () => {\n u?.destroy();\n resizeObserver.disconnect();\n };\n }\n }, [\n series,\n times,\n type,\n isDark,\n metadata,\n timeZone,\n tooltipAppearance,\n colors,\n orderByValues,\n ]);\n\n return <div ref={chartRef} className={className} />;\n}\n","import { ChartType, Colors, MetricMetadata, Serie } from \"@/types\";\n\nimport uPlot from \"uplot\";\nimport { getValueFormat } from \"@grafana/data\";\nimport { createXAxisConfig } from \"./XAxis\";\nimport { createYAxisConfig } from \"./YAxis\";\nimport { TooltipProps } from \"../Tooltip\";\nimport { tooltipPlugin } from \"../Tooltip/plugin\";\n\nexport const DEFAULT_FONT_STYLE = '11px \"Inter\", sans-serif';\n\n// Omit stacking those metrics/timeseries that are not needed to stack (common case)\nexport const omit = (chartType: ChartType) => {\n return chartType !== \"bar\" && chartType !== \"area\";\n};\n\nexport const compareAndResolveUnit = (\n paramsUnit?: string,\n metricMetadata?: MetricMetadata,\n) => {\n let unit = paramsUnit;\n\n if (\n metricMetadata &&\n metricMetadata.unit &&\n metricMetadata.unit.code &&\n metricMetadata.unit.code !== \"1\"\n ) {\n if (!unit && metricMetadata.unit) {\n unit = metricMetadata.unit.code;\n } else if (unit && metricMetadata.unit.code !== unit) {\n unit = undefined;\n }\n }\n\n return unit;\n};\n\n// Util to get a color from the colros variable.\nexport const getColor = (serie: Serie, index: number, colors: Colors) => {\n return Array.isArray(colors) ? colors[index] : colors(serie, index);\n};\n\n// ============================================================================\n// Chart Path & Fill Generation\n// ============================================================================\n\n/**\n * Generates the path for a particular chart type\n */\nconst generatePath = (type: \"bar\" | \"line\" | \"area\" | \"step\" | \"spline\") => {\n const barsPath = uPlot.paths.bars!({ size: [0.6, 100], radius: 0, gap: 0 });\n const linearPath = uPlot.paths.linear!({\n alignGaps: 0,\n });\n const splinePath = uPlot.paths.spline!({ alignGaps: 1 });\n const steppedPath = uPlot.paths.stepped!({ alignGaps: 1 });\n\n switch (type) {\n case \"line\":\n return linearPath;\n case \"bar\":\n return barsPath;\n case \"area\":\n return linearPath;\n case \"step\":\n return steppedPath;\n case \"spline\":\n return splinePath;\n default:\n return linearPath;\n }\n};\n\n/**\n * Fill color for a particular chart type\n */\nconst generateFill = (\n serie: Serie,\n index: number,\n colors: Colors,\n type: \"bar\" | \"line\" | \"area\" | \"step\",\n) => {\n switch (type) {\n case \"area\":\n return getColor(serie, index, colors);\n case \"bar\":\n return getColor(serie, index, colors);\n default:\n return undefined;\n }\n};\n\n// ============================================================================\n// Chart Utilities\n// ============================================================================\n\nconst getStrokeWidthByType = (type: ChartType): number => {\n switch (type) {\n case \"bar\":\n return 1;\n case \"line\":\n return 2;\n case \"area\":\n return 2;\n case \"step\":\n return 1.5;\n default:\n return 1; // optional: handle unexpected types\n }\n};\n\n// ============================================================================\n// Chart Options Creation\n// ============================================================================\n\n/**\n * Creates the complete uPlot options object for the chart\n */\nexport const createChartOptions = (\n container: HTMLDivElement | null,\n unit: string | undefined,\n series: Array<Serie>,\n type: ChartType,\n stacked: boolean,\n isDark: boolean,\n colors: Colors,\n timeZone?: string,\n tooltipAppearance?: React.ComponentType<TooltipProps>,\n relativeTimeAxis?: boolean,\n orderByValues?: boolean,\n): uPlot.Options => {\n const initialWidth = container?.clientWidth ?? 1050;\n const initialHeight = container?.clientHeight ?? 250;\n const formatUnit =\n typeof unit === \"string\"\n ? String(unit).toLowerCase().replaceAll(\"by\", \"bytes\")\n : unit;\n const formatter = getValueFormat(formatUnit === \"1\" ? null : formatUnit);\n const strokeWidth = getStrokeWidthByType(type);\n\n const opts: uPlot.Options = {\n width: initialWidth,\n height: initialHeight,\n scales: {\n y: {\n range: {\n min: { mode: 1, soft: 0 },\n max: { pad: 2 },\n },\n },\n },\n plugins: [\n tooltipPlugin(\n (v: number) => {\n const f = formatter(v, 2);\n return f.text + (f.suffix?.trim() || \"\");\n },\n stacked,\n timeZone,\n tooltipAppearance,\n series,\n orderByValues,\n ),\n ],\n padding: relativeTimeAxis ? [10, 15, 10, 15] : [8, 15, 8, 15],\n cursor: {\n y: false,\n sync: { key: \"_\" },\n drag: {\n setScale: true,\n x: true,\n y: false,\n },\n move: (u, left, top) => {\n // Snap cursor to nearest non-null data point (backward bias)\n const xVal = u.posToVal(left, \"x\");\n\n // Find the data index closest to the x position\n const xData = u.data[0];\n if (!xData || xData.length === 0) {\n return [left, top];\n }\n\n let nearestIdx = 0;\n\n // Binary search for closest x value\n let lo = 0;\n let hi = xData.length - 1;\n\n while (hi - lo > 1) {\n const mid = Math.floor((lo + hi) / 2);\n const midVal = xData[mid];\n if (midVal != null && midVal < xVal) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n // Determine which is closer\n const loVal = xData[lo];\n const hiVal = xData[hi];\n if (loVal != null && hiVal != null) {\n nearestIdx =\n Math.abs(loVal - xVal) < Math.abs(hiVal - xVal) ? lo : hi;\n } else if (loVal != null) {\n nearestIdx = lo;\n } else if (hiVal != null) {\n nearestIdx = hi;\n }\n\n // Search backward from nearestIdx to find first non-null value\n let snapIdx = nearestIdx;\n for (let i = nearestIdx; i >= 0; i--) {\n let hasValue = false;\n // Check if any series has a non-null value at this index\n for (let j = 1; j < u.data.length; j++) {\n const seriesData = u.data[j];\n if (seriesData && seriesData[i] != null) {\n hasValue = true;\n break;\n }\n }\n if (hasValue) {\n snapIdx = i;\n break;\n }\n }\n\n // Convert the data index back to pixel position\n const snapVal = xData[snapIdx];\n if (snapVal == null) {\n return [left, top];\n }\n\n const snappedLeft = u.valToPos(snapVal, \"x\");\n\n return [snappedLeft, top];\n },\n },\n series: [\n {},\n ...series.map((serie, index) => ({\n label: serie.metric,\n stroke: getColor(serie, index, colors),\n width: strokeWidth,\n points: { show: false },\n spanGaps: true,\n paths: generatePath(type),\n fill: generateFill(serie, index, colors, type),\n })),\n ],\n axes: [\n createXAxisConfig(timeZone, relativeTimeAxis),\n createYAxisConfig(formatter),\n ],\n legend: {\n show: false,\n },\n };\n\n // Otherwise if we use them as they are it breaks.\n if (isDark) {\n const xAxis = opts.axes?.[0];\n const yAxis = opts.axes?.[1];\n if (xAxis) {\n xAxis.stroke = \"#dadada\";\n if (xAxis.grid) {\n xAxis.grid.stroke = \"#2c3235\";\n }\n }\n\n if (yAxis) {\n yAxis.stroke = \"#dadada\";\n if (yAxis.grid) {\n yAxis.grid.stroke = \"#2c3235\";\n }\n }\n } else {\n const xAxis = opts.axes?.[0];\n const yAxis = opts.axes?.[1];\n if (xAxis) {\n xAxis.stroke = \"#45556c\";\n }\n\n if (yAxis) {\n yAxis.stroke = \"#45556c\";\n }\n }\n\n return opts;\n};\n","import {\n dateTimeFormat,\n dateTimeFormatTimeAgo,\n systemDateFormats,\n} from \"@grafana/data\";\nimport { DEFAULT_FONT_STYLE } from \"./utils\";\n\n// Time unit sizes in milliseconds\nconst timeUnitSize = {\n millisecond: 1,\n second: 1000,\n minute: 60000,\n hour: 3600000,\n day: 86400000,\n month: 2419200000, // 28 days\n year: 31536000000,\n};\n\n// Time axis increments (in seconds)\nconst timeIncrs = {\n second: [1, 2, 5, 10, 15, 30],\n minute: [1, 2, 5, 10, 15, 30],\n hour: [1, 2, 3, 4, 6, 8, 12],\n day: [1, 2, 3, 7, 14],\n month: [1, 2, 3, 6],\n year: [1, 2, 5, 10, 20, 50, 100],\n};\n\nfunction formatTime(\n splits: number[],\n foundIncr: number,\n range: number,\n timeZone?: string,\n): string[] {\n // 1. Handle Month/Year increments\n // If the increment is larger than a day, we generally just want the Date\n if (foundIncr > timeUnitSize.day) {\n let format = systemDateFormats.interval.year;\n\n // Logic from your original code for large scales\n const yearRoundedToDay =\n Math.round(timeUnitSize.year / timeUnitSize.day) * timeUnitSize.day;\n const incrementRoundedToDay =\n Math.round(foundIncr / timeUnitSize.day) * timeUnitSize.day;\n\n if (incrementRoundedToDay === yearRoundedToDay) {\n format = systemDateFormats.interval.year;\n } else if (foundIncr <= timeUnitSize.year) {\n format = systemDateFormats.interval.month;\n } else {\n format = systemDateFormats.interval.day;\n }\n\n return splits.map((v) => dateTimeFormat(v, { format, timeZone }));\n }\n\n // 2. Handle Intraday (Hours, Minutes, Seconds)\n // This handles the \"36 hours\" case where you want mixed labels.\n return splits.map((v) => {\n const date = new Date(v);\n\n // Determine if we need seconds/milliseconds based on precision\n const showSeconds = foundIncr < timeUnitSize.minute;\n const showMillis = foundIncr < timeUnitSize.second;\n\n // Check for Midnight in the specific TimeZone\n // We use \"en-GB\" here strictly to check the \"00:00\" pattern reliably\n // without worrying about AM/PM locale differences in the logic.\n const checkTimeStr = date.toLocaleTimeString(\"en-GB\", {\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false,\n timeZone,\n });\n\n // IF it is midnight (and we aren't zooming into seconds), SHOW DATE\n if (\n (checkTimeStr === \"00:00\" || checkTimeStr === \"24:00\") &&\n !showSeconds &&\n !showMillis\n ) {\n return date.toLocaleDateString(undefined, {\n day: \"2-digit\",\n month: \"short\",\n timeZone,\n }); // Result: \"14 Jan\" or \"Jan 14\" depending on user locale\n }\n\n // ELSE show Time\n // We stick to the standard format for the time part\n return date.toLocaleTimeString(undefined, {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: showSeconds ? \"2-digit\" : undefined,\n fractionalSecondDigits: showMillis ? 3 : undefined,\n hour12: false,\n timeZone,\n }); // Result: \"13:00\"\n });\n}\n\n/**\n * X-Axis: Tick interval selection.\n * Picks the best increment size based on target spacing\n */\nfunction findBestIncrement(targetSeconds: number): {\n increment: number;\n multiplier: number;\n} {\n // Try to find the best increment at each time scale\n const scales = [\n { size: timeUnitSize.second / 1000, increments: timeIncrs.second },\n { size: timeUnitSize.minute / 1000, increments: timeIncrs.minute },\n { size: timeUnitSize.hour / 1000, increments: timeIncrs.hour },\n { size: timeUnitSize.day / 1000, increments: timeIncrs.day },\n { size: timeUnitSize.month / 1000, increments: timeIncrs.month },\n { size: timeUnitSize.year / 1000, increments: timeIncrs.year },\n ];\n\n for (const scale of scales) {\n for (const mult of scale.increments) {\n const incr = scale.size * mult;\n if (incr >= targetSeconds) {\n return { increment: incr * 1000, multiplier: mult };\n }\n }\n }\n\n // Fallback to largest year increment\n const lastScale = scales[scales.length - 1] as (typeof scales)[0];\n const lastMult = lastScale.increments[\n lastScale.increments.length - 1\n ] as number;\n return {\n increment: lastScale.size * lastMult * 1000,\n multiplier: lastMult,\n };\n}\n\n/**\n * X-Axis: Generates splits using the x latest timestamp and soonest timestamp.\n */\nfunction generateRelativeXAxisSplits(\n u: uPlot,\n min: number,\n max: number,\n): number[] {\n return [min, max];\n}\n\n/**\n * X-Axis: Generate splits (tick positions) for the time axis\n */\nfunction generateXAxisSplits(\n u: uPlot,\n min: number,\n max: number,\n timeZone?: string,\n): number[] {\n const range = max - min;\n const px = u.width;\n const approxTicks = Math.floor(px / 100);\n const targetSeconds = range / approxTicks;\n const { increment, multiplier } = findBestIncrement(targetSeconds);\n const step = increment / 1000;\n const splits: number[] = [];\n\n // For very short ranges (< 12 hours), use simple rounding - no special alignment\n if (range < 12 * 3600) {\n let t = Math.ceil(min / step) * step;\n for (; t <= max; t += step) splits.push(t);\n return splits;\n }\n\n // For ranges 12h-3days, use time-based alignment (not forcing midnight)\n if (range < 3 * 86400) {\n // For 6h+ increments, align to nice hour boundaries\n if (increment >= 6 * 3600 * 1000) {\n const hourStep = increment / 1000 / 3600; // e.g., 6, 12, etc.\n const startDate = new Date(min * 1000);\n\n if (timeZone === \"UTC\") {\n const currentHour = startDate.getUTCHours();\n const alignedHour = Math.floor(currentHour / hourStep) * hourStep;\n startDate.setUTCHours(alignedHour, 0, 0, 0);\n } else {\n const currentHour = startDate.getHours();\n const alignedHour = Math.floor(currentHour / hourStep) * hourStep;\n startDate.setHours(alignedHour, 0, 0, 0);\n }\n\n let t = startDate.getTime() / 1000;\n if (t < min) {\n t += step;\n }\n\n while (t <= max) {\n splits.push(t);\n t += step;\n }\n return splits;\n }\n\n // For smaller increments in 12h-3day ranges\n let t = Math.ceil(min / step) * step;\n for (; t <= max; t += step) splits.push(t);\n return splits;\n }\n\n // For ranges >= 3 days, align to midnight\n if (increment >= timeUnitSize.day) {\n const currentDate = new Date(min * 1000);\n\n if (timeZone === \"UTC\") {\n currentDate.setUTCHours(0, 0, 0, 0);\n if (currentDate.getTime() / 1000 < min) {\n currentDate.setUTCDate(currentDate.getUTCDate() + multiplier);\n }\n } else {\n currentDate.setHours(0, 0, 0, 0);\n if (currentDate.getTime() / 1000 < min) {\n currentDate.setDate(currentDate.getDate() + multiplier);\n }\n }\n\n let t = currentDate.getTime() / 1000;\n while (t <= max) {\n splits.push(t);\n if (timeZone === \"UTC\") {\n currentDate.setUTCDate(currentDate.getUTCDate() + multiplier);\n } else {\n currentDate.setDate(currentDate.getDate() + multiplier);\n }\n t = currentDate.getTime() / 1000;\n }\n return splits;\n }\n\n // Default: simple rounding\n let t = Math.ceil(min / step) * step;\n for (; t <= max; t += step) splits.push(t);\n return splits;\n}\n\n/**\n * X-Axis: Format splits for relative time axis (only show labels at first and last)\n */\nfunction generateRelativeXAxisValues(\n u: uPlot,\n splits: number[],\n timeZone?: string,\n): string[] {\n if (splits.length === 0) return [];\n\n return splits.map((timestamp, index) => {\n // Only show labels for first and last positions\n if (index === 0 || index === splits.length - 1) {\n // Convert seconds to milliseconds for dateTimeFormatTimeAgo\n return dateTimeFormatTimeAgo(timestamp * 1000, { timeZone });\n }\n return \"\"; // Return empty string for all other positions\n });\n}\n\n/**\n * X-Axis: Format splits into display labels\n */\nfunction generateXAxisValues(\n u: uPlot,\n splits: number[],\n timeZone?: string,\n): string[] {\n const scale = u.scales.x;\n const range = ((scale?.max ?? 0) - (scale?.min ?? 0)) * 1000; // Convert to ms\n const approxTicks = Math.floor(u.width / 100);\n const targetSeconds = range / 1000 / approxTicks;\n const { increment } = findBestIncrement(targetSeconds);\n\n // Convert splits from seconds to milliseconds for formatTime\n const splitsInMs = splits.map((s) => s * 1000);\n return formatTime(splitsInMs, increment, range, timeZone);\n}\n\n/**\n * X-Axis: Create complete axis configuration\n */\nexport function createXAxisConfig(\n timeZone?: string,\n relativeTimeAxis: boolean = false,\n): uPlot.Axis {\n const splits: uPlot.Axis.Splits = relativeTimeAxis\n ? (u, _, min, max) => generateRelativeXAxisSplits(u, min, max)\n : (u, _, min, max) => generateXAxisSplits(u, min, max, timeZone);\n const values: uPlot.Axis.Values = relativeTimeAxis\n ? (u, splits) => generateRelativeXAxisValues(u, splits, timeZone)\n : (u, splits) => generateXAxisValues(u, splits, timeZone);\n const space: uPlot.Axis.Space | undefined = relativeTimeAxis\n ? (u, axisIdx, min, max, plotDim) => {\n // Create a canvas context for measuring text\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return 0;\n\n ctx.font = DEFAULT_FONT_STYLE;\n const vals = generateRelativeXAxisValues(\n u,\n generateRelativeXAxisSplits(u, min, max),\n timeZone,\n );\n\n // Find the maximum width of the labels\n let maxWidth = Math.max(\n ctx.measureText(vals[0] || \"\").width,\n ctx.measureText(vals[1] || \"\").width,\n );\n\n // Return half the max width plus some padding\n // This ensures labels at the edges don't get cut off\n return Math.ceil(maxWidth / 2) + 15;\n }\n : undefined;\n\n return {\n font: DEFAULT_FONT_STYLE,\n labelFont: DEFAULT_FONT_STYLE,\n grid: {\n show: false,\n width: 0.5,\n },\n ticks: {\n width: 0.5,\n },\n splits,\n values,\n size: 20,\n // space: 20,\n };\n}\n","import { computePosition, flip, offset } from \"@floating-ui/dom\";\nimport uPlot from \"uplot\";\nimport type { Serie } from \"../../types\";\nimport { tooltipManager } from \"./TooltipManager\";\nimport { Tooltip, TooltipItem, TooltipProps } from \".\";\n\n// Constants\nconst TOOLTIP_DISTANCE_CURSOR = 4;\nconst TOOLTIP_PADDING_FROM_CURSOR = 8;\n\ninterface AnchorPosition {\n left?: number;\n top?: number;\n}\n\n/**\n * Checks if the data spans multiple calendar days\n * @param xData - Array of timestamps\n * @param timeZone - Timezone string (e.g., \"UTC\", \"America/New_York\")\n * @returns True if data spans multiple days\n */\nfunction checkSpansMultipleDays(\n xData: uPlot.TypedArray | number[],\n timeZone?: string,\n): boolean {\n if (!xData || xData.length === 0) return false;\n\n const minTimestamp = xData[0];\n const maxTimestamp = xData[xData.length - 1];\n\n if (minTimestamp == null || maxTimestamp == null) return false;\n\n const minDate = new Date(minTimestamp * 1000);\n const maxDate = new Date(maxTimestamp * 1000);\n\n const getDateString = (date: Date) => {\n if (timeZone === \"UTC\") {\n return `${date.getUTCFullYear()}-${date.getUTCMonth()}-${date.getUTCDate()}`;\n }\n return date.toLocaleDateString(undefined, { timeZone });\n };\n\n return getDateString(minDate) !== getDateString(maxDate);\n}\n\n/**\n * Finds the nearest data point with a non-null value\n * @param u - uPlot instance\n * @param idx - Current cursor index\n * @returns Index of nearest non-null data point, or the original index\n */\nfunction findNearestNonNullIndex(u: uPlot, idx: number): number {\n let hasNonNullValue = false;\n\n // Check if current index has any non-null values\n for (let seriesIndex = 1; seriesIndex < u.series.length; seriesIndex++) {\n if (u.data[seriesIndex]?.[idx] != null) {\n hasNonNullValue = true;\n break;\n }\n }\n\n if (hasNonNullValue) return idx;\n\n // Search for nearest non-null value\n const dataLength = u.data[0].length;\n\n for (\n let distance = 1;\n idx + distance < dataLength || idx - distance >= 0;\n distance++\n ) {\n const leftCandidate = idx - distance;\n const rightCandidate = idx + distance;\n\n if (leftCandidate >= 0) {\n for (let seriesIndex = 1; seriesIndex < u.series.length; seriesIndex++) {\n if (u.data[seriesIndex]?.[leftCandidate] != null) {\n return leftCandidate;\n }\n }\n }\n\n if (rightCandidate < dataLength) {\n for (let seriesIndex = 1; seriesIndex < u.series.length; seriesIndex++) {\n if (u.data[seriesIndex]?.[rightCandidate] != null) {\n return rightCandidate;\n }\n }\n }\n }\n\n return idx;\n}\n\n/**\n * Builds tooltip items from uPlot data\n * @param u - uPlot instance\n * @param actualIdx - Data index to extract values from\n * @param formatValue - Function to format numeric values\n * @param stacked - Whether the chart is stacked\n * @param series - Original series configuration\n * @returns Array of tooltip items and hasAttributes flag\n */\nfunction buildTooltipItems(\n u: uPlot,\n actualIdx: number,\n formatValue: (v: number) => string,\n stacked: boolean,\n series?: Serie[],\n): { items: TooltipItem[]; hasAttributes: boolean } {\n let hasAttributes = false;\n const items: TooltipItem[] = [];\n\n for (let seriesIndex = 1; seriesIndex < u.series.length; seriesIndex++) {\n let rawVal = (u.data[seriesIndex]?.[actualIdx] ?? null) as number | null;\n\n if (stacked && rawVal != null && seriesIndex > 1) {\n const prevVal = u.data[seriesIndex - 1]?.[actualIdx] ?? 0;\n rawVal = rawVal - prevVal;\n }\n\n const s = u.series[seriesIndex] as uPlot.Series;\n const originalSerie = series?.[seriesIndex - 1];\n const metric: string | HTMLElement = s?.label ?? `Series ${seriesIndex}`;\n const stroke = s?.stroke;\n const color: string =\n typeof stroke === \"function\"\n ? (stroke(uPlot as any, seriesIndex) as string)\n : ((stroke as string) ?? \"#ffffff00\");\n const formattedValue =\n rawVal == null\n ? undefined\n : formatValue\n ? formatValue(rawVal)\n : String(rawVal);\n\n if (\n originalSerie?.attributes &&\n Object.keys(originalSerie?.attributes).length > 0\n ) {\n hasAttributes = true;\n }\n\n items.push({\n metric,\n color,\n value: rawVal === null ? undefined : rawVal,\n formattedValue,\n attributes: originalSerie?.attributes,\n });\n }\n\n return { items, hasAttributes };\n}\n\n/**\n * Positions the tooltip overlay relative to the cursor\n * @param overlay - Tooltip DOM element\n * @param anchor - Cursor position\n */\nasync function positionTooltip(\n overlay: HTMLElement,\n anchor: AnchorPosition,\n): Promise<void> {\n const { x, y } = await computePosition(\n {\n getBoundingClientRect: () => ({\n x: anchor.left!,\n y: anchor.top!,\n width: 0,\n height: 0,\n top: anchor.top!,\n left: anchor.left!,\n right: anchor.left!,\n bottom: anchor.top!,\n }),\n } as any,\n overlay,\n {\n placement: \"top-start\",\n strategy: \"fixed\" as const,\n middleware: [\n offset({\n mainAxis: TOOLTIP_DISTANCE_CURSOR,\n crossAxis: TOOLTIP_PADDING_FROM_CURSOR\n }),\n flip(),\n ]\n },\n );\n\n overlay.style.left = `${x}px`;\n overlay.style.top = `${y}px`;\n}\n\n/**\n * uPlot plugin for interactive tooltips\n * Extracted from: https://github.com/leeoniya/uPlot/blob/master/demos/cursor-tooltip.html\n */\nexport function tooltipPlugin(\n formatValue: (v: number) => string,\n stacked: boolean,\n timeZone?: string,\n appearance?: React.ComponentType<TooltipProps>,\n series?: Serie[],\n orderByValues?: boolean,\n) {\n let over: HTMLElement;\n let boundingLeft: number;\n let boundingTop: number;\n let isHovering = false;\n\n function syncBounds(): void {\n const bbox = over.getBoundingClientRect();\n boundingLeft = bbox.left;\n boundingTop = bbox.top;\n }\n\n function hideTooltip(): void {\n tooltipManager.hide();\n }\n\n function showTooltip(): void {\n tooltipManager.show();\n }\n\n return {\n hooks: {\n init: (u: uPlot) => {\n tooltipManager.initialize();\n over = u.over;\n\n window.addEventListener(\"scroll\", syncBounds, true);\n window.addEventListener(\"resize\", syncBounds);\n\n over.onmouseenter = () => {\n isHovering = true;\n showTooltip();\n };\n\n over.onmouseleave = () => {\n isHovering = false;\n hideTooltip();\n };\n\n syncBounds();\n },\n\n setSize: () => {\n syncBounds();\n },\n\n setCursor: (u: uPlot) => {\n const { left, top, idx } = u.cursor;\n const shouldHideTooltip = !isHovering || idx == null;\n\n if (shouldHideTooltip) {\n hideTooltip();\n return;\n }\n\n const xData = u.data[0];\n const spansMultipleDays = checkSpansMultipleDays(xData, timeZone);\n const actualIdx = findNearestNonNullIndex(u, idx);\n const timestamp = u.data[0][actualIdx];\n\n if (timestamp === undefined) {\n return;\n }\n\n const { items, hasAttributes } = buildTooltipItems(\n u,\n actualIdx,\n formatValue,\n stacked,\n series,\n );\n\n const anchor: AnchorPosition = {\n left: (left || 0) + boundingLeft,\n top: (top || 0) + boundingTop,\n };\n\n showTooltip();\n\n const AppearanceTooltip = appearance;\n const tooltipElement = AppearanceTooltip ? (\n <AppearanceTooltip\n timestamp={timestamp}\n items={items}\n timeZone={timeZone}\n />\n ) : (\n <Tooltip\n timestamp={timestamp}\n items={items}\n timeZone={timeZone}\n spansMultipleDays={spansMultipleDays}\n stacked={stacked}\n hasAttributes={hasAttributes}\n orderByValues={orderByValues}\n />\n );\n\n tooltipManager.render(tooltipElement);\n\n const overlay = tooltipManager.getOverlay();\n if (overlay) {\n positionTooltip(overlay, anchor);\n }\n },\n\n destroy() {\n window.removeEventListener(\"scroll\", syncBounds, true);\n window.removeEventListener(\"resize\", syncBounds);\n },\n },\n };\n}\n","import { createRoot } from \"react-dom/client\";\n\n/**\n * We want to make sure we have a single\n * Tooltip in the whole application.\n *\n * The tooltip manager is in charge of the tooltip lifecycle.\n */\nclass TooltipManager {\n private overlay: HTMLElement | null = null;\n private reactRoot: any = null;\n\n initialize() {\n if (this.overlay) return;\n\n this.overlay = document.createElement(\"div\");\n this.overlay.id = \"unblind-tooltip-overlay\";\n this.overlay.style.display = \"none\";\n this.overlay.style.position = \"fixed\";\n this.overlay.style.pointerEvents = \"none\";\n this.overlay.style.zIndex = \"9999\";\n document.body.appendChild(this.overlay);\n\n this.reactRoot = createRoot(this.overlay);\n }\n\n getOverlay(): HTMLElement | null {\n return this.overlay;\n }\n\n render(content: React.ReactElement | null) {\n if (this.reactRoot) {\n this.reactRoot.render(content);\n }\n }\n\n show() {\n if (this.overlay) {\n this.overlay.style.display = \"block\";\n }\n }\n\n hide() {\n if (this.overlay) {\n this.overlay.style.display = \"none\";\n }\n this.render(null);\n }\n\n destroy() {\n if (this.reactRoot) {\n this.reactRoot.unmount();\n this.reactRoot = null;\n }\n if (this.overlay) {\n this.overlay.remove();\n this.overlay = null;\n }\n }\n}\n\n// Singleton instance\nexport const tooltipManager = new TooltipManager();\n","import { dateTimeFormat } from \"@grafana/data\";\nimport { useMemo } from \"react\";\nimport { Divider } from \"../Divider\";\n\n// Types\nexport interface TooltipItem {\n metric: string | HTMLElement | undefined;\n color: string;\n value?: number;\n formattedValue?: string;\n attributes?: Record<string, any>;\n}\n\nexport interface TooltipProps {\n timestamp: number;\n items: TooltipItem[];\n timeZone?: string;\n}\n\ninterface TooltipExtendedProps extends TooltipProps {\n spansMultipleDays?: boolean;\n orderByValues?: boolean;\n stacked: boolean;\n hasAttributes: boolean;\n}\n\ninterface GroupedItems {\n metricName: string;\n items: TooltipItem[];\n}\n\n// Helper Functions for Tooltip Component\nfunction getMetricName(item: TooltipItem): string {\n return typeof item.metric === \"string\"\n ? item.metric\n : item.metric?.textContent || \"Unknown Metric\";\n}\n\nfunction getSortKey(item: TooltipItem): string {\n const attrs = item.attributes || {};\n if (attrs[\"service.name\"]) return String(attrs[\"service.name\"]);\n\n const keys = Object.keys(attrs);\n const firstKey = keys[0];\n return firstKey ? String(attrs[firstKey]) : \"z-fallback\";\n}\n\nfunction sortItemsByValue(items: TooltipItem[]): TooltipItem[] {\n return [...items].sort(\n (a, b) => (Number(b.value) || 0) - (Number(a.value) || 0),\n );\n}\n\nfunction sortItemsAlphabetically(items: TooltipItem[]): TooltipItem[] {\n return [...items].sort((a, b) => getSortKey(a).localeCompare(getSortKey(b)));\n}\n\nfunction groupItemsByMetric(\n items: TooltipItem[],\n): Record<string, TooltipItem[]> {\n const groups: Record<string, TooltipItem[]> = {};\n\n items.forEach((item) => {\n const metricName = getMetricName(item);\n if (!groups[metricName]) {\n groups[metricName] = [];\n }\n groups[metricName].push(item);\n });\n\n return groups;\n}\n\nfunction createGroupedItems(\n items: TooltipItem[],\n orderByValues?: boolean,\n): GroupedItems[] {\n const groups = groupItemsByMetric(items);\n const sortedGroupKeys = Object.keys(groups).sort((a, b) =>\n a.localeCompare(b),\n );\n\n return sortedGroupKeys.map((key) => {\n const groupItems = groups[key];\n if (!groupItems) return { metricName: key, items: [] };\n\n const sortedGroupItems = orderByValues\n ? sortItemsByValue(groupItems)\n : sortItemsAlphabetically(groupItems);\n\n return { metricName: key, items: sortedGroupItems };\n });\n}\n\n// Tooltip Item Component\nfunction TooltipItemRow({\n item,\n groupIdx,\n idx,\n}: {\n item: TooltipItem;\n groupIdx: number;\n idx: number;\n}) {\n const attributes = item.attributes || {};\n const entries = Object.entries(attributes);\n\n let primaryLabel: string =\n typeof item.metric === \"string\"\n ? item.metric\n : item.metric?.textContent || \"Unknown\";\n let secondaryAttributes: [string, any][] = entries;\n\n const serviceNameEntry = entries.find(([k]) => k === \"service.name\");\n\n if (serviceNameEntry) {\n primaryLabel = String(serviceNameEntry[1]);\n secondaryAttributes = entries.filter(([k]) => k !== \"service.name\");\n } else if (entries.length > 0 && entries[0]) {\n primaryLabel = `${entries[0][0]}: ${entries[0][1]}`;\n secondaryAttributes = entries.slice(1);\n }\n\n return (\n <div\n key={`item-${groupIdx}-${idx}`}\n className=\"group flex flex-col px-3 py-1 hover:bg-slate-50 dark:hover:bg-white/5 transition-colors\"\n >\n <div className=\"flex items-center justify-between gap-4\">\n <div className=\"flex items-center gap-2.5 min-w-0 overflow-hidden\">\n <span\n className=\"size-2 mt-0.5 shrink-0 rounded-full shadow-sm ring-1 ring-black/5 dark:ring-white/10\"\n style={{ backgroundColor: item.color }}\n />\n <span\n className=\"truncate font-medium text-slate-600 dark:text-slate-300\"\n title={primaryLabel}\n >\n {primaryLabel}\n </span>\n </div>\n <div className=\"font-semibold tabular-nums text-slate-900 dark:text-white whitespace-nowrap\">\n {item.formattedValue ?? (\n <span className=\"font-normal text-slate-400\">—</span>\n )}\n </div>\n </div>\n\n {secondaryAttributes.length > 0 && (\n <div className=\"mt-0.5 flex flex-wrap gap-x-3 gap-y-0.5 pl-4.5 text-xs text-slate-500 dark:text-slate-400\">\n {secondaryAttributes.map(([key, value]) => (\n <span key={key} className=\"flex items-center gap-1\">\n <span className=\"opacity-70\">{key}:</span>\n <span className=\"font-medium text-slate-600 dark:text-slate-300\">\n {String(value)}\n </span>\n </span>\n ))}\n </div>\n )}\n </div>\n );\n}\n\n// Tooltip Group Component\nfunction TooltipGroup({\n group,\n groupIdx,\n hasAttributes,\n}: {\n group: GroupedItems;\n groupIdx: number;\n hasAttributes: boolean;\n}) {\n return (\n <div key={group.metricName} className=\"flex flex-col\">\n {hasAttributes && (\n <div\n className={`sticky top-0 z-10 bg-white/95 dark:bg-zinc-900/95 backdrop-blur-sm px-3 py-1.5 font-semibold text-slate-900 dark:text-white ${\n groupIdx > 0\n ? \"mt-2 border-t border-gray-100 dark:border-white/5 pt-3\"\n : \"\"\n }`}\n >\n {group.metricName}\n </div>\n )}\n\n <div className=\"flex flex-col gap-1\">\n {group.items.map((item, idx) => (\n <TooltipItemRow\n key={`item-${groupIdx}-${idx}`}\n item={item}\n groupIdx={groupIdx}\n idx={idx}\n />\n ))}\n </div>\n </div>\n );\n}\n\n// Main Tooltip Component\nexport function Tooltip({\n timestamp,\n items,\n timeZone,\n spansMultipleDays,\n orderByValues,\n hasAttributes,\n}: TooltipExtendedProps) {\n const formattedTime = spansMultipleDays\n ? dateTimeFormat(timestamp * 1000, {\n format: \"MMM DD, HH:mm:ss\",\n timeZone,\n })\n : dateTimeFormat(timestamp * 1000, {\n format: \"HH:mm:ss\",\n timeZone,\n });\n\n const groupedItems = useMemo(() => {\n const grouped = createGroupedItems(items, orderByValues);\n\n // If there are no attributes and we should order by values,\n // sort all items across groups by value\n if (!hasAttributes && orderByValues && grouped.length > 0) {\n const allItems = grouped.flatMap((group) => group.items);\n const sortedItems = sortItemsByValue(allItems);\n const firstGroup = grouped[0];\n return [\n { metricName: firstGroup?.metricName || \"Metrics\", items: sortedItems },\n ];\n }\n\n return grouped;\n }, [items, orderByValues, hasAttributes]);\n\n return (\n <div\n style={{ fontSize: \"13px\" }}\n className=\"flex w-[320px] flex-col gap-1 text-sm rounded-lg text-slate-900 dark:text-slate-200 border border-gray-200/80 bg-white shadow-xl ring-1 ring-gray-200/40 dark:ring-zinc-800/40 dark:border-white/10 dark:bg-zinc-900\"\n >\n <div className=\"flex items-center justify-between px-3 pt-2.5 font-medium text-slate-700 dark:text-slate-100\">\n {formattedTime}\n </div>\n\n <Divider className=\"my-1 border-gray-100 dark:border-white/5\" />\n\n {groupedItems.length > 0 ? (\n <div className=\"flex flex-col pb-2\">\n {groupedItems.map((group, groupIdx) => (\n <TooltipGroup\n key={group.metricName}\n group={group}\n groupIdx={groupIdx}\n hasAttributes={hasAttributes}\n />\n ))}\n </div>\n ) : (\n <div className=\"px-3 pb-3 italic text-zinc-400 text-xs\">\n No data available\n </div>\n )}\n </div>\n );\n}\n","import React from \"react\";\n\nexport function Divider({\n className = \"h-px bg-gray-200 border-gray-200 dark:bg-white/15\",\n ...props\n}: React.ComponentPropsWithoutRef<\"hr\">) {\n return <hr role=\"presentation\" {...props} className={className} />;\n}\n","import { DEFAULT_FONT_STYLE } from \"./utils\";\n\n/**\n * Y-Axis: Calculate axis size based on longest value label\n */\nconst calculateYAxisSize = (\n self: uPlot,\n values: Array<string>,\n axisIdx: number,\n cycleNum: number,\n) => {\n let axis = self.axes[axisIdx];\n\n // bail out, force convergence\n if (cycleNum > 1)\n // Do not use .size, it doesn't work. Use ._size instead.\n return (axis as any)?._size;\n\n let axisSize = (axis?.ticks?.size || 0) + (axis?.gap || 0);\n\n // find longest value\n let longestVal = (values ?? []).reduce(\n (acc, val) => (val.length > acc.length ? val : acc),\n \"\",\n );\n\n if (longestVal != \"\") {\n self.ctx.font = axis?.font?.[0] ?? self.ctx.font;\n axisSize += self.ctx.measureText(longestVal).width / devicePixelRatio;\n }\n\n return Math.ceil(axisSize);\n};\n\n/**\n * Y-Axis: Create complete axis configuration\n */\nexport function createYAxisConfig(\n formatter: (\n v: number,\n decimals?: number,\n ) => { text: string; suffix?: string },\n): uPlot.Axis {\n return {\n gap: 0,\n font: DEFAULT_FONT_STYLE,\n labelFont: DEFAULT_FONT_STYLE,\n grid: {\n show: true,\n width: 0.5,\n },\n ticks: {\n width: 0.5,\n },\n values: (_, vals) =>\n vals.map((v) => {\n const formmatedVal = formatter(v);\n return formmatedVal.text + (formmatedVal.suffix?.trim() || \"\");\n }),\n size: calculateYAxisSize,\n };\n}\n\n/**\n * Stacking data for bar charts\n * Extracted and mutated from: https://github.com/leeoniya/uPlot/blob/master/demos/stack.js\n */\nexport function stack(\n data: uPlot.AlignedData,\n omit: boolean,\n): {\n data: uPlot.AlignedData;\n bands: Array<any>;\n} {\n const bands: Array<any> = [];\n const xAxis = data[0];\n const xAxisLen = xAxis.length;\n const accum = Array(xAxisLen).fill(0);\n const accumData: uPlot.AlignedData = [xAxis];\n\n data.forEach((serie, i) => {\n // Skip x-axis\n if (i === 0) return;\n if (omit) {\n accumData.push(serie);\n } else {\n accumData.push(\n serie.map(\n (value, index) => (accum[index] = accum[index] + (value || 0)),\n ),\n );\n }\n });\n\n data.forEach((_, serieIndex) => {\n if (serieIndex === 0 || omit) return;\n bands.push({\n series: [data.findIndex((_, j) => j > serieIndex), serieIndex],\n });\n });\n\n return {\n data: accumData,\n bands: bands.filter((b) => b.series[1] > -1),\n };\n}\n","import { useTimeseries } from \"../hooks/useTimeseries\";\nimport { useScope } from \"@/providers/UnblindProvider\";\nimport type {\n ChartType,\n TimeseriesProps,\n ChartVisualConfig,\n TimeRange,\n Appearance,\n} from \"../types\";\nimport { Chart } from \"./Chart\";\n\nexport type TimeseriesChartProps = TimeseriesProps & {\n /**\n * Chart type. Defaults to \"line\".\n */\n type?: ChartType;\n /**\n * Optional className for the chart container.\n */\n className?: string;\n /**\n * Optional appearance configuration for this chart\n */\n appearance?: Appearance;\n};\n\n/**\n * TimeseriesChart component that displays time series data for the given metrics.\n * It uses values from the UnblindProvider for timeRange, attributes, groupBy, and operator\n * if not explicitly provided as props.\n *\n * @example Using [UnblindProvider]\n * ```tsx\n * <UnblindProvider timeRange=\"1h\">\n * <TimeseriesChart metrics={[\"host.cpu\"]} />\n * <TimeseriesChart metrics={[\"host.memory\"]} />\n * </UnblindProvider>\n * ```\n *\n * @example Using <UnblindScope>\n * ```tsx\n * <UnblindScope timeRange=\"1h\">\n * <TimeseriesChart metrics={[\"host.cpu\"]} />\n * <TimeseriesChart metrics={[\"host.memory\"]} />\n * </UnblindScope>\n * ```\n */\nexport function TimeseriesChart({\n metrics,\n operator: propOperator,\n attributes: propAttributes,\n groupBy: propGroupBy,\n timeRange: propTimeRange,\n startTime: propStartTime,\n endTime: propEndTime,\n interval: propInterval,\n type = \"line\",\n className,\n appearance: propAppearance,\n}: TimeseriesChartProps) {\n // Get defaults from scope\n const scope = useScope();\n\n // Use props if provided, otherwise fall back to scope values\n // Priority: props > scope > defaults\n const timeRange = propTimeRange ?? (scope.timeRange as unknown as TimeRange);\n const startTime = propStartTime ?? scope.startTime;\n const endTime = propEndTime ?? scope.endTime;\n const interval = propInterval ?? scope.interval;\n const attributes = propAttributes ?? scope.attributes;\n const groupBy = propGroupBy ?? scope.groupBy;\n const operator = propOperator ?? scope.operator;\n const colors = propAppearance?.colors ?? scope.appearance.colors;\n const orderByValues =\n propAppearance?.orderByValues ?? scope.appearance.orderByValues;\n\n const { isLoading, data, hasError } = useTimeseries({\n queries: metrics.map((metric) => ({\n metrics: [metric],\n operator,\n attributes,\n groupBy,\n })),\n timeRange,\n startTime,\n endTime,\n interval,\n });\n\n const { series, times, metadata } = data;\n const isEmpty = series.every((x) => x.isEmpty);\n\n // Ensure container has proper dimensions - default to h-full w-full if no className provided\n const containerClassName = className || \"h-full w-full\";\n\n if (isLoading) {\n const LoadingComponent =\n propAppearance?.components?.Loading ??\n scope.appearance.components.Loading;\n return (\n <div className={containerClassName}>\n <LoadingComponent />\n </div>\n );\n }\n\n if (isEmpty) {\n const EmptyComponent =\n propAppearance?.components?.Empty ?? scope.appearance.components.Empty;\n\n return (\n <div className={containerClassName}>\n <EmptyComponent />\n </div>\n );\n }\n\n if (hasError) {\n const ErrorComponent =\n propAppearance?.components?.Error ?? scope.appearance.components.Error;\n return (\n <div className={containerClassName}>\n <ErrorComponent />\n </div>\n );\n }\n\n return (\n <Chart\n times={times}\n series={series}\n metadata={metadata}\n type={type}\n className={containerClassName}\n tooltipAppearance={\n propAppearance?.components?.Tooltip ??\n scope.appearance.components.Tooltip\n }\n colors={colors}\n orderByValues={orderByValues}\n />\n );\n}\n"],"mappings":"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","names":["index_exports","__export","Chart","Divider","TimeseriesChart","UnblindClientProvider","UnblindProvider","UnblindScope","useLogs","useMetrics","useRefresh","useScope","useTheme","useTimeseries","useUnblindClientConfig","useUsage","__toCommonJS","import_react_query","import_react","import_jsx_runtime","UnblindClientConfigContext","UnblindClientProvider","children","providedQueryClient","providedQueryClientConfig","apiBaseUrl","fetchImpl","queryClient","defaultQueryConfig","baseConfig","configValue","useUnblindClientConfig","ctx","useRefresh","import_react","import_jsx_runtime","UnblindScopeConfigContext","UnblindScope","children","timeRange","startTime","endTime","interval","attributes","groupBy","operator","appearance","parentContext","LoadingComponent","ErrorComponent","TooltipComponent","memoizedAppearance","scopeConfigValue","DefaultLoading","DefaultError","error","DefaultEmpty","DEFAULT_COLORS","DEFAULT_TIMERANGE","useScope","ctx","import_jsx_runtime","UnblindProvider","children","queryClient","apiBaseUrl","fetchImpl","timeRange","startTime","endTime","interval","attributes","groupBy","operator","appearance","UnblindClientProvider","UnblindScope","import_react_query","useMetrics","apiBaseUrl","fetchImpl","useUnblindClientConfig","query","res","metrics","import_react_query","import_react","import_ms","timeRangeToCalculatedTimestamp","timeRange","now","ms","deduceTimestamp","startTime","endTime","calculatedStartTime","calculatedEndTime","useTimeseries","queries","timeRange","startTime","endTime","interval","apiBaseUrl","fetchImpl","useUnblindClientConfig","metricNames","x","attributes","attrs","attributeKeys","key","operators","groupBy","query","calculatedStartTime","calculatedEndTime","deduceTimestamp","apiRequestBody","res","series","times","metadata","isLoading","isFetching","hasError","import_react_query","import_react","useUsage","timeRange","startTime","endTime","apiBaseUrl","fetchImpl","useUnblindClientConfig","query","calculatedStartTime","calculatedEndTime","deduceTimestamp","endpoint","res","usage","isLoading","hasError","import_react_query","import_react","useLogs","timeRange","filters","startTime","endTime","apiBaseUrl","fetchImpl","useUnblindClientConfig","hasValidTimeConfig","query","x","pageParam","filtersByName","acc","filter","body","severity","service","traceId","spanId","attributes","deducedStartTime","deducedEndTime","deduceTimestamp","now","res","data","next_page","lastPage","page","import_react","useTheme","isDark","setIsDark","update","import_react","import_uplot","import_uplot","import_data","import_data","timeUnitSize","timeIncrs","formatTime","splits","foundIncr","range","timeZone","format","yearRoundedToDay","v","date","showSeconds","showMillis","checkTimeStr","findBestIncrement","targetSeconds","scales","scale","mult","incr","lastScale","lastMult","generateRelativeXAxisSplits","u","min","max","generateXAxisSplits","px","approxTicks","increment","multiplier","step","t","hourStep","startDate","currentHour","alignedHour","currentDate","generateRelativeXAxisValues","timestamp","index","generateXAxisValues","splitsInMs","s","createXAxisConfig","relativeTimeAxis","_","values","space","axisIdx","plotDim","ctx","DEFAULT_FONT_STYLE","vals","maxWidth","import_dom","import_uplot","import_client","TooltipManager","content","tooltipManager","import_data","import_react","import_jsx_runtime","Divider","className","props","import_jsx_runtime","getMetricName","item","getSortKey","attrs","firstKey","sortItemsByValue","items","a","b","sortItemsAlphabetically","groupItemsByMetric","groups","metricName","createGroupedItems","orderByValues","key","groupItems","sortedGroupItems","TooltipItemRow","groupIdx","idx","attributes","entries","primaryLabel","secondaryAttributes","serviceNameEntry","k","value","TooltipGroup","group","hasAttributes","Tooltip","timestamp","timeZone","spansMultipleDays","formattedTime","groupedItems","grouped","allItems","sortedItems","Divider","import_jsx_runtime","TOOLTIP_DISTANCE_CURSOR","TOOLTIP_PADDING_FROM_CURSOR","checkSpansMultipleDays","xData","timeZone","minTimestamp","maxTimestamp","minDate","maxDate","getDateString","date","findNearestNonNullIndex","u","idx","hasNonNullValue","seriesIndex","dataLength","distance","leftCandidate","rightCandidate","buildTooltipItems","actualIdx","formatValue","stacked","series","hasAttributes","items","rawVal","prevVal","s","originalSerie","metric","stroke","color","uPlot","formattedValue","positionTooltip","overlay","anchor","x","y","tooltipPlugin","appearance","orderByValues","over","boundingLeft","boundingTop","isHovering","syncBounds","bbox","hideTooltip","tooltipManager","showTooltip","left","top","spansMultipleDays","timestamp","AppearanceTooltip","tooltipElement","Tooltip","DEFAULT_FONT_STYLE","compareAndResolveUnit","paramsUnit","metricMetadata","unit","getColor","serie","index","colors","generatePath","type","barsPath","uPlot","linearPath","splinePath","steppedPath","generateFill","getStrokeWidthByType","createChartOptions","container","series","stacked","isDark","timeZone","tooltipAppearance","relativeTimeAxis","orderByValues","initialWidth","initialHeight","formatUnit","formatter","strokeWidth","opts","tooltipPlugin","v","f","u","left","top","xVal","xData","nearestIdx","lo","hi","mid","midVal","loVal","hiVal","snapIdx","i","hasValue","j","seriesData","snapVal","createXAxisConfig","createYAxisConfig","xAxis","yAxis","calculateYAxisSize","self","values","axisIdx","cycleNum","axis","axisSize","longestVal","acc","val","createYAxisConfig","formatter","DEFAULT_FONT_STYLE","_","vals","v","formmatedVal","stack","data","omit","bands","xAxis","xAxisLen","accum","accumData","serie","i","value","index","serieIndex","j","b","import_jsx_runtime","Chart","props","times","series","metadata","type","className","timeZone","propsOptions","tooltipAppearance","propsColors","orderByValues","chartRef","isDark","useTheme","scope","useScope","colors","data","unit","serie","metricMetadata","compareAndResolveUnit","stacked","stackedData","stack","container","opts","createChartOptions","u","uPlot","resizeObserver","import_jsx_runtime","TimeseriesChart","metrics","propOperator","propAttributes","propGroupBy","propTimeRange","propStartTime","propEndTime","propInterval","type","className","propAppearance","scope","useScope","timeRange","startTime","endTime","interval","attributes","groupBy","operator","colors","orderByValues","isLoading","data","hasError","useTimeseries","metric","series","times","metadata","isEmpty","x","containerClassName","LoadingComponent","EmptyComponent","ErrorComponent","Chart"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{QueryClient as oe,QueryClientProvider as Pe,useQueryClient as Ue}from"@tanstack/react-query";import{createContext as Se,useCallback as Ee,useContext as Re,useMemo as ie}from"react";import{jsx as ne}from"react/jsx-runtime";var ae=Se(void 0);function $({children:e,queryClient:t,queryClientConfig:r,apiBaseUrl:i="/api/unblind",fetchImpl:o}){let s=ie(()=>{if(t)return t;let n={refetchOnWindowFocus:!1,refetchOnReconnect:!1,refetchOnMount:!1},l={defaultOptions:{queries:n}};return r?new oe({...r,defaultOptions:{...r.defaultOptions,queries:{...n,...r.defaultOptions?.queries}}}):new oe(l)},[t,r]),a=ie(()=>({apiBaseUrl:i,fetchImpl:o}),[i,o]);return ne(Pe,{client:s,children:ne(ae.Provider,{value:a,children:e})})}function R(){let e=Re(ae);if(!e)throw new Error("useUnblindConfig must be used within an UnblindClientProvider. Please wrap your app or component tree with <UnblindClientProvider>.");return e}function se(){let e=Ue();return Ee(async()=>{await e.refetchQueries({queryKey:["unblind","timeseries"]})},[e])}import{createContext as Me,useContext as ce,useMemo as le}from"react";import{jsx as L,jsxs as De}from"react/jsx-runtime";var W=Me(void 0);function K({children:e,timeRange:t,startTime:r,endTime:i,interval:o,attributes:s,groupBy:a,operator:n,appearance:l}){let c=ce(W),p=l?.components?.Loading,u=l?.components?.Error,m=l?.components?.Tooltip,d=le(()=>{if(!(!p&&!u&&!m))return{components:{...p&&{Loading:p},...u&&{Error:u},...m&&{Tooltip:m}}}},[p,u,m]),h=le(()=>({timeRange:t??c?.timeRange,startTime:r??c?.startTime,endTime:i??c?.endTime,interval:o??c?.interval,attributes:s??c?.attributes,groupBy:a??c?.groupBy,operator:n??c?.operator,appearance:d??c?.appearance}),[t,r,i,o,s,a,n,d,c]);return L(W.Provider,{value:h,children:e})}var Le=()=>L("div",{className:"flex h-full items-center justify-center text-sm text-gray-500",children:"Loading data..."}),Ne=({error:e})=>L("div",{className:"flex h-full items-center justify-center text-sm text-red-500",children:e?.message??"Something went wrong"}),ze=()=>L("div",{className:"flex h-full items-center justify-center",children:De("div",{className:"text-center",children:[L("div",{className:"mx-auto flex h-10 w-10 items-center justify-center rounded-full bg-gray-100 dark:bg-zinc-800",children:L("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-5 text-gray-400 dark:text-gray-500",children:L("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"})})}),L("p",{className:"mt-2 text-sm text-gray-500 dark:text-gray-400",children:"No data available for this time range"})]})}),Ae=["#7c3aed","#eab308","#2563eb","#dc2626","#16a34a","#f97316","#0891b2","#9333ea","#ca8a04","#4f46e5","#0d9488","#be185d"],Ie="6h";function V(){let e=ce(W);return{timeRange:e?.timeRange||Ie,startTime:e?.startTime,endTime:e?.endTime,interval:e?.interval,attributes:e?.attributes,groupBy:e?.groupBy,operator:e?.operator,appearance:{components:{Loading:e?.appearance?.components?.Loading??Le,Error:e?.appearance?.components?.Error??Ne,Empty:e?.appearance?.components?.Empty??ze,Tooltip:e?.appearance?.components?.Tooltip},colors:e?.appearance?.colors||Ae}}}import{jsx as pe}from"react/jsx-runtime";function Oe({children:e,queryClient:t,apiBaseUrl:r,fetchImpl:i,timeRange:o,startTime:s,endTime:a,interval:n,attributes:l,groupBy:c,operator:p,appearance:u}){return pe($,{queryClient:t,apiBaseUrl:r,fetchImpl:i,children:pe(K,{timeRange:o,startTime:s,endTime:a,interval:n,attributes:l,groupBy:c,operator:p,appearance:u,children:e})})}import{useQuery as Ve}from"@tanstack/react-query";function Be(){let{apiBaseUrl:e,fetchImpl:t=fetch}=R(),r=Ve({queryKey:["unblind","metrics"],queryFn:async()=>{let i=await t(`${e}/metrics`,{headers:{"Content-Type":"application/json"}});if(!i.ok)throw new Error("Error loading metrics metadata");if(i.status===200){let{data:o}=await i.json();return o}else throw new Error("Unexpected status code")}});return{list:r.data,isLoading:r.isLoading,hasError:r.isError,refetch:r.refetch}}import{useQuery as He}from"@tanstack/react-query";import{useMemo as q}from"react";import je from"ms";function Fe(e){let t=Date.now();return[t-je(e),t]}function B(e,t,r){let i,o;if(typeof t=="number"&&typeof r=="number")i=t,o=r;else if(e){let[s,a]=Fe(e);i=s,o=a}else throw new Error("Either timeRange or both startTime and endTime must be provided");return[i,o]}function Y({queries:e,timeRange:t,startTime:r,endTime:i,interval:o}){let{apiBaseUrl:s,fetchImpl:a=fetch}=R(),n=q(()=>e.map(b=>b.metrics.join(",")).join(","),[e]),l=q(()=>e.map(b=>{let k=b.attributes;if(!k)return"";let y=Object.keys(k);return y.length===0?"":y.map(x=>x+":"+k[x]?.join(",")).join(",")}).join(","),[e]),c=q(()=>e.map(b=>b.operator),[e]),p=q(()=>e.map(b=>b.groupBy).join(", "),[e]),m=He({queryKey:["unblind","timeseries",n,l,r,i,t,o,c,p],queryFn:async()=>{if(!n)throw new Error("Missing required parameters");if(n.length===0)throw new Error("No series provided");let[b,k]=B(t,r,i),y={queries:e,startTime:b,endTime:k,interval:o},x=await a(`${s}/tenants/timeseries`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(y)});if(!x.ok)throw new Error("Error fetching metric");let{series:T,times:C,metadata:S}=await x.json();if(!T)throw console.error("Series not found"),new Error("Series not found");return{series:T,times:C,metadata:S}},enabled:!!n&&(typeof r=="number"&&typeof i=="number"||!!t)}),{metadata:d,series:h,times:v}=q(()=>m.data?{series:m.data.series,times:m.data.times,metadata:m.data.metadata}:{series:[],times:[],metadata:{}},[m]),w=m.isLoading,f=m.isFetching,g=m.isError;return{data:{series:h,times:v,metadata:d},isLoading:w,isFetching:f,hasError:g,refetch:m.refetch}}import{useQuery as qe}from"@tanstack/react-query";import{useMemo as _e}from"react";function Ge({timeRange:e,startTime:t,endTime:r}){let{apiBaseUrl:i,fetchImpl:o=fetch}=R(),a=qe({queryKey:["unblind","usage",e,t,r],queryFn:async()=>{let[p,u]=B(e,t,r),m=`${i}/tenants/usage`,d=await o(m,{headers:{"Content-Type":"application/json"},body:JSON.stringify({startTime:p,endTime:u})});if(!d.ok)throw new Error("Error fetching usage");let{data:h}=await d.json();if(!h)throw new Error("usage not found");return h},enabled:typeof t=="number"&&typeof r=="number"||!!e}),n=_e(()=>a.data?a.data||[]:[],[a]),l=a.isLoading||a.isRefetching,c=a.isError;return{usage:n,isLoading:l,hasError:c,refetch:a.refetch}}import{useInfiniteQuery as Qe}from"@tanstack/react-query";import{useMemo as $e}from"react";function We({timeRange:e,filters:t,startTime:r,endTime:i}){let{apiBaseUrl:o,fetchImpl:s=fetch}=R(),a=typeof r=="number"&&typeof i=="number"||!!e,n=Qe({queryKey:["unblind","logs",e,t.map(c=>c.name+":"+c.value).sort().join(",")],queryFn:async({pageParam:c})=>{let p=t.reduce((T,C)=>(T[C.name]||(T[C.name]=[]),T[C.name].push(C.value),T),{}),{body:u=[],severity:m=[],"service.name":d=[],"trace.id":h=[],"span.id":v=[],...w}=p,[f,g]=B(e,r,i),b=Date.now(),k=await s(`${o}/tenants/logs`,{method:"POST",body:JSON.stringify({filter:{attributes:w,body:u,severity:m,traceId:h,spanId:v,service:d},startTime:f,endTime:g,pagination:{page:c}}),headers:{"Content-Type":"application/json"}});if(!k.ok)throw new Error("Error fetching logs");let{data:y,next_page:x}=await k.json();if(!y)throw new Error("logs not found");return{data:y,next_page:x}},enabled:a,initialPageParam:void 0,getNextPageParam:c=>c.next_page});return{logs:$e(()=>n.data?n.data.pages.flatMap(c=>c.data||[]):[],[n.data]),isLoading:n.isLoading,hasError:n.isError,hasNextPage:n.hasNextPage??!1,fetchNextPage:n.fetchNextPage,isFetchingNextPage:n.isFetchingNextPage,refetch:n.refetch}}import{useEffect as Ke,useState as Ye}from"react";function X(){let[e,t]=Ye(!1);return Ke(()=>{let r=()=>t(document.documentElement.classList.contains("dark"));return r(),window.addEventListener("storage",r),window.addEventListener("theme-change",r),()=>{window.removeEventListener("storage",r),window.removeEventListener("theme-change",r)}},[]),e}import{useEffect as Ut,useRef as St}from"react";import Et from"uplot";import G from"uplot";import{getValueFormat as vt}from"@grafana/data";import{dateTimeFormat as Xe,dateTimeFormatTimeAgo as Je,systemDateFormats as _}from"@grafana/data";var P={millisecond:1,second:1e3,minute:6e4,hour:36e5,day:864e5,month:24192e5,year:31536e6},j={second:[1,2,5,10,15,30],minute:[1,2,5,10,15,30],hour:[1,2,3,4,6,8,12],day:[1,2,3,7,14],month:[1,2,3,6],year:[1,2,5,10,20,50,100]};function Ze(e,t,r,i){if(t>P.day){let o=_.interval.year,s=Math.round(P.year/P.day)*P.day;return Math.round(t/P.day)*P.day===s?o=_.interval.year:t<=P.year?o=_.interval.month:o=_.interval.day,e.map(n=>Xe(n,{format:o,timeZone:i}))}return e.map(o=>{let s=new Date(o),a=t<P.minute,n=t<P.second,l=s.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",hour12:!1,timeZone:i});return(l==="00:00"||l==="24:00")&&!a&&!n?s.toLocaleDateString(void 0,{day:"2-digit",month:"short",timeZone:i}):s.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit",second:a?"2-digit":void 0,fractionalSecondDigits:n?3:void 0,hour12:!1,timeZone:i})})}function me(e){let t=[{size:P.second/1e3,increments:j.second},{size:P.minute/1e3,increments:j.minute},{size:P.hour/1e3,increments:j.hour},{size:P.day/1e3,increments:j.day},{size:P.month/1e3,increments:j.month},{size:P.year/1e3,increments:j.year}];for(let o of t)for(let s of o.increments){let a=o.size*s;if(a>=e)return{increment:a*1e3,multiplier:s}}let r=t[t.length-1],i=r.increments[r.increments.length-1];return{increment:r.size*i*1e3,multiplier:i}}function de(e,t,r){return[t,r]}function et(e,t,r,i){let o=r-t,s=e.width,a=Math.floor(s/100),n=o/a,{increment:l,multiplier:c}=me(n),p=l/1e3,u=[];if(o<12*3600){let d=Math.ceil(t/p)*p;for(;d<=r;d+=p)u.push(d);return u}if(o<3*86400){if(l>=6*3600*1e3){let h=l/1e3/3600,v=new Date(t*1e3);if(i==="UTC"){let f=v.getUTCHours(),g=Math.floor(f/h)*h;v.setUTCHours(g,0,0,0)}else{let f=v.getHours(),g=Math.floor(f/h)*h;v.setHours(g,0,0,0)}let w=v.getTime()/1e3;for(w<t&&(w+=p);w<=r;)u.push(w),w+=p;return u}let d=Math.ceil(t/p)*p;for(;d<=r;d+=p)u.push(d);return u}if(l>=P.day){let d=new Date(t*1e3);i==="UTC"?(d.setUTCHours(0,0,0,0),d.getTime()/1e3<t&&d.setUTCDate(d.getUTCDate()+c)):(d.setHours(0,0,0,0),d.getTime()/1e3<t&&d.setDate(d.getDate()+c));let h=d.getTime()/1e3;for(;h<=r;)u.push(h),i==="UTC"?d.setUTCDate(d.getUTCDate()+c):d.setDate(d.getDate()+c),h=d.getTime()/1e3;return u}let m=Math.ceil(t/p)*p;for(;m<=r;m+=p)u.push(m);return u}function ue(e,t,r){return t.length===0?[]:t.map((i,o)=>o===0||o===t.length-1?Je(i*1e3,{timeZone:r}):"")}function tt(e,t,r){let i=e.scales.x,o=((i?.max??0)-(i?.min??0))*1e3,s=Math.floor(e.width/100),a=o/1e3/s,{increment:n}=me(a),l=t.map(c=>c*1e3);return Ze(l,n,o,r)}function fe(e,t=!1){let r=t?(s,a,n,l)=>de(s,n,l):(s,a,n,l)=>et(s,n,l,e),i=t?(s,a)=>ue(s,a,e):(s,a)=>tt(s,a,e),o=t?(s,a,n,l,c)=>{let u=document.createElement("canvas").getContext("2d");if(!u)return 0;u.font=I;let m=ue(s,de(s,n,l),e),d=Math.max(u.measureText(m[0]||"").width,u.measureText(m[1]||"").width);return Math.ceil(d/2)+15}:void 0;return{font:I,labelFont:I,grid:{show:!1,width:.5},ticks:{width:.5},splits:r,values:i,size:20}}import{computePosition as dt,flip as ut,offset as mt}from"@floating-ui/dom";import ft from"uplot";import{createRoot as rt}from"react-dom/client";var J=class{overlay=null;reactRoot=null;initialize(){this.overlay||(this.overlay=document.createElement("div"),this.overlay.id="unblind-tooltip-overlay",this.overlay.style.display="none",this.overlay.style.position="fixed",this.overlay.style.pointerEvents="none",this.overlay.style.zIndex="9999",document.body.appendChild(this.overlay),this.reactRoot=rt(this.overlay))}getOverlay(){return this.overlay}render(t){this.reactRoot&&this.reactRoot.render(t)}show(){this.overlay&&(this.overlay.style.display="block")}hide(){this.overlay&&(this.overlay.style.display="none"),this.render(null)}destroy(){this.reactRoot&&(this.reactRoot.unmount(),this.reactRoot=null),this.overlay&&(this.overlay.remove(),this.overlay=null)}},F=new J;import{dateTimeFormat as ge}from"@grafana/data";import{useMemo as it}from"react";import{jsx as ot}from"react/jsx-runtime";function Z({className:e="h-px bg-gray-200 border-gray-200 dark:bg-white/15",...t}){return ot("hr",{role:"presentation",...t,className:e})}import{jsx as U,jsxs as D}from"react/jsx-runtime";function nt(e){return typeof e.metric=="string"?e.metric:e.metric?.textContent||"Unknown Metric"}function he(e){let t=e.attributes||{};if(t["service.name"])return String(t["service.name"]);let i=Object.keys(t)[0];return i?String(t[i]):"z-fallback"}function be(e){return[...e].sort((t,r)=>(Number(r.value)||0)-(Number(t.value)||0))}function at(e){return[...e].sort((t,r)=>he(t).localeCompare(he(r)))}function st(e){let t={};return e.forEach(r=>{let i=nt(r);t[i]||(t[i]=[]),t[i].push(r)}),t}function lt(e,t){let r=st(e);return Object.keys(r).sort((o,s)=>o.localeCompare(s)).map(o=>{let s=r[o];if(!s)return{metricName:o,items:[]};let a=t?be(s):at(s);return{metricName:o,items:a}})}function ct({item:e,groupIdx:t,idx:r}){let i=e.attributes||{},o=Object.entries(i),s=typeof e.metric=="string"?e.metric:e.metric?.textContent||"Unknown",a=o,n=o.find(([l])=>l==="service.name");return n?(s=String(n[1]),a=o.filter(([l])=>l!=="service.name")):o.length>0&&o[0]&&(s=`${o[0][0]}: ${o[0][1]}`,a=o.slice(1)),D("div",{className:"group flex flex-col px-3 py-1 hover:bg-slate-50 dark:hover:bg-white/5 transition-colors",children:[D("div",{className:"flex items-center justify-between gap-4",children:[D("div",{className:"flex items-center gap-2.5 min-w-0 overflow-hidden",children:[U("span",{className:"size-2 mt-0.5 shrink-0 rounded-full shadow-sm ring-1 ring-black/5 dark:ring-white/10",style:{backgroundColor:e.color}}),U("span",{className:"truncate font-medium text-slate-600 dark:text-slate-300",title:s,children:s})]}),U("div",{className:"font-semibold tabular-nums text-slate-900 dark:text-white whitespace-nowrap",children:e.formattedValue??U("span",{className:"font-normal text-slate-400",children:"\u2014"})})]}),a.length>0&&U("div",{className:"mt-0.5 flex flex-wrap gap-x-3 gap-y-0.5 pl-4.5 text-xs text-slate-500 dark:text-slate-400",children:a.map(([l,c])=>D("span",{className:"flex items-center gap-1",children:[D("span",{className:"opacity-70",children:[l,":"]}),U("span",{className:"font-medium text-slate-600 dark:text-slate-300",children:String(c)})]},l))})]},`item-${t}-${r}`)}function pt({group:e,groupIdx:t,hasAttributes:r}){return D("div",{className:"flex flex-col",children:[r&&U("div",{className:`sticky top-0 z-10 bg-white/95 dark:bg-zinc-900/95 backdrop-blur-sm px-3 py-1.5 font-semibold text-slate-900 dark:text-white ${t>0?"mt-2 border-t border-gray-100 dark:border-white/5 pt-3":""}`,children:e.metricName}),U("div",{className:"flex flex-col gap-1",children:e.items.map((i,o)=>U(ct,{item:i,groupIdx:t,idx:o},`item-${t}-${o}`))})]},e.metricName)}function ye({timestamp:e,items:t,timeZone:r,spansMultipleDays:i,orderByValues:o,hasAttributes:s}){let a=i?ge(e*1e3,{format:"MMM DD, HH:mm:ss",timeZone:r}):ge(e*1e3,{format:"HH:mm:ss",timeZone:r}),n=it(()=>{let l=lt(t,o);if(!s&&o&&l.length>0){let c=l.flatMap(m=>m.items),p=be(c);return[{metricName:l[0]?.metricName||"Metrics",items:p}]}return l},[t,o,s]);return D("div",{style:{fontSize:"13px"},className:"flex w-[320px] flex-col gap-1 text-sm rounded-lg text-slate-900 dark:text-slate-200 border border-gray-200/80 bg-white shadow-xl ring-1 ring-gray-200/40 dark:ring-zinc-800/40 dark:border-white/10 dark:bg-zinc-900",children:[U("div",{className:"flex items-center justify-between px-3 pt-2.5 font-medium text-slate-700 dark:text-slate-100",children:a}),U(Z,{className:"my-1 border-gray-100 dark:border-white/5"}),n.length>0?U("div",{className:"flex flex-col pb-2",children:n.map((l,c)=>U(pt,{group:l,groupIdx:c,hasAttributes:s},l.metricName))}):U("div",{className:"px-3 pb-3 italic text-zinc-400 text-xs",children:"No data available"})]})}import{jsx as we}from"react/jsx-runtime";var gt=4,ht=8;function bt(e,t){if(!e||e.length===0)return!1;let r=e[0],i=e[e.length-1];if(r==null||i==null)return!1;let o=new Date(r*1e3),s=new Date(i*1e3),a=n=>t==="UTC"?`${n.getUTCFullYear()}-${n.getUTCMonth()}-${n.getUTCDate()}`:n.toLocaleDateString(void 0,{timeZone:t});return a(o)!==a(s)}function yt(e,t){let r=!1;for(let o=1;o<e.series.length;o++)if(e.data[o]?.[t]!=null){r=!0;break}if(r)return t;let i=e.data[0].length;for(let o=1;t+o<i||t-o>=0;o++){let s=t-o,a=t+o;if(s>=0){for(let n=1;n<e.series.length;n++)if(e.data[n]?.[s]!=null)return s}if(a<i){for(let n=1;n<e.series.length;n++)if(e.data[n]?.[a]!=null)return a}}return t}function wt(e,t,r,i,o){let s=!1,a=[];for(let n=1;n<e.series.length;n++){let l=e.data[n]?.[t]??null;if(i&&l!=null&&n>1){let v=e.data[n-1]?.[t]??0;l=l-v}let c=e.series[n],p=o?.[n-1],u=c?.label??`Series ${n}`,m=c?.stroke,d=typeof m=="function"?m(ft,n):m??"#ffffff00",h=l==null?void 0:r?r(l):String(l);p?.attributes&&Object.keys(p?.attributes).length>0&&(s=!0),a.push({metric:u,color:d,value:l===null?void 0:l,formattedValue:h,attributes:p?.attributes})}return{items:a,hasAttributes:s}}async function xt(e,t){let{x:r,y:i}=await dt({getBoundingClientRect:()=>({x:t.left,y:t.top,width:0,height:0,top:t.top,left:t.left,right:t.left,bottom:t.top})},e,{placement:"top-start",strategy:"fixed",middleware:[mt({mainAxis:gt,crossAxis:ht}),ut()]});e.style.left=`${r}px`,e.style.top=`${i}px`}function xe(e,t,r,i,o,s){let a,n,l,c=!1;function p(){let d=a.getBoundingClientRect();n=d.left,l=d.top}function u(){F.hide()}function m(){F.show()}return{hooks:{init:d=>{F.initialize(),a=d.over,window.addEventListener("scroll",p,!0),window.addEventListener("resize",p),a.onmouseenter=()=>{c=!0,m()},a.onmouseleave=()=>{c=!1,u()},p()},setSize:()=>{p()},setCursor:d=>{let{left:h,top:v,idx:w}=d.cursor;if(!c||w==null){u();return}let g=d.data[0],b=bt(g,r),k=yt(d,w),y=d.data[0][k];if(y===void 0)return;let{items:x,hasAttributes:T}=wt(d,k,e,t,o),C={left:(h||0)+n,top:(v||0)+l};m();let S=i,N=S?we(S,{timestamp:y,items:x,timeZone:r}):we(ye,{timestamp:y,items:x,timeZone:r,spansMultipleDays:b,stacked:t,hasAttributes:T,orderByValues:s});F.render(N);let z=F.getOverlay();z&&xt(z,C)},destroy(){window.removeEventListener("scroll",p,!0),window.removeEventListener("resize",p)}}}}var I='11px "Inter", sans-serif';var ve=(e,t)=>{let r=e;return t&&t.unit&&t.unit.code&&t.unit.code!=="1"&&(!r&&t.unit?r=t.unit.code:r&&t.unit.code!==r&&(r=void 0)),r},ee=(e,t,r)=>Array.isArray(r)?r[t]:r(e,t),kt=e=>{let t=G.paths.bars({size:[.6,100],radius:0,gap:0}),r=G.paths.linear({alignGaps:0}),i=G.paths.spline({alignGaps:1}),o=G.paths.stepped({alignGaps:1});switch(e){case"line":return r;case"bar":return t;case"area":return r;case"step":return o;case"spline":return i;default:return r}},Tt=(e,t,r,i)=>{switch(i){case"area":return ee(e,t,r);case"bar":return ee(e,t,r);default:return}},Ct=e=>{switch(e){case"bar":return 1;case"line":return 2;case"area":return 2;case"step":return 1.5;default:return 1}},ke=(e,t,r,i,o,s,a,n,l,c,p)=>{let u=e?.clientWidth??1050,m=e?.clientHeight??250,d=typeof t=="string"?String(t).toLowerCase().replaceAll("by","bytes"):t,h=vt(d==="1"?null:d),v=Ct(i),w={width:u,height:m,scales:{y:{range:{min:{mode:1,soft:0},max:{pad:2}}}},plugins:[xe(f=>{let g=h(f,2);return g.text+(g.suffix?.trim()||"")},o,n,l,r,p)],padding:c?[10,15,10,15]:[8,15,8,15],cursor:{y:!1,sync:{key:"_"},drag:{setScale:!0,x:!0,y:!1},move:(f,g,b)=>{let k=f.posToVal(g,"x"),y=f.data[0];if(!y||y.length===0)return[g,b];let x=0,T=0,C=y.length-1;for(;C-T>1;){let M=Math.floor((T+C)/2),H=y[M];H!=null&&H<k?T=M:C=M}let S=y[T],N=y[C];S!=null&&N!=null?x=Math.abs(S-k)<Math.abs(N-k)?T:C:S!=null?x=T:N!=null&&(x=C);let z=x;for(let M=x;M>=0;M--){let H=!1;for(let Q=1;Q<f.data.length;Q++){let re=f.data[Q];if(re&&re[M]!=null){H=!0;break}}if(H){z=M;break}}let A=y[z];return A==null?[g,b]:[f.valToPos(A,"x"),b]}},series:[{},...r.map((f,g)=>({label:f.metric,stroke:ee(f,g,a),width:v,points:{show:!1},spanGaps:!0,paths:kt(i),fill:Tt(f,g,a,i)}))],axes:[fe(n,c),Te(h)],legend:{show:!1}};if(s){let f=w.axes?.[0],g=w.axes?.[1];f&&(f.stroke="#dadada",f.grid&&(f.grid.stroke="#2c3235")),g&&(g.stroke="#dadada",g.grid&&(g.grid.stroke="#2c3235"))}else{let f=w.axes?.[0],g=w.axes?.[1];f&&(f.stroke="#45556c"),g&&(g.stroke="#45556c")}return w};var Pt=(e,t,r,i)=>{let o=e.axes[r];if(i>1)return o?._size;let s=(o?.ticks?.size||0)+(o?.gap||0),a=(t??[]).reduce((n,l)=>l.length>n.length?l:n,"");return a!=""&&(e.ctx.font=o?.font?.[0]??e.ctx.font,s+=e.ctx.measureText(a).width/devicePixelRatio),Math.ceil(s)};function Te(e){return{gap:0,font:I,labelFont:I,grid:{show:!0,width:.5},ticks:{width:.5},values:(t,r)=>r.map(i=>{let o=e(i);return o.text+(o.suffix?.trim()||"")}),size:Pt}}function Ce(e,t){let r=[],i=e[0],o=i.length,s=Array(o).fill(0),a=[i];return e.forEach((n,l)=>{l!==0&&(t?a.push(n):a.push(n.map((c,p)=>s[p]=s[p]+(c||0))))}),e.forEach((n,l)=>{l===0||t||r.push({series:[e.findIndex((c,p)=>p>l),l]})}),{data:a,bands:r.filter(n=>n.series[1]>-1)}}import{jsx as Rt}from"react/jsx-runtime";function te(e){let{times:t,series:r,metadata:i,type:o,className:s,timeZone:a,options:n,tooltipAppearance:l,colors:c,orderByValues:p}=e,u=St(null),m=X(),d=V(),h=c||d.appearance.colors;return Ut(()=>{if(!r||r.length===0){console.warn("No series provided");return}let v=[t],w;r.forEach(x=>{let T=i[x.metric];w=ve(w,T),v.push(x.values)});let f=o==="bar"||o==="area",g=Ce(v,!f),b=u.current,k=ke(b,w,r,o,f,m,h,a,l,p),y=null;if(b){k.bands=g.bands,y=new Et({...k,...n},g.data,b);let x=new ResizeObserver(()=>{y?.setSize({width:b.clientWidth,height:b.clientHeight})});return x.observe(b),()=>{y?.destroy(),x.disconnect()}}},[r,t,o,m,i,a,l,h,p]),Rt("div",{ref:u,className:s})}import{jsx as O}from"react/jsx-runtime";function Mt({metrics:e,operator:t,attributes:r,groupBy:i,timeRange:o,startTime:s,endTime:a,interval:n,type:l="line",className:c,appearance:p}){let u=V(),m=o??u.timeRange,d=s??u.startTime,h=a??u.endTime,v=n??u.interval,w=r??u.attributes,f=i??u.groupBy,g=t??u.operator,b=p?.colors??u.appearance.colors,k=p?.orderByValues??u.appearance.orderByValues,{isLoading:y,data:x,hasError:T}=Y({queries:e.map(E=>({metrics:[E],operator:g,attributes:w,groupBy:f})),timeRange:m,startTime:d,endTime:h,interval:v}),{series:C,times:S,metadata:N}=x,z=C.every(E=>E.isEmpty),A=c||"h-full w-full";if(y){let E=p?.components?.Loading??u.appearance.components.Loading;return O("div",{className:A,children:O(E,{})})}if(z){let E=p?.components?.Empty??u.appearance.components.Empty;return O("div",{className:A,children:O(E,{})})}if(T){let E=p?.components?.Error??u.appearance.components.Error;return O("div",{className:A,children:O(E,{})})}return O(te,{times:S,series:C,metadata:N,type:l,className:A,tooltipAppearance:p?.components?.Tooltip??u.appearance.components.Tooltip,colors:b,orderByValues:k})}export{te as Chart,Z as Divider,Mt as TimeseriesChart,$ as UnblindClientProvider,Oe as UnblindProvider,K as UnblindScope,We as useLogs,Be as useMetrics,se as useRefresh,V as useScope,X as useTheme,Y as useTimeseries,R as useUnblindClientConfig,Ge as useUsage};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|