@amritk/generate-validators
Advanced tools
@@ -583,3 +583,8 @@ import { escapeRegexPattern } from '@amritk/helpers/escape-regex-pattern'; | ||
| } | ||
| const body = propertyLines.length > 0 ? '\n' + propertyLines.join('\n') + '\n' : ''; | ||
| // Lazily allocate the errors array so a valid input never builds one — the same | ||
| // allocation-free happy path the runtime interpreter uses. Each emitted | ||
| // `errors.push(...)` becomes a create-on-first-use push; nothing is allocated | ||
| // until the first actual error, so the common valid case stays alloc-free even | ||
| // when the schema is too rich for the boolean guard. | ||
| const body = (propertyLines.length > 0 ? '\n' + propertyLines.join('\n') + '\n' : '').replaceAll('errors.push(', '(errors ??= []).push('); | ||
| // Hoisted statements (e.g. known-keys Sets) come first so every call of the | ||
@@ -606,5 +611,5 @@ // validator reuses them instead of rebuilding them. | ||
| ``, | ||
| ` const errors: ValidationError[] = []`, | ||
| ` let errors: ValidationError[] | undefined`, | ||
| body, | ||
| ` return errors.length > 0 ? { valid: false, errors } : true`, | ||
| ` return errors !== undefined ? { valid: false, errors } : true`, | ||
| `}`, | ||
@@ -667,2 +672,19 @@ ].join('\n'); | ||
| /** | ||
| * Builds the membership test for an `enum`, matching the slow path's | ||
| * `[...].includes(value)` verdict exactly. For the common all-primitive case it | ||
| * emits a parenthesized `a === x || a === y` chain — no per-call array | ||
| * allocation and no linear scan, so it stays on the allocation-free hot path — | ||
| * and falls back to `.includes` when a member is an object/array (reference | ||
| * equality) or `NaN` (where `includes`'s SameValueZero differs from `===`). | ||
| */ | ||
| const enumMembershipExpr = (values, acc) => { | ||
| const allPrimitive = values.length > 0 && | ||
| values.every((v) => (v === null || typeof v !== 'object') && typeof v !== 'function') && | ||
| !values.some((v) => typeof v === 'number' && Number.isNaN(v)); | ||
| if (allPrimitive) { | ||
| return `(${values.map((v) => `${acc} === ${JSON.stringify(v)}`).join(' || ')})`; | ||
| } | ||
| return `(${JSON.stringify(values)} as unknown[]).includes(${acc})`; | ||
| }; | ||
| /** | ||
| * Builds a boolean expression that is TRUE iff `acc` satisfies `schema`, with the | ||
@@ -692,3 +714,3 @@ * *exact same verdict* as the error-collecting validator — or `null` when the | ||
| if (hasEnum(schema)) { | ||
| return `(${JSON.stringify(schema.enum)} as unknown[]).includes(${acc})`; | ||
| return enumMembershipExpr(schema.enum, acc); | ||
| } | ||
@@ -745,2 +767,8 @@ if (!hasType(schema)) | ||
| * constraints. Returns `null` for `$ref` items (those defer to the validator). | ||
| * | ||
| * Item iteration goes through `Array.from` rather than `Array.prototype.every` | ||
| * because `every` *skips holes* in a sparse array (`[, 'x']`), whereas the | ||
| * validator's index-based `for` loop reads a hole as `undefined` and rejects it. | ||
| * Materialising the array first makes the guard's verdict match the slow path's | ||
| * on sparse input — the guard must never accept what the slow path would reject. | ||
| */ | ||
@@ -761,3 +789,3 @@ const booleanArrayExpr = (schema, acc) => { | ||
| return base; | ||
| return `${base} && (${acc} as unknown[]).every((_it) => ${itemCheck})`; | ||
| return `${base} && Array.from(${acc} as unknown[]).every((_it) => ${itemCheck})`; | ||
| }; | ||
@@ -987,5 +1015,5 @@ /** | ||
| ` }`, | ||
| ` const errors: ValidationError[] = []`, | ||
| constraintLines.join('\n'), | ||
| ` return errors.length > 0 ? { valid: false, errors } : true`, | ||
| ` let errors: ValidationError[] | undefined`, | ||
| constraintLines.join('\n').replaceAll('errors.push(', '(errors ??= []).push('), | ||
| ` return errors !== undefined ? { valid: false, errors } : true`, | ||
| `}`, | ||
@@ -992,0 +1020,0 @@ ].join('\n'); |
+1
-1
| { | ||
| "name": "@amritk/generate-validators", | ||
| "version": "0.10.0", | ||
| "version": "0.10.1", | ||
| "description": "Generate TypeScript validation functions from JSON Schemas.", | ||
@@ -5,0 +5,0 @@ "module": "./dist/index.js", |
75288
2.48%1441
1.98%