@databricks/appkit-ui 0.57.0 → 0.59.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 (55) hide show
  1. package/dist/js/format/index.d.ts +38 -0
  2. package/dist/js/format/index.d.ts.map +1 -0
  3. package/dist/js/format/index.js +160 -0
  4. package/dist/js/format/index.js.map +1 -0
  5. package/dist/js/index.d.ts +5 -1
  6. package/dist/js/index.js +3 -1
  7. package/dist/js/metric-filter/index.d.ts +40 -0
  8. package/dist/js/metric-filter/index.d.ts.map +1 -0
  9. package/dist/js/metric-filter/index.js +56 -0
  10. package/dist/js/metric-filter/index.js.map +1 -0
  11. package/dist/react/charts/base.d.ts +19 -2
  12. package/dist/react/charts/base.d.ts.map +1 -1
  13. package/dist/react/charts/base.js +28 -8
  14. package/dist/react/charts/base.js.map +1 -1
  15. package/dist/react/charts/index.d.ts +4 -3
  16. package/dist/react/charts/normalize.d.ts.map +1 -1
  17. package/dist/react/charts/normalize.js +6 -6
  18. package/dist/react/charts/normalize.js.map +1 -1
  19. package/dist/react/charts/options.d.ts +31 -2
  20. package/dist/react/charts/options.d.ts.map +1 -1
  21. package/dist/react/charts/options.js +150 -13
  22. package/dist/react/charts/options.js.map +1 -1
  23. package/dist/react/charts/types.d.ts +54 -1
  24. package/dist/react/charts/types.d.ts.map +1 -1
  25. package/dist/react/charts/types.js.map +1 -1
  26. package/dist/react/charts/utils.d.ts +8 -9
  27. package/dist/react/charts/utils.d.ts.map +1 -1
  28. package/dist/react/charts/utils.js +135 -28
  29. package/dist/react/charts/utils.js.map +1 -1
  30. package/dist/react/hooks/analytics-sse.js +117 -0
  31. package/dist/react/hooks/analytics-sse.js.map +1 -0
  32. package/dist/react/hooks/index.d.ts +4 -1
  33. package/dist/react/hooks/types.d.ts +90 -4
  34. package/dist/react/hooks/types.d.ts.map +1 -1
  35. package/dist/react/hooks/use-analytics-query.d.ts.map +1 -1
  36. package/dist/react/hooks/use-analytics-query.js +14 -74
  37. package/dist/react/hooks/use-analytics-query.js.map +1 -1
  38. package/dist/react/hooks/use-metric-view.d.ts +39 -0
  39. package/dist/react/hooks/use-metric-view.d.ts.map +1 -0
  40. package/dist/react/hooks/use-metric-view.js +178 -0
  41. package/dist/react/hooks/use-metric-view.js.map +1 -0
  42. package/dist/react/index.d.ts +9 -5
  43. package/dist/react/index.js +5 -3
  44. package/dist/react/lib/format.js +3 -1
  45. package/dist/react/lib/format.js.map +1 -1
  46. package/dist/shared/src/index.d.ts +2 -0
  47. package/dist/shared/src/metric-filter.d.ts +36 -0
  48. package/dist/shared/src/metric-filter.d.ts.map +1 -0
  49. package/dist/shared/src/metric-metadata.d.ts +15 -0
  50. package/dist/shared/src/metric-metadata.d.ts.map +1 -0
  51. package/docs/development/type-generation.md +7 -6
  52. package/docs/plugins/ai-search.md +10 -0
  53. package/docs/plugins/analytics.md +287 -12
  54. package/package.json +1 -1
  55. package/sbom.cdx.json +1 -1
@@ -1,3 +1,7 @@
1
+ import { MetricFilter, MetricFilterOperatorName, MetricOrderBy, MetricOrderDirection, MetricPredicate } from "../../shared/src/metric-filter.js";
2
+ import { MetricViewColumnDisplay } from "../../shared/src/metric-metadata.js";
3
+ import "../../shared/src/index.js";
4
+ import "../../js/index.js";
1
5
  import { Table } from "apache-arrow";
2
6
 
3
7
  //#region src/react/hooks/types.d.ts
@@ -197,11 +201,93 @@ type InferServingResponse<K> = K extends AugmentedRegistry<ServingEndpointRegist
197
201
  type InferServingRequest<K> = K extends AugmentedRegistry<ServingEndpointRegistry> ? ServingEndpointRegistry[K] extends {
198
202
  request: infer Req;
199
203
  } ? Req : Record<string, unknown> : Record<string, unknown>;
