@amritk/generate-validators
Advanced tools
@@ -48,2 +48,31 @@ import { generateIndexBarrel } from '@amritk/helpers/generate-index-barrel'; | ||
| } | ||
| /** | ||
| * True when every element of \`arr\` is distinct under structural equality | ||
| * ({@link valuesEqual}). Backs generated \`uniqueItems\` checks whose items may be | ||
| * objects or arrays, where a \`JSON.stringify\` dedupe key would be key-order | ||
| * sensitive and let a reordered-but-equal duplicate (\`{ a: 1, b: 2 }\` vs | ||
| * \`{ b: 2, a: 1 }\`) slip through. A native \`Set\` dedupes the all-primitive case | ||
| * in one linear pass; object/array elements fall back to an exact pairwise | ||
| * structural comparison. | ||
| */ | ||
| export const allUnique = (arr: readonly unknown[]): boolean => { | ||
| const len = arr.length | ||
| if (len < 2) return true | ||
| let allPrimitive = true | ||
| for (let i = 0; i < len; i++) { | ||
| const v = arr[i] | ||
| if (v !== null && typeof v === 'object') { | ||
| allPrimitive = false | ||
| break | ||
| } | ||
| } | ||
| if (allPrimitive) return new Set(arr).size === len | ||
| for (let i = 0; i < len; i++) { | ||
| for (let j = i + 1; j < len; j++) { | ||
| if (valuesEqual(arr[i], arr[j])) return false | ||
| } | ||
| } | ||
| return true | ||
| } | ||
| `; | ||
@@ -50,0 +79,0 @@ /** |
@@ -57,3 +57,3 @@ import { refToFilename } from '@amritk/helpers/ref-to-filename'; | ||
| // harmless no-op. | ||
| const subSchemaMaps = ['properties', 'patternProperties']; | ||
| const subSchemaMaps = ['properties', 'patternProperties', 'dependentSchemas']; | ||
| for (const mapKey of subSchemaMaps) { | ||
@@ -60,0 +60,0 @@ const map = schema[mapKey]; |
@@ -40,7 +40,10 @@ import { generateTypeDefinition } from '@amritk/helpers/generate-type-definition'; | ||
| let result = `import type { ValidationResult, ValidationError } from './validation-result.js'\n`; | ||
| // `const` checks on object/array values call the runtime `valuesEqual` helper. | ||
| // Only import it when the generated body actually uses it, so files without a | ||
| // structural `const` do not carry an unused import. | ||
| if (validatorFunction.includes('valuesEqual(')) { | ||
| result += `import { valuesEqual } from './validation-result.js'\n`; | ||
| // Structural `const` checks call the runtime `valuesEqual` helper; structural | ||
| // `uniqueItems` checks call `allUnique`. Both live in `validation-result.js`; | ||
| // import each only when the generated body (validator or boolean guard) uses | ||
| // it, so files that need neither carry no unused import. | ||
| const body = validatorFunction + booleanGuard; | ||
| const runtimeHelpers = ['valuesEqual', 'allUnique'].filter((name) => body.includes(`${name}(`)); | ||
| if (runtimeHelpers.length > 0) { | ||
| result += `import { ${runtimeHelpers.join(', ')} } from './validation-result.js'\n`; | ||
| } | ||
@@ -47,0 +50,0 @@ for (const imp of refImports) { |
+3
-2
| { | ||
| "name": "@amritk/generate-validators", | ||
| "version": "0.11.5", | ||
| "version": "0.11.6", | ||
| "description": "Generate TypeScript validation functions from JSON Schemas.", | ||
@@ -49,5 +49,6 @@ "module": "./dist/index.js", | ||
| "json-schema-typed": "^8.0.1", | ||
| "@amritk/helpers": "0.12.0" | ||
| "@amritk/helpers": "0.13.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@amritk/runtime-validators": "0.7.0", | ||
| "@ryoppippi/unplugin-typia": "^2.6.5", | ||
@@ -54,0 +55,0 @@ "@scalar/openapi-parser": "^0.26.1", |
+21
-0
@@ -95,2 +95,23 @@ <div align="center"> | ||
| ## Semantics | ||
| Generated validators track the `@amritk/runtime-validators` interpreter. Array | ||
| items are validated in full — an item's type, `$ref`, nested `properties` / | ||
| `required`, and scalar constraints (`minLength`, `minimum`, …) are all enforced, | ||
| recursing to any depth — and the boolean guard (`isX`) reaches the identical | ||
| verdict. Validating array item *contents* costs throughput proportional to the | ||
| per-item work (a bare `string[]` is free; a closed object with several fields is | ||
| meaningfully slower), which is why array-heavy schemas validate more slowly than | ||
| scalar/object ones. | ||
| One divergence is worth calling out: **`NaN` satisfies a constrained number.** | ||
| Because the numeric bound checks are the exact negation of the error condition | ||
| (e.g. `!(x < minimum)`), and every comparison against `NaN` is `false`, a `NaN` | ||
| passes `minimum`/`maximum`/`exclusive*`/`multipleOf`. This matches the interpreter | ||
| but differs from validators (e.g. Ajv) that reject `NaN` for `type: "number"`. | ||
| `NaN` never appears in parsed JSON; guard against it upstream if your values can | ||
| be non-JSON. | ||
| --- | ||
| ## Benchmarks | ||
@@ -97,0 +118,0 @@ |
Sorry, the diff of this file is too big to display
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
116819
15.64%2235
15.86%170
14.09%8
14.29%+ Added
- Removed
Updated