ata-validator 1.6.0 → 1.6.2
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 +21 -0
- package/README.md +32 -15
- package/index.d.ts +9 -4
- package/lib/enrich-error.js +14 -4
- package/lib/error-codes.js +16 -9
- package/lib/js-compiler.js +69 -9
- package/lib/version.js +1 -1
- package/package.json +8 -8
- package/t.d.ts +16 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
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
|
+
## 1.6.2 - 2026-08-19
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- The Standard Schema surface carried no output type. `~standard.validate()` returned `{ value: unknown }` and the `types` carrier the specification defines for inference was missing, so every consumer that reads the validated type off `~standard` (Fastify, tRPC, TanStack Form, Drizzle) saw `unknown` and needed a cast. `~standard` is now typed against the validator's own data type, and `types.output` carries it. Type-only: the runtime object is unchanged, and the specification defines `types` as never present at runtime.
|
|
10
|
+
- Boolean schemas were rejected by the `Validator` constructor's TypeScript signature. `true` and `false` are schemas anywhere JSON Schema allows one, and both have always worked at runtime; only the types disagreed, which made a schema of unknown shape (`object | boolean`) impossible to pass without a cast. Nested boolean subschemas are still typed as objects, so `{ properties: { a: true } }` needs `defineSchema` or a cast.
|
|
11
|
+
- The `t` builder's option bags rejected vendor keywords. `t.object({}, { instanceof: 'Date' })` is what a custom keyword package expects to be given, and the option types only allowed the keywords the builder itself emits, so callers wrote `as never`. Every option bag now accepts unknown keywords alongside the typed ones.
|
|
12
|
+
- `tests/test_interop_types.ts` covers all three under `tsc --noEmit`.
|
|
13
|
+
|
|
14
|
+
## 1.6.1 - 2026-08-09
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- A `$ref` into another document whose own root carries a fragment-only `$ref` validated everything. The code generator has three entry points and only one of them, the boolean path, ran the bail that routes a reference it cannot follow to the interpreted engine. The other two emitted no lines for the reference and returned the empty program as always-valid, so every constraint behind it was dropped without an error. `{ "$ref": "other.json" }` against a document holding its constraints under `#/$defs/...` accepted any input at all. Both paths now run the same two bails the boolean path runs. This is the third silent-accept defect in `$ref` resolution after the two fixed in 1.3.0, and it affects Draft 2020-12 as well as the v1 dialect.
|
|
19
|
+
- `tests/test_cross_doc_root_ref.js` states the case directly, including a cross-document reference to a target that holds its constraints inline, which must stay on the compiled path so the bail is not widened into "any cross-document reference".
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- The official test suite moved on five months, from the March snapshot to 6 August. Every published figure is remeasured against it. Draft 2020-12 is 1294 of 1299, draft 7 is 916 of 927, and the v1 dialect is 1131 of 1133. With code generation blocked, Draft 2020-12 is 1295 of 1299 and v1 is 1132 of 1133. One case that the fix above corrects is no longer a known failure under v1.
|
|
24
|
+
- The buffer path disagrees with `validate()` on 245 of 2222 suite cases rather than 243 of 2208. The two additional disagreements are new suite cases, not a widening: measured against the March snapshot the number is still exactly 243.
|
|
25
|
+
|
|
5
26
|
## 1.6.0 - 2026-08-08
|
|
6
27
|
|
|
7
28
|
### Added
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ata-validator
|
|
2
2
|
|
|
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.
|
|
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. Draft 2020-12, draft 7 and the [JSON Schema v1 dialect](#dialects).
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/ata-validator)
|
|
6
6
|
[](LICENSE)
|
|
@@ -20,11 +20,11 @@ The `ata-validator` package itself is pure JavaScript. The native accelerator (s
|
|
|
20
20
|
npm install ata-validator --omit=optional
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
or set `ATA_NO_NATIVE=1` at runtime. Typical schemas compile to specialized JS; shapes the compiler cannot represent (some `$dynamicRef`, cyclic `$ref`, unusual keyword interactions) fall back to an interpreted engine, so every schema validates in every environment. The pure-JS setup scores the same on the official suite as the native one,
|
|
23
|
+
or set `ATA_NO_NATIVE=1` at runtime. Typical schemas compile to specialized JS; shapes the compiler cannot represent (some `$dynamicRef`, cyclic `$ref`, unusual keyword interactions) fall back to an interpreted engine, so every schema validates in every environment. The pure-JS setup scores the same on the official suite as the native one, 1294 of 1299 Draft 2020-12 cases; the two miss a different `$dynamicRef` scope corner each. Only the buffer and parallel APIs (`isValid` on raw buffers, `countValid`, `batchIsValid`, `validateAndParse`) need the native engine and say so with a clear error.
|
|
24
24
|
|
|
25
|
-
Those four also do not yet agree with `validate()`. Over the official suite they differ on
|
|
25
|
+
Those four also do not yet agree with `validate()`. Over the official suite they differ on 245 of 2222 cases, in both directions, concentrated in `unevaluatedProperties`, `contains`, `const` and the `$ref` family. `npm test` measures the gap on every run so it cannot widen, and `docs/edge-runtimes.md` has the detail. Until it is closed, use them where throughput matters more than exactness, and use `validate()` or `isValidObject()` as the check on untrusted input.
|
|
26
26
|
|
|
27
|
-
Where `new Function` is refused altogether, on Cloudflare Workers, Deno Deploy or under a strict Content-Security-Policy, ata drops to the interpreted engine and scores
|
|
27
|
+
Where `new Function` is refused altogether, on Cloudflare Workers, Deno Deploy or under a strict Content-Security-Policy, ata drops to the interpreted engine and scores 1295 of 1299 with code generation blocked. No flags, and on Workers no `nodejs_compat` either. See [docs/edge-runtimes.md](docs/edge-runtimes.md).
|
|
28
28
|
|
|
29
29
|
In your code:
|
|
30
30
|
|
|
@@ -43,15 +43,29 @@ The `.compiled.mjs` modules are self-contained: zero runtime dependency on ata-v
|
|
|
43
43
|
|
|
44
44
|
| Dimension | Schema | ata-AOT | AJV-runtime | Difference |
|
|
45
45
|
|---|---|---|---|---|
|
|
46
|
-
| Bundle (gzipped) | simple |
|
|
47
|
-
| Bundle (gzipped) | complex |
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
| Bundle (gzipped) | simple | 1.0 KB | 52.7 KB | 50.5x smaller |
|
|
47
|
+
| Bundle (gzipped) | complex | 4.8 KB | 52.7 KB | 11.0x smaller |
|
|
48
|
+
| Bundle (gzipped) | nested | 2.3 KB | 52.7 KB | 23.1x smaller |
|
|
49
|
+
| Cold start | simple | 21 ms | 40 ms | 1.9x faster |
|
|
50
|
+
| Throughput (1M ops) | simple | 258 Mops/s | 102 Mops/s | 2.5x faster |
|
|
51
|
+
| Compile time | simple | 8 µs | 1.61 ms | 191x faster |
|
|
52
|
+
|
|
53
|
+
Reproduce on your machine with `npm run bench:aot-vs-ajv`. Numbers from one run on Apple
|
|
54
|
+
M4 Pro, Node 25.2.1, 2026-08-13. Across three runs throughput moved between 258 and 278
|
|
55
|
+
Mops/s and the compile ratio between 150x and 199x, so treat the last two rows as an order
|
|
56
|
+
of magnitude rather than a constant.
|
|
57
|
+
|
|
58
|
+
The wins are largest on bundle size and compile time because AOT moves work from runtime to
|
|
59
|
+
build time. Throughput and cold start are also faster because the compiled validator is a
|
|
60
|
+
tight straight-line function with no schema-walk overhead.
|
|
61
|
+
|
|
62
|
+
What the table does not cover is data that fails. It measures compiled modules on input that
|
|
63
|
+
passes, and a rejection costs more than a verdict: ata builds an error carrying a code, the
|
|
64
|
+
offending value, a documentation link and a suggestion. On a five-field object schema a
|
|
65
|
+
passing payload costs about 17 ns and a rejected one about 155 ns. `abortEarly: true` or
|
|
66
|
+
`isValidObject()` skips that work when only the verdict matters. Schemas ata declines to
|
|
67
|
+
compile, mostly cross-document `$ref`, `$dynamicRef` and `unevaluated*`, run on the
|
|
68
|
+
interpreted engine and are slower again.
|
|
55
69
|
|
|
56
70
|
## Error messages
|
|
57
71
|
|
|
@@ -385,7 +399,10 @@ const { toStandaloneModule } = require('ata-validator/build');
|
|
|
385
399
|
fs.writeFileSync('./user.validator.mjs', toStandaloneModule(schema, { format: 'esm' }));
|
|
386
400
|
```
|
|
387
401
|
|
|
388
|
-
**Fastify startup
|
|
402
|
+
**Fastify startup, 10 route schemas, from a cold process to the first validated request:
|
|
403
|
+
ajv 19.6 ms, ata 3.1 ms, no build step required.** ata registers in 1.1 ms of that and
|
|
404
|
+
compiles on the first request, so counting only registration would overstate the gap.
|
|
405
|
+
Reproduce with `node benchmark/bench_fastify_boot.mjs`.
|
|
389
406
|
|
|
390
407
|
### Standard Schema V1
|
|
391
408
|
|
|
@@ -479,7 +496,7 @@ Both are implemented in the interpreted engine, so a v1 schema that uses `$dynam
|
|
|
479
496
|
|
|
480
497
|
### Known limitations
|
|
481
498
|
|
|
482
|
-
Running the whole Draft 2020-12 suite with nothing excluded, `format` and `default` under specification semantics (`assertFormat: false`, `useDefaults: false`), gives
|
|
499
|
+
Running the whole Draft 2020-12 suite with nothing excluded, `format` and `default` under specification semantics (`assertFormat: false`, `useDefaults: false`), gives 1294 of 1299 cases, 99.6%. Draft 7 gives 916 of 927, 98.8%. The v1 dialect gives 1131 of 1133, 99.8%, and the two it misses are among the five that fail on 2020-12. `npm run test:suite` reproduces all three and lists the remaining failures by name.
|
|
483
500
|
|
|
484
501
|
Areas that remain deliberate scope decisions for 1.x:
|
|
485
502
|
|
package/index.d.ts
CHANGED
|
@@ -364,13 +364,13 @@ export interface BundleStandaloneOptions extends ValidatorOptions {
|
|
|
364
364
|
format?: 'esm' | 'cjs';
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
export interface StandardSchemaV1Props {
|
|
367
|
+
export interface StandardSchemaV1Props<Output = unknown, Input = unknown> {
|
|
368
368
|
version: 1;
|
|
369
369
|
vendor: "ata-validator";
|
|
370
370
|
validate(
|
|
371
371
|
value: unknown
|
|
372
372
|
):
|
|
373
|
-
| { value:
|
|
373
|
+
| { value: Output }
|
|
374
374
|
| {
|
|
375
375
|
issues: Array<{
|
|
376
376
|
message: string;
|
|
@@ -378,6 +378,11 @@ export interface StandardSchemaV1Props {
|
|
|
378
378
|
path?: ReadonlyArray<{ key: PropertyKey }>;
|
|
379
379
|
}>;
|
|
380
380
|
};
|
|
381
|
+
/**
|
|
382
|
+
* Type-only carrier the Standard Schema spec uses for inference: consumers
|
|
383
|
+
* read the validated type off `types.output`. Never present at runtime.
|
|
384
|
+
*/
|
|
385
|
+
readonly types?: { readonly input: Input; readonly output: Output } | undefined;
|
|
381
386
|
}
|
|
382
387
|
|
|
383
388
|
export interface StandaloneModule {
|
|
@@ -425,7 +430,7 @@ export interface Validator<T = unknown> {
|
|
|
425
430
|
isValidNDJSON(ndjsonBuffer: Buffer): boolean[];
|
|
426
431
|
|
|
427
432
|
/** Standard Schema V1 interface, compatible with Fastify, tRPC, TanStack, etc. */
|
|
428
|
-
readonly "~standard": StandardSchemaV1Props
|
|
433
|
+
readonly "~standard": StandardSchemaV1Props<T>;
|
|
429
434
|
}
|
|
430
435
|
|
|
431
436
|
/** Constructor + statics for {@link Validator}. */
|
|
@@ -433,7 +438,7 @@ export interface ValidatorConstructor {
|
|
|
433
438
|
/** Construct from a JSON Schema literal; the validated data type is inferred. */
|
|
434
439
|
new <const S extends JSONSchema>(schema: S, options?: ValidatorOptions): Validator<Infer<S>>;
|
|
435
440
|
/** Construct from a plain object/string schema, or with an explicit data type. */
|
|
436
|
-
new <T = unknown>(schema: object | string, options?: ValidatorOptions): Validator<T>;
|
|
441
|
+
new <T = unknown>(schema: object | string | boolean, options?: ValidatorOptions): Validator<T>;
|
|
437
442
|
|
|
438
443
|
/** Load a pre-compiled standalone module. Zero schema compilation at startup. */
|
|
439
444
|
fromStandalone<T = unknown>(mod: StandaloneModule, schema: object | string, options?: ValidatorOptions): Validator<T>;
|
package/lib/enrich-error.js
CHANGED
|
@@ -46,14 +46,24 @@ function expectedFor (err) {
|
|
|
46
46
|
|
|
47
47
|
function pickReceived (err, data) {
|
|
48
48
|
if (!data && data !== 0 && data !== false) return undefined;
|
|
49
|
-
// Walk JSON pointer to extract the actual offending value
|
|
49
|
+
// Walk JSON pointer to extract the actual offending value. This runs for
|
|
50
|
+
// every error on every rejected payload, so the walk reads segments straight
|
|
51
|
+
// out of the pointer: no leading-slash regex, no parts array, and no unescape
|
|
52
|
+
// pass on the segments that carry no `~`.
|
|
50
53
|
const p = err.instancePath || err.path || '';
|
|
51
54
|
if (!p) return reprValue(data);
|
|
52
|
-
const
|
|
55
|
+
const len = p.length;
|
|
53
56
|
let cur = data;
|
|
54
|
-
|
|
57
|
+
let i = p.charCodeAt(0) === 47 ? 1 : 0; // 47 is '/'
|
|
58
|
+
for (;;) {
|
|
59
|
+
let j = p.indexOf('/', i);
|
|
60
|
+
if (j === -1) j = len;
|
|
61
|
+
let seg = p.slice(i, j);
|
|
62
|
+
if (seg.indexOf('~') !== -1) seg = seg.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
55
63
|
if (cur == null) return undefined;
|
|
56
|
-
cur = cur[
|
|
64
|
+
cur = cur[seg];
|
|
65
|
+
if (j === len) break;
|
|
66
|
+
i = j + 1;
|
|
57
67
|
}
|
|
58
68
|
return reprValue(cur);
|
|
59
69
|
}
|
package/lib/error-codes.js
CHANGED
|
@@ -77,20 +77,27 @@ function all () {
|
|
|
77
77
|
return Object.keys(CODES).sort();
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
// Reverse lookups, built once. codeFor runs per error on every failing
|
|
81
|
+
// validation, so walking the table there showed up as most of the cost of
|
|
82
|
+
// enriching a rejected payload. Insertion follows sorted code order, so the
|
|
83
|
+
// first writer wins and each keyword keeps the lowest code that carries it.
|
|
84
|
+
const BY_KEYWORD = new Map();
|
|
85
|
+
const BY_FORMAT = new Map();
|
|
86
|
+
for (const c of Object.keys(CODES).sort()) {
|
|
87
|
+
const meta = CODES[c];
|
|
88
|
+
if (!BY_KEYWORD.has(meta.keyword)) BY_KEYWORD.set(meta.keyword, c);
|
|
89
|
+
if (meta.keyword === 'format' && meta.format && !BY_FORMAT.has(meta.format)) BY_FORMAT.set(meta.format, c);
|
|
90
|
+
}
|
|
91
|
+
|
|
80
92
|
// Map a (keyword, optional format) tuple back to a code. Used by the codegen
|
|
81
93
|
// integration to attach codes to existing error sites without rewriting them all.
|
|
82
94
|
function codeFor (keyword, format) {
|
|
83
95
|
if (keyword === 'format' && format) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (meta.keyword === 'format' && meta.format === format) return c;
|
|
87
|
-
}
|
|
88
|
-
return 'ATA3099';
|
|
89
|
-
}
|
|
90
|
-
for (const c of all()) {
|
|
91
|
-
if (CODES[c].keyword === keyword) return c;
|
|
96
|
+
const hit = BY_FORMAT.get(format);
|
|
97
|
+
return hit === undefined ? 'ATA3099' : hit;
|
|
92
98
|
}
|
|
93
|
-
|
|
99
|
+
const hit = BY_KEYWORD.get(keyword);
|
|
100
|
+
return hit === undefined ? null : hit;
|
|
94
101
|
}
|
|
95
102
|
|
|
96
103
|
module.exports = { CODES, get, all, codeFor };
|
package/lib/js-compiler.js
CHANGED
|
@@ -845,6 +845,16 @@ function codegenSafe(schema, schemaMap) {
|
|
|
845
845
|
if (typeof schema.unevaluatedProperties === 'object' && schema.unevaluatedProperties !== null) {
|
|
846
846
|
if (!codegenSafe(schema.unevaluatedProperties, schemaMap)) return false
|
|
847
847
|
}
|
|
848
|
+
// When evaluation is both dynamic (branch-dependent) and some branch
|
|
849
|
+
// unconditionally evaluates all properties, static tracking cannot
|
|
850
|
+
// correctly determine which properties are unevaluated at runtime.
|
|
851
|
+
// For example, a oneOf whose branches include both patternProperties
|
|
852
|
+
// and unevaluatedProperties:true requires annotation-aware evaluation
|
|
853
|
+
// that only the interpreted engine provides.
|
|
854
|
+
if (schema.unevaluatedProperties === false || (typeof schema.unevaluatedProperties === 'object' && schema.unevaluatedProperties !== null)) {
|
|
855
|
+
const evalResult = collectEvaluated(schema, schemaMap)
|
|
856
|
+
if (evalResult.dynamic && evalResult.allProps) return false
|
|
857
|
+
}
|
|
848
858
|
}
|
|
849
859
|
// unevaluatedItems: allow boolean and schema values
|
|
850
860
|
if (schema.unevaluatedItems !== undefined) {
|
|
@@ -861,15 +871,52 @@ function codegenSafe(schema, schemaMap) {
|
|
|
861
871
|
const defs = schema.$defs || schema.definitions
|
|
862
872
|
if (defs) {
|
|
863
873
|
const defNames = new Set(Object.keys(defs))
|
|
864
|
-
// Detect cyclic local refs:
|
|
865
|
-
//
|
|
866
|
-
//
|
|
867
|
-
//
|
|
868
|
-
//
|
|
869
|
-
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
874
|
+
// Detect cyclic local refs: build a directed graph of def->def references and
|
|
875
|
+
// check for cycles using DFS. A plain cross-reference (A -> B where B doesn't
|
|
876
|
+
// refer back to A) is fine; only true cycles (A -> B -> A) must be rejected.
|
|
877
|
+
// The JS codegen returns (() => true) on cycle (permissive), so cyclic schemas
|
|
878
|
+
// must route to the interpreted engine instead.
|
|
879
|
+
const defRefs = {}
|
|
880
|
+
for (const [name, def] of Object.entries(defs)) {
|
|
881
|
+
defRefs[name] = []
|
|
882
|
+
if (typeof def === 'object' && def !== null) {
|
|
883
|
+
const refs = []
|
|
884
|
+
const seen = new WeakSet()
|
|
885
|
+
const collectRefs = (node) => {
|
|
886
|
+
if (typeof node !== 'object' || node === null) return
|
|
887
|
+
if (seen.has(node)) return
|
|
888
|
+
seen.add(node)
|
|
889
|
+
if (node.$ref) {
|
|
890
|
+
const m = /^#\/(?:\$defs|definitions)\/([^/]+)$/.exec(node.$ref)
|
|
891
|
+
if (m && defNames.has(m[1])) refs.push(m[1])
|
|
892
|
+
}
|
|
893
|
+
for (const val of Object.values(node)) {
|
|
894
|
+
if (typeof val === 'object' && val !== null) {
|
|
895
|
+
if (Array.isArray(val)) { for (const item of val) collectRefs(item) }
|
|
896
|
+
else collectRefs(val)
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
collectRefs(def)
|
|
901
|
+
defRefs[name] = refs
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
const hasCycle = (() => {
|
|
905
|
+
const visited = new Set()
|
|
906
|
+
const inStack = new Set()
|
|
907
|
+
const dfs = (node) => {
|
|
908
|
+
if (inStack.has(node)) return true
|
|
909
|
+
if (visited.has(node)) return false
|
|
910
|
+
visited.add(node)
|
|
911
|
+
inStack.add(node)
|
|
912
|
+
for (const neighbor of (defRefs[node] || [])) {
|
|
913
|
+
if (dfs(neighbor)) return true
|
|
914
|
+
}
|
|
915
|
+
inStack.delete(node)
|
|
916
|
+
return false
|
|
917
|
+
}
|
|
918
|
+
return Object.keys(defRefs).some(dfs)
|
|
919
|
+
})()
|
|
873
920
|
if (hasCycle) return false
|
|
874
921
|
for (const [name, def] of Object.entries(defs)) {
|
|
875
922
|
if (/[~/"']/.test(name)) return false // special chars in def name
|
|
@@ -2828,6 +2875,11 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
|
|
|
2828
2875
|
}
|
|
2829
2876
|
}
|
|
2830
2877
|
|
|
2878
|
+
// As in the boolean generator: a reference this path cannot resolve or follow
|
|
2879
|
+
// emits no lines, and an empty program is returned below as always-valid.
|
|
2880
|
+
if (hasUnresolvableRef(schema, eRootDefs, eAnchors, schemaMap, new Set())) return null
|
|
2881
|
+
if (needsBaseTracking(schema, schemaMap, new Set())) return null
|
|
2882
|
+
|
|
2831
2883
|
const ctx = { varCounter: 0, helperCode: [], rootDefs: eRootDefs, refStack: new Set(), schemaMap: schemaMap || null, anchors: eAnchors, rootSchema: schema, userFormats: userFormats || null,
|
|
2832
2884
|
sourceMap: (sourceOpts && sourceOpts.sourceMap && sourceOpts.schemaFile)
|
|
2833
2885
|
? { file: sourceOpts.schemaFile, map: sourceOpts.sourceMap }
|
|
@@ -3449,6 +3501,14 @@ function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats) {
|
|
|
3449
3501
|
}
|
|
3450
3502
|
}
|
|
3451
3503
|
|
|
3504
|
+
// The same two bails the boolean generator makes. Without them this path
|
|
3505
|
+
// emits nothing for a reference it cannot resolve or follow, and an empty
|
|
3506
|
+
// program is returned below as `() => VALID_RESULT`, so every constraint
|
|
3507
|
+
// behind the reference is dropped instead of the schema routing to the
|
|
3508
|
+
// interpreted engine.
|
|
3509
|
+
if (hasUnresolvableRef(schema, cRootDefs, cAnchors, schemaMap, new Set())) return null
|
|
3510
|
+
if (needsBaseTracking(schema, schemaMap, new Set())) return null
|
|
3511
|
+
|
|
3452
3512
|
const ctx = { varCounter: 0, helperCode: [], closureVars: ['_cpLen'], closureVals: [_cpLen],
|
|
3453
3513
|
rootDefs: cRootDefs, refStack: new Set(), schemaMap: schemaMap || null, anchors: cAnchors, rootSchema: schema, userFormats: userFormats || null }
|
|
3454
3514
|
const lines = []
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "JSON Schema validation with first-class TypeScript and zero runtime cost. AOT compile to per-schema ESM modules with zero validator dependency. Generic Validator<T> for TypeBox/Zod/Valibot composition. Optional runtime API. Standard Schema V1 compatible.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"release:check": "node scripts/regen-safe-regex-source.js && node tests/test_pack_purity.js && node scripts/check-doc-coverage.js && node tests/test_error_codes_lock.js && node tests/test_safe_regex_source_sync.js && node tests/test_version_sync.js",
|
|
45
45
|
"build": "cmake-js build --target ata",
|
|
46
46
|
"rebuild": "cmake-js rebuild --target ata",
|
|
47
|
-
"test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_no_eval.js && node tests/test_property_dependencies.js && node tests/test_v1_dialect.js && node tests/test_buffer_path_parity.js && node tests/test_pure_js_unsupported.js && node tests/test_native_load_order.js && node tests/test_pack_purity.js && node tests/test_make_native_package.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_version_sync.js && node tests/test_safe_regex_source_sync.js && node tests/test_t_builder.js && node tests/test_async_refine.js && node tests/test_safe_regex.js && node tests/test_safe_regex_integration.js && node tests/test_aot_build.js && node tests/test_aot_differential.js && node tests/test_aot_cli_build.js && node tests/test_aot_cli_smoke.js && node tests/test_bundle_standalone.js && node tests/test_standalone_anyof.js && node tests/test_standalone_formats.js && node tests/test_aot_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_engine_routing.js && node tests/test_format_engine_parity.js && node tests/test_defs_pointer_alias.js && node tests/test_no_input_mutation.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_error_order.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.js && node tests/test_rich_errors_optout.js && node tests/test_error_messages.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_data_positions.js && node tests/test_render_shared.js && node tests/test_renderers.js && node tests/test_runtime_error_dx.js && node tests/test_aot_error_dx.js && node tests/test_abort_early.js && node tests/test_branch_collapse.js && node tests/test_suggestions.js && node tests/test_cli_validate.js && node tests/test_cli_version.js && node benchmark/bench_aot_size.mjs",
|
|
47
|
+
"test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_no_eval.js && node tests/test_property_dependencies.js && node tests/test_v1_dialect.js && node tests/test_buffer_path_parity.js && node tests/test_pure_js_unsupported.js && node tests/test_native_load_order.js && node tests/test_pack_purity.js && node tests/test_make_native_package.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_version_sync.js && node tests/test_safe_regex_source_sync.js && node tests/test_t_builder.js && node tests/test_async_refine.js && node tests/test_safe_regex.js && node tests/test_safe_regex_integration.js && node tests/test_aot_build.js && node tests/test_aot_differential.js && node tests/test_aot_cli_build.js && node tests/test_aot_cli_smoke.js && node tests/test_bundle_standalone.js && node tests/test_standalone_anyof.js && node tests/test_standalone_formats.js && node tests/test_aot_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_engine_routing.js && node tests/test_format_engine_parity.js && node tests/test_defs_pointer_alias.js && node tests/test_cross_doc_root_ref.js && node tests/test_no_input_mutation.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_error_code_lookup.js && node tests/test_error_order.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.js && node tests/test_enrich_received.js && node tests/test_rich_errors_optout.js && node tests/test_error_messages.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_data_positions.js && node tests/test_render_shared.js && node tests/test_renderers.js && node tests/test_runtime_error_dx.js && node tests/test_aot_error_dx.js && node tests/test_abort_early.js && node tests/test_branch_collapse.js && node tests/test_suggestions.js && node tests/test_cli_validate.js && node tests/test_cli_version.js && node benchmark/bench_aot_size.mjs",
|
|
48
48
|
"bench:size": "node benchmark/bench_aot_size.mjs",
|
|
49
49
|
"test:suite": "node tests/run_suite.js && node tests/run_suite.js draft7 && node tests/run_suite.js v1",
|
|
50
50
|
"test:compat": "node tests/test_compat.js",
|
|
@@ -112,12 +112,12 @@
|
|
|
112
112
|
"LICENSE"
|
|
113
113
|
],
|
|
114
114
|
"optionalDependencies": {
|
|
115
|
-
"@ata-validator/native-darwin-arm64": "1.6.
|
|
116
|
-
"@ata-validator/native-linux-x64-gnu": "1.6.
|
|
117
|
-
"@ata-validator/native-linux-arm64-gnu": "1.6.
|
|
118
|
-
"@ata-validator/native-linux-x64-musl": "1.6.
|
|
119
|
-
"@ata-validator/native-linux-arm64-musl": "1.6.
|
|
120
|
-
"@ata-validator/native-win32-x64": "1.6.
|
|
115
|
+
"@ata-validator/native-darwin-arm64": "1.6.2",
|
|
116
|
+
"@ata-validator/native-linux-x64-gnu": "1.6.2",
|
|
117
|
+
"@ata-validator/native-linux-arm64-gnu": "1.6.2",
|
|
118
|
+
"@ata-validator/native-linux-x64-musl": "1.6.2",
|
|
119
|
+
"@ata-validator/native-linux-arm64-musl": "1.6.2",
|
|
120
|
+
"@ata-validator/native-win32-x64": "1.6.2"
|
|
121
121
|
},
|
|
122
122
|
"peerDependencies": {
|
|
123
123
|
"yaml": "^2.0.0"
|
package/t.d.ts
CHANGED
|
@@ -34,7 +34,18 @@ export type TOptional<S> = S & { readonly [OPTIONAL]: true };
|
|
|
34
34
|
|
|
35
35
|
export type TSchema = JSONSchema;
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Shared by every builder option bag. Annotations and vendor keywords (custom
|
|
39
|
+
* keywords outside the JSON Schema vocabulary) are carried through to the
|
|
40
|
+
* emitted schema, so the option types accept them.
|
|
41
|
+
*/
|
|
42
|
+
export interface CommonOptions {
|
|
43
|
+
description?: string;
|
|
44
|
+
title?: string;
|
|
45
|
+
[keyword: string]: unknown;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface StringOptions extends CommonOptions {
|
|
38
49
|
minLength?: number;
|
|
39
50
|
maxLength?: number;
|
|
40
51
|
pattern?: string;
|
|
@@ -45,7 +56,7 @@ export interface StringOptions {
|
|
|
45
56
|
examples?: readonly string[];
|
|
46
57
|
}
|
|
47
58
|
|
|
48
|
-
export interface NumericOptions {
|
|
59
|
+
export interface NumericOptions extends CommonOptions {
|
|
49
60
|
minimum?: number;
|
|
50
61
|
maximum?: number;
|
|
51
62
|
exclusiveMinimum?: number;
|
|
@@ -57,7 +68,7 @@ export interface NumericOptions {
|
|
|
57
68
|
examples?: readonly number[];
|
|
58
69
|
}
|
|
59
70
|
|
|
60
|
-
export interface ArrayOptions {
|
|
71
|
+
export interface ArrayOptions extends CommonOptions {
|
|
61
72
|
minItems?: number;
|
|
62
73
|
maxItems?: number;
|
|
63
74
|
uniqueItems?: boolean;
|
|
@@ -65,13 +76,13 @@ export interface ArrayOptions {
|
|
|
65
76
|
title?: string;
|
|
66
77
|
}
|
|
67
78
|
|
|
68
|
-
export interface ObjectOptions {
|
|
79
|
+
export interface ObjectOptions extends CommonOptions {
|
|
69
80
|
additionalProperties?: boolean | JSONSchema;
|
|
70
81
|
description?: string;
|
|
71
82
|
title?: string;
|
|
72
83
|
}
|
|
73
84
|
|
|
74
|
-
export interface RecordOptions {
|
|
85
|
+
export interface RecordOptions extends CommonOptions {
|
|
75
86
|
minProperties?: number;
|
|
76
87
|
maxProperties?: number;
|
|
77
88
|
description?: string;
|