204
+ /** Metric view registry for type-safe metric keys */
205
+ interface MetricRegistry {}
206
+ type MetricKey = AugmentedRegistry<MetricRegistry> extends never ? string : AugmentedRegistry<MetricRegistry>;
207
+ type InferMeasureKeys<K> = K extends AugmentedRegistry<MetricRegistry> ? MetricRegistry[K] extends {
208
+ measureKeys: infer M;
209
+ } ? M : string : string;
210
+ type InferDimensionKeys<K> = K extends AugmentedRegistry<MetricRegistry> ? MetricRegistry[K] extends {
211
+ dimensionKeys: infer D;
212
+ } ? D : string : string;
213
+ type InferTimeGrains<K> = K extends AugmentedRegistry<MetricRegistry> ? MetricRegistry[K] extends {
214
+ timeGrains: infer G;
215
+ } ? G : string : string;
200
216
  /**
201
- * Metric view registry populated through module augmentation by the generated
202
- * `metric-views.ts` file.
217
+ * Infers the full row shape (every measure + dimension) from registry
218
+ * otherwise a total `Record<string, unknown>`.
203
219
  */
204
- interface MetricRegistry {}
220
+ type InferMetricRow<K> = K extends AugmentedRegistry<MetricRegistry> ? MetricRegistry[K] extends {
221
+ measures: infer Meas;
222
+ dimensions: infer Dim;
223
+ } ? Meas & Dim : Record<string, unknown> : Record<string, unknown>;
224
+ type PickMetricRow<K, M extends ReadonlyArray<PropertyKey>, D extends ReadonlyArray<PropertyKey>> = K extends AugmentedRegistry<MetricRegistry> ? MetricRegistry[K] extends {
225
+ measures: infer Meas;
226
+ dimensions: infer Dim;
227
+ } ? Pick<Meas, Extract<M[number], keyof Meas>> & Pick<Dim, Extract<D[number], keyof Dim>> : Record<string, unknown> : Record<string, unknown>;
228
+ type MetricDimensionMeta<K> = K extends AugmentedRegistry<MetricRegistry> ? MetricRegistry[K] extends {
229
+ metadata: {
230
+ dimensions: infer DM;
231
+ };
232
+ } ? DM : never : never;
233
+ /**
234
+ * The dimension keys of K that are TEMPORAL — i.e. carry a `time_grain` tuple in
235
+ * the generated metadata. Only these can be a `timeDimension`.
236
+ * Degrades to `string` for an unknown key.
237
+ */
238
+ type InferTimeDimensionKeys<K> = K extends AugmentedRegistry<MetricRegistry> ? { [P in keyof MetricDimensionMeta<K>]: MetricDimensionMeta<K>[P] extends {
239
+ time_grain: unknown;
240
+ } ? P : never }[keyof MetricDimensionMeta<K>] : string;
241
+ /**
242
+ * The valid grains for the SELECTED temporal dimensions `D` of K — the union of
243
+ * each selected temporal dimension's `time_grain` tuple. In practice grains are
244
+ * type-driven (all `timestamp` dims share one set, all `date` dims another), so
245
+ * the union is exactly the grains applicable to the query. Falls back to the
246
+ * metric's whole `timeGrains` union (or `string`) for an unknown/degraded key.
247
+ */
248
+ type GrainsForSelectedTimeDims<K, D extends ReadonlyArray<PropertyKey>> = K extends AugmentedRegistry<MetricRegistry> ? { [P in Extract<D[number], keyof MetricDimensionMeta<K>>]: MetricDimensionMeta<K>[P] extends {
249
+ time_grain: infer G extends readonly unknown[];
250
+ } ? G[number] : never }[Extract<D[number], keyof MetricDimensionMeta<K>>] : string;
251
+ /**
252
+ * Options for configuring a `useMetricView` query.
253
+ *
254
+ * Generic over the selected measure tuple `M` and dimension tuple `D` so the
255
+ * returned row shape ({@link PickMetricRow}) narrows to exactly the columns the
256
+ * query asked for. `timeDimension` must be a SELECTED, TEMPORAL dimension, and
257
+ * `timeGrain` is correlated to the grains valid for those dimensions — so
258
+ * bucketing a non-temporal dimension is a type error. `orderBy` is constrained
259
+ * to SELECTED measures and dimensions, so ordering by an unselected column is a
260
+ * compile-time error.
261
+ */
262
+ type MetricViewTimeOptions<K extends MetricKey, D extends ReadonlyArray<InferDimensionKeys<K>>> = {
263
+ timeGrain?: undefined;
264
+ timeDimension?: Extract<D[number], InferTimeDimensionKeys<K>>;
265
+ } | {
266
+ timeGrain: GrainsForSelectedTimeDims<K, D>;
267
+ timeDimension: Extract<D[number], InferTimeDimensionKeys<K>>;
268
+ };
269
+ type UseMetricViewOptions<K extends MetricKey = MetricKey, M extends ReadonlyArray<InferMeasureKeys<K>> = ReadonlyArray<InferMeasureKeys<K>>, D extends ReadonlyArray<InferDimensionKeys<K>> = ReadonlyArray<InferDimensionKeys<K>>> = {
270
+ measures: M;
271
+ dimensions?: D;
272
+ filter?: MetricFilter;
273
+ orderBy?: ReadonlyArray<MetricOrderBy<M[number] | D[number]>>;
274
+ limit?: number; /** Whether to automatically start the metric query. Default is true. */
275
+ autoStart?: boolean;
276
+ } & MetricViewTimeOptions<K, D>;
277
+ interface UseMetricViewResult<T = Record<string, unknown>[]> {
278
+ data: T | null;
279
+ loading: boolean;
280
+ error: string | null;
281
+ /** Structured upstream error code, mirroring useAnalyticsQuery. */
282
+ errorCode: string | null;
283
+ /** Per-column display metadata for the queried columns, carried in the SSE result payload. `undefined` when the server injected no metadata (dormant / unknown key). */
284
+ metadata: Record<string, MetricViewColumnDisplay> | undefined;
285
+ /**
286
+ * Latest warehouse status emitted while waiting for the SQL warehouse to
287
+ * reach RUNNING. `null` until the current request receives a status event.
288
+ */
289
+ warehouseStatus: WarehouseStatus | null;
290
+ }
205
291
  //#endregion
