@databricks/appkit-ui 0.44.0 → 0.46.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.
Files changed (41) hide show
  1. package/CLAUDE.md +1 -0
  2. package/dist/js/arrow/arrow-client.d.ts +8 -1
  3. package/dist/js/arrow/arrow-client.d.ts.map +1 -1
  4. package/dist/js/arrow/arrow-client.js +21 -2
  5. package/dist/js/arrow/arrow-client.js.map +1 -1
  6. package/dist/js/sse/connect-sse.js +1 -1
  7. package/dist/js/sse/connect-sse.js.map +1 -1
  8. package/dist/react/charts/types.d.ts +11 -4
  9. package/dist/react/charts/types.d.ts.map +1 -1
  10. package/dist/react/charts/types.js.map +1 -1
  11. package/dist/react/hooks/types.d.ts +10 -2
  12. package/dist/react/hooks/types.d.ts.map +1 -1
  13. package/dist/react/hooks/use-analytics-query.d.ts +5 -3
  14. package/dist/react/hooks/use-analytics-query.d.ts.map +1 -1
  15. package/dist/react/hooks/use-analytics-query.js +114 -33
  16. package/dist/react/hooks/use-analytics-query.js.map +1 -1
  17. package/dist/react/hooks/use-chart-data.d.ts +3 -3
  18. package/dist/react/hooks/use-chart-data.d.ts.map +1 -1
  19. package/dist/react/hooks/use-chart-data.js +3 -3
  20. package/dist/react/hooks/use-chart-data.js.map +1 -1
  21. package/dist/shared/src/index.d.ts +1 -0
  22. package/dist/shared/src/plugin.d.ts.map +1 -1
  23. package/dist/shared/src/sse/analytics.d.ts +1 -0
  24. package/docs/api/appkit/Class.AppKitError.md +40 -8
  25. package/docs/api/appkit/Class.AuthenticationError.md +56 -16
  26. package/docs/api/appkit/Class.ConfigurationError.md +57 -17
  27. package/docs/api/appkit/Class.ConnectionError.md +57 -17
  28. package/docs/api/appkit/Class.ExecutionError.md +80 -22
  29. package/docs/api/appkit/Class.InitializationError.md +55 -15
  30. package/docs/api/appkit/Class.Plugin.md +2 -0
  31. package/docs/api/appkit/Class.ResourceRegistry.md +1 -1
  32. package/docs/api/appkit/Class.ServerError.md +55 -15
  33. package/docs/api/appkit/Class.TunnelError.md +56 -16
  34. package/docs/api/appkit/Class.ValidationError.md +55 -15
  35. package/docs/api/appkit/Interface.AgentDefinition.md +11 -0
  36. package/docs/api/appkit/Interface.GenerationParams.md +58 -0
  37. package/docs/api/appkit/Interface.RegisteredAgent.md +11 -0
  38. package/docs/api/appkit.md +1 -0
  39. package/llms.txt +1 -0
  40. package/package.json +1 -1
  41. package/sbom.cdx.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"use-analytics-query.js","names":[],"sources":["../../../src/react/hooks/use-analytics-query.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { ArrowClient, connectSSE } from \"@/js\";\nimport type {\n AnalyticsFormat,\n InferParams,\n InferResultByFormat,\n QueryKey,\n UseAnalyticsQueryOptions,\n UseAnalyticsQueryResult,\n WarehouseStatus,\n} from \"./types\";\nimport { useAnalyticsWarehousePublisher } from \"./use-analytics-warehouse-status\";\nimport { useQueryHMR } from \"./use-query-hmr\";\n\n/** Shallow equality for plain-object query parameters (primitive values only). */\nfunction shallowEqualParams(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (\n a === null ||\n b === null ||\n typeof a !== \"object\" ||\n typeof b !== \"object\"\n ) {\n return false;\n }\n const aKeys = Object.keys(a as Record<string, unknown>);\n const bKeys = Object.keys(b as Record<string, unknown>);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n if (!Object.hasOwn(b, key)) return false;\n if (\n !Object.is(\n (a as Record<string, unknown>)[key],\n (b as Record<string, unknown>)[key],\n )\n ) {\n return false;\n }\n }\n return true;\n}\n\n/** Keep structurally-equal params referentially stable across renders. */\nfunction useStableParams<T>(value: T): T {\n const ref = useRef<T>(value);\n if (!shallowEqualParams(ref.current, value)) {\n ref.current = value;\n }\n return ref.current;\n}\n\nfunction getDevMode(): string {\n const dev = new URL(window.location.href).searchParams.get(\"dev\");\n return dev ? `?dev=${dev}` : \"\";\n}\n\nfunction getArrowStreamUrl(id: string): string {\n return `/api/analytics/arrow-result/${id}`;\n}\n\nconst GENERIC_LOAD_ERROR = \"Unable to load data, please try again\";\n\ninterface AnalyticsQuerySseContext<ResultType> {\n setLoading: (loading: boolean) => void;\n setError: (error: string | null) => void;\n setData: (data: ResultType | null) => void;\n setWarehouseStatus: (status: WarehouseStatus | null) => void;\n publishWarehouseStatus: (status: WarehouseStatus | null) => void;\n unpublishWarehouseStatus: () => void;\n}\n\nfunction isWarehouseStatusPayload(value: unknown): value is WarehouseStatus {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as WarehouseStatus).state === \"string\"\n );\n}\n\nasync function handleAnalyticsSseMessage<ResultType>(\n parsed: Record<string, unknown>,\n ctx: AnalyticsQuerySseContext<ResultType>,\n): Promise<void> {\n if (parsed.type === \"warehouse_status\") {\n if (!isWarehouseStatusPayload(parsed.status)) {\n ctx.setLoading(false);\n ctx.setError(GENERIC_LOAD_ERROR);\n ctx.unpublishWarehouseStatus();\n console.error(\n \"[useAnalyticsQuery] Malformed warehouse_status event\",\n parsed,\n );\n return;\n }\n ctx.setWarehouseStatus(parsed.status);\n ctx.publishWarehouseStatus(parsed.status);\n return;\n }\n\n if (parsed.type === \"result\") {\n ctx.setLoading(false);\n ctx.setData(parsed.data as ResultType);\n ctx.unpublishWarehouseStatus();\n return;\n }\n\n if (parsed.type === \"arrow\") {\n try {\n const arrowData = await ArrowClient.fetchArrow(\n getArrowStreamUrl(parsed.statement_id as string),\n );\n const table = await ArrowClient.processArrowBuffer(arrowData);\n ctx.setLoading(false);\n ctx.setData(table as ResultType);\n ctx.unpublishWarehouseStatus();\n } catch (error) {\n console.error(\"[useAnalyticsQuery] Failed to fetch Arrow data\", error);\n ctx.setLoading(false);\n ctx.setError(GENERIC_LOAD_ERROR);\n ctx.unpublishWarehouseStatus();\n }\n return;\n }\n\n if (parsed.type === \"error\" || parsed.error || parsed.code) {\n const errorMsg =\n (parsed.error as string | undefined) ||\n (parsed.message as string | undefined) ||\n \"Unable to execute query\";\n ctx.setLoading(false);\n ctx.setError(errorMsg);\n ctx.unpublishWarehouseStatus();\n if (parsed.code) {\n console.error(\n `[useAnalyticsQuery] Code: ${parsed.code}, Message: ${errorMsg}`,\n );\n }\n }\n}\n\n/**\n * Subscribe to an analytics query over SSE and returns its latest result.\n * Integration hook between client and analytics plugin.\n *\n * The return type is automatically inferred based on the format:\n * - `format: \"JSON_ARRAY\"` (default): Returns typed array from QueryRegistry\n * - `format: \"ARROW_STREAM\"`: Returns TypedArrowTable with row type preserved\n *\n * Note: User context execution is determined by query file naming:\n * - `queryKey.obo.sql`: Executes as user (OBO = on-behalf-of / user delegation)\n * - `queryKey.sql`: Executes as service principal\n *\n * @param queryKey - Analytics query identifier\n * @param parameters - Query parameters (type-safe based on QueryRegistry)\n * @param options - Analytics query settings including format\n * @returns Query result state with format-appropriate data type\n *\n * @example JSON format (default)\n * ```typescript\n * const { data } = useAnalyticsQuery(\"spend_data\", params);\n * // data: Array<{ group_key: string; cost_usd: number; ... }> | null\n * ```\n *\n * @example Arrow format\n * ```typescript\n * const { data } = useAnalyticsQuery(\"spend_data\", params, { format: \"ARROW_STREAM\" });\n * // data: TypedArrowTable<{ group_key: string; cost_usd: number; ... }> | null\n * ```\n */\nexport function useAnalyticsQuery<\n T = unknown,\n K extends QueryKey = QueryKey,\n F extends AnalyticsFormat = \"JSON_ARRAY\",\n>(\n queryKey: K,\n parameters?: InferParams<K> | null,\n options: UseAnalyticsQueryOptions<F> = {} as UseAnalyticsQueryOptions<F>,\n): UseAnalyticsQueryResult<InferResultByFormat<T, K, F>> {\n const format = options?.format ?? \"JSON_ARRAY\";\n const maxParametersSize = options?.maxParametersSize ?? 100 * 1024;\n const autoStart = options?.autoStart ?? true;\n\n const devMode = getDevMode();\n const urlSuffix = `/api/analytics/query/${encodeURIComponent(queryKey)}${devMode}`;\n\n type ResultType = InferResultByFormat<T, K, F>;\n const [data, setData] = useState<ResultType | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [warehouseStatus, setWarehouseStatus] =\n useState<WarehouseStatus | null>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const publisherId = useId();\n const {\n publish: publishWarehouseStatus,\n unpublish: unpublishWarehouseStatus,\n } = useAnalyticsWarehousePublisher(publisherId, queryKey);\n\n if (!queryKey || queryKey.trim().length === 0) {\n throw new Error(\n \"useAnalyticsQuery: 'queryKey' must be a non-empty string.\",\n );\n }\n\n const stableParameters = useStableParams(parameters);\n\n const payload = useMemo(() => {\n try {\n const serialized = JSON.stringify({\n parameters: stableParameters,\n format,\n });\n const sizeInBytes = new Blob([serialized]).size;\n if (sizeInBytes > maxParametersSize) {\n throw new Error(\n \"useAnalyticsQuery: Parameters size exceeds the maximum allowed size\",\n );\n }\n\n return serialized;\n } catch (error) {\n console.error(\"useAnalyticsQuery: Failed to serialize parameters\", error);\n return null;\n }\n }, [stableParameters, format, maxParametersSize]);\n\n const start = useCallback(() => {\n if (payload === null) {\n setError(\"Failed to serialize query parameters\");\n return;\n }\n\n abortControllerRef.current?.abort();\n\n setLoading(true);\n setError(null);\n setData(null);\n setWarehouseStatus(null);\n publishWarehouseStatus(null);\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n const sseContext: AnalyticsQuerySseContext<ResultType> = {\n setLoading,\n setError,\n setData,\n setWarehouseStatus,\n publishWarehouseStatus,\n unpublishWarehouseStatus,\n };\n\n connectSSE({\n url: urlSuffix,\n payload,\n signal: abortController.signal,\n onMessage: async (message) => {\n // Drop late envelopes from a stream whose controller was already\n // aborted (React StrictMode unmount→remount). Mirrors onError below.\n if (abortController.signal.aborted) return;\n try {\n const parsed = JSON.parse(message.data) as Record<string, unknown>;\n await handleAnalyticsSseMessage(parsed, sseContext);\n } catch (error) {\n console.warn(\"[useAnalyticsQuery] Malformed message received\", error);\n }\n },\n onError: (error) => {\n if (abortController.signal.aborted) return;\n setLoading(false);\n unpublishWarehouseStatus();\n\n let userMessage = GENERIC_LOAD_ERROR;\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n userMessage = \"Request timed out, please try again\";\n } else if (error.message.includes(\"Failed to fetch\")) {\n userMessage = \"Network error. Please check your connection.\";\n }\n console.error(\"[useAnalyticsQuery] Error\", {\n queryKey,\n error: error.message,\n stack: error.stack,\n });\n }\n setError(userMessage);\n },\n });\n }, [\n queryKey,\n payload,\n urlSuffix,\n publishWarehouseStatus,\n unpublishWarehouseStatus,\n ]);\n\n useEffect(() => {\n if (autoStart) {\n start();\n }\n\n return () => {\n abortControllerRef.current?.abort();\n unpublishWarehouseStatus();\n };\n }, [start, autoStart, unpublishWarehouseStatus]);\n\n useQueryHMR(queryKey, start);\n\n return { data, loading, error, warehouseStatus };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-analytics-query.js","names":[],"sources":["../../../src/react/hooks/use-analytics-query.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { ArrowClient, connectSSE } from \"@/js\";\nimport type {\n AnalyticsFormat,\n InferParams,\n InferResultByFormat,\n QueryKey,\n UseAnalyticsQueryOptions,\n UseAnalyticsQueryResult,\n WarehouseStatus,\n} from \"./types\";\nimport { useAnalyticsWarehousePublisher } from \"./use-analytics-warehouse-status\";\nimport { useQueryHMR } from \"./use-query-hmr\";\n\n/** Shallow equality for plain-object query parameters (primitive values only). */\nfunction shallowEqualParams(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (\n a === null ||\n b === null ||\n typeof a !== \"object\" ||\n typeof b !== \"object\"\n ) {\n return false;\n }\n const aKeys = Object.keys(a as Record<string, unknown>);\n const bKeys = Object.keys(b as Record<string, unknown>);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n if (!Object.hasOwn(b, key)) return false;\n if (\n !Object.is(\n (a as Record<string, unknown>)[key],\n (b as Record<string, unknown>)[key],\n )\n ) {\n return false;\n }\n }\n return true;\n}\n\n/** Keep structurally-equal params referentially stable across renders. */\nfunction useStableParams<T>(value: T): T {\n const ref = useRef<T>(value);\n if (!shallowEqualParams(ref.current, value)) {\n ref.current = value;\n }\n return ref.current;\n}\n\nfunction getDevMode(): string {\n const dev = new URL(window.location.href).searchParams.get(\"dev\");\n return dev ? `?dev=${dev}` : \"\";\n}\n\nconst GENERIC_LOAD_ERROR = \"Unable to load data, please try again\";\n\n/** Map a fetch/SSE transport error to a user-facing message. */\nfunction userFacingFetchError(error: unknown): string {\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n return \"Request timed out, please try again\";\n }\n if (error.message.includes(\"Failed to fetch\")) {\n return \"Network error. Please check your connection.\";\n }\n }\n return GENERIC_LOAD_ERROR;\n}\n\ninterface AnalyticsQuerySseContext<ResultType> {\n setLoading: (loading: boolean) => void;\n setError: (error: string | null) => void;\n setErrorCode: (code: string | null) => void;\n setData: (data: ResultType | null) => void;\n setWarehouseStatus: (status: WarehouseStatus | null) => void;\n publishWarehouseStatus: (status: WarehouseStatus | null) => void;\n unpublishWarehouseStatus: () => void;\n}\n\nfunction isWarehouseStatusPayload(value: unknown): value is WarehouseStatus {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as WarehouseStatus).state === \"string\"\n );\n}\n\nasync function handleAnalyticsSseMessage<ResultType>(\n parsed: Record<string, unknown>,\n ctx: AnalyticsQuerySseContext<ResultType>,\n): Promise<void> {\n if (parsed.type === \"warehouse_status\") {\n if (!isWarehouseStatusPayload(parsed.status)) {\n ctx.setLoading(false);\n ctx.setError(GENERIC_LOAD_ERROR);\n ctx.unpublishWarehouseStatus();\n console.error(\n \"[useAnalyticsQuery] Malformed warehouse_status event\",\n parsed,\n );\n return;\n }\n ctx.setWarehouseStatus(parsed.status);\n ctx.publishWarehouseStatus(parsed.status);\n return;\n }\n\n // JSON result. The SSE wire schema is intentionally loose (`data` is an\n // optional array of unknown values), so a structural check is enough here —\n // no need to ship a schema validator (zod, ~60 KB gz) to the browser just\n // to read our own same-origin server's messages. Missing or non-array\n // `data` normalizes to [] so `undefined` never bleeds into the hook's\n // `T | null` state.\n if (parsed.type === \"result\") {\n ctx.setLoading(false);\n ctx.setData((Array.isArray(parsed.data) ? parsed.data : []) as ResultType);\n ctx.unpublishWarehouseStatus();\n return;\n }\n\n // NOTE: ARROW_STREAM no longer flows over SSE — the server streams the\n // raw Arrow IPC bytes back as the query response body, handled by\n // `fetchArrowDirect` instead of this SSE handler.\n\n if (parsed.type === \"error\" || parsed.error || parsed.code) {\n const errorMsg =\n (parsed.error as string | undefined) ||\n (parsed.message as string | undefined) ||\n \"Unable to execute query\";\n ctx.setLoading(false);\n ctx.setError(errorMsg);\n ctx.unpublishWarehouseStatus();\n // Propagate the upstream structured code so UI consumers can branch on\n // a stable identifier (e.g. format-switch on\n // RESULT_TOO_LARGE_FOR_JSON_FALLBACK or ARROW_DELIVERY_UNSUPPORTED)\n // instead of parsing the human-readable message.\n if (typeof parsed.errorCode === \"string\") {\n ctx.setErrorCode(parsed.errorCode);\n }\n if (parsed.code) {\n console.error(\n `[useAnalyticsQuery] Code: ${parsed.code}, Message: ${errorMsg}`,\n );\n }\n return;\n }\n\n // Not a warehouse-status, result, or error event — surface a generic error\n // rather than silently dropping an unrecognized payload.\n console.error(\"[useAnalyticsQuery] Unrecognized SSE payload\", parsed);\n ctx.setLoading(false);\n ctx.setError(GENERIC_LOAD_ERROR);\n ctx.unpublishWarehouseStatus();\n}\n\ninterface ArrowDirectContext {\n url: string;\n payload: string;\n signal: AbortSignal;\n setLoading: (loading: boolean) => void;\n setError: (error: string | null) => void;\n setErrorCode: (code: string | null) => void;\n setData: (data: unknown) => void;\n unpublishWarehouseStatus: () => void;\n}\n\n/**\n * Fetch the real column names for a statement from the fallback endpoint,\n * used when a very wide schema's names didn't fit in the response header.\n * Returns undefined on any failure so decoding falls back to the raw Arrow\n * schema names.\n */\nasync function fetchArrowColumns(\n statementId: string,\n signal: AbortSignal,\n): Promise<string[] | undefined> {\n try {\n const res = await fetch(\n `/api/analytics/columns/${encodeURIComponent(statementId)}`,\n { signal },\n );\n if (!res.ok) return undefined;\n const body = (await res.json()) as { columns?: unknown };\n return Array.isArray(body.columns) ? (body.columns as string[]) : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Fetch an ARROW_STREAM query result as raw Arrow IPC bytes directly from\n * the query endpoint (no SSE, no second /arrow-result request) and decode\n * it into a Table. The server streams the bytes back as the POST response\n * body; errors before the first byte arrive as a JSON `{ error, errorCode }`.\n */\nasync function fetchArrowDirect(ctx: ArrowDirectContext): Promise<void> {\n try {\n const response = await fetch(ctx.url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: ctx.payload,\n signal: ctx.signal,\n });\n if (ctx.signal.aborted) return;\n\n if (!response.ok) {\n let message = GENERIC_LOAD_ERROR;\n let code: string | null = null;\n try {\n const body = (await response.json()) as {\n error?: string;\n errorCode?: string;\n };\n if (body.error) message = body.error;\n if (typeof body.errorCode === \"string\") code = body.errorCode;\n } catch {\n // Non-JSON error body — keep the generic message.\n }\n ctx.setLoading(false);\n ctx.setError(message);\n if (code) ctx.setErrorCode(code);\n ctx.unpublishWarehouseStatus();\n return;\n }\n\n const buffer = await response.arrayBuffer();\n if (ctx.signal.aborted) return;\n // Databricks encodes ARROW_STREAM columns positionally (col_0, …); the\n // server sends the real manifest names so we can relabel the decoded\n // Table (charts look columns up by name). Normally inline in the\n // `X-Appkit-Arrow-Columns` header; for very wide schemas the header\n // carries only a statement-id reference and we fetch the names.\n let columnNames: string[] | undefined;\n const header = response.headers.get(\"X-Appkit-Arrow-Columns\");\n if (header) {\n try {\n columnNames = JSON.parse(decodeURIComponent(header));\n } catch {\n // Malformed header — fall back to the raw Arrow schema names.\n }\n } else {\n const ref = response.headers.get(\"X-Appkit-Arrow-Columns-Ref\");\n if (ref) {\n columnNames = await fetchArrowColumns(ref, ctx.signal);\n }\n }\n const table = await ArrowClient.processArrowBuffer(\n new Uint8Array(buffer),\n columnNames,\n );\n ctx.setData(table);\n ctx.setLoading(false);\n ctx.unpublishWarehouseStatus();\n } catch (error) {\n if (ctx.signal.aborted) return;\n ctx.setLoading(false);\n ctx.unpublishWarehouseStatus();\n ctx.setError(userFacingFetchError(error));\n }\n}\n\n/**\n * Subscribe to an analytics query and return its latest result. JSON_ARRAY\n * results stream over SSE (with warehouse-readiness progress); ARROW_STREAM\n * results are fetched as raw Arrow bytes directly from the query endpoint.\n * Integration hook between client and analytics plugin.\n *\n * The return type is automatically inferred based on the format:\n * - `format: \"JSON_ARRAY\"` (default): Returns typed array from QueryRegistry\n * - `format: \"ARROW_STREAM\"`: Returns TypedArrowTable with row type preserved\n *\n * Note: User context execution is determined by query file naming:\n * - `queryKey.obo.sql`: Executes as user (OBO = on-behalf-of / user delegation)\n * - `queryKey.sql`: Executes as service principal\n *\n * @param queryKey - Analytics query identifier\n * @param parameters - Query parameters (type-safe based on QueryRegistry)\n * @param options - Analytics query settings including format\n * @returns Query result state with format-appropriate data type\n *\n * @example JSON_ARRAY format (default)\n * ```typescript\n * const { data } = useAnalyticsQuery(\"spend_data\", params);\n * // data: Array<{ group_key: string; cost_usd: number; ... }> | null\n * ```\n *\n * @example ARROW_STREAM format\n * ```typescript\n * const { data } = useAnalyticsQuery(\"spend_data\", params, { format: \"ARROW_STREAM\" });\n * // data: TypedArrowTable<{ group_key: string; cost_usd: number; ... }> | null\n * ```\n */\nexport function useAnalyticsQuery<\n T = unknown,\n K extends QueryKey = QueryKey,\n F extends AnalyticsFormat = \"JSON_ARRAY\",\n>(\n queryKey: K,\n parameters?: InferParams<K> | null,\n options: UseAnalyticsQueryOptions<F> = {} as UseAnalyticsQueryOptions<F>,\n): UseAnalyticsQueryResult<InferResultByFormat<T, K, F>> {\n const format = options?.format ?? \"JSON_ARRAY\";\n const maxParametersSize = options?.maxParametersSize ?? 100 * 1024;\n const autoStart = options?.autoStart ?? true;\n\n const devMode = getDevMode();\n const urlSuffix = `/api/analytics/query/${encodeURIComponent(queryKey)}${devMode}`;\n\n type ResultType = InferResultByFormat<T, K, F>;\n const [data, setData] = useState<ResultType | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [errorCode, setErrorCode] = useState<string | null>(null);\n const [warehouseStatus, setWarehouseStatus] =\n useState<WarehouseStatus | null>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const publisherId = useId();\n const {\n publish: publishWarehouseStatus,\n unpublish: unpublishWarehouseStatus,\n } = useAnalyticsWarehousePublisher(publisherId, queryKey);\n\n if (!queryKey || queryKey.trim().length === 0) {\n throw new Error(\n \"useAnalyticsQuery: 'queryKey' must be a non-empty string.\",\n );\n }\n\n const stableParameters = useStableParams(parameters);\n\n const payload = useMemo(() => {\n try {\n const serialized = JSON.stringify({\n parameters: stableParameters,\n format,\n });\n const sizeInBytes = new Blob([serialized]).size;\n if (sizeInBytes > maxParametersSize) {\n throw new Error(\n \"useAnalyticsQuery: Parameters size exceeds the maximum allowed size\",\n );\n }\n\n return serialized;\n } catch (error) {\n console.error(\"useAnalyticsQuery: Failed to serialize parameters\", error);\n return null;\n }\n }, [stableParameters, format, maxParametersSize]);\n\n const start = useCallback(() => {\n if (payload === null) {\n setError(\"Failed to serialize query parameters\");\n return;\n }\n\n abortControllerRef.current?.abort();\n\n setLoading(true);\n setError(null);\n setErrorCode(null);\n setData(null);\n setWarehouseStatus(null);\n publishWarehouseStatus(null);\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n // ARROW_STREAM: the server streams raw Arrow IPC bytes back on the query\n // response body (no SSE). Fetch and decode directly.\n if (format === \"ARROW_STREAM\") {\n void fetchArrowDirect({\n url: urlSuffix,\n payload,\n signal: abortController.signal,\n setLoading,\n setError,\n setErrorCode,\n setData: (table) => setData(table as ResultType),\n unpublishWarehouseStatus,\n });\n return;\n }\n\n const sseContext: AnalyticsQuerySseContext<ResultType> = {\n setLoading,\n setError,\n setErrorCode,\n setData,\n setWarehouseStatus,\n publishWarehouseStatus,\n unpublishWarehouseStatus,\n };\n\n connectSSE({\n url: urlSuffix,\n payload,\n signal: abortController.signal,\n onMessage: async (message) => {\n // Drop late envelopes from a stream whose controller was already\n // aborted (React StrictMode unmount→remount). Mirrors onError below.\n if (abortController.signal.aborted) return;\n try {\n const parsed = JSON.parse(message.data) as Record<string, unknown>;\n await handleAnalyticsSseMessage(parsed, sseContext);\n } catch (error) {\n // A `JSON.parse` failure (or any other thrown error inside the\n // SSE message handler) used to leave the hook permanently in\n // `loading=true` with no error surfaced — the UI would just\n // spin forever. Clear loading and report a user-facing error\n // so the consumer can render a retry affordance.\n //\n // We also abort the SSE connection: if the upstream is\n // emitting un-parseable frames, leaving the stream open just\n // re-fires the same failure on the next message. Closing\n // forces the consumer into a clean retry path.\n console.warn(\"[useAnalyticsQuery] Malformed message received\", error);\n setLoading(false);\n setError(GENERIC_LOAD_ERROR);\n abortController.abort();\n }\n },\n onError: (error) => {\n if (abortController.signal.aborted) return;\n setLoading(false);\n unpublishWarehouseStatus();\n\n if (error instanceof Error) {\n console.error(\"[useAnalyticsQuery] Error\", {\n queryKey,\n error: error.message,\n stack: error.stack,\n });\n }\n setError(userFacingFetchError(error));\n },\n });\n }, [\n queryKey,\n payload,\n urlSuffix,\n format,\n publishWarehouseStatus,\n unpublishWarehouseStatus,\n ]);\n\n useEffect(() => {\n if (autoStart) {\n start();\n }\n\n return () => {\n abortControllerRef.current?.abort();\n unpublishWarehouseStatus();\n };\n }, [start, autoStart, unpublishWarehouseStatus]);\n\n useQueryHMR(queryKey, start);\n\n return { data, loading, error, errorCode, warehouseStatus };\n}\n"],"mappings":"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"}
