@exodus/datapoints
v2.0.0
Published
Provenance-backed datapoint contract: schemas, formula evaluation, registry validation, and data report rendering
Maintainers
Keywords
Readme
@exodus/datapoints
The contract for provenance-backed datapoints: typed definitions, deterministic
formula evaluation, fail-closed registry validation, value formatting,
render-by-reference data reports, and the payload schemas consumers validate
tool responses against. Zero I/O — data sources live behind the
DatapointSource interface, not in this package.
A datapoint is a typed value with an auditable origin chain: which source and
query produced it, when it was retrieved, and for derived values the formula
and each input's chain. A data report is agent-authored text rendered by
reference: the model writes {{dp:datapoint_id}} placeholders, the renderer
substitutes evaluated values, and bare numbers are rejected — the model never
types the final figures.
Install
npm install @exodus/datapointsValidating a report payload
import { reportPayloadSchema } from '@exodus/datapoints';
const parsed = reportPayloadSchema.safeParse(structuredContent.report);
if (parsed.success) {
for (const datapoint of parsed.data.datapoints) {
console.log(datapoint.id, datapoint.formatted, datapoint.tier, datapoint.provenance);
}
}evaluatedDatapointSchema validates single values, and evaluateResultsSchema
validates an evaluate response. Every value carries value, formatted,
unit, tier (human-reviewed or ai-reviewed), retrievedAt, and
provenance; lookups may carry query, description, and freshness, and
derived values carry formulaProvenance.
Rendering placeholders
import { collectPlaceholderIds, renderDataReport } from '@exodus/datapoints';
const text = 'Pay reached {{dp:pay_mtu_30d}} users, up {{dp:pay_mtu_growth_30d}}.';
const ids = collectPlaceholderIds(text);
const report = renderDataReport(text, evaluatedValues);renderDataReport fails closed: a submission containing any digit outside a
placeholder is rejected in full with the offending digits listed. Use
{{lit:3}} for numbers that are ordinary prose rather than data claims.
Series and charts
A lookup with valueType: 'series' holds an ordered list of { label, value }
points (at most MAX_SERIES_POINTS); formatValue summarizes it as
"12 points · Jan 2026 – Dec 2026". Series cannot feed a formula.
A report puts series on a chart with a fenced block the renderer draws:
```chart
kind: line
title: Monthly MTU
series:
- {{dp:custom_mtu_monthly_1a2b3c4d}}
```Kinds and options:
| Key | Values | Applies to |
| ------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------- |
| kind | line, bar, area, stat | required. stat renders one KPI tile per entry; a series entry shows its last value with a sparkline |
| title | text | any |
| series | list of {{dp:<id>}} | required; series datapoints for plotted kinds, scalars or series for stat |
| stacked | true / false | bar, area; only count or USD series |
| orientation | vertical, horizontal | bar |
| layout | overlay, multiples | plotted kinds; multiples draws one panel per series and allows mixed units |
| target | {{dp:<scalar id>}} | plotted kinds without multiples; a reference line sharing the series unit |
renderDataReport parses every block with parseCharts (unclosed fences,
unknown keys, kinds or values, duplicate keys or ids, and option conflicts are
chart_invalid) and validates it with validateChart: plotted entries are
series datapoints with the same number of points and, unless layout:
multiples, one shared unit. Renderers reuse the same parser via
matchChartBlocks so the gateway and the UI agree on the grammar.
rendered is plain text: every placeholder, including those inside a chart
block, is replaced by its formatted value. A renderer that draws charts needs
the placeholder source, which the payload carries as the optional text
field; persist the whole payload so a dashboard can redraw it.
Surface
| Export | Purpose |
| ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| datapointDefinitionSchema | Validates a lookup, derived, or constant definition |
| evaluatedDatapointSchema / reportPayloadSchema / evaluateResultsSchema | Payload schemas for tool responses |
| buildRegistry | Validates a definition set against the available source ids and fails closed |
| resolutionOrder | Dependency order for evaluating a derived datapoint |
| validateFormula / evaluateFormula | Restricted arithmetic over declared inputs |
| evaluateLookup / evaluateConstant / evaluateDerivedDatapoint | Assemble EvaluatedDatapoint values with provenance |
| formatValue | Deterministic en-US formatting for USD, percent, count |
| collectPlaceholderIds / renderDataReport | Render-by-reference data reports |
| parseCharts / validateChart / matchChartBlocks | Chart block grammar shared by the gateway and renderers |
| isSeriesValue / valueTypeOf | Narrow a DatapointValue to its ValueType |
| displayDatapointId / displayFormula | Custom ids without their trailing hash, for display only |
| DatapointSource | Interface every source implementation satisfies |
Formula evaluation
Formulas are arithmetic over declared inputs — rev_2026_q1 / rev_2025_q4 - 1.
They are evaluated by a restricted mathjs build and constrained by a character
allowlist, so assignment, function definition, indexing, comparison,
exponentiation, string literals, and units are rejected at registry load.
