@prism-reporting/core
v0.1.0-beta.1
Published
Core ReportSpec types, validation, resolution, and reporting context contracts.
Downloads
26
Maintainers
Readme
@prism-reporting/core
Core types, engine, and interfaces for the reporting beta foundation.
Beta status
@prism-reporting/core is currently in beta.
- The DSL and runtime contracts are expected to evolve.
- Breaking changes may happen without long deprecation windows during this phase.
- This package is intended as a foundation for integrations, not a final product surface.
- External pull requests are not being accepted right now; please open an issue if a capability you need is missing.
ReportSpec v1
The ReportSpec is a structured DSL that describes reports. AI tools (future phase) will generate and modify ReportSpec—they do not produce raw HTML or control UI libraries directly.
Required Fields (Safe for AI)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| id | string | Yes | Unique report identifier |
| title | string | Yes | Display title |
| layout | "singleColumn" | "twoColumn" | Yes | Page layout |
| dataSources | Record<string, DataSourceSpec> | Yes | Named data sources |
| filters | FilterSpec[] | Yes | Filter definitions (may be empty) |
| widgets | WidgetSpec[] | Yes | Visual widgets |
| groups | ReportGroupSpec[] | No | Optional logical widget groups used to scope filters to part of a report. |
| presets | ReportSpecPreset[] | No | Optional named filter states (e.g. "This quarter"); host applies preset.filterState. |
| version | string | No | Optional report version (e.g. "1.0", "2024.03"); exposed on ResolvedReport.version for UI display. |
| refreshInterval | number | No | Optional interval in seconds; when set, the host should re-call resolveReport after that interval (engine does not implement timers). Hosts may cache by spec.id + filterState and TTL. |
| layoutOptions | LayoutOptions | No | Optional columnGap / rowGap CSS hints for the report grid. |
| sections | ReportSectionSpec[] | No | Optional titled widget groupings; widgets are referenced by id. |
| tabs | ReportTabSpec[] | No | Optional tabbed layout; takes precedence over sections. |
| owner | string | No | Optional owner (e.g. user id or email); pass-through for governance and UI (e.g. "Owner: {owner}"). |
| author | string | No | Optional author (e.g. user id or email); pass-through for UI (e.g. "By {author}"). |
DataSourceSpec
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| name | string | Yes | Unique name within the report |
| query | string | Yes | Query identifier (passed to DataProvider.runQuery) |
| params | Record<string, unknown> | No | Static default parameters |
| delivery | { mode, pageSize?, maxRows? } | No | Explicit integration contract. Use paginatedList for table/list sources, fullVisual for full chart datasets and engine-side grouped summaries, summary for backend-aggregated KPI sources. |
FilterSpec Variants
- SelectFilter:
type: "select",options: { value, label }[],groupIds?,paramKey? - MultiSelectFilter:
type: "multiSelect",options: { value, label }[],groupIds?,paramKey? - DateRangeFilter:
type: "dateRange",groupIds?,paramKeyFrom?,paramKeyTo? - SearchFilter:
type: "search",groupIds?,paramKey?,placeholder? - NumericRangeFilter:
type: "numericRange",groupIds?,min?,max?,step?,paramKeyFrom?,paramKeyTo?
All filters require: id, label, dataSource (must reference a key in dataSources).
WidgetSpec Variants
- TableWidget:
type: "table",config.columns?: { key, label }[],config.summary?: { key, op }[],config.groupByKey?,config.groupAggregations?,config.drillDown? - CardViewWidget:
type: "cardView",config.titleKey, optionalconfig.subtitleKey,config.badges,config.metadata,config.primaryMetric,config.template - BarChartWidget:
type: "barChart",config.categoryKey,config.valueKey - LineChartWidget / AreaChartWidget:
config.categoryKey,config.valueKey, optionalconfig.series - PieChartWidget / DoughnutChartWidget / SpiralChartWidget / FunnelChartWidget:
config.categoryKey,config.valueKey - StackedBarChartWidget:
config.categoryKey,config.series - ScatterChartWidget / BubbleChartWidget:
config.xKey,config.yKey, optional or requiredconfig.zKey - TimelineViewWidget / GanttChartWidget:
config.startDateKey,config.endDateKey,config.labelKey, optionalconfig.groupKey,config.statusKey - KpiWidget:
type: "kpi",config.valueKey,config.label?, optional aggregation/format/trend settings
All widgets require: id, dataSource, config. title, groupIds, width, and height are optional.
Sizing hints:
width/heightare optional CSS size strings such as100%,320px, or24rem.- The renderer enforces per-widget minimum sizes even when a smaller hint is provided.
- Current minimums are exported as
WIDGET_SIZE_CONSTRAINTSand available throughgetWidgetSizeConstraints():- table: min
320pxwide,180pxtall - cardView: min
320pxwide,220pxtall - bar/line/stacked charts: min
320pxwide,260pxtall - KPI: min
180pxwide,80pxtall
- table: min
Safe-for-AI Constraints
- Referential integrity: Filter and widget
dataSourcevalues must exist indataSources. - Unique IDs: Widget and filter
idvalues must be unique within the report. - No arbitrary layout: Use only
singleColumnortwoColumn—no pixel-level control. - Stable schema: Adding new filter/widget types requires schema updates; AI should only use documented types.
DataProvider Interface
The host implements DataProvider to provide data. The engine never accesses databases directly.
interface DataProvider {
runQuery(
request: {
name: string;
params?: Record<string, unknown>;
execution?: {
deliveryMode: "paginatedList" | "fullVisual" | "summary";
page?: number;
pageSize?: number;
maxRows?: number;
};
}
): Promise<
| unknown[]
| {
kind?: "rows";
data: unknown[];
totalCount?: number;
pagination?: {
page: number;
pageSize: number;
totalCount?: number;
hasMore?: boolean;
};
}
| {
kind: "limitExceeded";
totalCount: number;
limit: number;
message?: string;
}
>;
}Framework expectations:
paginatedList: backend should useexecution.page/execution.pageSizeand return rows plus pagination metadata.fullVisual: backend must not paginate. Return all rows when they fit underexecution.maxRows; otherwise returnkind: "limitExceeded"withtotalCount.summary: backend returns already-aggregated rows for KPIs/summary widgets.
When a host returns the object form, the engine preserves pagination or limitExceeded metadata on the resolved query so the UI can render table paging controls and explicit “too much data” messages for visuals.
Engine
validateReportSpec(spec, context?, options?): Returns{ version, valid, errors, diagnostics }. Optionaloptions.policy(spec)runs after structural validation; when the policy returnsallowed: false, policy errors are added to diagnostics andvalidis set to false. Hosts can supply a policy for governance (e.g. max widgets, allowed query names).resolveReport(spec, dataProvider, filterState?, options?): Validates, runs queries with merged filter params, returnsResolvedReport. Optionaloptions.onAudit(event)is called with a minimal audit event on success or error so hosts can log to their audit system.
validateReportSpec supports optional grounding context:
availableQueries?: string[]availableFields?: Record<string, string[]>
When grounding context is provided, the validator can also catch:
- unknown
dataSource.queryvalues - unknown widget field references for the selected query
- invalid enum-like values and missing type-specific config
- duplicate filter ids in addition to duplicate widget ids
Shareable URLs (filter state in URL)
Hosts can persist filter state in the URL so links are shareable. Use:
serializeFilterStateToSearchParams(filterState, spec.filters)– returns a query string (e.g.?status=NEW&dueDateFrom=2024-01-01) from current filter state.parseFilterStateFromSearchParams(search, spec.filters)– parseswindow.location.search(or any query string) intoRecord<string, unknown>filter state.
Conventions: single-value filters use the filter id as the param key; multiSelect uses the same key with comma-separated values; dateRange/numericRange use idFrom and idTo. On load, parse the URL and pass the result as initial filter state; on filter change, call serializeFilterStateToSearchParams and update the URL with history.replaceState or pushState.
Save/load reports
Hosts may persist a report by saving the spec as JSON and restore it later. Use serializeReportSpecToJson(spec) to get a string (e.g. for localStorage or a server API), and parseReportSpecFromJson(json) to parse it back; the parser returns { ok: true, spec } or { ok: false, error }. No backend or database is required—persistence is the host's responsibility.
Export
- CSV: Use
exportTableToCsv(columns, rows)with resolved table columns and rows to produce a CSV string. The react-ui table widget exposes an "Export CSV" button that uses this. - PDF: PDF export is not currently exposed in the react-ui renderer. If this is reintroduced later, it will likely use the browser print dialog rather than a dedicated PDF library.
Reporting context contract
Shared types for base and semantic reporting context, and the context provider interface, are exported from @prism-reporting/core. Both the MCP package and application code (e.g. starter example) can use these types.
Base reporting context
BaseReportingContext:source?,tenantId?,queries: QueryCatalogEntry[]QueryCatalogEntry:name,description?,fields?,params?,notes?
Base context drives deterministic validation and runtime behavior (e.g. validateReportSpec grounding).
Semantic reporting context (optional)
SemanticReportingContext:queryAliases?,fieldAliases?,examples?,clarificationHints?- Element types:
QueryAliasEntry,FieldAliasEntry,SemanticExampleEntry,ClarificationHintEntry
Semantic context is for AI grounding only; it must not redefine validation rules.
Context provider interface
ReportingContextProvider:getBaseContext(input?):Promise<BaseReportingContext>(required)getSemanticContext(input?):Promise<SemanticReportingContext | null>(optional method)
Implementations may be local (e.g. starter reading from query catalog) or remote. The MCP server and app both consume context via this provider.
Component Registry
The ComponentRegistry interface maps primitives (table, cardView, barChart, kpi, filterBar, and the other supported visual widgets) to React components. Hosts can provide custom implementations (e.g., Ant Design, MUI) without changing the spec.
Example: Natural Language → ReportSpec
Request: "Show me tasks by status with a date filter"
Generated ReportSpec (conceptual):
{
"id": "tasks-by-status",
"title": "Tasks by Status",
"layout": "singleColumn",
"dataSources": { "tasks": { "name": "tasks", "query": "tasks" } },
"filters": [
{ "type": "select", "id": "status", "label": "Status", "dataSource": "tasks", "options": [...] },
{ "type": "dateRange", "id": "dateRange", "label": "Due Date", "dataSource": "tasks" }
],
"widgets": [
{ "type": "table", "id": "t1", "dataSource": "tasks", "config": { "columns": [...] } }
]
}