206
- export { AiSearchClientConfig, AiSearchIndexSummary, AiSearchQueryType, AiSearchRequest, AiSearchResponse, AiSearchResult, AnalyticsFormat, InferParams, InferResultByFormat, InferRowType, InferServingChunk, InferServingRequest, InferServingResponse, MetricRegistry, PluginRegistry, QueryKey, QueryRegistry, ServingAlias, ServingEndpointRegistry, TypedArrowTable, UseAnalyticsQueryOptions, UseAnalyticsQueryResult, WarehouseState, WarehouseStatus };
292
+ export { AiSearchClientConfig, AiSearchIndexSummary, AiSearchQueryType, AiSearchRequest, AiSearchResponse, AiSearchResult, AnalyticsFormat, GrainsForSelectedTimeDims, InferDimensionKeys, InferMeasureKeys, InferMetricRow, InferParams, InferResultByFormat, InferRowType, InferServingChunk, InferServingRequest, InferServingResponse, InferTimeDimensionKeys, InferTimeGrains, MetricKey, MetricRegistry, PickMetricRow, PluginRegistry, QueryKey, QueryRegistry, ServingAlias, ServingEndpointRegistry, TypedArrowTable, UseAnalyticsQueryOptions, UseAnalyticsQueryResult, UseMetricViewOptions, UseMetricViewResult, WarehouseState, WarehouseStatus };
207
293
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/react/hooks/types.ts"],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/react/hooks/types.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-analytics-query.d.ts","names":[],"sources":["../../../src/react/hooks/use-analytics-query.ts"],"mappings":";;;;;AA6SA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,iBAAA,wBAEJ,QAAA,GAAW,QAAA,YACX,eAAA,gBAAA,CAEV,QAAA,EAAU,CAAA,EACV,UAAA,GAAa,WAAA,CAAY,CAAA,UACzB,OAAA,GAAS,wBAAA,CAAyB,CAAA,IACjC,uBAAA,CAAwB,mBAAA,CAAoB,CAAA,EAAG,CAAA,EAAG,CAAA"}
1
+ {"version":3,"file":"use-analytics-query.d.ts","names":[],"sources":["../../../src/react/hooks/use-analytics-query.ts"],"mappings":";;;;;AA2MA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,iBAAA,wBAEJ,QAAA,GAAW,QAAA,YACX,eAAA,gBAAA,CAEV,QAAA,EAAU,CAAA,EACV,UAAA,GAAa,WAAA,CAAY,CAAA,UACzB,OAAA,GAAS,wBAAA,CAAyB,CAAA,IACjC,uBAAA,CAAwB,mBAAA,CAAoB,CAAA,EAAG,CAAA,EAAG,CAAA"}
@@ -1,5 +1,6 @@
1
1
  import { ArrowClient } from "../../js/arrow/arrow-client.js";
2
2
  import { connectSSE } from "../../js/sse/connect-sse.js";
3
+ import { GENERIC_LOAD_ERROR, getDevMode, handleAnalyticsSseError, handleAnalyticsSseMessage, userFacingFetchError } from "./analytics-sse.js";
3
4
  import { useAnalyticsWarehousePublisher } from "./use-analytics-warehouse-status.js";
4
5
  import { useQueryHMR } from "./use-query-hmr.js";
5
6
  import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
@@ -24,55 +25,6 @@ function useStableParams(value) {
24
25
  if (!shallowEqualParams(ref.current, value)) ref.current = value;
25
26
  return ref.current;
26
27
  }
