/** * Which RETURN columns each Insights chart type needs in order to render. * * These are renderer-side conventions: the API accepts any valid J1QL for any chart type, so a * mismatch is created successfully and then draws nothing — a number widget shows 0, a pie shows a * blank ring — with no error anywhere in the flow (TD-9016). The contract is not derivable from the * GraphQL schema (`CreateInsightsWidgetInput` says nothing about columns), so it is transcribed * here from the shipped tool description. * * `required` BLOCKS widget creation when missing, so a row belongs there only when a missing column * certainly means a blank widget. `recommended` is reported but never blocks — the chart uses the * column when present and copes without it. Multi-query widgets label their series by query name * (`mapColorsByQuery`), which is why the series/label columns sit in `recommended`: requiring one * would hard-block a legitimate two-query chart. * * `number`, `pie`, `bar` and `matrix` are confirmed against rendering widgets on live j1dev boards * (`All Chart Types`, the J1-managed `device-management`, `CSPM Progress`). `line` and `area` are * from the tool description only — hence the conservative required sets. * * SOURCE OF TRUTH. `src/descriptions/server-instructions.md` republishes this table for the model, * and `widget-render-contract.test.ts` fails if the two drift apart or if a chart type is added to * the API's list without a decision here. */ export interface ChartColumnContract { required: readonly string[]; /** * Columns that are required only when the widget carries exactly ONE query. * * `pie` is the case this exists for. The renderer GROUPS slices by the `name` column, so a lone * query without it does not draw an unlabelled pie — every row collapses into a single slice * labelled `undefined` summing the whole population (seen on a live board: one slice, 193,770). * A multi-query pie labels each slice by its query name (`mapColorsByQuery`) and needs no `name` * column at all, which is why this cannot go in `required`: doing so false-positived five * rendering widgets in the live corpus audit, every one of them multi-query. */ singleQueryRequired?: readonly string[]; recommended?: readonly string[]; /** * A projection the query must use instead of column aliases. `graph` renders the tree J1QL * returns for `RETURN TREE`; anything else gives it rows it cannot draw. */ projection?: 'tree'; /** Checked on every query the widget carries, not just the first — `status` correlates several. */ perQuery?: boolean; /** * Each query reads exactly the required columns and nothing else. A per-QUERY rule, not a * per-widget one: a `number` widget legitimately carries several queries in percentage mode, and * each of them still returns a single `value`. * * Reported, never blocked: field experience is that extras "can break" the render, but whether * the current renderer tolerates them is unverified. */ exactColumns?: boolean; } /** * Every chart type the API accepts. The widget input schema builds its enum from this list, so a * new chart type is added in exactly one place — and the drift test then fails until it is either * given a contract below or declared exempt. */ export declare const CHART_TYPES: readonly ["area", "bar", "graph", "line", "markdown", "matrix", "number", "pie", "status", "table"]; export type ChartType = (typeof CHART_TYPES)[number]; export declare const WIDGET_COLUMN_CONTRACT: Readonly>>; /** * Chart types deliberately outside the contract: `table` renders whatever columns it is given, * `graph` takes `RETURN TREE`, `status` correlates several free-form queries keyed on `id`, and * `markdown` carries no query at all. */ export declare const CONTRACT_EXEMPT_CHART_TYPES: ReadonlySet; /** Why a query was not checked. Every one of these is a deliberate pass, never a failure. */ export type SkipReason = 'exempt-chart-type' | 'unknown-chart-type' | 'parameterized' | 'unparseable' | 'tree-projection'; /** The property a chart groups its categories by, with the target it is read from. */ export interface CategorySource { target: string; field: string; /** * The RETURN alias the category arrives under, which is the row key to group the results on. * * Read from the query rather than assumed from the chart type: a widget can alias its category * anything and still group on it, and reading `row.name` when the query wrote `AS severity` finds * nothing to judge. */ column: string; /** * The chart's measure alias (`value` or `y`), so advice about this source can show a per-category * query in the shape the render gate accepts for THIS chart type — a `count(e) AS value` example * on a bar widget is advice the gate itself then rejects. */ measure: string; } /** * The property this chart will group its categories by, if it has one. * * A chart groups on whatever feeds its category column, and the values of that property become the * bars or slices. If those values are stored inconsistently the chart splits one category across * several — which is why this is worth knowing before the widget is built. * * `queryCount` gates the fallback below, and nothing else. */ export declare function categorySource(chartType: string, query: string, options?: { queryCount?: number; }): CategorySource | null; export interface WidgetContractCheck { /** Set when the query was not checked; callers must treat this as a pass. */ skipped?: SkipReason; /** Aliases the query actually declares, in order. `null` marks a selector with no `AS`. */ aliases: Array; /** Required columns the query does not declare. Non-empty means the widget cannot render. */ missingRequired: string[]; /** Recommended columns the query does not declare. Never blocks. */ missingRecommended: string[]; /** * Columns beyond what the chart reads, where each query is documented to return exactly the * required ones. Never blocks. */ extraColumns?: string[]; /** A ready-to-run rewrite, when one can be derived unambiguously and verified. */ correctedQuery?: string; } /** * Check one widget query against its chart type's column contract. * * Purely static — the bundled grammar parses locally, so this costs no backend round-trip. Fails * open on every uncertainty (unknown chart type, unparseable query, dashboard parameters, and any * error the rewriter throws), because a stale or confused contract must never block a widget that * would have rendered. * * `queryCount` is how many queries the whole widget carries, not how many are being checked here. * Some requirements only bind on a lone query — see `singleQueryRequired` — and omitting it keeps * the unconditional contract. */ export declare function checkWidgetContract(chartType: string, query: string, options?: { queryCount?: number; }): WidgetContractCheck; //# sourceMappingURL=widget-render-contract.d.ts.map