@codama/validators
Version:
Validator visitors for the Codama framework
281 lines (276 loc) • 10.5 kB
JavaScript
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
;