ata-validator 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  All notable changes to ata-validator are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/), and this project adheres to semantic versioning.
4
4
 
5
+ ## 0.15.0 - 2026-05-18
6
+
7
+ ### Added
8
+
9
+ - **Compiler-grade error output.** Every validation error now carries a stable `code` (`ATA####`), an `expected`/`received` pair, a `docUrl`, and, when the input came in as a JSON string or Buffer, a `dataFrame` pointing at the offending bytes. The full registry of 46 codes lives at [`docs/error-codes.md`](docs/error-codes.md) with permalinks at `https://ata-validator.com/e/<CODE>`.
10
+ - **Renderer API.** `renderPretty`, `renderCompact`, and `renderJSON` are exported from `ata-validator`. Pretty output mirrors rustc-style code frames with carets, help, and note lines; compact collapses to one line per error; JSON is structured for tooling.
11
+ - **`ata validate` subcommand.** `ata validate <schema> <data>` runs a schema against a JSON data file and prints renderer output. TTY auto-renders pretty; pipes default to compact; `--format=json` returns structured output. `--pretty`, `--compact`, `--max-errors`, `--color`, `--no-color` cover the rest of the surface.
12
+ - **Runtime source maps.** `new Validator(schema, { source: { path, content } })` attaches per-error `schemaSource` (file, line, col, text) by re-parsing the schema with a position-aware scanner.
13
+ - **AOT source maps.** AOT-compiled validators carry the structured error fields (`code`, `docUrl`) and embed per-error `schemaSource` when built with the source map enabled. On by default in development, off when `NODE_ENV=production` or `--no-source` is passed.
14
+ - **`ata compile` / `ata build` flags.** New `--source` / `--no-source` flags. `ata build --dual` emits both a source-mapped artifact (`*.compiled.mjs`) and a stripped one (`*.compiled.min.mjs`) in a single run.
15
+ - **Size budget gate.** `npm run bench:size` enforces a gzipped-byte budget over the AOT codegen output to catch silent bundle bloat. Baseline at `benchmark/baselines/aot-size.json`, gates derive from the baseline with 1.5x headroom.
16
+ - **`oneOf` / `anyOf` collapse.** Branching failures collapse to a single best-branch error (`ATA4001` / `ATA4002` / `ATA4003`) instead of the full branch-tree. The closest matching variant's errors are still available under `branchErrors`. `allOf` errors continue to surface every failing branch.
17
+ - **Suggestions.** A new `suggestion` field nudges users when ata is confident: typo against enum, missing-required typo, format-violation hint, type-coercion nudge. Runtime validators populate automatically; AOT validators expose `attachSuggestions(errors, data)` to keep AOT bundles small.
18
+ - **`richErrors: false` opt-out.** `new Validator(schema, { richErrors: false })` preserves the v0.14 error shape byte-for-byte. `abortEarly: true` continues to short-circuit; the returned error carries `code: 'ATA9000'` and no enrichment.
19
+ - **`release:check` npm script.** Runs the prebuilds, doc-coverage, and error-code lockfile checks in strict mode before a publish.
20
+
21
+ ### Changed
22
+
23
+ - `prepublishOnly` now chains `check-prebuilds`, `check-doc-coverage` (lenient until per-code prose lands), and the error-code lockfile test.
24
+ - `ata compile` and `ata build` failures route through the renderer with code `ATA9002`, so command-line schema errors look the same as runtime ones.
25
+
26
+ ### Notes
27
+
28
+ - **Log scrapers**: errors now carry `code`, `dataFrame`, `suggestion`, and `docUrl` fields. If you serialize `result.errors` directly into logs, line size will grow. Pass `richErrors: false` for the v0.14 shape, or pipe through `renderCompact` for a stable one-line format.
29
+ - **AOT bundle size**: source-mapped variants (`.compiled.mjs`) add up to 200 bytes gzipped for a 10-field schema. Production builds (`NODE_ENV=production` or `--no-source`) emit the no-source variant. Use `ata build --dual` to emit both.
30
+ - **Fastify**: a companion `fastify-ata` release wires the new format into route error responses.
31
+
5
32
  ## 0.14.0 - 2026-05-16
6
33
 
7
34
  ### Added
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ata-validator
2
2
 
