@fajarnugraha37/validator
Version:
A typed façade over [AJV](https://ajv.js.org/) that brings schema builders, caching, and loader pipelines to the `nope` workspace
2 lines (1 loc) • 5.9 kB
JavaScript
var u=Object.defineProperty;var r=(n,e)=>u(n,"name",{value:e,configurable:!0});import f from"ajv";import y from"ajv-formats";import T from"ajv-errors";import p from"ajv-keywords";import{ValidationError as l}from"@fajarnugraha37/error";import{LruTtlCache as v}from"@fajarnugraha37/cache";var o=class n{static{r(this,"ValidatorBuilder")}options={};schemas={};loader;cacheOptions;static create(){return new n}withOptions(e){return this.options={...this.options,...e},this}withSchema(e,a){return this.schemas={...this.schemas,[e]:a},this}withSchemaBuilder(e,a){return this.withSchema(e,a.build())}withSchemas(e){return this.schemas={...this.schemas,...e},this}withSchemaBuilders(e){for(let[a,t]of Object.entries(e))t&&this.withSchemaBuilder(a,t);return this}fromJSON(e){let a=typeof e=="string"?JSON.parse(e):e;if(typeof a!="object"||a===null||Array.isArray(a))throw new Error("Schema JSON must be an object keyed by schema type");return this.withSchemas(a),this}withSchemaLoader(e){return this.loader=e,this}withCache(e={}){return this.cacheOptions=e,this}build(){let e=new m(this.options);return this.cacheOptions&&e.enableSchemaCache(this.cacheOptions),this.loader&&e.setSchemaLoader(this.loader),Object.keys(this.schemas).length>0&&e.registerSchemas(this.schemas),e}};var m=class{static{r(this,"Validator")}ajv;validators=new Map;schemas={};schemaLoader;schemaCache;schemaCacheTtl;constructor(e={}){let a={strict:!0,allErrors:!0,removeAdditional:!1,useDefaults:!0,coerceTypes:!1,verbose:!0,...e};this.ajv=new f.default({...a,strict:!1,validateSchema:!1,addUsedSchema:!1});try{y.default(this.ajv)}catch{}try{T.default(this.ajv)}catch{}try{p.default(this.ajv)}catch{}}static builder(){return new o}registerSchema(e,a){try{this.schemas[e]=a;let t=this.ajv.compile(a);return this.validators.set(e,t),this.cacheSchema(e,a),this}catch(t){throw new Error(`Failed to register schema for type '${String(e)}': ${t}`)}}registerSchemas(e){for(let[a,t]of Object.entries(e))this.registerSchema(a,t);return this}registerSchemaBuilder(e,a){return this.registerSchema(e,a.build())}registerSchemaBuilders(e){for(let[a,t]of Object.entries(e))t&&this.registerSchemaBuilder(a,t);return this}enableSchemaCache(e={}){this.schemaCache=new v({maxEntries:e.maxEntries,maxSize:e.maxSize,sizer:e.sizer,sweepIntervalMs:e.sweepIntervalMs}),this.schemaCacheTtl={ttlMs:e.ttlMs,slidingTtlMs:e.slidingTtlMs};for(let[a,t]of Object.entries(this.schemas))t&&this.cacheSchema(a,t);return this}setSchemaLoader(e){return this.schemaLoader=e,this}importSchemasFromJSON(e,a={}){let t=this.normalizeSchemaInput(e);return(a.register??!0)&&this.registerSchemas(t),t}exportSchemas(e){let a=e??Object.keys(this.schemas),t={};for(let s of a){let i=this.schemas[s];i&&(t[s]=i)}return t}exportSchemasToJSON(e,a=2){return JSON.stringify(this.exportSchemas(e),null,a)}validate(e,a){let t=this.validators.get(e);if(!t)throw new Error(`No schema registered for type '${String(e)}'`);try{return t(a)?{valid:!0,data:a}:{valid:!1,errors:this.formatErrors(t.errors||[])}}catch(s){return{valid:!1,errors:[{keyword:"exception",instancePath:"",schemaPath:"",params:{},message:`Validation exception: ${s}`,data:a}]}}}async validateAsync(e,a){return await this.ensureSchemaLoaded(e),this.validate(e,a)}validateStrict(e,a){let t=this.validate(e,a);if(!t.valid){let s=t.errors?.map(i=>i.message).join(", ")||"Unknown validation error";throw new l(`Validation failed for type '${String(e)}': ${s}`)}return t.data}async validateStrictAsync(e,a){let t=await this.validateAsync(e,a);if(!t.valid){let s=t.errors?.map(i=>i.message).join(", ")||"Unknown validation error";throw new l(`Validation failed for type '${String(e)}': ${s}`)}return t.data}hasSchema(e){return this.validators.has(e)}getSchema(e){return this.schemas[e]}getRegisteredTypes(){return Array.from(this.validators.keys())}validateMany(e,a){let t=a.map(h=>this.validate(e,h)),s=[],i=[];return t.forEach((h,c)=>{if(h.valid&&h.data)i.push(h.data);else if(h.errors){let S=h.errors.map(d=>({...d,instancePath:`[${c}]${d.instancePath}`,message:`Item ${c}: ${d.message}`}));s.push(...S)}}),{valid:s.length===0,data:s.length===0?i:void 0,errors:s.length>0?s:void 0}}async validateManyAsync(e,a){return await this.ensureSchemaLoaded(e),this.validateMany(e,a)}createTypeValidator(e){if(!this.hasSchema(e))throw new Error(`No schema registered for type '${String(e)}'`);return{validate:r(a=>this.validate(e,a),"validate"),validateAsync:r(a=>this.validateAsync(e,a),"validateAsync"),validateStrict:r(a=>this.validateStrict(e,a),"validateStrict"),validateStrictAsync:r(a=>this.validateStrictAsync(e,a),"validateStrictAsync"),validateMany:r(a=>this.validateMany(e,a),"validateMany"),validateManyAsync:r(a=>this.validateManyAsync(e,a),"validateManyAsync")}}addCustomValidation(e,a,t){return this.ajv.addKeyword({keyword:a,validate:r(function s(i,h){let c=t(h);return typeof c=="string"?(s.errors=[{message:c}],!1):c},"validate")}),this}async ensureSchemaLoaded(e){if(this.hasSchema(e))return;let a=this.schemaCache?.get(e);if(a){this.registerSchema(e,a);return}if(!this.schemaLoader)throw new Error(`No schema registered for type '${String(e)}' and no schema loader configured`);let t=await this.schemaLoader(e);if(!t)throw new Error(`Schema loader did not return a schema for type '${String(e)}'`);this.registerSchema(e,t)}cacheSchema(e,a){this.schemaCache&&this.schemaCache.set(e,a,{ttlMs:this.schemaCacheTtl?.ttlMs,slidingTtlMs:this.schemaCacheTtl?.slidingTtlMs})}normalizeSchemaInput(e){let a=typeof e=="string"?JSON.parse(e):e;if(typeof a!="object"||a===null||Array.isArray(a))throw new Error("Schema JSON must be an object keyed by schema type");return a}formatErrors(e){return e.map(a=>({keyword:a.keyword,instancePath:a.instancePath||"",schemaPath:a.schemaPath||"",params:a.params||{},message:a.message||"Validation error",data:a.data,parentSchema:a.parentSchema,schema:a.schema}))}};export{m as Validator};