샘플
ajv 8.20.0: Handle Ajv async schema validation where invalid data rejects the Promise with Ajv.ValidationError instead of resolving false
검증된 샘플 — npm ajv 8.20.0: Handle Ajv async schema validation where invalid data rejects the Promise with Ajv.ValidationError instead of resolving false. node…
sha256:d0612b722f729d61a5720643776ca58d07362ba8ff7616968d4d9b67559b1d84
이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다.
통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다.
MIT-0
실행 증거
선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.
- 증거 기준
- 서명된 컨트랙트 통과
- 검증 영수증
- 2
- 빌드한 서명 키
- 2
선언된 환경
node linux x64 node node npm
검증 실행 환경
| 환경 | 컨트랙트 | 단계 | 실행일 |
|---|---|---|---|
| node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-16 |
| node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-18 |
케이스
HOW- 목표
- Handle Ajv async schema validation where invalid data rejects the Promise with Ajv.ValidationError instead of resolving false
- 패키지
- 심벌
-
- Ajv
- Ajv.ValidationError
- ajv.compile
- 환경
- node
- 생성일
- 2026-08-16T06:22:57Z
컨트랙트
- compile a schema containing $async: true
- assert valid data resolves the promise with the input data object rather than boolean true
- assert invalid data rejects the promise with an Ajv.ValidationError instance
- assert validate.errors is null on the compiled function when the promise rejects
- assert caught error carries the errors array with keyword and instancePath details
- assert wrapping in try/catch handles async validation errors without uncaught promise rejection
파일
- NOTES.md
- csx.json
- package-lock.json
- package.json
- src/index.mjs
- test/contract.mjs
소스
# Ajv Async Validation Semantics
## What `search_known_solution` answered
`search_known_solution` returned sample `sha256:de249b2f65992de63dd2adece0fd7b0829b25af3cd75a5ad075a49696ed69ffc` ("Fix ajv throwing 'strict mode: unknown keyword' when compiling an existing JSON Schema"), which addresses Ajv v8 strict-mode keyword registration and `instancePath`.
## What a model would have written instead
`const isValid = await validate(data); if (!isValid) { return { errors: validate.errors }; }`
## How the wrong version fails
Loudly: on invalid data, `await validate(data)` rejects the promise with an uncaught `Ajv.ValidationError` instead of resolving `false`, crashing runtime execution while `validate.errors` remains `null`.
{"case":{"caseId":"case:sha256:0b53c2fbdcafd76870b93f4ee4d4560e1ff61b8e1d4236e1f6030def3384791c","contract":["compile a schema containing $async: true","assert valid data resolves the promise with the input data object rather than boolean true","assert invalid data rejects the promise with an Ajv.ValidationError instance","assert validate.errors is null on the compiled function when the promise rejects","assert caught error carries the errors array with keyword and instancePath details","assert wrapping in try/catch handles async validation errors without uncaught promise rejection"],"goal":"Handle Ajv async schema validation where invalid data rejects the Promise with Ajv.ValidationError instead of resolving false","kind":"HOW","packages":["pkg:npm/ajv@8.20.0"],"schemaVersion":1,"symbols":["Ajv","Ajv.ValidationError","ajv.compile"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/ajv@8.20.0"],"schemaVersion":1,"symbols":["Ajv","Ajv.ValidationError","ajv.compile"],"verifierAdapter":"node-typescript@1"}
{
"name": "csx-sample-ajv-async-validation",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "csx-sample-ajv-async-validation",
"version": "1.0.0",
"license": "MIT-0",
"dependencies": {
"ajv": "8.20.0"
}
},
"node_modules/ajv": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz",
"integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "BSD-3-Clause"
},
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
}
}
}
{
"name": "csx-sample-ajv-async-validation",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT-0",
"dependencies": {
"ajv": "8.20.0"
}
}
import Ajv from 'ajv';
export const ValidationError = Ajv.ValidationError;
/**
* Compiles a JSON Schema with Ajv.
* When the schema contains `$async: true`, Ajv generates an asynchronous validation function:
* - Valid input resolves the Promise with the input `data` object (truthy), NOT boolean `true`.
* - Invalid input rejects the Promise with `Ajv.ValidationError` containing `.errors`.
* It does NOT resolve to `false`, and `validate.errors` remains `null`.
*/
export function compileSchema(schema, options = {}) {
const ajv = new Ajv({ allErrors: true, ...options });
return ajv.compile(schema);
}
/**
* Safe wrapper for async schema validation that converts the rejected `Ajv.ValidationError`
* into a structured result object without unhandled promise rejections.
*/
export async function validateAsync(validateFn, data) {
try {
const validData = await validateFn(data);
return { valid: true, data: validData, errors: null };
} catch (err) {
if (err instanceof Ajv.ValidationError) {
return { valid: false, data: null, errors: err.errors };
}
throw err;
}
}
import { strict as assert } from 'node:assert';
import { compileSchema, validateAsync, ValidationError } from '../src/index.mjs';
const schema = {
$async: true,
type: 'object',
properties: {
userId: { type: 'integer', minimum: 1 },
email: { type: 'string' }
},
required: ['userId', 'email']
};
const validate = compileSchema(schema);
// 1. Valid data: the promise resolves with the input data object, NOT boolean true
const validPayload = { userId: 101, email: 'user@example.com' };
const validResult = await validate(validPayload);
assert.equal(validResult, validPayload, 'Async validation must resolve to the input data object');
assert.notEqual(validResult, true, 'Async validation does not return boolean true');
// 2. Invalid data: the promise REJECTS with Ajv.ValidationError instead of resolving to false
let caughtError = null;
try {
await validate({ userId: 0 }); // missing email, userId < 1
assert.fail('Expected promise to reject on invalid data');
} catch (err) {
caughtError = err;
}
assert.ok(caughtError instanceof ValidationError, 'Rejection must be an instance of Ajv.ValidationError');
assert.equal(Array.isArray(caughtError.errors), true, 'Caught error must contain an array of errors');
assert.equal(caughtError.errors.length, 2, 'With allErrors: true, both errors must be reported');
// 3. validate.errors remains null on async rejection (errors live exclusively on the caught error)
assert.equal(validate.errors, null, 'validate.errors is null; errors are only attached to the thrown ValidationError');
// 4. Using the safe wrapper converts the rejection into a structured outcome
const safeFailure = await validateAsync(validate, { userId: -5 });
assert.equal(safeFailure.valid, false);
assert.equal(safeFailure.data, null);
assert.ok(Array.isArray(safeFailure.errors) && safeFailure.errors.length > 0);
const safeSuccess = await validateAsync(validate, validPayload);
assert.equal(safeSuccess.valid, true);
assert.equal(safeSuccess.data, validPayload);
assert.equal(safeSuccess.errors, null);
console.log('CONTRACT PASS: ajv async validation return semantics and error rejection verified');