ata-validator 0.16.0 → 0.17.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,16 @@
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.0 - 2026-05-23
6
+
7
+ ### Added
8
+
9
+ - 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.
10
+
11
+ ### Fixed
12
+
13
+ - `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.
14
+
5
15
  ## 0.16.0 - 2026-05-23
6
16
 
7
17
  ### 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
- export class Validator<T = unknown> {
235
- constructor(schema: object | string, options?: ValidatorOptions);
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 with simdjson + validate against schema. Returns parsed value and validation result. Requires native addon. */
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
- static fromStandalone<T = unknown>(mod: StandaloneModule, schema: object | string, options?: ValidatorOptions): Validator<T>;
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
- static bundle(schemas: object[], options?: ValidatorOptions): string;
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
- static bundleStandalone(schemas: object[], options?: BundleStandaloneOptions): string;
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, including
303
- * `format: 'esm' | 'cjs'` and cross-schema `$ref` resolution.
375
+ * than bundle(). Accepts the same options as bundleStandalone.
304
376
  */
305
- static bundleCompact(schemas: object[], options?: BundleStandaloneOptions): string;
377
+ bundleCompact(schemas: object[], options?: BundleStandaloneOptions): string;
306
378
 
307
379
  /** Load a bundle created by Validator.bundle(). Returns array of Validator instances. */
308
- static loadBundle(mods: object[], schemas: object[], options?: ValidatorOptions): Validator[];
380
+ loadBundle(mods: object[], schemas: object[], options?: ValidatorOptions): Validator[];
309
381
 
310
- /** Standard Schema V1 interface, compatible with Fastify, tRPC, TanStack, etc. */
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: requires native addon for simdjson parsing
872
- if (native) {
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
- self._ensureNative();
876
- self.validateAndParse = (s) => self._compiled.validateAndParse(s);
877
- return self.validateAndParse(jsonStr);
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().
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
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_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",