import type { EBSIVerifiableAccreditationRecord } from "@cef-ebsi/vcdm1.1-accreditation-schema"; import type { EBSIVerifiableAttestation } from "@cef-ebsi/vcdm1.1-attestation-schema"; import { metadata as accreditationMetadata, schema as accreditationSchema, } from "@cef-ebsi/vcdm1.1-accreditation-schema"; import { metadata as attestationMetadata, schema as attestationSchema, } from "@cef-ebsi/vcdm1.1-attestation-schema"; import { metadata as statusList2021Metadata, schema as statusList2021Schema, } from "@cef-ebsi/vcdm1.1-revocation-statuslist-schema"; import { AggregateAjvError } from "@segment/ajv-human-errors"; import addFormats from "ajv-formats"; import ajv2020 from "ajv/dist/2020.js"; import memoize from "memoize"; import type { EbsiEnvConfiguration } from "../types.ts"; import { JsonSchemaLoadingError } from "../errors/JsonSchemaLoadingError.ts"; import { JsonSchemaValidationError } from "../errors/JsonSchemaValidationError.ts"; import { ValidationError } from "../errors/ValidationError.ts"; import { loadSchema } from "../utils.ts"; /** * Returns a memoized Ajv instance * @param config - EBSI environment configuration * @param timeout - Axios requests timeout * @returns Ajv instance */ export const getAjvInstance = memoize( (config: EbsiEnvConfiguration, timeout?: number) => { // Configure Ajv const Ajv2020 = ajv2020.default; const ajv = new Ajv2020({ allErrors: true, loadSchema: loadSchema(config, timeout), verbose: true, }); // JSON Schema formats for Ajv addFormats.default(ajv); // Pre-register EBSI Verifiable Attestation schema ajv.addSchema(attestationSchema, attestationMetadata.id.base16); ajv.addSchema( attestationSchema, attestationMetadata.id.multibase_base58btc, ); // Pre-register EBSI Verifiable Accreditation Record schema ajv.addSchema(accreditationSchema, accreditationMetadata.id.base16); ajv.addSchema( accreditationSchema, accreditationMetadata.id.multibase_base58btc, ); // Pre-register EBSI StatusList2021Credential schema ajv.addSchema(statusList2021Schema, statusList2021Metadata.id.base16); ajv.addSchema( statusList2021Schema, statusList2021Metadata.id.multibase_base58btc, ); return ajv; }, { cacheKey: ([config, timeout]) => `ajv-vc-${JSON.stringify(config)}${(timeout ?? "undefined").toString()}`, }, ); /** * Validates that the given value is an EbsiVerifiableAccreditation object. * @param value - Credential payload * @param config - EBSI environment configuration * @param timeout - Axios requests timeout. Default: 15 seconds */ export function validateEbsiVerifiableAccreditation( value: unknown, config: EbsiEnvConfiguration, timeout?: number, ): asserts value is EBSIVerifiableAccreditationRecord { if (typeof value !== "object") { throw new ValidationError( "Invalid EBSI Verifiable Accreditation. The value must be an object.", ); } const ajv = getAjvInstance(config, timeout); const validate = ajv.getSchema(accreditationMetadata.id.base16); if (!validate) { throw new JsonSchemaLoadingError( "Unable to get EBSI Verifiable Accreditation schema", ); } const valid = validate(value); if (!valid && validate.errors) { const errors = new AggregateAjvError(validate.errors, { fieldLabels: "jsonPointer", }); throw new JsonSchemaValidationError( `Invalid EBSI Verifiable Accreditation. ${errors.message}`, validate.errors, ); } } /** * Validates that the given value is an EBSIVerifiableAttestation object. * @param value - Credential payload * @param config - EBSI environment configuration * @param timeout - Axios requests timeout. Default: 15 seconds */ export function validateEbsiVerifiableAttestation( value: unknown, config: EbsiEnvConfiguration, timeout?: number, ): asserts value is EBSIVerifiableAttestation { if (typeof value !== "object") { throw new ValidationError( "Invalid EBSI Verifiable Attestation. The value must be an object.", ); } const ajv = getAjvInstance(config, timeout); const validate = ajv.getSchema(attestationMetadata.id.base16); if (!validate) { throw new JsonSchemaLoadingError("Unable to get EBSI VA schema"); } const valid = validate(value); if (!valid && validate.errors) { const errors = new AggregateAjvError(validate.errors, { fieldLabels: "jsonPointer", }); throw new JsonSchemaValidationError( `Invalid EBSI Verifiable Attestation. ${errors.message}`, validate.errors, ); } }