38 lines
2.0 KiB
TypeScript
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);
|
|
};
|