@@ -9,8 +9,8 @@ interface UseChartDataOptions {
9
9
  parameters?: Record<string, unknown>;
10
10
  /**
11
11
  * Data format preference
12
- * - "json": Force JSON format
13
- * - "arrow": Force Arrow format
12
+ * - "json_array": Force JSON format
13
+ * - "arrow_stream": Force Arrow format
14
14
  * - "auto": Auto-select based on heuristics
15
15
  * @default "auto"
16
16
  */
@@ -52,7 +52,7 @@ interface UseChartDataResult {
52
52
  * // Force Arrow format
53
53
  * const { data } = useChartData({
54
54
  * queryKey: "big_query",
55
- * format: "arrow"
55
+ * format: "arrow_stream"
56
56
  * });
57
57
  * ```
58
58
  */
@@ -1 +1 @@
1
- {"version":3,"file":"use-chart-data.d.ts","names":[],"sources":["../../../src/react/hooks/use-chart-data.ts"],"mappings":";;;;UAaiB,mBAAA;;EAEf,QAAA;EAFkC;EAIlC,UAAA,GAAa,MAAA;EAAA;;;;;;;EAQb,MAAA,GAAS,UAAA;EARI;EAUb,WAAA,OAAkB,IAAA,EAAM,CAAA,KAAM,CAAA;AAAA;AAAA,UAGf,kBAAA;EAHA;EAKf,IAAA,EAAM,SAAA;EALY;EAOlB,OAAA;EAP+B;EAS/B,OAAA;EANe;EAQf,KAAA;;EAEA,OAAA;EARA;;;;;;EAeA,eAAA,EAAiB,eAAA;AAAA;;;AAgEnB;;;;;;;;;;;;;;;;;iBAAgB,YAAA,CAAa,OAAA,EAAS,mBAAA,GAAsB,kBAAA"}
1
+ {"version":3,"file":"use-chart-data.d.ts","names":[],"sources":["../../../src/react/hooks/use-chart-data.ts"],"mappings":";;;;UAaiB,mBAAA;;EAEf,QAAA;EAFkC;EAIlC,UAAA,GAAa,MAAA;EAAA;;;;;;;EAQb,MAAA,GAAS,UAAA;EARI;EAUb,WAAA,OAAkB,IAAA,EAAM,CAAA,KAAM,CAAA;AAAA;AAAA,UAGf,kBAAA;EAHA;EAKf,IAAA,EAAM,SAAA;EALY;EAOlB,OAAA;EAP+B;EAS/B,OAAA;EANe;EAQf,KAAA;;EAEA,OAAA;EARA;;;;;;EAeA,eAAA,EAAiB,eAAA;AAAA;;;AAiEnB;;;;;;;;;;;;;;;;;iBAAgB,YAAA,CAAa,OAAA,EAAS,mBAAA,GAAsB,kBAAA"}
@@ -8,8 +8,8 @@ const ARROW_THRESHOLD = 500;
8
8
  * Resolves the data format based on hints and preferences
9
9
  */
10
10
  function resolveFormat(format, parameters) {
11
- if (format === "json") return "JSON_ARRAY";
12
- if (format === "arrow") return "ARROW_STREAM";
11
+ if (format === "json_array" || format === "json") return "JSON_ARRAY";
12
+ if (format === "arrow_stream" || format === "arrow") return "ARROW_STREAM";
13
13
  if (format === "auto") {
14
14
  if (parameters?._preferArrow === true) return "ARROW_STREAM";
15
15
  if (parameters?._preferJson === true) return "JSON_ARRAY";
@@ -35,7 +35,7 @@ function resolveFormat(format, parameters) {
35
35
  * // Force Arrow format
36
36
  * const { data } = useChartData({
37
37
  * queryKey: "big_query",
38
- * format: "arrow"
38
+ * format: "arrow_stream"
39
39
  * });
40
40
  * ```
41
41
  */
@@ -1 +1 @@
1
- {"version":3,"file":"use-chart-data.js","names":[],"sources":["../../../src/react/hooks/use-chart-data.ts"],"sourcesContent":["import type { Table } from \"apache-arrow\";\nimport { useMemo } from \"react\";\nimport type { ChartData, DataFormat } from \"../charts/types\";\nimport type { WarehouseStatus } from \"./types\";\nimport { useAnalyticsQuery } from \"./use-analytics-query\";\n\n/** Threshold for auto-selecting Arrow format (row count hint) */\nconst ARROW_THRESHOLD = 500;\n\n// ============================================================================\n// Hook Options & Result Types\n// ============================================================================\n\nexport interface UseChartDataOptions {\n /** Analytics query key */\n queryKey: string;\n /** Query parameters */\n parameters?: Record<string, unknown>;\n /**\n * Data format preference\n * - \"json\": Force JSON format\n * - \"arrow\": Force Arrow format\n * - \"auto\": Auto-select based on heuristics\n * @default \"auto\"\n */\n format?: DataFormat;\n /** Transform data after fetching */\n transformer?: <T>(data: T) => T;\n}\n\nexport interface UseChartDataResult {\n /** The fetched data (Arrow Table or JSON array) */\n data: ChartData | null;\n /** Whether the data is in Arrow format */\n isArrow: boolean;\n /** Loading state */\n loading: boolean;\n /** Error message if any */\n error: string | null;\n /** Whether the data is empty */\n isEmpty: boolean;\n /**\n * Latest warehouse readiness status from SSE. Retains the last value\n * (including `RUNNING`) until the next `start()`; `null` only before\n * the first event. Use with `loading` to distinguish warehouse warm-up\n * from in-flight SQL fetch.\n */\n warehouseStatus: WarehouseStatus | null;\n}\n\n// ============================================================================\n// Format Resolution\n// ============================================================================\n\n/**\n * Resolves the data format based on hints and preferences\n */\nfunction resolveFormat(\n format: DataFormat,\n parameters?: Record<string, unknown>,\n): \"JSON_ARRAY\" | \"ARROW_STREAM\" {\n // Explicit format selection\n if (format === \"json\") return \"JSON_ARRAY\";\n if (format === \"arrow\") return \"ARROW_STREAM\";\n\n // Auto-selection heuristics\n if (format === \"auto\") {\n // Check for explicit hint in parameters\n if (parameters?._preferArrow === true) return \"ARROW_STREAM\";\n if (parameters?._preferJson === true) return \"JSON_ARRAY\";\n\n // Check limit parameter as data size hint\n const limit = parameters?.limit;\n if (typeof limit === \"number\" && limit > ARROW_THRESHOLD) {\n return \"ARROW_STREAM\";\n }\n\n // Check for date range queries (often large)\n if (parameters?.startDate && parameters?.endDate) {\n return \"ARROW_STREAM\";\n }\n\n return \"JSON_ARRAY\";\n }\n\n return \"JSON_ARRAY\";\n}\n\n// ============================================================================\n// Main Hook\n// ============================================================================\n\n/**\n * Hook for fetching chart data in either JSON or Arrow format.\n * Automatically selects the best format based on query hints.\n *\n * @example\n * ```tsx\n * // Auto-select format\n * const { data, isArrow, loading } = useChartData({\n * queryKey: \"spend_data\",\n * parameters: { limit: 1000 }\n * });\n *\n * // Force Arrow format\n * const { data } = useChartData({\n * queryKey: \"big_query\",\n * format: \"arrow\"\n * });\n * ```\n */\nexport function useChartData(options: UseChartDataOptions): UseChartDataResult {\n const { queryKey, parameters, format = \"auto\", transformer } = options;\n\n // Resolve the format to use\n const resolvedFormat = useMemo(\n () => resolveFormat(format, parameters),\n [format, parameters],\n );\n\n const isArrowFormat = resolvedFormat === \"ARROW_STREAM\";\n\n // Fetch data using the analytics query hook\n const {\n data: rawData,\n loading,\n error,\n warehouseStatus,\n } = useAnalyticsQuery(queryKey, parameters, {\n autoStart: true,\n format: resolvedFormat,\n });\n\n // Process and transform data\n const processedData = useMemo(() => {\n if (!rawData) return null;\n\n // Apply transformer if provided\n if (transformer) {\n try {\n return transformer(rawData);\n } catch (err) {\n console.error(\"[useChartData] Transformer error:\", err);\n return rawData;\n }\n }\n\n return rawData;\n }, [rawData, transformer]);\n\n // Determine if data is empty\n const isEmpty = useMemo(() => {\n if (!processedData) return true;\n\n // Arrow Table - check using duck typing\n if (\n typeof processedData === \"object\" &&\n \"numRows\" in processedData &&\n typeof (processedData as Table).numRows === \"number\"\n ) {\n return (processedData as Table).numRows === 0;\n }\n\n // JSON Array\n if (Array.isArray(processedData)) {\n return processedData.length === 0;\n }\n\n return true;\n }, [processedData]);\n\n // Detect actual data type (may differ from requested if server doesn't support format)\n const isArrow = useMemo(() => {\n if (!processedData) return isArrowFormat;\n // Duck type check for Arrow Table\n return (\n typeof processedData === \"object\" &&\n processedData !== null &&\n \"schema\" in processedData &&\n \"numRows\" in processedData &&\n typeof (processedData as Table).getChild === \"function\"\n );\n }, [processedData, isArrowFormat]);\n\n return {\n data: processedData as ChartData | null,\n isArrow,\n loading,\n error,\n isEmpty,\n warehouseStatus,\n };\n}\n"],"mappings":";;;;;AAOA,MAAM,kBAAkB;;;;AAkDxB,SAAS,cACP,QACA,YAC+B;AAE/B,KAAI,WAAW,OAAQ,QAAO;AAC9B,KAAI,WAAW,QAAS,QAAO;AAG/B,KAAI,WAAW,QAAQ;AAErB,MAAI,YAAY,iBAAiB,KAAM,QAAO;AAC9C,MAAI,YAAY,gBAAgB,KAAM,QAAO;EAG7C,MAAM,QAAQ,YAAY;AAC1B,MAAI,OAAO,UAAU,YAAY,QAAQ,gBACvC,QAAO;AAIT,MAAI,YAAY,aAAa,YAAY,QACvC,QAAO;AAGT,SAAO;;AAGT,QAAO;;;;;;;;;;;;;;;;;;;;;AA0BT,SAAgB,aAAa,SAAkD;CAC7E,MAAM,EAAE,UAAU,YAAY,SAAS,QAAQ,gBAAgB;CAG/D,MAAM,iBAAiB,cACf,cAAc,QAAQ,WAAW,EACvC,CAAC,QAAQ,WAAW,CACrB;CAED,MAAM,gBAAgB,mBAAmB;CAGzC,MAAM,EACJ,MAAM,SACN,SACA,OACA,oBACE,kBAAkB,UAAU,YAAY;EAC1C,WAAW;EACX,QAAQ;EACT,CAAC;CAGF,MAAM,gBAAgB,cAAc;AAClC,MAAI,CAAC,QAAS,QAAO;AAGrB,MAAI,YACF,KAAI;AACF,UAAO,YAAY,QAAQ;WACpB,KAAK;AACZ,WAAQ,MAAM,qCAAqC,IAAI;AACvD,UAAO;;AAIX,SAAO;IACN,CAAC,SAAS,YAAY,CAAC;CAG1B,MAAM,UAAU,cAAc;AAC5B,MAAI,CAAC,cAAe,QAAO;AAG3B,MACE,OAAO,kBAAkB,YACzB,aAAa,iBACb,OAAQ,cAAwB,YAAY,SAE5C,QAAQ,cAAwB,YAAY;AAI9C,MAAI,MAAM,QAAQ,cAAc,CAC9B,QAAO,cAAc,WAAW;AAGlC,SAAO;IACN,CAAC,cAAc,CAAC;AAenB,QAAO;EACL,MAAM;EACN,SAdc,cAAc;AAC5B,OAAI,CAAC,cAAe,QAAO;AAE3B,UACE,OAAO,kBAAkB,YACzB,kBAAkB,QAClB,YAAY,iBACZ,aAAa,iBACb,OAAQ,cAAwB,aAAa;KAE9C,CAAC,eAAe,cAAc,CAAC;EAKhC;EACA;EACA;EACA;EACD"}
1
+ {"version":3,"file":"use-chart-data.js","names":[],"sources":["../../../src/react/hooks/use-chart-data.ts"],"sourcesContent":["import type { Table } from \"apache-arrow\";\nimport { useMemo } from \"react\";\nimport type { ChartData, DataFormat } from \"../charts/types\";\nimport type { WarehouseStatus } from \"./types\";\nimport { useAnalyticsQuery } from \"./use-analytics-query\";\n\n/** Threshold for auto-selecting Arrow format (row count hint) */\nconst ARROW_THRESHOLD = 500;\n\n// ============================================================================\n// Hook Options & Result Types\n// ============================================================================\n\nexport interface UseChartDataOptions {\n /** Analytics query key */\n queryKey: string;\n /** Query parameters */\n parameters?: Record<string, unknown>;\n /**\n * Data format preference\n * - \"json_array\": Force JSON format\n * - \"arrow_stream\": Force Arrow format\n * - \"auto\": Auto-select based on heuristics\n * @default \"auto\"\n */\n format?: DataFormat;\n /** Transform data after fetching */\n transformer?: <T>(data: T) => T;\n}\n\nexport interface UseChartDataResult {\n /** The fetched data (Arrow Table or JSON array) */\n data: ChartData | null;\n /** Whether the data is in Arrow format */\n isArrow: boolean;\n /** Loading state */\n loading: boolean;\n /** Error message if any */\n error: string | null;\n /** Whether the data is empty */\n isEmpty: boolean;\n /**\n * Latest warehouse readiness status from SSE. Retains the last value\n * (including `RUNNING`) until the next `start()`; `null` only before\n * the first event. Use with `loading` to distinguish warehouse warm-up\n * from in-flight SQL fetch.\n */\n warehouseStatus: WarehouseStatus | null;\n}\n\n// ============================================================================\n// Format Resolution\n// ============================================================================\n\n/**\n * Resolves the data format based on hints and preferences\n */\nfunction resolveFormat(\n format: DataFormat,\n parameters?: Record<string, unknown>,\n): \"JSON_ARRAY\" | \"ARROW_STREAM\" {\n // Explicit format selection (legacy \"json\"/\"arrow\" accepted for back-compat\n // with appkit-ui < 0.33.0 — see DataFormat in ../charts/types.ts).\n if (format === \"json_array\" || format === \"json\") return \"JSON_ARRAY\";\n if (format === \"arrow_stream\" || format === \"arrow\") return \"ARROW_STREAM\";\n\n // Auto-selection heuristics\n if (format === \"auto\") {\n // Check for explicit hint in parameters\n if (parameters?._preferArrow === true) return \"ARROW_STREAM\";\n if (parameters?._preferJson === true) return \"JSON_ARRAY\";\n\n // Check limit parameter as data size hint\n const limit = parameters?.limit;\n if (typeof limit === \"number\" && limit > ARROW_THRESHOLD) {\n return \"ARROW_STREAM\";\n }\n\n // Check for date range queries (often large)\n if (parameters?.startDate && parameters?.endDate) {\n return \"ARROW_STREAM\";\n }\n\n return \"JSON_ARRAY\";\n }\n\n return \"JSON_ARRAY\";\n}\n\n// ============================================================================\n// Main Hook\n// ============================================================================\n\n/**\n * Hook for fetching chart data in either JSON or Arrow format.\n * Automatically selects the best format based on query hints.\n *\n * @example\n * ```tsx\n * // Auto-select format\n * const { data, isArrow, loading } = useChartData({\n * queryKey: \"spend_data\",\n * parameters: { limit: 1000 }\n * });\n *\n * // Force Arrow format\n * const { data } = useChartData({\n * queryKey: \"big_query\",\n * format: \"arrow_stream\"\n * });\n * ```\n */\nexport function useChartData(options: UseChartDataOptions): UseChartDataResult {\n const { queryKey, parameters, format = \"auto\", transformer } = options;\n\n // Resolve the format to use\n const resolvedFormat = useMemo(\n () => resolveFormat(format, parameters),\n [format, parameters],\n );\n\n const isArrowFormat = resolvedFormat === \"ARROW_STREAM\";\n\n // Fetch data using the analytics query hook\n const {\n data: rawData,\n loading,\n error,\n warehouseStatus,\n } = useAnalyticsQuery(queryKey, parameters, {\n autoStart: true,\n format: resolvedFormat,\n });\n\n // Process and transform data\n const processedData = useMemo(() => {\n if (!rawData) return null;\n\n // Apply transformer if provided\n if (transformer) {\n try {\n return transformer(rawData);\n } catch (err) {\n console.error(\"[useChartData] Transformer error:\", err);\n return rawData;\n }\n }\n\n return rawData;\n }, [rawData, transformer]);\n\n // Determine if data is empty\n const isEmpty = useMemo(() => {\n if (!processedData) return true;\n\n // Arrow Table - check using duck typing\n if (\n typeof processedData === \"object\" &&\n \"numRows\" in processedData &&\n typeof (processedData as Table).numRows === \"number\"\n ) {\n return (processedData as Table).numRows === 0;\n }\n\n // JSON Array\n if (Array.isArray(processedData)) {\n return processedData.length === 0;\n }\n\n return true;\n }, [processedData]);\n\n // Detect actual data type (may differ from requested if server doesn't support format)\n const isArrow = useMemo(() => {\n if (!processedData) return isArrowFormat;\n // Duck type check for Arrow Table\n return (\n typeof processedData === \"object\" &&\n processedData !== null &&\n \"schema\" in processedData &&\n \"numRows\" in processedData &&\n typeof (processedData as Table).getChild === \"function\"\n );\n }, [processedData, isArrowFormat]);\n\n return {\n data: processedData as ChartData | null,\n isArrow,\n loading,\n error,\n isEmpty,\n warehouseStatus,\n };\n}\n"],"mappings":";;;;;AAOA,MAAM,kBAAkB;;;;AAkDxB,SAAS,cACP,QACA,YAC+B;AAG/B,KAAI,WAAW,gBAAgB,WAAW,OAAQ,QAAO;AACzD,KAAI,WAAW,kBAAkB,WAAW,QAAS,QAAO;AAG5D,KAAI,WAAW,QAAQ;AAErB,MAAI,YAAY,iBAAiB,KAAM,QAAO;AAC9C,MAAI,YAAY,gBAAgB,KAAM,QAAO;EAG7C,MAAM,QAAQ,YAAY;AAC1B,MAAI,OAAO,UAAU,YAAY,QAAQ,gBACvC,QAAO;AAIT,MAAI,YAAY,aAAa,YAAY,QACvC,QAAO;AAGT,SAAO;;AAGT,QAAO;;;;;;;;;;;;;;;;;;;;;AA0BT,SAAgB,aAAa,SAAkD;CAC7E,MAAM,EAAE,UAAU,YAAY,SAAS,QAAQ,gBAAgB;CAG/D,MAAM,iBAAiB,cACf,cAAc,QAAQ,WAAW,EACvC,CAAC,QAAQ,WAAW,CACrB;CAED,MAAM,gBAAgB,mBAAmB;CAGzC,MAAM,EACJ,MAAM,SACN,SACA,OACA,oBACE,kBAAkB,UAAU,YAAY;EAC1C,WAAW;EACX,QAAQ;EACT,CAAC;CAGF,MAAM,gBAAgB,cAAc;AAClC,MAAI,CAAC,QAAS,QAAO;AAGrB,MAAI,YACF,KAAI;AACF,UAAO,YAAY,QAAQ;WACpB,KAAK;AACZ,WAAQ,MAAM,qCAAqC,IAAI;AACvD,UAAO;;AAIX,SAAO;IACN,CAAC,SAAS,YAAY,CAAC;CAG1B,MAAM,UAAU,cAAc;AAC5B,MAAI,CAAC,cAAe,QAAO;AAG3B,MACE,OAAO,kBAAkB,YACzB,aAAa,iBACb,OAAQ,cAAwB,YAAY,SAE5C,QAAQ,cAAwB,YAAY;AAI9C,MAAI,MAAM,QAAQ,cAAc,CAC9B,QAAO,cAAc,WAAW;AAGlC,SAAO;IACN,CAAC,cAAc,CAAC;AAenB,QAAO;EACL,MAAM;EACN,SAdc,cAAc;AAC5B,OAAI,CAAC,cAAe,QAAO;AAE3B,UACE,OAAO,kBAAkB,YACzB,kBAAkB,QAClB,YAAY,iBACZ,aAAa,iBACb,OAAQ,cAAwB,aAAa;KAE9C,CAAC,eAAe,cAAc,CAAC;EAKhC;EACA;EACA;EACA;EACD"}
@@ -5,4 +5,5 @@ import "./execute.js";
5
5
  import { GenieAttachmentResponse, GenieMessageResponse, GenieStatementResponse, GenieStreamEvent } from "./genie.js";
6
6
  import { SQLBinaryMarker, SQLBooleanMarker, SQLDateMarker, SQLNumberMarker, SQLStringMarker, SQLTimestampMarker, SQLTypeMarker } from "./sql/types.js";
7
7
  import { isSQLTypeMarker, sql } from "./sql/helpers.js";
8
+ import "./sse/analytics.js";
8
9
  import "./tunnel.js";
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../../../shared/src/plugin.ts"],"mappings":";;;;;;KA+QY,iBAAA,GAAoB,MAAA;;KAGpB,eAAA,GAAkB,MAAA,SAAe,iBAAA;;KAGjC,mBAAA,GAAsB,MAAA,SAAe,MAAA"}
1
+ {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../../../shared/src/plugin.ts"],"mappings":";;;;;;KAgSY,iBAAA,GAAoB,MAAA;;KAGpB,eAAA,GAAkB,MAAA,SAAe,iBAAA;;KAGjC,mBAAA,GAAsB,MAAA,SAAe,MAAA"}
@@ -0,0 +1 @@
1
+ import "zod";
@@ -43,6 +43,7 @@ console.error(error.toJSON()); // Safe for logging, sensitive values redacted
43
43
  ```ts
44
44
  new AppKitError(message: string, options?: {
45
45
  cause?: Error;
46
+ clientMessage?: string;
46
47
  context?: Record<string, unknown>;
47
48
  }): AppKitError;
48
49
 
@@ -50,12 +51,13 @@ new AppKitError(message: string, options?: {
50
51
 
51
52
  #### Parameters[​](#parameters "Direct link to Parameters")
52
53
 
53
- | Parameter | Type |
54
- | ------------------ | ----------------------------------------------------------------- |
55
- | `message` | `string` |
56
- | `options?` | { `cause?`: `Error`; `context?`: `Record`<`string`, `unknown`>; } |
57
- | `options.cause?` | `Error` |
58
- | `options.context?` | `Record`<`string`, `unknown`> |
54
+ | Parameter | Type |
55
+ | ------------------------ | --------------------------------------------------------------------------------------------- |
56
+ | `message` | `string` |
57
+ | `options?` | { `cause?`: `Error`; `clientMessage?`: `string`; `context?`: `Record`<`string`, `unknown`>; } |
58
+ | `options.cause?` | `Error` |
59
+ | `options.clientMessage?` | `string` |
60
+ | `options.context?` | `Record`<`string`, `unknown`> |
59
61
 
60
62
  #### Returns[​](#returns "Direct link to Returns")
61
63
 
@@ -70,6 +72,19 @@ Error.constructor
70
72
 
71
73
  ## Properties[​](#properties "Direct link to Properties")
72
74
 
75
+ ### \_clientMessage?[​](#_clientmessage "Direct link to _clientMessage?")
76
+
77
+ ```ts
78
+ protected readonly optional _clientMessage: string;
79
+
80
+ ```
81
+
82
+ Client-safe error message. When set, callers serializing the error to a client (SSE, HTTP body) MUST prefer `clientMessage` over `message` — `message` may contain raw upstream / SDK text including statement fragments, internal object names, and correlation IDs.
83
+
84
+ Subclasses can set this in their constructor for a fixed sanitized string. When unset, `clientMessage` defaults to a generic per-code string (see the getter), and the raw `message` is kept server-side only.
85
+
86
+ ***
87
+
73
88
  ### cause?[​](#cause "Direct link to cause?")
74
89
 
75
90
  ```ts
@@ -130,6 +145,23 @@ abstract readonly statusCode: number;
130
145
 
131
146
  HTTP status code suggestion (can be overridden)
132
147
 
148
+ ## Accessors[​](#accessors "Direct link to Accessors")
149
+
150
+ ### clientMessage[​](#clientmessage "Direct link to clientMessage")
151
+
152
+ #### Get Signature[​](#get-signature "Direct link to Get Signature")
153
+
154
+ ```ts
155
+ get clientMessage(): string;
156
+
157
+ ```
158
+
159
+ Sanitized message safe to forward to clients. Override in subclasses if a more specific default is appropriate.
160
+
161
+ ##### Returns[​](#returns-1 "Direct link to Returns")
162
+
163
+ `string`
164
+
133
165
  ## Methods[​](#methods "Direct link to Methods")
134
166
 
135
167
  ### toJSON()[​](#tojson "Direct link to toJSON()")
@@ -141,7 +173,7 @@ toJSON(): Record<string, unknown>;
141
173
 
142
174
  Convert error to JSON for logging/serialization. Sensitive values in context are automatically redacted.
143
175
 
144
- #### Returns[​](#returns-1 "Direct link to Returns")
176
+ #### Returns[​](#returns-2 "Direct link to Returns")
145
177
 
146
178
  `Record`<`string`, `unknown`>
147
179
 
@@ -156,6 +188,6 @@ toString(): string;
156
188
 
157
189
  Create a human-readable string representation
158
190
 
159
- #### Returns[​](#returns-2 "Direct link to Returns")
191
+ #### Returns[​](#returns-3 "Direct link to Returns")
160
192
 
161
193
  `string`
@@ -21,6 +21,7 @@ throw new AuthenticationError("Failed to generate credentials", { cause: origina
21
21
  ```ts
22
22
  new AuthenticationError(message: string, options?: {
23
23
  cause?: Error;
24
+ clientMessage?: string;
24
25
  context?: Record<string, unknown>;
25
26
  }): AuthenticationError;
26
27
 
@@ -28,12 +29,13 @@ new AuthenticationError(message: string, options?: {
28
29
 
29
30
  #### Parameters[​](#parameters "Direct link to Parameters")
30
31
 
31
- | Parameter | Type |
32
- | ------------------ | ----------------------------------------------------------------- |
33
- | `message` | `string` |
34
- | `options?` | { `cause?`: `Error`; `context?`: `Record`<`string`, `unknown`>; } |
35
- | `options.cause?` | `Error` |
36
- | `options.context?` | `Record`<`string`, `unknown`> |
32
+ | Parameter | Type |
33
+ | ------------------------ | --------------------------------------------------------------------------------------------- |
34
+ | `message` | `string` |
35
+ | `options?` | { `cause?`: `Error`; `clientMessage?`: `string`; `context?`: `Record`<`string`, `unknown`>; } |
36
+ | `options.cause?` | `Error` |
37
+ | `options.clientMessage?` | `string` |
38
+ | `options.context?` | `Record`<`string`, `unknown`> |
37
39
 
38
40
  #### Returns[​](#returns "Direct link to Returns")
39
41
 
@@ -45,6 +47,23 @@ new AuthenticationError(message: string, options?: {
45
47
 
46
48
  ## Properties[​](#properties "Direct link to Properties")
47
49
 
50
+ ### \_clientMessage?[​](#_clientmessage "Direct link to _clientMessage?")
51
+
52
+ ```ts
53
+ protected readonly optional _clientMessage: string;
54
+
55
+ ```
56
+
57
+ Client-safe error message. When set, callers serializing the error to a client (SSE, HTTP body) MUST prefer `clientMessage` over `message` — `message` may contain raw upstream / SDK text including statement fragments, internal object names, and correlation IDs.
58
+
59
+ Subclasses can set this in their constructor for a fixed sanitized string. When unset, `clientMessage` defaults to a generic per-code string (see the getter), and the raw `message` is kept server-side only.
60
+
61
+ #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from")
62
+
63
+ [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`_clientMessage`](./docs/api/appkit/Class.AppKitError.md#_clientmessage)
64
+
65
+ ***
66
+
48
67
  ### cause?[​](#cause "Direct link to cause?")
49
68
 
50
69
  ```ts
@@ -54,7 +73,7 @@ readonly optional cause: Error;
54
73
 
55
74
  Optional cause of the error
56
75
 
57
- #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from")
76
+ #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from")
58
77
 
59
78
  [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`cause`](./docs/api/appkit/Class.AppKitError.md#cause)
60
79
 
@@ -84,7 +103,7 @@ readonly optional context: Record<string, unknown>;
84
103
 
85
104
  Additional context for the error
86
105
 
87
- #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from")
106
+ #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from")
88
107
 
89
108
  [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`context`](./docs/api/appkit/Class.AppKitError.md#context)
90
109
 
@@ -118,6 +137,27 @@ HTTP status code suggestion (can be overridden)
118
137
 
119
138
  [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`statusCode`](./docs/api/appkit/Class.AppKitError.md#statuscode)
120
139
 
140
+ ## Accessors[​](#accessors "Direct link to Accessors")
141
+
142
+ ### clientMessage[​](#clientmessage "Direct link to clientMessage")
143
+
144
+ #### Get Signature[​](#get-signature "Direct link to Get Signature")
145
+
146
+ ```ts
147
+ get clientMessage(): string;
148
+
149
+ ```
150
+
151
+ Sanitized message safe to forward to clients. Override in subclasses if a more specific default is appropriate.
152
+
153
+ ##### Returns[​](#returns-1 "Direct link to Returns")
154
+
155
+ `string`
156
+
157
+ #### Inherited from[​](#inherited-from-4 "Direct link to Inherited from")
158
+
159
+ [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`clientMessage`](./docs/api/appkit/Class.AppKitError.md#clientmessage)
160
+
121
161
  ## Methods[​](#methods "Direct link to Methods")
122
162
 
123
163
  ### toJSON()[​](#tojson "Direct link to toJSON()")
@@ -129,11 +169,11 @@ toJSON(): Record<string, unknown>;
129
169
 
130
170
  Convert error to JSON for logging/serialization. Sensitive values in context are automatically redacted.
131
171
 
132
- #### Returns[​](#returns-1 "Direct link to Returns")
172
+ #### Returns[​](#returns-2 "Direct link to Returns")
133
173
 
134
174
  `Record`<`string`, `unknown`>
135
175
 
136
- #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from")
176
+ #### Inherited from[​](#inherited-from-5 "Direct link to Inherited from")
137
177
 
138
178
  [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`toJSON`](./docs/api/appkit/Class.AppKitError.md#tojson)
139
179
 
@@ -148,11 +188,11 @@ toString(): string;
148
188
 
149
189
  Create a human-readable string representation
150
190
 
151
- #### Returns[​](#returns-2 "Direct link to Returns")
191
+ #### Returns[​](#returns-3 "Direct link to Returns")
152
192
 
153
193
  `string`
154
194
 
155
- #### Inherited from[​](#inherited-from-4 "Direct link to Inherited from")
195
+ #### Inherited from[​](#inherited-from-6 "Direct link to Inherited from")
156
196
 
157
197
  [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`toString`](./docs/api/appkit/Class.AppKitError.md#tostring)
158
198
 
@@ -174,7 +214,7 @@ Create an authentication error for credential generation failure
174
214
  | `instance` | `string` |
175
215
  | `cause?` | `Error` |
176
216
 
177
- #### Returns[​](#returns-3 "Direct link to Returns")
217
+ #### Returns[​](#returns-4 "Direct link to Returns")
178
218
 
179
219
  `AuthenticationError`
180
220
 
@@ -195,7 +235,7 @@ Create an authentication error for missing token
195
235
  | ----------- | -------- | ---------------- |
196
236
  | `tokenType` | `string` | `"access token"` |
197
237
 
198
- #### Returns[​](#returns-4 "Direct link to Returns")
238
+ #### Returns[​](#returns-5 "Direct link to Returns")
199
239
 
200
240
  `AuthenticationError`
201
241
 
@@ -210,7 +250,7 @@ static missingUserId(): AuthenticationError;
210
250
 
211
251
  Create an authentication error for missing user identity
212
252
 
213
- #### Returns[​](#returns-5 "Direct link to Returns")
253
+ #### Returns[​](#returns-6 "Direct link to Returns")
214
254
 
215
255
  `AuthenticationError`
216
256
 
@@ -231,6 +271,6 @@ Create an authentication error for failed user lookup
231
271
  | --------- | ------- |
232
272
  | `cause?` | `Error` |
233
273
 
234
- #### Returns[​](#returns-6 "Direct link to Returns")
274
+ #### Returns[​](#returns-7 "Direct link to Returns")
235
275
 
236
276
  `AuthenticationError`
@@ -21,6 +21,7 @@ throw new ConfigurationError("Warehouse ID not found", { context: { env: "produc
21
21
  ```ts
22
22
  new ConfigurationError(message: string, options?: {
23
23
  cause?: Error;
24
+ clientMessage?: string;
24
25
  context?: Record<string, unknown>;
25
26
  }): ConfigurationError;
26
27
 
@@ -28,12 +29,13 @@ new ConfigurationError(message: string, options?: {
28
29
 
29
30
  #### Parameters[​](#parameters "Direct link to Parameters")
30
31
 
31
- | Parameter | Type |
32
- | ------------------ | ----------------------------------------------------------------- |
33
- | `message` | `string` |
34
- | `options?` | { `cause?`: `Error`; `context?`: `Record`<`string`, `unknown`>; } |
35
- | `options.cause?` | `Error` |
36
- | `options.context?` | `Record`<`string`, `unknown`> |
32
+ | Parameter | Type |
33
+ | ------------------------ | --------------------------------------------------------------------------------------------- |
34
+ | `message` | `string` |
35
+ | `options?` | { `cause?`: `Error`; `clientMessage?`: `string`; `context?`: `Record`<`string`, `unknown`>; } |
36
+ | `options.cause?` | `Error` |
37
+ | `options.clientMessage?` | `string` |
38
+ | `options.context?` | `Record`<`string`, `unknown`> |
37
39
 
38
40
  #### Returns[​](#returns "Direct link to Returns")
39
41
 
@@ -45,6 +47,23 @@ new ConfigurationError(message: string, options?: {
45
47
 
46
48
  ## Properties[​](#properties "Direct link to Properties")
47
49
 
50
+ ### \_clientMessage?[​](#_clientmessage "Direct link to _clientMessage?")
51
+
52
+ ```ts
53
+ protected readonly optional _clientMessage: string;
54
+
55
+ ```
56
+
57
+ Client-safe error message. When set, callers serializing the error to a client (SSE, HTTP body) MUST prefer `clientMessage` over `message` — `message` may contain raw upstream / SDK text including statement fragments, internal object names, and correlation IDs.
58
+
59
+ Subclasses can set this in their constructor for a fixed sanitized string. When unset, `clientMessage` defaults to a generic per-code string (see the getter), and the raw `message` is kept server-side only.
60
+
61
+ #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from")
62
+
63
+ [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`_clientMessage`](./docs/api/appkit/Class.AppKitError.md#_clientmessage)
64
+
65
+ ***
66
+
48
67
  ### cause?[​](#cause "Direct link to cause?")
49
68
 
50
69
  ```ts
@@ -54,7 +73,7 @@ readonly optional cause: Error;
54
73
 
55
74
  Optional cause of the error
56
75
 
57
- #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from")
76
+ #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from")
58
77
 
59
78
  [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`cause`](./docs/api/appkit/Class.AppKitError.md#cause)
60
79
 
@@ -84,7 +103,7 @@ readonly optional context: Record<string, unknown>;
84
103
 
85
104
  Additional context for the error
86
105
 
87
- #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from")
106
+ #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from")
88
107
 
89
108
  [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`context`](./docs/api/appkit/Class.AppKitError.md#context)
90
109
 
@@ -118,6 +137,27 @@ HTTP status code suggestion (can be overridden)
118
137
 
119
138
  [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`statusCode`](./docs/api/appkit/Class.AppKitError.md#statuscode)
120
139
 
140
+ ## Accessors[​](#accessors "Direct link to Accessors")
141
+
142
+ ### clientMessage[​](#clientmessage "Direct link to clientMessage")
143
+
144
+ #### Get Signature[​](#get-signature "Direct link to Get Signature")
145
+
146
+ ```ts
147
+ get clientMessage(): string;
148
+
149
+ ```
150
+
151
+ Sanitized message safe to forward to clients. Override in subclasses if a more specific default is appropriate.
152
+
153
+ ##### Returns[​](#returns-1 "Direct link to Returns")
154
+
155
+ `string`
156
+
157
+ #### Inherited from[​](#inherited-from-4 "Direct link to Inherited from")
158
+
159
+ [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`clientMessage`](./docs/api/appkit/Class.AppKitError.md#clientmessage)
160
+
121
161
  ## Methods[​](#methods "Direct link to Methods")
122
162
 
123
163
  ### toJSON()[​](#tojson "Direct link to toJSON()")
@@ -129,11 +169,11 @@ toJSON(): Record<string, unknown>;
129
169
 
130
170
  Convert error to JSON for logging/serialization. Sensitive values in context are automatically redacted.
131
171
 
132
- #### Returns[​](#returns-1 "Direct link to Returns")
172
+ #### Returns[​](#returns-2 "Direct link to Returns")
133
173
 
134
174
  `Record`<`string`, `unknown`>
135
175
 
136
- #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from")
176
+ #### Inherited from[​](#inherited-from-5 "Direct link to Inherited from")
137
177
 
138
178
  [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`toJSON`](./docs/api/appkit/Class.AppKitError.md#tojson)
139
179
 
@@ -148,11 +188,11 @@ toString(): string;
148
188
 
149
189
  Create a human-readable string representation
150
190
 
151
- #### Returns[​](#returns-2 "Direct link to Returns")
191
+ #### Returns[​](#returns-3 "Direct link to Returns")
152
192
 
153
193
  `string`
154
194
 
155
- #### Inherited from[​](#inherited-from-4 "Direct link to Inherited from")
195
+ #### Inherited from[​](#inherited-from-6 "Direct link to Inherited from")
156
196
 
157
197
  [`AppKitError`](./docs/api/appkit/Class.AppKitError.md).[`toString`](./docs/api/appkit/Class.AppKitError.md#tostring)
158
198
 
@@ -179,7 +219,7 @@ By default the message is short; key lines use **picocolors** when the terminal
179
219
  | `options?` | { `cause?`: `Error`; } |
180
220
  | `options.cause?` | `Error` |
181
221
 
182
- #### Returns[​](#returns-3 "Direct link to Returns")
222
+ #### Returns[​](#returns-4 "Direct link to Returns")
183
223
 
184
224
  `ConfigurationError`
185
225
 
@@ -201,7 +241,7 @@ Create a configuration error for invalid connection config
201
241
  | `service` | `string` |
202
242
  | `details?` | `string` |
203
243
 
204
- #### Returns[​](#returns-4 "Direct link to Returns")
244
+ #### Returns[​](#returns-5 "Direct link to Returns")
205
245
 
206
246
  `ConfigurationError`
207
247
 
@@ -222,7 +262,7 @@ Create a configuration error for missing connection string parameter
222
262
  | --------- | -------- |
223
263
  | `param` | `string` |
224
264
 
225
- #### Returns[​](#returns-5 "Direct link to Returns")
265
+ #### Returns[​](#returns-6 "Direct link to Returns")
226
266
 
227
267
  `ConfigurationError`
228
268
 
@@ -243,7 +283,7 @@ Create a configuration error for missing environment variable
243
283
  | --------- | -------- |
244
284
  | `varName` | `string` |
245
285
 
246
- #### Returns[​](#returns-6 "Direct link to Returns")
286
+ #### Returns[​](#returns-7 "Direct link to Returns")
247
287
 
248
288
  `ConfigurationError`
249
289
 
@@ -265,6 +305,6 @@ Create a configuration error for missing resource
265
305
  | `resource` | `string` |
266
306
  | `hint?` | `string` |
267
307
 
268
- #### Returns[​](#returns-7 "Direct link to Returns")
308
+ #### Returns[​](#returns-8 "Direct link to Returns")
269
309
 
270
310
  `ConfigurationError`