UNPKG

@codama/validators

Version:

Validator visitors for the Codama framework

281 lines (276 loc) 10.5 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { LOG_LEVELS: () => LOG_LEVELS, getLevelIndex: () => getLevelIndex, getValidationItemsVisitor: () => getValidationItemsVisitor, throwValidatorItemsVisitor: () => throwValidatorItemsVisitor, validationItem: () => validationItem }); module.exports = __toCommonJS(index_exports); // src/getValidationItemsVisitor.ts var import_nodes = require("@codama/nodes"); var import_visitors_core = require("@codama/visitors-core"); // src/ValidationItem.ts var LOG_LEVELS = ["debug", "trace", "info", "warn", "error"]; function validationItem(level, message, node, path) { return { level, message, node, path: Array.isArray(path) ? path : path.getPath() }; } var getLevelIndex = (level) => LOG_LEVELS.indexOf(level); // src/getValidationItemsVisitor.ts function getValidationItemsVisitor() { const linkables = new import_visitors_core.LinkableDictionary(); const stack = new import_visitors_core.NodeStack(); return (0, import_visitors_core.pipe)( (0, import_visitors_core.mergeVisitor)( () => [], (_, items) => items.flat() ), (v) => (0, import_visitors_core.recordLinkablesOnFirstVisitVisitor)(v, linkables), (v) => (0, import_visitors_core.recordNodeStackVisitor)(v, stack), (v) => (0, import_visitors_core.extendVisitor)(v, { visitAccount(node, { next }) { const items = []; if (!node.name) { items.push(validationItem("error", "Account has no name.", node, stack)); } return [...items, ...next(node)]; }, visitDefinedType(node, { next }) { const items = []; if (!node.name) { items.push(validationItem("error", "Defined type has no name.", node, stack)); } return [...items, ...next(node)]; }, visitDefinedTypeLink(node, { next }) { const items = []; if (!node.name) { items.push(validationItem("error", "Pointing to a defined type with no name.", node, stack)); } else if (!linkables.has(stack.getPath(node.kind))) { items.push( validationItem( "error", `Pointing to a missing defined type named "${node.name}"`, node, stack ) ); } return [...items, ...next(node)]; }, visitEnumEmptyVariantType(node, { next }) { const items = []; if (!node.name) { items.push(validationItem("error", "Enum variant has no name.", node, stack)); } return [...items, ...next(node)]; }, visitEnumStructVariantType(node, { next }) { const items = []; if (!node.name) { items.push(validationItem("error", "Enum variant has no name.", node, stack)); } return [...items, ...next(node)]; }, visitEnumTupleVariantType(node, { next }) { const items = []; if (!node.name) { items.push(validationItem("error", "Enum variant has no name.", node, stack)); } return [...items, ...next(node)]; }, visitEnumType(node, { next }) { const items = []; if (node.variants.length === 0) { items.push(validationItem("warn", "Enum has no variants.", node, stack)); } node.variants.forEach((variant) => { if (!variant.name) { items.push(validationItem("error", "Enum variant has no name.", node, stack)); } }); return [...items, ...next(node)]; }, visitError(node, { next }) { const items = []; if (!node.name) { items.push(validationItem("error", "Error has no name.", node, stack)); } if (typeof node.code !== "number") { items.push(validationItem("error", "Error has no code.", node, stack)); } if (!node.message) { items.push(validationItem("warn", "Error has no message.", node, stack)); } return [...items, ...next(node)]; }, visitInstruction(node, { next }) { const items = []; if (!node.name) { items.push(validationItem("error", "Instruction has no name.", node, stack)); } const accountNameHistogram = /* @__PURE__ */ new Map(); node.accounts.forEach((account) => { if (!account.name) { items.push(validationItem("error", "Instruction account has no name.", node, stack)); return; } const count = (accountNameHistogram.get(account.name) ?? 0) + 1; accountNameHistogram.set(account.name, count); if (count === 2) { items.push( validationItem( "error", `Account name "${account.name}" is not unique in instruction "${node.name}".`, node, stack ) ); } }); const cyclicCheckVisitor = (0, import_visitors_core.getResolvedInstructionInputsVisitor)(); try { (0, import_visitors_core.visit)(node, cyclicCheckVisitor); } catch (error) { items.push(validationItem("error", error.message, node, stack)); } const names = (0, import_nodes.getAllInstructionArguments)(node).map(({ name }) => (0, import_nodes.camelCase)(name)); const duplicates = names.filter((e, i, a) => a.indexOf(e) !== i); const uniqueDuplicates = [...new Set(duplicates)]; const hasConflictingNames = uniqueDuplicates.length > 0; if (hasConflictingNames) { items.push( validationItem( "error", `The names of the following instruction arguments are conflicting: [${uniqueDuplicates.join(", ")}].`, node, stack ) ); } (0, import_nodes.getAllInstructionArguments)(node).forEach((argument) => { const { defaultValue } = argument; if ((0, import_nodes.isNode)(defaultValue, "accountBumpValueNode")) { const defaultAccount = node.accounts.find((account) => account.name === defaultValue.name); if (defaultAccount && defaultAccount.isSigner !== false) { items.push( validationItem( "error", `Argument ${argument.name} cannot default to the bump attribute of the [${defaultValue.name}] account as it may be a Signer.`, node, stack ) ); } } }); return [...items, ...next(node)]; }, visitProgram(node, { next }) { const items = []; if (!node.name) { items.push(validationItem("error", "Program has no name.", node, stack)); } if (!node.publicKey) { items.push(validationItem("error", "Program has no public key.", node, stack)); } if (!node.version) { items.push(validationItem("warn", "Program has no version.", node, stack)); } if (!node.origin) { items.push(validationItem("info", "Program has no origin.", node, stack)); } return [...items, ...next(node)]; }, visitStructFieldType(node, { next }) { const items = []; if (!node.name) { items.push(validationItem("error", "Struct field has no name.", node, stack)); } return [...items, ...next(node)]; }, visitStructType(node, { next }) { const items = []; const fieldNameHistogram = /* @__PURE__ */ new Map(); node.fields.forEach((field) => { if (!field.name) return; const count = (fieldNameHistogram.get(field.name) ?? 0) + 1; fieldNameHistogram.set(field.name, count); if (count === 2) { items.push( validationItem( "error", `Struct field name "${field.name}" is not unique.`, field, stack ) ); } }); return [...items, ...next(node)]; }, visitTupleType(node, { next }) { const items = []; if (node.items.length === 0) { items.push(validationItem("warn", "Tuple has no items.", node, stack)); } return [...items, ...next(node)]; } }) ); } // src/throwValidatorItemsVisitor.ts var import_errors = require("@codama/errors"); var import_visitors_core2 = require("@codama/visitors-core"); function throwValidatorItemsVisitor(visitor, throwLevel = "error") { return (0, import_visitors_core2.mapVisitor)(visitor, (validationItems) => { const levelHistogram = [...validationItems].sort((a, b) => getLevelIndex(b.level) - getLevelIndex(a.level)).reduce( (acc, item) => { acc[item.level] = (acc[item.level] ?? 0) + 1; return acc; }, {} ); const maxLevel = Object.keys(levelHistogram).map((level) => getLevelIndex(level)).sort((a, b) => b - a)[0]; if (maxLevel >= getLevelIndex(throwLevel)) { const formattedHistogram = Object.keys(levelHistogram).map((level) => `${level}s: ${levelHistogram[level]}`).join(", "); throw new import_errors.CodamaError(import_errors.CODAMA_ERROR__VISITORS__FAILED_TO_VALIDATE_NODE, { formattedHistogram, validationItems }); } }); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { LOG_LEVELS, getLevelIndex, getValidationItemsVisitor, throwValidatorItemsVisitor, validationItem }); //# sourceMappingURL=index.node.cjs.map