ata-validator 0.17.5 → 0.18.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 CHANGED
@@ -2,6 +2,18 @@
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.18.1 - 2026-05-26
6
+
7
+ ### Added
8
+
9
+ - The browser entry (`index.browser.mjs`) re-exports `toTypeScript`, so the inferred TypeScript type for a schema can be generated client-side (for example in a web playground) alongside `Validator.toStandaloneModule()`. Pure re-export, no runtime change.
10
+
11
+ ## 0.18.0 - 2026-05-25
12
+
13
+ ### Added
14
+
15
+ - `Infer<S>` resolves the shapes 0.17.0 left as `unknown`. `anyOf` and `oneOf` map to unions, `allOf` to an intersection, `prefixItems` to a tuple, and a `$ref` to a local `#/$defs/...` or `#/definitions/...` entry resolves to the referenced type, including recursive references. An external or otherwise unresolvable `$ref` still resolves to `unknown` rather than erroring. `new Validator(schema)` carries the wider inference, so handlers narrow `result.data` for these schemas with no manual annotation, and the same applies to the Fastify type provider that builds on `Infer`. Pure `.d.ts` change, no runtime impact.
16
+
5
17
  ## 0.17.5 - 2026-05-25
6
18
 
7
19
  ### Fixed
package/README.md CHANGED
@@ -127,58 +127,79 @@ v.countValid(ndjson); // number
127
127
 
128
128
  ### Type-safe schemas
129
129
 
130
- `Validator` is generic. Pair it with any schema authoring tool, or a hand-written type, to get TypeScript narrowing in your handler code.
130
+ ata infers TypeScript types straight from plain JSON Schema. Write the schema once with `defineSchema`, and both runtime validation and the static type come from it, with no builder DSL and no second type declaration to keep in sync.
131
131
 
132
132
  ```ts
133
- import { Type, type Static } from '@sinclair/typebox'
134
- import { Validator } from 'ata-validator'
133
+ import { defineSchema, Validator } from 'ata-validator'
135
134
 
136
- const UserSchema = Type.Object({
137
- id: Type.Integer({ minimum: 1 }),
138
- name: Type.String({ minLength: 1 }),
139
- email: Type.String({ format: 'email' }),
135
+ const userSchema = defineSchema({
136
+ type: 'object',
137
+ properties: {
138
+ id: { type: 'integer', minimum: 1 },
139
+ role: { type: 'string', enum: ['admin', 'user'] },
140
+ },
141
+ required: ['id'],
140
142
  })
141
143
 
142
- type User = Static<typeof UserSchema>
143
-
144
- const v = new Validator<User>(UserSchema)
145
-
146
- if (v.isValidObject(data)) {
147
- // data is narrowed to User, no cast needed
148
- console.log(data.name)
149
- }
150
-
144
+ const v = new Validator(userSchema)
151
145
  const result = v.validate(data)
152
146
  if (result.valid) {
153
- // result.data is User
147
+ result.data.id // number
148
+ result.data.role // 'admin' | 'user' | undefined
154
149
  } else {
155
150
  // result.errors: ValidationError[]
156
151
  }
157
152
  ```
158
153
 
159
- The same pattern works with Zod-from-JSON-Schema, Valibot, or a hand-written `type User = {...}` alongside a JSON Schema literal. `Validator<T>` makes no library-specific assumption.
154
+ `defineSchema` returns the schema untouched at runtime; in TypeScript it gives keyword autocomplete and an error when a value has the wrong shape, with no `as const` needed. `new Validator(schema)` carries the inferred type, so a successful `validate` narrows `result.data` with no manual annotation.
160
155
 
161
- #### Authoring a schema inline: `defineSchema`
156
+ #### Extracting the type: `Infer`
162
157
 
163
- If you would rather write a plain JSON Schema object than reach for a schema library, wrap it in `defineSchema`. It returns the schema untouched at runtime, but in TypeScript it gives you keyword autocomplete and an error when a value has the wrong shape, with no `as const` needed.
158
+ You can also pull the type out directly with `Infer`, with no second declaration to keep in sync.
164
159
 
