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); };