3
- Compile JSON Schema files into per-schema ESM modules at build time. Drop the runtime validator from your production bundle. Optional runtime API for dynamic schemas.
3
+ JSON Schema validation with first-class TypeScript and zero runtime cost. AOT compile your schemas to per-schema ESM modules with no validator dependency. `Validator<T>` composes with TypeBox, Zod-from-JSON-Schema, Valibot, or hand-written types. Runtime API available for dynamic schemas.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/ata-validator)](https://www.npmjs.com/package/ata-validator)
6
6
  [![License](https://img.shields.io/npm/l/ata-validator)](LICENSE)
@@ -39,6 +39,41 @@ Reproduce on your machine with `npm run bench:aot-vs-ajv`. Numbers measured on A
39
39
 
40
40
  The wins are largest on bundle size and compile time because AOT moves work from runtime to build time. Throughput and cold start are also faster because the compiled validator is a tight straight-line function with no schema-walk overhead.
41
41
 
42
+ ## Error messages
43
+
44
+ ata's error output is compiler-grade: each error carries a stable code, an inline source frame pointing at the schema file, and another pointing at the offending bytes in the request payload. Renderers ship in three styles:
45
+
46
+ ```ts
47
+ import { Validator, renderPretty, renderCompact, renderJSON } from 'ata-validator'
48
+
49
+ const v = new Validator(schema, { source: { path: 'schemas/user.json', content: schemaText } })
50
+ const r = v.validateJSON(input)
51
+ if (!r.valid) {
52
+ console.error(renderPretty(r.errors))
53
+ // error[ATA3001]: value does not match format "email"
54
+ // --> schemas/user.json:5:7
55
+ // |
56
+ // 5 | "email": { "type": "string", "format": "email" }
57
+ // | ^^^^^^^ expected format 'email'
58
+ // |
59
+ // --> input, byte 23
60
+ // |
61
+ // 1 | {"name":"M","email":"not-an-email","age":-3}
62
+ // | ^^^^^^^^^^^^^^ got "not-an-email"
63
+ // |
64
+ // = help: missing '@' and domain part
65
+ // = note: see https://ata-validator.com/e/ATA3001
66
+ }
67
+ ```
68
+
69
+ The `ata` CLI ships `ata validate <schema> <data>` for one-off checks. TTY auto-renders pretty; pipes default to compact; `--format=json` produces structured output for tooling.
70
+
71
+ Errors carry a stable `code` field (`ATA####`), see the [error code registry](docs/error-codes.md). Each code has a permalink at `https://ata-validator.com/e/<CODE>`.
72
+
73
+ ### Opting out
74
+
75
+ For consumers who built log dashboards on the v0.14 error shape, `new Validator(schema, { richErrors: false })` returns the legacy shape exactly. For high-throughput paths, `abortEarly: true` continues to short-circuit; the returned error carries `code: 'ATA9000'` and no enrichment.
76
+
42
77
  ## When to use the runtime API instead
43
78
 
44
79
  `ata build` is for schemas you know at build time. If your schemas are user-supplied at runtime (form builders, no-code platforms, dynamic API ingestion), use the runtime API:
package/bin/ata.js CHANGED
@@ -8,8 +8,17 @@ function usage() {
8
8
  process.stdout.write(`ata-validator CLI
9
9
 
10
10
  Usage:
11
- ata compile <schema-file> [options] Compile one schema to a standalone module.
12
- ata build <glob>... [options] Compile a project's schemas (glob pattern) per file.
11
+ ata compile <schema-file> [options] Compile one schema to a standalone module.
12
+ ata build <glob>... [options] Compile a project's schemas (glob pattern) per file.
13
+ ata validate <schema> <data> [options] Validate a JSON data file against a schema.
14
+
15
+ Validate options:
16
+ --pretty Render errors with source frames (default on TTY)
17
+ --compact Render errors as one line each (default when piped)
18
+ --format <fmt> Output format: pretty | compact | json
19
+ --max-errors <n> Limit pretty output to N errors (0 = no limit). Default: 20
20
+ --color <when> Color output: auto | always | never. Default: auto
21
+ --no-color Disable color output (alias for --color=never)
13
22
 
14
23
  Compile options:
15
24
  -o, --output <file> Output path. Default: <schema-file>.validator.mjs
@@ -17,6 +26,8 @@ Compile options:
17
26
  --name <TypeName> Name of the top-level type in .d.ts. Default: inferred from filename
18
27
  --no-types Skip .d.ts generation
19
28
  --abort-early Use stub errors (smallest bundle)
29
+ --source Embed schema source map (default in development)
30
+ --no-source Omit source map (default in production, NODE_ENV=production)
20
31
 
21
32
  Build options:
22
33
  --out-dir <dir> Write outputs into this directory instead of alongside sources
@@ -29,6 +40,9 @@ Build options:
29
40
  --strict Treat any AOT-incompatible schema as a build error (default: skip + warn)
30
41
  --watch Re-emit on schema change (Ctrl-C to exit)
31
42
  --no-types Skip .d.mts/.d.cts emission alongside compiled modules
43
+ --source Embed schema source map (default in development)
44
+ --no-source Omit source map (default in production, NODE_ENV=production)
45
+ --dual Emit both .compiled.mjs (with source) and .compiled.min.mjs (without)
32
46
 
33
47
  -h, --help Show this message
34
48
 
@@ -36,11 +50,25 @@ Examples:
36
50
  ata compile schemas/user.json -o src/generated/user.validator.mjs
37
51
  ata build 'schemas/*.json'
38
52
  ata build 'src/**/*.schema.json' --out-dir build/validators
53
+ ata validate schemas/user.json payload.json --pretty
39
54
  `);
40
55
  }
41
56
 
42
57
  function parseArgs(argv) {
43
58
  const out = { _: [], opts: {} };
59
+ // Normalize --key=value into --key value pairs so equals-form is accepted
60
+ // for every option without per-option branches below.
61
+ const normalized = [];
62
+ for (let i = 0; i < argv.length; i++) {
63
+ const a = argv[i];
64
+ if (a.startsWith('--') && a.includes('=')) {
65
+ const eq = a.indexOf('=');
66
+ normalized.push(a.slice(0, eq), a.slice(eq + 1));
67
+ } else {
68
+ normalized.push(a);
69
+ }
70
+ }
71
+ argv = normalized;
44
72
  for (let i = 0; i < argv.length; i++) {
45
73
  const a = argv[i];
46
74
  if (a === '-h' || a === '--help') { out.opts.help = true; continue; }
@@ -64,12 +92,34 @@ function parseArgs(argv) {
64
92
  continue;
65
93
  }
66
94
  if (a === '--watch') { out.opts.watch = true; continue; }
95
+ if (a === '--source') { out.opts.source = true; continue; }
96
+ if (a === '--no-source') { out.opts.source = false; continue; }
97
+ if (a === '--dual') { out.opts.dual = true; continue; }
98
+ if (a === '--pretty') { out.opts.pretty = true; continue; }
99
+ if (a === '--compact') { out.opts.compact = true; continue; }
100
+ if (a === '--no-color') { out.opts.noColor = true; continue; }
101
+ if (a === '--color') { out.opts.color = argv[++i]; continue; }
102
+ if (a === '--max-errors') {
103
+ const v = argv[++i];
104
+ const n = Number(v);
105
+ if (!Number.isFinite(n) || n < 0 || !Number.isInteger(n)) {
106
+ throw new Error(`--max-errors requires a non-negative integer (got "${v}")`);
107
+ }
108
+ out.opts.maxErrors = n;
109
+ continue;
110
+ }
67
111
  if (a.startsWith('-')) { throw new Error(`Unknown option: ${a}`); }
68
112
  out._.push(a);
69
113
  }
70
114
  return out;
71
115
  }
72
116
 
117
+ function resolveSourceDefault (opts) {
118
+ if (opts.source === true) return true;
119
+ if (opts.source === false) return false;
120
+ return process.env.NODE_ENV !== 'production';
121
+ }
122
+
73
123
  function inferOutput(inputPath, format) {
74
124
  const dir = path.dirname(inputPath);
75
125
  const base = path.basename(inputPath, path.extname(inputPath));
@@ -77,6 +127,27 @@ function inferOutput(inputPath, format) {
77
127
  return path.join(dir, base + ext);
78
128
  }
79
129
 
130
+ // Renders a schema-compile failure through the structured error pipeline so
131
+ // `ata compile` / `ata build` surface the same look-and-feel as runtime
132
+ // validation errors. The actual position of the schema flaw isn't known here
133
+ // (parsing or codegen already bailed), so schemaSource is left undefined.
134
+ function reportCompileError (schemaFile, message) {
135
+ const { renderPretty, renderCompact } = require('..');
136
+ const fmt = process.stdout.isTTY ? 'pretty' : 'compact';
137
+ const err = {
138
+ code: 'ATA9002',
139
+ keyword: '__compile__',
140
+ path: '',
141
+ message,
142
+ schemaSource: undefined,
143
+ docUrl: 'https://ata-validator.com/e/ATA9002',
144
+ };
145
+ const out = fmt === 'pretty'
146
+ ? renderPretty([err], { color: 'auto', context: schemaFile })
147
+ : renderCompact([err], { color: 'auto', context: schemaFile });
148
+ process.stderr.write(out + '\n');
149
+ }
150
+
80
151
  function cmdCompile(args) {
81
152
  if (args._.length === 0) {
82
153
  process.stderr.write('error: missing <schema-file>\n\n');
@@ -96,7 +167,7 @@ function cmdCompile(args) {
96
167
  try {
97
168
  schemaStr = fs.readFileSync(input, 'utf8');
98
169
  } catch (e) {
99
- process.stderr.write(`error: cannot read ${input}: ${e.message}\n`);
170
+ reportCompileError(input, `cannot read ${input}: ${e.message}`);
100
171
  process.exit(1);
101
172
  }
102
173
 
@@ -104,15 +175,32 @@ function cmdCompile(args) {
104
175
  try {
105
176
  schema = JSON.parse(schemaStr);
106
177
  } catch (e) {
107
- process.stderr.write(`error: ${input} is not valid JSON: ${e.message}\n`);
178
+ reportCompileError(input, `${input} is not valid JSON: ${e.message}`);
108
179
  process.exit(1);
109
180
  }
110
181
 
111
182
  const { Validator } = require('..');
112
- const v = new Validator(schema);
113
- const src = v.toStandaloneModule({ format, abortEarly });
183
+ let v;
184
+ try {
185
+ v = new Validator(schema);
186
+ } catch (e) {
187
+ reportCompileError(input, e.message);
188
+ process.exit(1);
189
+ }
190
+ const source = resolveSourceDefault(args.opts);
191
+ let sourceMap = null;
192
+ if (source) {
193
+ try {
194
+ const { buildPositionMap } = require('../lib/source-positions');
195
+ sourceMap = buildPositionMap(schemaStr);
196
+ } catch {
197
+ sourceMap = null;
198
+ }
199
+ }
200
+ const schemaFile = path.relative(process.cwd(), input) || input;
201
+ const src = v.toStandaloneModule({ format, abortEarly, source, sourceMap, schemaFile });
114
202
  if (!src) {
115
- process.stderr.write('error: schema is too complex for standalone compilation\n');
203
+ reportCompileError(input, 'schema is too complex for standalone compilation');
116
204
  process.exit(1);
117
205
  }
118
206
 
@@ -165,6 +253,9 @@ function cmdBuild(args) {
165
253
  strict: !!args.opts.strict,
166
254
  types: args.opts.types,
167
255
  cacheFile: args.opts.cacheFile,
256
+ // Forward the explicit user choice (true / false / undefined). The
257
+ // aot-build module applies the NODE_ENV-aware default when undefined.
258
+ source: args.opts.source,
168
259
  };
169
260
 
170
261
  const printReport = (report) => {
@@ -182,7 +273,7 @@ function cmdBuild(args) {
182
273
  process.stdout.write(`ata: skipped ${s.input}: ${s.reason}\n`);
183
274
  }
184
275
  for (const f of report.failed) {
185
- process.stderr.write(`ata: failed ${f.input}: ${f.error}\n`);
276
+ reportCompileError(f.input, f.error);
186
277
  }
187
278
  };
188
279
 
@@ -199,16 +290,96 @@ function cmdBuild(args) {
199
290
  return;
200
291
  }
201
292
 
202
- buildLib.build(buildOpts).then((report) => {
203
- printReport(report);
204
- if (args.opts.check && report.staleCount > 0) process.exit(1);
205
- if (report.failed.length > 0) process.exit(1);
206
- }).catch((e) => {
293
+ // --dual emits two artifacts per schema: one source-mapped (default suffix)
294
+ // and one stripped (suffix .compiled.min). Useful for shipping both a
295
+ // developer-friendly bundle and a production-lean one from a single build.
296
+ const dual = !!args.opts.dual;
297
+ const runs = dual
298
+ ? [
299
+ { ...buildOpts, source: true, suffix: args.opts.suffix || '.compiled' },
300
+ { ...buildOpts, source: false, suffix: (args.opts.suffix || '.compiled') + '.min' },
301
+ ]
302
+ : [buildOpts];
303
+
304
+ (async () => {
305
+ let anyFailed = false;
306
+ let anyStale = false;
307
+ for (const run of runs) {
308
+ const report = await buildLib.build(run);
309
+ printReport(report);
310
+ if (args.opts.check && report.staleCount > 0) anyStale = true;
311
+ if (report.failed.length > 0) anyFailed = true;
312
+ }
313
+ if (anyFailed || anyStale) process.exit(1);
314
+ })().catch((e) => {
207
315
  process.stderr.write(`error: ${e.message}\n`);
208
316
  process.exit(1);
209
317
  });
210
318
  }
211
319
 
320
+ function cmdValidate (args) {
321
+ if (args._.length < 2) {
322
+ process.stderr.write('error: ata validate <schema> <data-file>\n');
323
+ process.exit(2);
324
+ }
325
+ const [schemaPath, dataPath] = args._;
326
+
327
+ let schemaContent;
328
+ try {
329
+ schemaContent = fs.readFileSync(schemaPath, 'utf8');
330
+ } catch (e) {
331
+ process.stderr.write(`error: cannot read ${schemaPath}: ${e.message}\n`);
332
+ process.exit(2);
333
+ }
334
+
335
+ let schema;
336
+ try {
337
+ schema = JSON.parse(schemaContent);
338
+ } catch (e) {
339
+ process.stderr.write(`error: ${schemaPath} is not valid JSON: ${e.message}\n`);
340
+ process.exit(2);
341
+ }
342
+
343
+ let data;
344
+ try {
345
+ data = fs.readFileSync(dataPath, 'utf8');
346
+ } catch (e) {
347
+ process.stderr.write(`error: cannot read ${dataPath}: ${e.message}\n`);
348
+ process.exit(2);
349
+ }
350
+
351
+ const { Validator, renderPretty, renderCompact, renderJSON } = require('..');
352
+ let v;
353
+ try {
354
+ v = new Validator(schema, { source: { path: schemaPath, content: schemaContent } });
355
+ } catch (e) {
356
+ process.stderr.write(`error: schema compile failed: ${e.message}\n`);
357
+ process.exit(2);
358
+ }
359
+
360
+ const r = v.validateJSON(data);
361
+ if (r.valid) { process.exit(0); }
362
+
363
+ const fmt = args.opts.format
364
+ || (args.opts.pretty ? 'pretty' : args.opts.compact ? 'compact' : null)
365
+ || (process.stdout.isTTY ? 'pretty' : 'compact');
366
+ const colorOpt = args.opts.color || (args.opts.noColor ? 'never' : 'auto');
367
+ const ctx = `${dataPath} against ${schemaPath}`;
368
+ let out;
369
+ if (fmt === 'json') {
370
+ out = renderJSON(r.errors, { pretty: true, context: ctx });
371
+ } else if (fmt === 'pretty') {
372
+ out = renderPretty(r.errors, { color: colorOpt, context: ctx, maxErrors: args.opts.maxErrors });
373
+ } else if (fmt === 'compact') {
374
+ out = renderCompact(r.errors, { color: colorOpt, context: ctx });
375
+ } else {
376
+ process.stderr.write(`error: --format must be pretty, compact, or json (got "${fmt}")\n`);
377
+ process.exit(2);
378
+ }
379
+ process.stderr.write(out + '\n');
380
+ process.exit(1);
381
+ }
382
+
212
383
  function main() {
213
384
  const argv = process.argv.slice(2);
214
385
  if (argv.length === 0) { usage(); process.exit(0); }
@@ -237,6 +408,11 @@ function main() {
237
408
  return;
238
409
  }
239
410
 
411
+ if (cmd === 'validate') {
412
+ cmdValidate(args);
413
+ return;
414
+ }
415
+
240
416
  process.stderr.write(`error: unknown command "${cmd}"\n\n`);
241
417
  usage();
242
418
  process.exit(1);
package/build.d.ts CHANGED
@@ -19,6 +19,12 @@ export interface BuildOptions {
19
19
  strict?: boolean;
20
20
  /** Emit a .d.mts/.d.cts/.d.ts sibling for each compiled module. Default: true. */
21
21
  types?: boolean;
22
+ /**
23
+ * Embed a schema source map and bake per-error `schemaSource` frames.
24
+ * Defaults to true when `NODE_ENV !== 'production'`, false otherwise.
25
+ * Set explicitly to override the environment default.
26
+ */
27
+ source?: boolean;
22
28
  }
23
29
 
24
30
  export interface CompiledEntry {
package/index.browser.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  // Browser ESM entry — same code, native addon stubbed out by bundler via "browser" field.
2
2
  import mod from './index.js';
3
- export const { Validator, validate, version, createPaddedBuffer, SIMDJSON_PADDING } = mod;
3
+ export const { Validator, validate, version, createPaddedBuffer, SIMDJSON_PADDING, renderPretty, renderCompact, renderJSON } = mod;
4
4
  export default mod;
package/index.d.ts CHANGED
@@ -12,6 +12,66 @@ export interface ValidationError {
12
12
  parentSchema?: object;
13
13
  }
14
14
 
15
+ export interface SchemaSource {
16
+ file: string;
17
+ line: number;
18
+ col: number;
19
+ text: string;
20
+ }
21
+
22
+ export interface DataFrame {
23
+ byteOffset: number;
24
+ length: number;
25
+ line: number;
26
+ col: number;
27
+ text: string;
28
+ }
29
+
30
+ export interface Suggestion {
31
+ text: string;
32
+ kind: 'typo' | 'format' | 'coercion' | 'similar-key';
33
+ }
34
+
35
+ export type ErrorCode = `ATA${number}`;
36
+
37
+ export interface RichValidationError {
38
+ code: ErrorCode;
39
+ message: string;
40
+ keyword: string;
41
+ path: string;
42
+ expected?: string;
43
+ received?: string;
44
+ schemaPath?: string;
45
+ schemaSource?: SchemaSource;
46
+ dataFrame?: DataFrame;
47
+ suggestion?: Suggestion;
48
+ docUrl?: string;
49
+ // Back-compat aliases retained indefinitely
50
+ instancePath?: string;
51
+ dataPath?: string;
52
+ params?: Record<string, unknown>;
53
+ parentSchema?: unknown;
54
+ }
55
+
56
+ export interface RenderOptions {
57
+ color?: 'auto' | 'always' | 'never';
58
+ cwd?: string;
59
+ }
60
+
61
+ export interface PrettyOptions extends RenderOptions {
62
+ maxErrors?: number;
63
+ }
64
+
65
+ export interface CompactOptions extends RenderOptions {}
66
+
67
+ export interface JSONRenderOptions {
68
+ pretty?: boolean;
69
+ }
70
+
71
+ export function renderPretty(errors: RichValidationError[], opts?: PrettyOptions): string;
72
+ export function renderCompact(errors: RichValidationError[], opts?: CompactOptions): string;
73
+ export function renderJSON(errors: RichValidationError[], opts?: JSONRenderOptions): string;
74
+
15
75
  /** A user-supplied format checker. Receives the candidate value, returns true if valid. */
16
76
  export type FormatChecker = (value: string) => boolean;
17
77
 
@@ -42,6 +102,17 @@ export interface ValidatorOptions {
42
102
  * instead of collecting full error details. Smaller hot-path allocation.
43
103
  */
44
104
  abortEarly?: boolean;
105
+ /**
106
+ * Optional source descriptor for the schema. When supplied, validation errors
107
+ * carry a `schemaSource` frame pointing to the originating file/line/col.
108
+ */
109
+ source?: { path: string; content: string };
110
+ /**
111
+ * When true (default), validate() returns enriched errors with stable codes,
112
+ * docUrl, expected/received hints. Set to false to opt back into the
113
+ * v0.14 error shape.
114
+ */
115
+ richErrors?: boolean;
45
116
  }
46
117
 
47
118
  export interface BundleStandaloneOptions extends ValidatorOptions {