165
160
  ```ts
166
- import { defineSchema, Validator } from 'ata-validator'
161
+ import { defineSchema, type Infer } from 'ata-validator'
167
162
 
168
- const userSchema = defineSchema({
163
+ const event = defineSchema({
164
+ $defs: {
165
+ Point: { type: 'object', properties: { x: { type: 'number' }, y: { type: 'number' } }, required: ['x', 'y'] },
166
+ },
169
167
  type: 'object',
170
168
  properties: {
171
- id: { type: 'integer', minimum: 1 },
172
- role: { type: 'string', enum: ['admin', 'user'] },
169
+ kind: { enum: ['click', 'scroll'] },
170
+ at: { $ref: '#/$defs/Point' },
171
+ path: { type: 'array', prefixItems: [{ type: 'string' }, { type: 'integer' }] },
173
172
  },
174
- required: ['id'],
173
+ required: ['kind', 'at'],
175
174
  })
176
175
 
177
- // type: 123 or required: 'id' would be a compile error here.
178
- const v = new Validator(userSchema)
176
+ type Event = Infer<typeof event>
177
+ // {
178
+ // kind: 'click' | 'scroll'
179
+ // at: { x: number; y: number }
180
+ // path?: [string, number]
181
+ // }
182
+ ```
183
+
184
+ `Infer` resolves `const`/`enum` to literals, `anyOf`/`oneOf` to unions, `allOf` to intersections, `prefixItems` to tuples, and local `$ref` into `#/$defs` or `#/definitions`, including recursive references. An external or unresolvable `$ref` resolves to `unknown` rather than erroring. The exported `JSONSchema` type is available if you want to annotate a schema by hand; custom and vendor keywords are allowed. Requires TypeScript >= 5.0.
185
+
186
+ #### Composes with TypeBox, Zod, or your own types
187
+
188
+ `Validator<T>` is generic, so if you already author schemas with a library, pass the type and ata narrows to it. No library-specific assumption.
189
+
190
+ ```ts
191
+ import { Type, type Static } from '@sinclair/typebox'
192
+ import { Validator } from 'ata-validator'
193
+
194
+ const UserSchema = Type.Object({
195
+ id: Type.Integer({ minimum: 1 }),
196
+ name: Type.String({ minLength: 1 }),
197
+ })
198
+
199
+ const v = new Validator<Static<typeof UserSchema>>(UserSchema)
179
200
  ```
180
201
 
181
- The exported `JSONSchema` type is also available directly if you want to annotate a schema yourself. Custom and vendor keywords are allowed, so exotic schemas still type-check. Requires TypeScript >= 5.0.
202
+ The same works with Zod-from-JSON-Schema, Valibot, or a hand-written `type User = {...}` alongside a JSON Schema literal.
182
203
 
183
204
  ### Cross-Schema `$ref`
184
205
 
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, renderPretty, renderCompact, renderJSON } = mod;
3
+ export const { Validator, validate, version, createPaddedBuffer, SIMDJSON_PADDING, renderPretty, renderCompact, renderJSON, toTypeScript } = mod;
4
4
  export default mod;
package/index.d.ts CHANGED
@@ -177,26 +177,55 @@ type RequiredKeys<S> = S extends { required: infer R }
177
177
  : never
178
178
  : never;
179
179
 
180
+ /** Collapse a union of types into their intersection (used for `allOf`). */
181
+ type UnionToIntersection<U> =
182
+ (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
183
+
184
+ /** The root `$defs`/`definitions` map, threaded through inference for `$ref` resolution. */
185
+ type RootDefs<S> = S extends { $defs: infer D }
186
+ ? D
187
+ : S extends { definitions: infer D }
188
+ ? D
189
+ : {};
190
+
191
+ /** Extract the definition name from a local `#/$defs/...` or `#/definitions/...` pointer. */
192
+ type RefName<R> = R extends `#/$defs/${infer N}`
193
+ ? N
194
+ : R extends `#/definitions/${infer N}`
195
+ ? N
196
+ : never;
197
+
198
+ /** Resolve a `$ref` against the root defs map; external/unresolvable refs -> unknown. */
199
+ type ResolveRef<R, D> = [RefName<R>] extends [never]
200
+ ? unknown
201
+ : RefName<R> extends keyof D
202
+ ? InferWith<D[RefName<R>], D>
203
+ : unknown;
204
+
180
205
  /** Object shape: required keys are required, all other declared keys optional. */
181
- type InferObject<S> = S extends { properties: infer P }
206
+ type InferObject<S, D> = S extends { properties: infer P }
182
207
  ? 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]> }
