ata-validator 0.16.0 → 0.17.1
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 +16 -0
- package/index.d.ts +92 -18
- package/index.js +24 -9
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
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.17.1 - 2026-05-24
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Standalone output for schemas with `anyOf` or `oneOf` no longer references undefined branch helpers. `toStandaloneModule` and `bundleCompact` now emit the hoisted branch functions, so the generated module runs instead of throwing on the first validation. `bundleStandalone` already emitted them.
|
|
10
|
+
|
|
11
|
+
## 0.17.0 - 2026-05-23
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Static type inference from JSON Schema literals. The new exported `Infer<S>` type maps a schema literal to its data type, and `new Validator(defineSchema({...}))` now returns `Validator<Infer<S>>`, so `validate()` narrows `result.data` with no manual type annotation. Write plain JSON Schema, get the type for free, no builder DSL. Covers primitives, type-array unions, `const`, `enum`, objects (required vs optional keys), and arrays; `$ref`, tuples, and `anyOf`/`oneOf` infer `unknown` for now. Pure `.d.ts` change, no runtime impact.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- `validateAndParse()` is now implemented in JavaScript (`JSON.parse` then validate) and returns `{ valid, value, errors }`. It previously called a native method that does not exist and threw on every call. It now works with or without the native addon and in the browser; malformed JSON returns `valid: false` with an `ATA9001` error instead of throwing.
|
|
20
|
+
|
|
5
21
|
## 0.16.0 - 2026-05-23
|
|
6
22
|
|
|
7
23
|
### Added
|
package/index.d.ts
CHANGED
|
@@ -164,6 +164,74 @@ export interface JSONSchema {
|
|
|
164
164
|
[keyword: string]: unknown;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Collapse an intersection of mapped object types into a single readable object
|
|
169
|
+
* type. `& {}` forces TypeScript to evaluate the mapped type eagerly.
|
|
170
|
+
*/
|
|
171
|
+
type Simplify<T> = { [K in keyof T]: T[K] } & {};
|
|
172
|
+
|
|
173
|
+
/** Keys listed in a schema's `required` array, as a string union (or never). */
|
|
174
|
+
type RequiredKeys<S> = S extends { required: infer R }
|
|
175
|
+
? R extends ReadonlyArray<infer K extends string>
|
|
176
|
+
? K
|
|
177
|
+
: never
|
|
178
|
+
: never;
|
|
179
|
+
|
|
180
|
+
/** Object shape: required keys are required, all other declared keys optional. */
|
|
181
|
+
type InferObject<S> = S extends { properties: infer P }
|
|
182
|
+
? Simplify<
|
|
183
|
+
{ [K in keyof P as K extends RequiredKeys<S> ? K : never]: Infer<P[K]> } &
|
|
184
|
+
{ [K in keyof P as K extends RequiredKeys<S> ? never : K]?: Infer<P[K]> }
|
|
185
|
+
>
|
|
186
|
+
: Record<string, unknown>;
|
|
187
|
+
|
|
188
|
+
/** Array shape: `items` as a single schema maps to an element type; tuple/absent -> unknown[]. */
|
|
189
|
+
type InferArray<S> = S extends { items: infer I }
|
|
190
|
+
? I extends ReadonlyArray<unknown>
|
|
191
|
+
? unknown[]
|
|
192
|
+
: Infer<I>[]
|
|
193
|
+
: unknown[];
|
|
194
|
+
|
|
195
|
+
/** Map a single JSON Schema type name (+ its schema) to a TS type. */
|
|
196
|
+
type InferByTypeName<N, S> = N extends 'object'
|
|
197
|
+
? InferObject<S>
|
|
198
|
+
: N extends 'array'
|
|
199
|
+
? InferArray<S>
|
|
200
|
+
: N extends 'string'
|
|
201
|
+
? string
|
|
202
|
+
: N extends 'number'
|
|
203
|
+
? number
|
|
204
|
+
: N extends 'integer'
|
|
205
|
+
? number
|
|
206
|
+
: N extends 'boolean'
|
|
207
|
+
? boolean
|
|
208
|
+
: N extends 'null'
|
|
209
|
+
? null
|
|
210
|
+
: unknown;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Infer the TypeScript data type a JSON Schema literal describes (Core scope).
|
|
214
|
+
*
|
|
215
|
+
* Handles: primitives, `type` arrays (union), `const`, `enum`, objects
|
|
216
|
+
* (`properties` + `required` -> required/optional keys), and arrays
|
|
217
|
+
* (`items` as a single schema). `$ref`/`$defs`, tuples, and `anyOf`/`oneOf`/
|
|
218
|
+
* `allOf` are not yet inferred and resolve to `unknown` rather than erroring.
|
|
219
|
+
*
|
|
220
|
+
* Pair with {@link defineSchema}:
|
|
221
|
+
* `const s = defineSchema({...}); type T = Infer<typeof s>;`
|
|
222
|
+
*/
|
|
223
|
+
export type Infer<S> = S extends { const: infer C }
|
|
224
|
+
? C
|
|
225
|
+
: S extends { enum: infer E }
|
|
226
|
+
? E extends ReadonlyArray<infer U>
|
|
227
|
+
? U
|
|
228
|
+
: unknown
|
|
229
|
+
: S extends { type: infer T }
|
|
230
|
+
? T extends ReadonlyArray<infer N>
|
|
231
|
+
? InferByTypeName<N, S>
|
|
232
|
+
: InferByTypeName<T, S>
|
|
233
|
+
: unknown;
|
|
234
|
+
|
|
167
235
|
export type ValidationResult<T = unknown> =
|
|
168
236
|
| { valid: true; data: T; errors: ValidationError[] }
|
|
169
237
|
| { valid: false; data?: never; errors: ValidationError[] };
|
|
@@ -231,9 +299,8 @@ export interface StandaloneModule {
|
|
|
231
299
|
errFn: ((data: unknown, allErrors?: boolean) => ValidationResult) | null;
|
|
232
300
|
}
|
|
233
301
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
302
|
+
/** Instance surface of a compiled validator. */
|
|
303
|
+
export interface Validator<T = unknown> {
|
|
237
304
|
/** Add a schema to the registry for cross-schema $ref resolution */
|
|
238
305
|
addSchema(schema: object): void;
|
|
239
306
|
|
|
@@ -249,7 +316,7 @@ export class Validator<T = unknown> {
|
|
|
249
316
|
/** Fast boolean check for a JSON string */
|
|
250
317
|
isValidJSON(jsonString: string): boolean;
|
|
251
318
|
|
|
252
|
-
/** Parse JSON
|
|
319
|
+
/** Parse JSON and validate against the schema. Returns the parsed value and the validation result. Works without the native addon. */
|
|
253
320
|
validateAndParse(jsonString: string | Buffer): ValidateAndParseResult<T>;
|
|
254
321
|
|
|
255
322
|
/** Ultra-fast buffer validation via native addon */
|
|
@@ -276,41 +343,48 @@ export class Validator<T = unknown> {
|
|
|
276
343
|
/**
|
|
277
344
|
* Generate a self-contained module string with `validate`/`isValid` exports.
|
|
278
345
|
* The output has zero runtime dependency on ata-validator.
|
|
279
|
-
*
|
|
280
|
-
* - format: 'esm' (default) or 'cjs'.
|
|
281
|
-
* - abortEarly: if true, invalid results are a shared frozen stub (smaller output, no error details).
|
|
282
|
-
*
|
|
283
|
-
* Returns null if the schema cannot be compiled to a standalone module.
|
|
284
346
|
*/
|
|
285
347
|
toStandaloneModule(options?: { format?: 'esm' | 'cjs'; abortEarly?: boolean }): string | null;
|
|
286
348
|
|
|
349
|
+
/** Standard Schema V1 interface, compatible with Fastify, tRPC, TanStack, etc. */
|
|
350
|
+
readonly "~standard": StandardSchemaV1Props;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** Constructor + statics for {@link Validator}. */
|
|
354
|
+
export interface ValidatorConstructor {
|
|
355
|
+
/** Construct from a JSON Schema literal; the validated data type is inferred. */
|
|
356
|
+
new <const S extends JSONSchema>(schema: S, options?: ValidatorOptions): Validator<Infer<S>>;
|
|
357
|
+
/** Construct from a plain object/string schema, or with an explicit data type. */
|
|
358
|
+
new <T = unknown>(schema: object | string, options?: ValidatorOptions): Validator<T>;
|
|
359
|
+
|
|
287
360
|
/** Load a pre-compiled standalone module. Zero schema compilation at startup. */
|
|
288
|
-
|
|
361
|
+
fromStandalone<T = unknown>(mod: StandaloneModule, schema: object | string, options?: ValidatorOptions): Validator<T>;
|
|
289
362
|
|
|
290
363
|
/** Bundle multiple schemas into a single JS module string. Load with Validator.loadBundle(). */
|
|
291
|
-
|
|
364
|
+
bundle(schemas: object[], options?: ValidatorOptions): string;
|
|
292
365
|
|
|
293
366
|
/**
|
|
294
367
|
* Bundle multiple schemas into a self-contained JS module with no
|
|
295
368
|
* ata-validator runtime dependency. Cross-schema `$ref` resolves between
|
|
296
369
|
* the supplied schemas. Set `format: 'esm'` for ESM output (default 'cjs').
|
|
297
370
|
*/
|
|
298
|
-
|
|
371
|
+
bundleStandalone(schemas: object[], options?: BundleStandaloneOptions): string;
|
|
299
372
|
|
|
300
373
|
/**
|
|
301
374
|
* Bundle multiple schemas with deduplicated shared templates. Smaller output
|
|
302
|
-
* than bundle(). Accepts the same options as bundleStandalone
|
|
303
|
-
* `format: 'esm' | 'cjs'` and cross-schema `$ref` resolution.
|
|
375
|
+
* than bundle(). Accepts the same options as bundleStandalone.
|
|
304
376
|
*/
|
|
305
|
-
|
|
377
|
+
bundleCompact(schemas: object[], options?: BundleStandaloneOptions): string;
|
|
306
378
|
|
|
307
379
|
/** Load a bundle created by Validator.bundle(). Returns array of Validator instances. */
|
|
308
|
-
|
|
380
|
+
loadBundle(mods: object[], schemas: object[], options?: ValidatorOptions): Validator[];
|
|
309
381
|
|
|
310
|
-
|
|
311
|
-
readonly "~standard": StandardSchemaV1Props;
|
|
382
|
+
readonly prototype: Validator;
|
|
312
383
|
}
|
|
313
384
|
|
|
385
|
+
/** Compile a schema into a reusable validator. */
|
|
386
|
+
export const Validator: ValidatorConstructor;
|
|
387
|
+
|
|
314
388
|
/** One-shot validate: creates a Validator, validates data, returns result. */
|
|
315
389
|
export function validate<T = unknown>(
|
|
316
390
|
schema: object | string,
|
package/index.js
CHANGED
|
@@ -868,16 +868,20 @@ class Validator {
|
|
|
868
868
|
return false;
|
|
869
869
|
}
|
|
870
870
|
};
|
|
871
|
-
// validateAndParse:
|
|
872
|
-
|
|
871
|
+
// validateAndParse: parse the JSON, then validate. Pure JS (JSON.parse +
|
|
872
|
+
// validate) so it works with or without the native addon and in browsers.
|
|
873
|
+
{
|
|
873
874
|
const self = this;
|
|
874
875
|
this.validateAndParse = (jsonStr) => {
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
876
|
+
let value;
|
|
877
|
+
try {
|
|
878
|
+
value = JSON.parse(typeof jsonStr === 'string' ? jsonStr : new TextDecoder().decode(jsonStr));
|
|
879
|
+
} catch (e) {
|
|
880
|
+
return { valid: false, value: undefined, errors: [{ code: 'ATA9001', message: 'invalid JSON: ' + e.message, keyword: '__parse__', instancePath: '', schemaPath: '', params: {} }] };
|
|
881
|
+
}
|
|
882
|
+
const r = self.validate(value);
|
|
883
|
+
return { valid: r.valid, value, errors: r.errors };
|
|
878
884
|
};
|
|
879
|
-
} else {
|
|
880
|
-
this.validateAndParse = () => { throw new Error('Native addon required for validateAndParse()'); };
|
|
881
885
|
}
|
|
882
886
|
// Buffer APIs: lazy native init — only compile native schema on first buffer call.
|
|
883
887
|
// This keeps cold start fast (JS codegen only) for users who only use validate().
|
|
@@ -1219,6 +1223,11 @@ module.exports = { boolFn, hybridFactory, errFn };
|
|
|
1219
1223
|
if (lines.length) closureDecls = lines.join('\n') + '\n';
|
|
1220
1224
|
}
|
|
1221
1225
|
|
|
1226
|
+
// Hoisted oneOf/anyOf branch checks live in the boolean fn's preamble (the
|
|
1227
|
+
// runtime emits them before the function). The standalone module must declare
|
|
1228
|
+
// them at module scope too, or _fn references undefined names (e.g. _af1_b0).
|
|
1229
|
+
const preambleDecls = jsFn._preambleSource ? jsFn._preambleSource + '\n' : '';
|
|
1230
|
+
|
|
1222
1231
|
const validBody = errCore
|
|
1223
1232
|
? 'return _fn(data) ? VALID : { valid: false, errors: errFn(data, true).errors }'
|
|
1224
1233
|
: 'return _fn(data) ? VALID : ABORT';
|
|
@@ -1241,7 +1250,7 @@ const ABORT = Object.freeze({
|
|
|
1241
1250
|
path: '',
|
|
1242
1251
|
})]),
|
|
1243
1252
|
});
|
|
1244
|
-
${closureDecls}const _fn = function(d) {
|
|
1253
|
+
${closureDecls}${preambleDecls}const _fn = function(d) {
|
|
1245
1254
|
${src}
|
|
1246
1255
|
};
|
|
1247
1256
|
${errCore}function isValid(data) { return _fn(data); }
|
|
@@ -1516,8 +1525,14 @@ Validator.bundleCompact = function (schemas, opts) {
|
|
|
1516
1525
|
typeof schema === "string" ? JSON.parse(schema) : schema,
|
|
1517
1526
|
v._schemaMap,
|
|
1518
1527
|
);
|
|
1528
|
+
// Hoisted anyOf/oneOf branch helpers (e.g. `_af1_b0`) must travel with the
|
|
1529
|
+
// hybrid body or it references undefined names. Prepending keeps dedup honest:
|
|
1530
|
+
// schemas with different branch sets no longer collide on body alone.
|
|
1531
|
+
const hybrid = jsFn._preambleSource
|
|
1532
|
+
? `${jsFn._preambleSource}\n${jsFn._hybridSource}`
|
|
1533
|
+
: jsFn._hybridSource;
|
|
1519
1534
|
return {
|
|
1520
|
-
hybrid
|
|
1535
|
+
hybrid,
|
|
1521
1536
|
err: jsErrFn && jsErrFn._errSource ? jsErrFn._errSource : null,
|
|
1522
1537
|
};
|
|
1523
1538
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
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",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"rebuild": "cmake-js rebuild --target ata",
|
|
43
43
|
"prebuild": "pkg-prebuilds-copy --baseDir build/Release --source ata.node --name=ata --strip --napi_version=10",
|
|
44
44
|
"prebuild-all": "npm run prebuild -- --arch x64 && npm run prebuild -- --arch arm64",
|
|
45
|
-
"test": "node test.js && node tests/test_no_native.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_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_nullable.js && node tests/test_enrich_error.js && node tests/test_rich_errors_optout.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 benchmark/bench_aot_size.mjs",
|
|
45
|
+
"test": "node test.js && node tests/test_no_native.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_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_enrich_error.js && node tests/test_rich_errors_optout.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 benchmark/bench_aot_size.mjs",
|
|
46
46
|
"bench:size": "node benchmark/bench_aot_size.mjs",
|
|
47
47
|
"test:suite": "node tests/run_suite.js",
|
|
48
48
|
"test:compat": "node tests/test_compat.js",
|