@amritk/generate-validators 0.11.11 → 0.11.12

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/AI.md CHANGED
@@ -34,5 +34,10 @@ const files = await buildValidatorSchema(schema, 'Document')
34
34
  `ValidationResult`, helpers) plus the `index.ts` barrel.
35
35
  4. **`NaN` satisfies numeric bounds** (`minimum`/`maximum`/`multipleOf`) — differs
36
36
  from Ajv. Draft-07 schemas are auto-upgraded to 2020-12.
37
+ 5. **`format` emits no check.** It stays an annotation, like the interpreter's
38
+ default — but *not* like the interpreter run with `{ formats: 'all' }`
39
+ (`@amritk/lint`, `createApi({ formats })`), which rejects strings a generated
40
+ validator accepts. `unevaluatedProperties`/`unevaluatedItems` go the other
41
+ way: unimplemented, so generation **throws** instead of widening the verdict.
37
42
 
38
43
  Only the `.` entry. Install: `bun add @amritk/generate-validators`.
package/README.md CHANGED
@@ -23,6 +23,9 @@ Each generated file exports:
23
23
 
24
24
  - A TypeScript `type` definition for the schema
25
25
  - A `validateFoo(input: unknown, _path?: string): ValidationResult` function
26
+ - An `isFoo(input: unknown): input is Foo` boolean type guard — a single flat
27
+ predicate (no error array, no cold-path call) reaching the same verdict as
28
+ `validateFoo`, for the common "is this valid?" question
26
29
 
27
30
  A shared `validation-result.ts` template and an `index.ts` barrel are emitted alongside the generated files.
28
31
 
@@ -81,12 +84,13 @@ if (!result.valid) {
81
84
 
82
85
  ## API
83
86
 
84
- ### `buildValidatorSchema(rootSchema, rootTypeName)`
87
+ ### `buildValidatorSchema(rootSchema, rootTypeName, typeSuffix?)`
85
88
 
86
- | Parameter | Type | Description |
87
- |:---|:---|:---|
88
- | `rootSchema` | `JSONSchema` | The root schema to traverse. `$ref` and `$dynamicRef` are resolved recursively. Draft-07 schemas are upgraded to 2020-12 automatically. |
89
- | `rootTypeName` | `string` | Name used for the root type (e.g. `"Document"`). |
89
+ | Parameter | Type | Default | Description |
90
+ |:---|:---|:---|:---|
91
+ | `rootSchema` | `JSONSchema` | — | The root schema to traverse. `$ref` and `$dynamicRef` are resolved recursively. Draft-07 schemas are upgraded to 2020-12 automatically. |
92
+ | `rootTypeName` | `string` | — | Name used for the root type (e.g. `"Document"`). |
93
+ | `typeSuffix` | `string` | `''` | Suffix appended to every `$ref`-derived type name (`'Object'` turns `Contact` into `ContactObject`). The root type name is unaffected. |
90
94
 
91
95
  Returns: `Promise<GeneratedFile[]>` where `GeneratedFile = { filename: string; content: string }`.
92
96
 
@@ -103,7 +107,17 @@ per-item work (a bare `string[]` is free; a closed object with several fields is
103
107
  meaningfully slower), which is why array-heavy schemas validate more slowly than
104
108
  scalar/object ones.
105
109
 
106
- One divergence is worth calling out: **`NaN` satisfies a constrained number.**
110
+ **`format` emits no check.** JSON Schema treats `format` as an annotation, and so
111
+ does this generator: `{ type: 'string', format: 'uuid' }` produces the `typeof`
112
+ check and nothing more. That matches the interpreter's default, but *not* the
113
+ interpreter run with `{ formats: 'all' }` — as `@amritk/lint` and
114
+ `createApi({ formats })` do — so a generated validator accepts strings those
115
+ reject. Two keywords are handled the other way: `unevaluatedProperties` and
116
+ `unevaluatedItems` are not implemented and **throw at generation time** rather
117
+ than silently widening the verdict, because unlike `format` there is no reading of
118
+ the spec under which ignoring them is correct.
119
+
120
+ One further divergence is worth calling out: **`NaN` satisfies a constrained number.**
107
121
  Because the numeric bound checks are the exact negation of the error condition
108
122
  (e.g. `!(x < minimum)`), and every comparison against `NaN` is `false`, a `NaN`
109
123
  passes `minimum`/`maximum`/`exclusive*`/`multipleOf`. This matches the interpreter
@@ -144,8 +158,15 @@ the first error rather than collecting a full error list.)
144
158
 
145
159
  Preparing a validator costs ~0.3–0.6 ms for mjst codegen and ~0.05–0.2 ms for a
146
160
  TypeBox `TypeCompiler` compile, versus ~9–12 ms for an Ajv compile. Every library
147
- agrees on every verdict; parity is asserted before timing (TypeBox is given
148
- uuid/email format checkers so every library does the same work). Each library is
161
+ agrees on every verdict; parity is asserted before timing.
162
+
163
+ One caveat on the first two rows: their schemas declare `format` (`uuid`,
164
+ `email`), and Ajv, typia, zod, and TypeBox all check it, while mjst's generated
165
+ validators treat it as an annotation (see [Semantics](#semantics)). So on `small`
166
+ and `order`, mjst is doing slightly less work than the columns beside it — the
167
+ parity samples fail other constraints too, which is why the verdicts still agree.
168
+ The `assert-loose` / `assert-strict` rows carry no `format` and are the
169
+ constraint-for-constraint comparison. Each library is
149
170
  timed in an isolated process over a pool of distinct inputs, reporting the median
150
171
  of many trials — so the optimiser can't hoist or eliminate the work and the
151
172
  numbers stay reproducible. Micro-benchmark figures vary by machine and runtime —
@@ -8,7 +8,10 @@ const generateValidatorFile = (schema, typeName, options) => {
8
8
  rootSchema: options?.rootSchema,
9
9
  typeSuffix
10
10
  });
11
- const typeDefinition = generateTypeDefinition(schema, typeName, { typeSuffix });
11
+ const typeDefinition = generateTypeDefinition(schema, typeName, {
12
+ typeSuffix,
13
+ ...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {}
14
+ });
12
15
  const validatorFunction = generateValidatorFunction(schema, typeName, typeSuffix);
13
16
  const booleanGuard = generateBooleanGuard(schema, typeName, typeSuffix);
14
17
  let result = `import type { ValidationResult, ValidationError } from './validation-result.js'
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@amritk/generate-validators",
3
- "version": "0.11.11",
3
+ "version": "0.11.12",
4
4
  "description": "Generate TypeScript validation functions from JSON Schemas.",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",
7
+ "sideEffects": false,
8
+ "engines": {
9
+ "node": ">=20"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "amritk",
9
13
  "keywords": [
@@ -47,10 +51,10 @@
47
51
  },
48
52
  "dependencies": {
49
53
  "json-schema-typed": "^8.0.1",
50
- "@amritk/helpers": "0.13.5"
54
+ "@amritk/helpers": "0.14.0"
51
55
  },
52
56
  "devDependencies": {
53
- "@amritk/runtime-validators": "0.9.0",
57
+ "@amritk/runtime-validators": "0.9.1",
54
58
  "@ryoppippi/unplugin-typia": "^2.6.5",
55
59
  "@scalar/openapi-parser": "^0.26.1",
56
60
  "@sinclair/typebox": "^0.34.49",