208
+ { [K in keyof P as K extends RequiredKeys<S> ? K : never]: InferWith<P[K], D> } &
209
+ { [K in keyof P as K extends RequiredKeys<S> ? never : K]?: InferWith<P[K], D> }
185
210
  >
186
211
  : Record<string, unknown>;
187
212
 
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[];
213
+ /** Array shape: `prefixItems` -> tuple; `items` (single schema) -> element type; otherwise unknown[]. */
214
+ type InferArray<S, D> = S extends { prefixItems: infer P }
215
+ ? P extends ReadonlyArray<unknown>
216
+ ? { -readonly [K in keyof P]: InferWith<P[K], D> }
217
+ : unknown[]
218
+ : S extends { items: infer I }
219
+ ? I extends ReadonlyArray<unknown>
220
+ ? unknown[]
221
+ : InferWith<I, D>[]
222
+ : unknown[];
194
223
 
195
224
  /** Map a single JSON Schema type name (+ its schema) to a TS type. */
196
- type InferByTypeName<N, S> = N extends 'object'
197
- ? InferObject<S>
225
+ type InferByTypeName<N, S, D> = N extends 'object'
226
+ ? InferObject<S, D>
198
227
  : N extends 'array'
199
- ? InferArray<S>
228
+ ? InferArray<S, D>
200
229
  : N extends 'string'
201
230
  ? string
202
231
  : N extends 'number'
@@ -209,28 +238,46 @@ type InferByTypeName<N, S> = N extends 'object'
209
238
  ? null
210
239
  : unknown;
211
240
 
241
+ /** Core inference with the root defs map `D` threaded for `$ref` resolution. */
242
+ type InferWith<S, D> = S extends { $ref: infer R }
243
+ ? ResolveRef<R, D>
244
+ : S extends { const: infer C }
245
+ ? C
246
+ : S extends { enum: infer E }
247
+ ? E extends ReadonlyArray<infer U>
248
+ ? U
249
+ : unknown
250
+ : S extends { allOf: infer A }
251
+ ? A extends ReadonlyArray<unknown>
252
+ ? Simplify<UnionToIntersection<{ [K in keyof A]: InferWith<A[K], D> }[number]>>
253
+ : unknown
254
+ : S extends { anyOf: infer A }
255
+ ? A extends ReadonlyArray<unknown>
256
+ ? { [K in keyof A]: InferWith<A[K], D> }[number]
257
+ : unknown
258
+ : S extends { oneOf: infer A }
259
+ ? A extends ReadonlyArray<unknown>
260
+ ? { [K in keyof A]: InferWith<A[K], D> }[number]
261
+ : unknown
262
+ : S extends { type: infer T }
263
+ ? T extends ReadonlyArray<infer N>
264
+ ? InferByTypeName<N, S, D>
265
+ : InferByTypeName<T, S, D>
266
+ : unknown;
267
+
212
268
  /**
213
- * Infer the TypeScript data type a JSON Schema literal describes (Core scope).
269
+ * Infer the TypeScript data type a JSON Schema literal describes.
214
270
  *
215
271
  * 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.
272
+ * (`properties` + `required` -> required/optional keys), arrays (`items`),
273
+ * tuples (`prefixItems`), `anyOf`/`oneOf` (union), `allOf` (intersection),
274
+ * and `$ref` to local `#/$defs/...` or `#/definitions/...`. External or
275
+ * unresolvable `$ref` resolves to `unknown` rather than erroring.
219
276
  *
220
277
  * Pair with {@link defineSchema}:
221
278
  * `const s = defineSchema({...}); type T = Infer<typeof s>;`
222
279
  */
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;
280
+ export type Infer<S> = InferWith<S, RootDefs<S>>;
234
281
 
235
282
  export type ValidationResult<T = unknown> =
236
283
  | { valid: true; data: T; errors: ValidationError[] }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "0.17.5",
3
+ "version": "0.18.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",