Appearance
Validation
Pre-mount checks for chartData. For the look-and-export loop, see Visual QA.
ChartBuddy validates chart data at runtime, not just at compile time. TypeScript types are erased at build time, and chart data almost always arrives at runtime — parsed JSON, a Sheets range, untyped JSON, or an untyped setChartData().
Two ways in:
new Insight(),setChartData(),setData(), andupdate()validate their input and throwChartDataValidationErroron an error-severity problem.validateChartData()never throws and returns every problem it found. Use it to check a config before you mount one.
The second is what makes a generate → check → repair loop possible without rendering anything, which matters when configs are generated or assembled at runtime.
Checking before you mount
js
import { validateChartData, Insight } from '@chartbuddy.io/embed';
const { valid, errors, warnings } = validateChartData(candidate);
if (!valid) {
// Branch on `code`, fix the value at `path`.
for (const issue of errors) {
console.log(issue.code, issue.path, issue.expected, issue.received);
}
} else {
new Insight('#chart', { chartData: candidate });
}ValidationResult
| Field | Type | Meaning |
|---|---|---|
valid | boolean | True when there are no error-severity issues. Warnings do not invalidate. |
issues | ValidationIssue[] | Everything found, errors and warnings, in document order. |
errors | ValidationIssue[] | The error-severity subset — what makes valid false. |
warnings | ValidationIssue[] | Rendered anyway, but probably a bug. |
ValidationIssue
| Field | Type | Always present | Meaning |
|---|---|---|---|
path | string | yes | Where the problem is, e.g. pie.innerRadiusRatio, seriesData[2][0]. Empty string for whole-object problems. |
code | ValidationIssueCode | yes | Stable classification. Branch on this. |
severity | 'error' | 'warning' | yes | Errors reject at the API boundary; warnings are logged. |
message | string | yes | For humans. May be reworded in any release — never parse it. |
expected | string | no | What the schema wanted: 'number', 'integer', '>= 0', '2D array'. |
received | string | no | What arrived: 'string', 'null', '1.5'. |
allowed | string[] | no | The full legal set, for not-in-enum and unknown-chart-type. |
suggestion | string | no | Nearest legal value, when one is close enough to be a likely typo. |
Optional fields are omitted, not set to undefined, so issues serialize cleanly to JSON.
Issue codes
code is the stable contract. New codes may be added in a minor release, so treat an unrecognized code as a generic failure rather than crashing.
| Code | Severity | Cause | Repair |
|---|---|---|---|
not-an-object | error | The value is not a chart-data object at all. | Pass an object. |
unknown-chart-type | error | chartType is not a known type or alias. | Use suggestion, or pick from allowed. |
empty-patch | error | Neither chartType nor seriesData supplied. | Include at least one, or set requireSomething: false. |
wrong-type | error | A known field holds the wrong JavaScript type. | Coerce to expected. Covers NaN / Infinity. |
out-of-range | error | A numeric field is outside its documented range. | Clamp to the bound in expected. |
not-in-enum | error | A string field is outside its legal set. | Use suggestion, or pick from allowed. |
series-data-shape | error | seriesData is not a usable grid for this chart type. | Reshape — expected names the row/column minimum. |
ragged-series-data | warning | Rows have unequal lengths. | Pad the short rows. The renderer pads for you, but the result is rarely what you meant. |
foreign-option-bag | error | An option bag belongs to a different chart type. | Move the options to the bag in suggestion. |
Unknown keys are never an issue at any level. Extra fields are ignored.
Repairing automatically
suggestion and allowed exist so a fix does not need a second model call:
js
function repair(chartData) {
const patched = structuredClone(chartData);
for (const issue of validateChartData(patched).errors) {
if (issue.suggestion) setAtPath(patched, issue.path, issue.suggestion);
}
return patched;
}'clusterdBar' → suggestion: 'clusteredBar'; orientation: 'verical' → suggestion: 'vertical'.
Catching the throw
When you skip the pre-check, the API boundary still stops bad data. The thrown error carries the same structured issues:
js
import { ChartDataValidationError } from '@chartbuddy.io/embed';
try {
insight.setChartData(candidate);
} catch (err) {
if (err instanceof ChartDataValidationError) {
console.log(err.errors); // error-severity issues
console.log(err.warnings); // logged, did not cause the throw
console.log(err.issues); // both
JSON.stringify(err); // { name, message, issues }
}
}Warnings never throw. They go to console.warn and the call proceeds.
Options
js
validateChartData(chartData, {
form: 'auto', // 'patch' | 'resolved' | 'auto'
requireSomething: true, // demand chartType and/or seriesData
maxIssues: 20, // cap the report
});form decides whether a foreign option bag is a mistake. A hand-authored patch names one chart type, so { chartType: 'pie', bar: {…} } is worth reporting. A resolved snapshot from getChartData() legitimately carries every bag, because defaults seed them all. auto tells them apart by whether every bag is present, which is right in practice — pass form explicitly when you know.
requireSomething should be false when validating a styling-only patch:
js
validateChartData({ title: { text: 'FY26' } }, { requireSomething: false });Formatting for humans
js
import { formatValidationIssues } from '@chartbuddy.io/embed';
console.error(formatValidationIssues(result.issues, '[my-app]'));Where else validation runs
Inside the ChartBuddy app, validation is advisory (logged, not thrown) so charts keep opening while you edit. The embed API is strict and throws, so callers can catch and fix bad configs before retrying.
Generating valid data in the first place
The machine-readable schema ships in the package and describes every field the validator checks:
js
import schema from '@chartbuddy.io/embed/chart-schema.json';Constrain generation with it and most of this page stops mattering. See chartData schema.