|
1 | 1 | import type { Table } from "apache-arrow"; |
| 2 | +import type { MetricColumnMeta } from "shared"; |
2 | 3 |
|
3 | 4 | // ============================================================================ |
4 | 5 | // Data Format Types |
@@ -247,3 +248,128 @@ export type InferServingRequest<K> = |
247 | 248 | ? Req |
248 | 249 | : Record<string, unknown> |
249 | 250 | : Record<string, unknown>; |
| 251 | + |
| 252 | +// ============================================================================ |
| 253 | +// Metric View Registry |
| 254 | +// ============================================================================ |
| 255 | + |
| 256 | +/** |
| 257 | + * Metric view registry for type-safe metric keys, measure/dimension names, |
| 258 | + * time grains, and row shapes. Extend this interface via module augmentation |
| 259 | + * to get autocomplete for `useMetricView`: |
| 260 | + * |
| 261 | + * @example |
| 262 | + * ```typescript |
| 263 | + * // Auto-generated (generated metric-views.ts) |
| 264 | + * declare module "@databricks/appkit-ui/react" { |
| 265 | + * interface MetricRegistry { |
| 266 | + * orders: { |
| 267 | + * measureKeys: "revenue" | "order_count"; |
| 268 | + * dimensionKeys: "region" | "order_date"; |
| 269 | + * timeGrains: "day" | "month"; |
| 270 | + * measures: { revenue: number; order_count: number }; |
| 271 | + * dimensions: { region: string; order_date: string }; |
| 272 | + * }; |
| 273 | + * } |
| 274 | + * } |
| 275 | + * ``` |
| 276 | + */ |
| 277 | +// biome-ignore lint/suspicious/noEmptyInterface: intentionally empty — populated via module augmentation (generated metric-views.ts) |
| 278 | +export interface MetricRegistry {} |
| 279 | + |
| 280 | +/** Resolves to registry keys if populated, otherwise string */ |
| 281 | +export type MetricKey = AugmentedRegistry<MetricRegistry> extends never |
| 282 | + ? string |
| 283 | + : AugmentedRegistry<MetricRegistry>; |
| 284 | + |
| 285 | +/** Infers measure key names from the registry when K is a known key */ |
| 286 | +export type InferMeasureKeys<K> = K extends AugmentedRegistry<MetricRegistry> |
| 287 | + ? MetricRegistry[K] extends { measureKeys: infer M } |
| 288 | + ? M |
| 289 | + : string |
| 290 | + : string; |
| 291 | + |
| 292 | +/** Infers dimension key names from the registry when K is a known key */ |
| 293 | +export type InferDimensionKeys<K> = K extends AugmentedRegistry<MetricRegistry> |
| 294 | + ? MetricRegistry[K] extends { dimensionKeys: infer D } |
| 295 | + ? D |
| 296 | + : string |
| 297 | + : string; |
| 298 | + |
| 299 | +/** Infers time-grain names from the registry when K is a known key */ |
| 300 | +export type InferTimeGrains<K> = K extends AugmentedRegistry<MetricRegistry> |
| 301 | + ? MetricRegistry[K] extends { timeGrains: infer G } |
| 302 | + ? G |
| 303 | + : string |
| 304 | + : string; |
| 305 | + |
| 306 | +/** |
| 307 | + * Infers the row shape (measures + dimensions) from the registry when K is a |
| 308 | + * known key, otherwise a total `Record<string, unknown>`. Never resolves to |
| 309 | + * `never` — always assignable to `Record<string, unknown>`. |
| 310 | + */ |
| 311 | +export type InferMetricRow<K> = K extends AugmentedRegistry<MetricRegistry> |
| 312 | + ? MetricRegistry[K] extends { |
| 313 | + measures: infer Meas; |
| 314 | + dimensions: infer Dim; |
| 315 | + } |
| 316 | + ? Meas & Dim |
| 317 | + : Record<string, unknown> |
| 318 | + : Record<string, unknown>; |
| 319 | + |
| 320 | +// ──────────────────────────────────────────────────────────────────────────── |
| 321 | +// Metric filter vocabulary. |
| 322 | +// |
| 323 | +// **Kept in sync with appkit `plugins/analytics/types.ts`** — appkit-ui cannot |
| 324 | +// depend on appkit, so this mirrors the twelve-operator filter grammar by hand. |
| 325 | +// ──────────────────────────────────────────────────────────────────────────── |
| 326 | + |
| 327 | +/** v1 filter operator vocabulary — exactly twelve names. */ |
| 328 | +export type MetricFilterOperatorName = |
| 329 | + | "equals" |
| 330 | + | "notEquals" |
| 331 | + | "in" |
| 332 | + | "notIn" |
| 333 | + | "gt" |
| 334 | + | "gte" |
| 335 | + | "lt" |
| 336 | + | "lte" |
| 337 | + | "contains" |
| 338 | + | "notContains" |
| 339 | + | "set" |
| 340 | + | "notSet"; |
| 341 | + |
| 342 | +/** A single filter predicate — the leaf node of the recursive {@link MetricFilter} tree. */ |
| 343 | +export interface MetricPredicate { |
| 344 | + member: string; |
| 345 | + operator: MetricFilterOperatorName; |
| 346 | + values?: ReadonlyArray<string | number>; |
| 347 | +} |
| 348 | + |
| 349 | +/** Recursive filter expression: a leaf {@link MetricPredicate} or an `and`/`or` group. */ |
| 350 | +export type MetricFilter = |
| 351 | + | MetricPredicate |
| 352 | + | { and: ReadonlyArray<MetricFilter> } |
| 353 | + | { or: ReadonlyArray<MetricFilter> }; |
| 354 | + |
| 355 | +/** Options for configuring a `useMetricView` query. */ |
| 356 | +export interface UseMetricViewOptions<K extends MetricKey = MetricKey> { |
| 357 | + measures: ReadonlyArray<InferMeasureKeys<K>>; |
| 358 | + dimensions?: ReadonlyArray<InferDimensionKeys<K>>; |
| 359 | + filter?: MetricFilter; |
| 360 | + timeGrain?: InferTimeGrains<K>; |
| 361 | + timeDimension?: InferDimensionKeys<K>; |
| 362 | + limit?: number; |
| 363 | + autoStart?: boolean; |
| 364 | +} |
| 365 | + |
| 366 | +/** Result state returned by `useMetricView`. */ |
| 367 | +export interface UseMetricViewResult<T = Record<string, unknown>[]> { |
| 368 | + data: T | null; |
| 369 | + loading: boolean; |
| 370 | + error: string | null; |
| 371 | + /** Structured upstream error code, mirroring useAnalyticsQuery. */ |
| 372 | + errorCode: string | null; |
| 373 | + /** Per-column display metadata for the queried columns, carried in the SSE result payload. `undefined` when the server injected no metadata (dormant / unknown key). */ |
| 374 | + metadata: Record<string, MetricColumnMeta> | undefined; |
| 375 | +} |
0 commit comments