/** * This code is inspired from https://github.com/bitauth/libauth */ import { lossyNormalize } from '@bitauth/libauth'; import type { AjvValidator, XOAjvError } from './ajv-types.ts'; import { FailedToParseTemplateError } from '../../errors.ts'; const errorsToDescription = (errors: XOAjvError[]): string => { // TODO: translate instancePath and make errors more descriptive. const descriptions = errors.map((error) => `${error.instancePath}: ${error.message}`).join(','); return descriptions; }; /** * Given an untrusted JSON string or object, verify that * the untrusted value is of the expected shape. Note, this method first * normalizes all characters in the input using `Normalization Form KC` * (Compatibility Decomposition, followed by Canonical Composition). * * @param {unknown} jsonOrObject - The JSON string or object to parse * @returns {T} The parsed object * @throws {FailedToParseTemplateError} If JSON parsing fails */ export const parseJson = (jsonOrObject: unknown): T => { try { // If the JSON object is a string, use it directly. const stringified = typeof jsonOrObject === 'string' ? jsonOrObject : JSON.stringify(jsonOrObject); // Normalize the JSON string using `Normalization Form KC` (Compatibility Decomposition, followed by Canonical Composition). const normalized = lossyNormalize(stringified); // Parse the normalized JSON string into the expected type. const parsed = JSON.parse(normalized); // Return the parsed object. return parsed; } catch (error) { // Error wrapping. If the JSON parsing fails, throw an error. throw new FailedToParseTemplateError(error instanceof Error ? error.message : String(error)); } }; /** * Validates a parsed object against an AJV schema. * * @param parsed - The parsed object to validate * @param validator - The AJV validator function * @returns The validated object if valid, or an error message string if invalid */ export const validateSchema = (parsed: unknown, validator: AjvValidator): T | string => { if (validator(parsed)) { return parsed; } // If the object is invalid, return the error message. return errorsToDescription(validator.errors ?? []); };