27
- function getDevMode() {
28
- const dev = new URL(window.location.href).searchParams.get("dev");
29
- return dev ? `?dev=${dev}` : "";
30
- }
31
- const GENERIC_LOAD_ERROR = "Unable to load data, please try again";
32
- /** Map a fetch/SSE transport error to a user-facing message. */
33
- function userFacingFetchError(error) {
34
- if (error instanceof Error) {
35
- if (error.name === "AbortError") return "Request timed out, please try again";
36
- if (error.message.includes("Failed to fetch")) return "Network error. Please check your connection.";
37
- }
38
- return GENERIC_LOAD_ERROR;
39
- }
40
- function isWarehouseStatusPayload(value) {
41
- return typeof value === "object" && value !== null && typeof value.state === "string";
42
- }
43
- async function handleAnalyticsSseMessage(parsed, ctx) {
44
- if (parsed.type === "warehouse_status") {
45
- if (!isWarehouseStatusPayload(parsed.status)) {
46
- ctx.setLoading(false);
47
- ctx.setError(GENERIC_LOAD_ERROR);
48
- ctx.unpublishWarehouseStatus();
49
- console.error("[useAnalyticsQuery] Malformed warehouse_status event", parsed);
50
- return;
51
- }
52
- ctx.setWarehouseStatus(parsed.status);
53
- ctx.publishWarehouseStatus(parsed.status);
54
- return;
55
- }
56
- if (parsed.type === "result") {
57
- ctx.setLoading(false);
58
- ctx.setData(Array.isArray(parsed.data) ? parsed.data : []);
59
- ctx.unpublishWarehouseStatus();
60
- return;
61
- }
62
- if (parsed.type === "error" || parsed.error || parsed.code) {
63
- const errorMsg = parsed.error || parsed.message || "Unable to execute query";
64
- ctx.setLoading(false);
65
- ctx.setError(errorMsg);
66
- ctx.unpublishWarehouseStatus();
67
- if (typeof parsed.errorCode === "string") ctx.setErrorCode(parsed.errorCode);
68
- if (parsed.code) console.error(`[useAnalyticsQuery] Code: ${parsed.code}, Message: ${errorMsg}`);
69
- return;
70
- }
71
- console.error("[useAnalyticsQuery] Unrecognized SSE payload", parsed);
72
- ctx.setLoading(false);
73
- ctx.setError(GENERIC_LOAD_ERROR);
74
- ctx.unpublishWarehouseStatus();
75
- }
76
28
  /**
77
29
  * Fetch the real column names for a statement from the fallback endpoint,
78
30
  * used when a very wide schema's names didn't fit in the response header.
@@ -231,40 +183,28 @@ function useAnalyticsQuery(queryKey, parameters, options = {}) {
231
183
  return;
232
184
  }
233
185
  const sseContext = {
186
+ source: "useAnalyticsQuery",
187
+ resource: { queryKey },
188
+ defaultExecutionError: "Unable to execute query",
189
+ unpublishOnMalformedMessage: false,
190
+ signal: abortController.signal,
191
+ abort: () => abortController.abort(),
234
192
  setLoading,
235
193
  setError,
236
194
  setErrorCode,
237
- setData,
238
- setWarehouseStatus,
239
- publishWarehouseStatus,
195
+ onWarehouseStatus: (status) => {
196
+ setWarehouseStatus(status);
197
+ publishWarehouseStatus(status);
198
+ },
199
+ onResult: (message) => setData(message.data),
240
200
  unpublishWarehouseStatus
241
201
  };
242
202
  connectSSE({
243
203
  url: urlSuffix,
244
204
  payload,
245
205
  signal: abortController.signal,
246
- onMessage: async (message) => {
247
- if (abortController.signal.aborted) return;
248
- try {
249
- await handleAnalyticsSseMessage(JSON.parse(message.data), sseContext);
250
- } catch (error) {
251
- console.warn("[useAnalyticsQuery] Malformed message received", error);
252
- setLoading(false);
253
- setError(GENERIC_LOAD_ERROR);
254
- abortController.abort();
255
- }
256
- },
257
- onError: (error) => {
258
- if (abortController.signal.aborted) return;
259
- setLoading(false);
260
- unpublishWarehouseStatus();
261
- if (error instanceof Error) console.error("[useAnalyticsQuery] Error", {
262
- queryKey,
263
- error: error.message,
264
- stack: error.stack
265
- });
266
- setError(userFacingFetchError(error));
267
- }
206
+ onMessage: (message) => handleAnalyticsSseMessage(message.data, sseContext),
207
+ onError: (error) => handleAnalyticsSseError(error, sseContext)
268
208
  });
269
209
  }, [
270
210
  queryKey,
@@ -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\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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"}
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 {\n type AnalyticsSseHandlerContext,\n GENERIC_LOAD_ERROR,\n getDevMode,\n handleAnalyticsSseError,\n handleAnalyticsSseMessage,\n userFacingFetchError,\n} from \"./analytics-sse\";\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\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: AnalyticsSseHandlerContext = {\n source: \"useAnalyticsQuery\",\n resource: { queryKey },\n defaultExecutionError: \"Unable to execute query\",\n unpublishOnMalformedMessage: false,\n signal: abortController.signal,\n abort: () => abortController.abort(),\n setLoading,\n setError,\n setErrorCode,\n onWarehouseStatus: (status) => {\n setWarehouseStatus(status);\n publishWarehouseStatus(status);\n },\n onResult: (message) => setData(message.data as ResultType),\n unpublishWarehouseStatus,\n };\n\n connectSSE({\n url: urlSuffix,\n payload,\n signal: abortController.signal,\n onMessage: (message) =>\n handleAnalyticsSseMessage(message.data, sseContext),\n onError: (error) => handleAnalyticsSseError(error, sseContext),\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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"}
@@ -0,0 +1,39 @@
1
+ import { InferDimensionKeys, InferMeasureKeys, MetricKey, PickMetricRow, UseMetricViewOptions, UseMetricViewResult } from "./types.js";
2
+
3
+ //#region src/react/hooks/use-metric-view.d.ts
4
+ /**
5
+ * Subscribe to a Unity Catalog Metric View and return its latest result.
6
+ *
7
+ * @param key - Metric view identifier
8
+ * @param options - Measures (required) plus optional dimensions, filter,
9
+ * orderBy, timeGrain/timeDimension, and limit
10
+ * @returns Metric result state with typed rows, display metadata, and warehouse readiness
11
+ *
12
+ * @remarks
13
+ * `orderBy` and `limit` interact. With `limit`, the route completes the ordering
14
+ * with the remaining dimensions so the capped rows are the *same* rows on every
15
+ * run — pass `orderBy` to choose WHICH rows (top-N), since the completion only
16
+ * makes the result stable, not ranked. Without `limit`, `orderBy` is presentation
17
+ * ordering over the full result and gets no completion.
18
+ *
19
+ * When a request refetches with the same metric key, measures, and dimensions,
20
+ * the previous `data` and `metadata` remain available while `loading` is true.
21
+ * Changing any of those result-shaping fields clears the previous result.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const { data, metadata } = useMetricView("orders", {
26
+ * measures: ["revenue"],
27
+ * dimensions: ["region"],
28
+ * filter: { member: "region", operator: "in", values: ["EMEA", "APAC"] },
29
+ * orderBy: [{ field: "revenue", direction: "DESC" }],
30
+ * limit: 10,
31
+ * });
32
+ * // JSON_ARRAY preserves SQL scalar cells as strings and nullable columns as null:
33
+ * // data: Array<{ revenue: string | null; region: string | null }> | null
34
+ * ```
35
+ */
36
+ declare function useMetricView<K extends MetricKey = MetricKey, const M extends ReadonlyArray<InferMeasureKeys<K>> = ReadonlyArray<InferMeasureKeys<K>>, const D extends ReadonlyArray<InferDimensionKeys<K>> = readonly []>(key: K, options: UseMetricViewOptions<K, M, D>): UseMetricViewResult<PickMetricRow<K, M, D>[]>;
37
+ //#endregion
38
+ export { useMetricView };
39
+ //# sourceMappingURL=use-metric-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-metric-view.d.ts","names":[],"sources":["../../../src/react/hooks/use-metric-view.ts"],"mappings":";;;;;AAqEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,aAAA,WACJ,SAAA,GAAY,SAAA,kBACN,aAAA,CAAc,gBAAA,CAAiB,CAAA,KAAM,aAAA,CACnD,gBAAA,CAAiB,CAAA,oBAEH,aAAA,CAAc,kBAAA,CAAmB,CAAA,iBAAA,CAEjD,GAAA,EAAK,CAAA,EACL,OAAA,EAAS,oBAAA,CAAqB,CAAA,EAAG,CAAA,EAAG,CAAA,IACnC,mBAAA,CAAoB,aAAA,CAAc,CAAA,EAAG,CAAA,EAAG,CAAA"}
@@ -0,0 +1,178 @@
1
+ import { connectSSE } from "../../js/sse/connect-sse.js";
2
+ import { getDevMode, handleAnalyticsSseError, handleAnalyticsSseMessage } from "./analytics-sse.js";
3
+ import { useAnalyticsWarehousePublisher } from "./use-analytics-warehouse-status.js";
4
+ import { useQueryHMR } from "./use-query-hmr.js";
5
+ import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
6
+
7
+ //#region src/react/hooks/use-metric-view.ts
8
+ function asMetricMetadata(value) {
9
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) return value;
10
+ }
11
+ /**
12
+ * Subscribe to a Unity Catalog Metric View and return its latest result.
13
+ *
14
+ * @param key - Metric view identifier
15
+ * @param options - Measures (required) plus optional dimensions, filter,
16
+ * orderBy, timeGrain/timeDimension, and limit
17
+ * @returns Metric result state with typed rows, display metadata, and warehouse readiness
18
+ *
19
+ * @remarks
20
+ * `orderBy` and `limit` interact. With `limit`, the route completes the ordering
21
+ * with the remaining dimensions so the capped rows are the *same* rows on every
22
+ * run — pass `orderBy` to choose WHICH rows (top-N), since the completion only
23
+ * makes the result stable, not ranked. Without `limit`, `orderBy` is presentation
24
+ * ordering over the full result and gets no completion.
25
+ *
26
+ * When a request refetches with the same metric key, measures, and dimensions,
27
+ * the previous `data` and `metadata` remain available while `loading` is true.
28
+ * Changing any of those result-shaping fields clears the previous result.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const { data, metadata } = useMetricView("orders", {
33
+ * measures: ["revenue"],
34
+ * dimensions: ["region"],
35
+ * filter: { member: "region", operator: "in", values: ["EMEA", "APAC"] },
36
+ * orderBy: [{ field: "revenue", direction: "DESC" }],
37
+ * limit: 10,
38
+ * });
39
+ * // JSON_ARRAY preserves SQL scalar cells as strings and nullable columns as null:
40
+ * // data: Array<{ revenue: string | null; region: string | null }> | null
41
+ * ```
42
+ */
43
+ function useMetricView(key, options) {
44
+ const autoStart = options.autoStart ?? true;
45
+ const devMode = getDevMode();
46
+ const urlSuffix = `/api/analytics/metric/${encodeURIComponent(key)}${devMode}`;
47
+ const [result, setResult] = useState(null);
48
+ const [loading, setLoading] = useState(false);
49
+ const [error, setError] = useState(null);
50
+ const [errorCode, setErrorCode] = useState(null);
51
+ const [warehouseStatus, setWarehouseStatus] = useState(null);
52
+ const abortControllerRef = useRef(null);
53
+ const activeRequestKeyRef = useRef(null);
54
+ const effectLeaseRef = useRef(0);
55
+ const requestedShapeRef = useRef(null);
56
+ const { publish: publishWarehouseStatus, unpublish: unpublishWarehouseStatus } = useAnalyticsWarehousePublisher(useId(), key);
57
+ if (!key || key.trim().length === 0) throw new Error("useMetricView: 'key' must be a non-empty string.");
58
+ const payload = useMemo(() => {
59
+ const body = { measures: options.measures };
60
+ if (options.dimensions !== void 0) body.dimensions = options.dimensions;
61
+ if (options.filter !== void 0) body.filter = options.filter;
62
+ if (options.timeGrain !== void 0) body.timeGrain = options.timeGrain;
63
+ if (options.timeDimension !== void 0) body.timeDimension = options.timeDimension;
64
+ if (options.orderBy !== void 0) body.orderBy = options.orderBy;
65
+ if (options.limit !== void 0) body.limit = options.limit;
66
+ return JSON.stringify(body);
67
+ }, [
68
+ options.measures,
69
+ options.dimensions,
70
+ options.filter,
71
+ options.timeGrain,
72
+ options.timeDimension,
73
+ options.orderBy,
74
+ options.limit
75
+ ]);
76
+ const resultShape = JSON.stringify({
77
+ key,
78
+ measures: options.measures,
79
+ dimensions: options.dimensions ?? []
80
+ });
81
+ const requestKey = `${urlSuffix}\0${payload}`;
82
+ const isCurrentShape = result?.shape === resultShape;
83
+ const data = isCurrentShape ? result.data : null;
84
+ const metadata = isCurrentShape ? result.metadata : void 0;
85
+ const start = useCallback(() => {
86
+ abortControllerRef.current?.abort();
87
+ setLoading(true);
88
+ setError(null);
89
+ setErrorCode(null);
90
+ setWarehouseStatus(null);
91
+ if (requestedShapeRef.current !== resultShape) {
92
+ requestedShapeRef.current = resultShape;
93
+ setResult(null);
94
+ }
95
+ publishWarehouseStatus(null);
96
+ const abortController = new AbortController();
97
+ abortControllerRef.current = abortController;
98
+ activeRequestKeyRef.current = requestKey;
99
+ const sseContext = {
100
+ source: "useMetricView",
101
+ resource: { key },
102
+ defaultExecutionError: "Unable to execute metric query",
103
+ unpublishOnMalformedMessage: true,
104
+ signal: abortController.signal,
105
+ abort: () => abortController.abort(),
106
+ setLoading,
107
+ setError,
108
+ setErrorCode,
109
+ onWarehouseStatus: (status) => {
110
+ setWarehouseStatus(status);
111
+ publishWarehouseStatus(status);
112
+ },
113
+ onResult: (message) => {
114
+ setError(null);
115
+ setErrorCode(null);
116
+ setResult({
117
+ shape: resultShape,
118
+ data: message.data,
119
+ metadata: asMetricMetadata(message.payload.metadata)
120
+ });
121
+ },
122
+ unpublishWarehouseStatus
123
+ };
124
+ connectSSE({
125
+ url: urlSuffix,
126
+ payload,
127
+ signal: abortController.signal,
128
+ onMessage: (message) => handleAnalyticsSseMessage(message.data, sseContext),
129
+ onError: (error) => handleAnalyticsSseError(error, sseContext)
130
+ });
131
+ }, [
132
+ key,
133
+ payload,
134
+ requestKey,
135
+ resultShape,
136
+ urlSuffix,
137
+ publishWarehouseStatus,
138
+ unpublishWarehouseStatus
139
+ ]);
140
+ useEffect(() => {
141
+ const lease = ++effectLeaseRef.current;
142
+ if (autoStart) {
143
+ const activeController = abortControllerRef.current;
144
+ if (activeRequestKeyRef.current !== requestKey || !activeController || activeController.signal.aborted) start();
145
+ } else {
146
+ abortControllerRef.current?.abort();
147
+ activeRequestKeyRef.current = null;
148
+ unpublishWarehouseStatus();
149
+ }
150
+ return () => {
151
+ const controller = abortControllerRef.current;
152
+ queueMicrotask(() => {
153
+ if (effectLeaseRef.current !== lease) return;
154
+ controller?.abort();
155
+ if (abortControllerRef.current === controller) activeRequestKeyRef.current = null;
156
+ unpublishWarehouseStatus();
157
+ });
158
+ };
159
+ }, [
160
+ start,
161
+ autoStart,
162
+ requestKey,
163
+ unpublishWarehouseStatus
164
+ ]);
165
+ useQueryHMR(key, start);
166
+ return {
167
+ data,
168
+ loading,
169
+ error,
170
+ errorCode,
171
+ metadata,
172
+ warehouseStatus
173
+ };
174
+ }
175
+
176
+ //#endregion
177
+ export { useMetricView };
178
+ //# sourceMappingURL=use-metric-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-metric-view.js","names":[],"sources":["../../../src/react/hooks/use-metric-view.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { MetricViewColumnDisplay } from \"shared\";\nimport { connectSSE } from \"@/js\";\nimport {\n type AnalyticsSseHandlerContext,\n getDevMode,\n handleAnalyticsSseError,\n handleAnalyticsSseMessage,\n} from \"./analytics-sse\";\nimport type {\n InferDimensionKeys,\n InferMeasureKeys,\n MetricKey,\n PickMetricRow,\n UseMetricViewOptions,\n UseMetricViewResult,\n WarehouseStatus,\n} from \"./types\";\nimport { useAnalyticsWarehousePublisher } from \"./use-analytics-warehouse-status\";\nimport { useQueryHMR } from \"./use-query-hmr\";\n\nfunction asMetricMetadata(\n value: unknown,\n): Record<string, MetricViewColumnDisplay> | undefined {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n return value as Record<string, MetricViewColumnDisplay>;\n }\n return undefined;\n}\n\n/**\n * Subscribe to a Unity Catalog Metric View and return its latest result.\n *\n * @param key - Metric view identifier\n * @param options - Measures (required) plus optional dimensions, filter,\n * orderBy, timeGrain/timeDimension, and limit\n * @returns Metric result state with typed rows, display metadata, and warehouse readiness\n *\n * @remarks\n * `orderBy` and `limit` interact. With `limit`, the route completes the ordering\n * with the remaining dimensions so the capped rows are the *same* rows on every\n * run — pass `orderBy` to choose WHICH rows (top-N), since the completion only\n * makes the result stable, not ranked. Without `limit`, `orderBy` is presentation\n * ordering over the full result and gets no completion.\n *\n * When a request refetches with the same metric key, measures, and dimensions,\n * the previous `data` and `metadata` remain available while `loading` is true.\n * Changing any of those result-shaping fields clears the previous result.\n *\n * @example\n * ```typescript\n * const { data, metadata } = useMetricView(\"orders\", {\n * measures: [\"revenue\"],\n * dimensions: [\"region\"],\n * filter: { member: \"region\", operator: \"in\", values: [\"EMEA\", \"APAC\"] },\n * orderBy: [{ field: \"revenue\", direction: \"DESC\" }],\n * limit: 10,\n * });\n * // JSON_ARRAY preserves SQL scalar cells as strings and nullable columns as null:\n * // data: Array<{ revenue: string | null; region: string | null }> | null\n * ```\n */\nexport function useMetricView<\n K extends MetricKey = MetricKey,\n const M extends ReadonlyArray<InferMeasureKeys<K>> = ReadonlyArray<\n InferMeasureKeys<K>\n >,\n const D extends ReadonlyArray<InferDimensionKeys<K>> = readonly [],\n>(\n key: K,\n options: UseMetricViewOptions<K, M, D>,\n): UseMetricViewResult<PickMetricRow<K, M, D>[]> {\n const autoStart = options.autoStart ?? true;\n const devMode = getDevMode();\n const urlSuffix = `/api/analytics/metric/${encodeURIComponent(key)}${devMode}`;\n\n type Rows = PickMetricRow<K, M, D>[];\n const [result, setResult] = useState<{\n shape: string;\n data: Rows;\n metadata: Record<string, MetricViewColumnDisplay> | undefined;\n } | 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 const activeRequestKeyRef = useRef<string | null>(null);\n const effectLeaseRef = useRef(0);\n const requestedShapeRef = useRef<string | null>(null);\n\n const publisherId = useId();\n const {\n publish: publishWarehouseStatus,\n unpublish: unpublishWarehouseStatus,\n } = useAnalyticsWarehousePublisher(publisherId, key);\n\n if (!key || key.trim().length === 0) {\n throw new Error(\"useMetricView: 'key' must be a non-empty string.\");\n }\n\n // Stringify to compare request by value; prevent re-firing on every render.\n const payload = useMemo(() => {\n const body: {\n measures: ReadonlyArray<unknown>;\n dimensions?: ReadonlyArray<unknown>;\n filter?: unknown;\n timeGrain?: unknown;\n timeDimension?: unknown;\n orderBy?: ReadonlyArray<unknown>;\n limit?: number;\n } = { measures: options.measures };\n if (options.dimensions !== undefined) body.dimensions = options.dimensions;\n if (options.filter !== undefined) body.filter = options.filter;\n if (options.timeGrain !== undefined) body.timeGrain = options.timeGrain;\n if (options.timeDimension !== undefined)\n body.timeDimension = options.timeDimension;\n if (options.orderBy !== undefined) body.orderBy = options.orderBy;\n if (options.limit !== undefined) body.limit = options.limit;\n return JSON.stringify(body);\n }, [\n options.measures,\n options.dimensions,\n options.filter,\n options.timeGrain,\n options.timeDimension,\n options.orderBy,\n options.limit,\n ]);\n\n // Detect shape changes: clear rows when metric/measures/dimensions change,\n // but keep stale rows during filter/order/limit/time-grain revalidations.\n const resultShape = JSON.stringify({\n key,\n measures: options.measures,\n dimensions: options.dimensions ?? [],\n });\n const requestKey = `${urlSuffix}\\0${payload}`;\n\n // Return stale rows only if shape is current; hide during shape transitions.\n const isCurrentShape = result?.shape === resultShape;\n const data = isCurrentShape ? result.data : null;\n const metadata = isCurrentShape ? result.metadata : undefined;\n\n const start = useCallback(() => {\n abortControllerRef.current?.abort();\n\n setLoading(true);\n setError(null);\n setErrorCode(null);\n setWarehouseStatus(null);\n if (requestedShapeRef.current !== resultShape) {\n requestedShapeRef.current = resultShape;\n setResult(null);\n }\n // Register an empty slot to clear stale status from the prior run.\n publishWarehouseStatus(null);\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n activeRequestKeyRef.current = requestKey;\n\n const sseContext: AnalyticsSseHandlerContext = {\n source: \"useMetricView\",\n resource: { key },\n defaultExecutionError: \"Unable to execute metric query\",\n unpublishOnMalformedMessage: true,\n signal: abortController.signal,\n abort: () => abortController.abort(),\n setLoading,\n setError,\n setErrorCode,\n onWarehouseStatus: (status) => {\n setWarehouseStatus(status);\n publishWarehouseStatus(status);\n },\n onResult: (message) => {\n setError(null);\n setErrorCode(null);\n setResult({\n shape: resultShape,\n data: message.data as Rows,\n metadata: asMetricMetadata(message.payload.metadata),\n });\n },\n unpublishWarehouseStatus,\n };\n\n connectSSE({\n url: urlSuffix,\n payload,\n signal: abortController.signal,\n onMessage: (message) =>\n handleAnalyticsSseMessage(message.data, sseContext),\n onError: (error) => handleAnalyticsSseError(error, sseContext),\n });\n }, [\n key,\n payload,\n requestKey,\n resultShape,\n urlSuffix,\n publishWarehouseStatus,\n unpublishWarehouseStatus,\n ]);\n\n useEffect(() => {\n const lease = ++effectLeaseRef.current;\n\n if (autoStart) {\n // React Strict Mode runs an effect's setup/cleanup/setup sequence on\n // mount. Keep the still-active identical stream for the second setup so\n // development sends one POST, not an immediately-aborted duplicate.\n // A real request change still starts synchronously and aborts the prior\n // controller in start().\n const activeController = abortControllerRef.current;\n if (\n activeRequestKeyRef.current !== requestKey ||\n !activeController ||\n activeController.signal.aborted\n ) {\n start();\n }\n } else {\n abortControllerRef.current?.abort();\n activeRequestKeyRef.current = null;\n unpublishWarehouseStatus();\n }\n\n return () => {\n // Defer teardown by one microtask so Strict Mode's immediate second\n // setup can claim the same request. On a genuine unmount there is no new\n // lease, so the stream is still cancelled before later async work runs.\n const controller = abortControllerRef.current;\n queueMicrotask(() => {\n if (effectLeaseRef.current !== lease) return;\n controller?.abort();\n if (abortControllerRef.current === controller) {\n activeRequestKeyRef.current = null;\n }\n unpublishWarehouseStatus();\n });\n };\n }, [start, autoStart, requestKey, unpublishWarehouseStatus]);\n\n useQueryHMR(key, start);\n\n return { data, loading, error, errorCode, metadata, warehouseStatus };\n}\n"],"mappings":"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"}