/** * This code is inspired by and adapted from: * https://github.com/bitauth/libauth */ import type { XOTemplate } from '@xo-cash/types'; import type { AjvValidator } from './ajv/ajv-types.ts'; import { parseJson, validateSchema } from './ajv/ajv-utils.ts'; import { InvalidTemplateError } from '../errors.ts'; import xoTemplateValidator from './ajv/validate-xo-template.js'; /** * Safely parse and validate a XO template, returning either an * error message as a string or a valid {@link XOTemplate}. The * template may be provided either as an untrusted JSON string or as a * pre-parsed object. * * This method validates both the structure and the contents of a template: * - All properties and sub-properties are verified to be of the expected type. * - The template contains no unknown properties. * - The ID of each entity, script, and scenario is confirmed to be unique. * - Script IDs referenced by entities and other scripts (via `unlocks`) are * confirmed to exist. * - The derivation paths of each HdKey are validated against each other. * * This method does not perform the following: * - TODO: Validate the CashAssembly contents of scripts (by * attempting compilation, evaluating {@link XOTemplateScriptTest}s, * or testing scenario generation). * - TODO: Perform search to verify references. * * @param {unknown} untrustedJsonOrObject - the JSON string or object to validate as a * XO template * @returns {XOTemplate} The parsed and validated template * @throws {InvalidTemplateError} If the template is invalid */ export const parseTemplate = (untrustedJsonOrObject: unknown): XOTemplate => { // Parse the JSON string or object into an unknown object. const parsed = parseJson(untrustedJsonOrObject); // Validate the unknown object against the XO template schema. const templateOrError = validateSchema(parsed, xoTemplateValidator as AjvValidator); // If the template is invalid, throw an error. if (typeof templateOrError === 'string') { throw new InvalidTemplateError(templateOrError); } // Return the validated template. return templateOrError; };