Files
xo-cash-utils/source/template/parser.ts

38 lines
2.0 KiB
TypeScript

import type { XOTemplate } from '@xo-cash/types';
import { xoTemplateSchema } from './schemas.ts';
import { TemplateInvalidError, buildErrorDescription } from './errors.ts';
import { deserializeTemplate, serializeTemplate } from './serialization.ts';
/**
* Accepts a template value and returns a validated XOTemplate object. The input may be
* either an Extended JSON string or a pre-parsed object. Both are validated
* against the XOTemplate schema.
*
* @param inputTemplate - The value to validate. May be an Extended JSON string or a pre-parsed object.
* @returns The validated template object
* @throws {TemplateSerializationFailedError} If a pre-parsed object input cannot be serialized to JSON for normalization.
* @throws {TemplateJsonMalformedError} If the string input is not valid JSON.
* @throws {TemplateInvalidError} If the value does not conform to the XOTemplate schema.
*/
export const parseTemplate = (inputTemplate: string | XOTemplate): XOTemplate => {
// Regardless of whether the inputTemplate is a string, an imported template or an in-memory object, serialize and then
// deserialize it to ensure the output shape is consistent. For example, optional fields explicitly set to 'undefined' would be passed through
// and then dropped on the string path, resulting in structurally different results for the same template.
const serializedTemplate = typeof inputTemplate === 'string' ? inputTemplate : serializeTemplate(inputTemplate);
const templateObject = deserializeTemplate(serializedTemplate);
// Validate the template against the schema.
const parseResult = xoTemplateSchema.safeParse(templateObject);
if (parseResult.success) {
// Return the validated template object
return parseResult.data as XOTemplate;
}
// Build a human-readable description of every validation failure
const errorDescription = buildErrorDescription(parseResult.error.issues);
// Throw a typed error with the description
throw new TemplateInvalidError(errorDescription);
};