@codama/visitors
Version:
All visitors for the Codama framework
1,120 lines (1,116 loc) • 45.6 kB
JavaScript
import { bottomUpTransformerVisitor, LinkableDictionary, pipe, recordLinkablesOnFirstVisitVisitor, rootNodeVisitor, getUniqueHashStringVisitor, visit, deleteNodesVisitor, getLastNodeFromPath, identityVisitor, extendVisitor, NodeStack, mergeVisitor, interceptVisitor, findProgramNodeFromPath, recordNodeStackVisitor, topDownTransformerVisitor, isNodePath, getByteSizeVisitor, nonNullableIdentityVisitor, getRecordLinkablesVisitor, getNodeSelectorFunction } from '@codama/visitors-core';
export * from '@codama/visitors-core';
import { CodamaError, CODAMA_ERROR__VISITORS__CANNOT_ADD_DUPLICATED_PDA_NAMES, CODAMA_ERROR__VISITORS__CANNOT_FLATTEN_STRUCT_WITH_CONFLICTING_ATTRIBUTES, CODAMA_ERROR__VISITORS__INSTRUCTION_ENUM_ARGUMENT_NOT_FOUND, CODAMA_ERROR__VISITORS__INVALID_PDA_SEED_VALUES, CODAMA_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND, CODAMA_ERROR__VISITORS__INVALID_NUMBER_WRAPPER } from '@codama/errors';
import { payerValueNode, identityValueNode, programIdValueNode, publicKeyValueNode, camelCase, assertIsNode, programNode, pdaNode, instructionNode, isNode, instructionArgumentNode, numberTypeNode, numberValueNode, getAllPrograms, INSTRUCTION_INPUT_VALUE_NODES, pdaValueNode, pdaSeedValueNode, accountValueNode, getAllInstructionArguments, argumentValueNode, isNodeFilter, structTypeNode, resolveNestedTypeNode, accountNode, fieldDiscriminatorNode, transformNestedTypeNode, structFieldTypeNode, assertIsNestedTypeNode, amountTypeNode, solAmountTypeNode, dateTimeTypeNode, TYPE_NODES, fixedSizeTypeNode, bytesTypeNode, arrayTypeNode, assertIsNodeFilter, definedTypeLinkNode, getAllDefinedTypes, enumStructVariantTypeNode, accountLinkNode, pdaLinkNode, errorNode, instructionAccountNode, programLinkNode, REGISTERED_NODE_KINDS, definedTypeNode, enumTypeNode, enumTupleVariantTypeNode, enumEmptyVariantTypeNode } from '@codama/nodes';
// src/index.ts
function addPdasVisitor(pdas) {
return bottomUpTransformerVisitor(
Object.entries(pdas).map(([uncasedProgramName, newPdas]) => {
const programName = camelCase(uncasedProgramName);
return {
select: `[programNode]${programName}`,
transform: (node) => {
assertIsNode(node, "programNode");
const existingPdaNames = new Set((node.pdas ?? []).map((pda) => pda.name));
const newPdaNames = new Set(newPdas.map((pda) => pda.name));
const overlappingPdaNames = new Set([...existingPdaNames].filter((name) => newPdaNames.has(name)));
if (overlappingPdaNames.size > 0) {
throw new CodamaError(CODAMA_ERROR__VISITORS__CANNOT_ADD_DUPLICATED_PDA_NAMES, {
duplicatedPdaNames: [...overlappingPdaNames],
program: node,
programName: node.name
});
}
return programNode({
...node,
pdas: [
...node.pdas ?? [],
...newPdas.map(({ name, seeds, docs }) => pdaNode({ docs, name, seeds }))
]
});
}
};
})
);
}
function flattenInstructionDataArgumentsVisitor() {
return bottomUpTransformerVisitor([
{
select: "[instructionNode]",
transform: (instruction) => {
assertIsNode(instruction, "instructionNode");
return instructionNode({
...instruction,
arguments: flattenInstructionArguments(instruction.arguments ?? [])
});
}
}
]);
}
var flattenInstructionArguments = (nodes, options = "*") => {
const camelCaseOptions = options === "*" ? options : options.map(camelCase);
const shouldInline = (node) => options === "*" || camelCaseOptions.includes(camelCase(node.name));
const inlinedArguments = nodes.flatMap((node) => {
if (isNode(node.type, "structTypeNode") && shouldInline(node)) {
return (node.type.fields ?? []).map((field) => instructionArgumentNode({ ...field }));
}
return node;
});
const inlinedFieldsNames = inlinedArguments.map((arg) => arg.name);
const duplicates = inlinedFieldsNames.filter((e, i, a) => a.indexOf(e) !== i);
const uniqueDuplicates = [...new Set(duplicates)];
const hasConflictingNames = uniqueDuplicates.length > 0;
if (hasConflictingNames) {
throw new CodamaError(CODAMA_ERROR__VISITORS__CANNOT_FLATTEN_STRUCT_WITH_CONFLICTING_ATTRIBUTES, {
conflictingAttributes: uniqueDuplicates
});
}
return hasConflictingNames ? nodes : inlinedArguments;
};
// src/createSubInstructionsFromEnumArgsVisitor.ts
function createSubInstructionsFromEnumArgsVisitor(map) {
const linkables = new LinkableDictionary();
const visitor = bottomUpTransformerVisitor(
Object.entries(map).map(([selector, argNameInput]) => ({
select: ["[instructionNode]", selector],
transform: (node, stack) => {
assertIsNode(node, "instructionNode");
const argFields = node.arguments ?? [];
const argName = camelCase(argNameInput);
const argFieldIndex = argFields.findIndex((field) => field.name === argName);
const argField = argFieldIndex >= 0 ? argFields[argFieldIndex] : null;
if (!argField) {
throw new CodamaError(CODAMA_ERROR__VISITORS__INSTRUCTION_ENUM_ARGUMENT_NOT_FOUND, {
argumentName: argName,
instruction: node,
instructionName: node.name
});
}
let argType;
if (isNode(argField.type, "enumTypeNode")) {
argType = argField.type;
} else if (isNode(argField.type, "definedTypeLinkNode") && linkables.has([...stack.getPath(), argField.type])) {
const linkedType = linkables.get([...stack.getPath(), argField.type])?.type;
assertIsNode(linkedType, "enumTypeNode");
argType = linkedType;
} else {
throw new CodamaError(CODAMA_ERROR__VISITORS__INSTRUCTION_ENUM_ARGUMENT_NOT_FOUND, {
argumentName: argName,
instruction: node,
instructionName: node.name
});
}
const subInstructions = (argType.variants ?? []).map((variant, index) => {
const subName = camelCase(`${node.name} ${variant.name}`);
const subFields = argFields.slice(0, argFieldIndex);
subFields.push(
instructionArgumentNode({
defaultValue: numberValueNode(index),
defaultValueStrategy: "omitted",
name: `${subName}Discriminator`,
type: numberTypeNode("u8")
})
);
if (isNode(variant, "enumStructVariantTypeNode")) {
subFields.push(
instructionArgumentNode({
...argField,
type: variant.struct
})
);
} else if (isNode(variant, "enumTupleVariantTypeNode")) {
subFields.push(
instructionArgumentNode({
...argField,
type: variant.tuple
})
);
}
subFields.push(...argFields.slice(argFieldIndex + 1));
return instructionNode({
...node,
arguments: flattenInstructionArguments(subFields),
name: subName
});
});
return instructionNode({
...node,
subInstructions: [...node.subInstructions ?? [], ...subInstructions]
});
}
}))
);
return pipe(visitor, (v) => recordLinkablesOnFirstVisitVisitor(v, linkables));
}
function deduplicateIdenticalDefinedTypesVisitor() {
return rootNodeVisitor((root) => {
const typeMap = /* @__PURE__ */ new Map();
const allPrograms = getAllPrograms(root);
allPrograms.forEach((program) => {
(program.definedTypes ?? []).forEach((type) => {
const typeWithProgram = { program, type };
const list = typeMap.get(type.name) ?? [];
typeMap.set(type.name, [...list, typeWithProgram]);
});
});
typeMap.forEach((list, name) => {
if (list.length <= 1) {
typeMap.delete(name);
}
});
const hashVisitor = getUniqueHashStringVisitor({ removeDocs: true });
typeMap.forEach((list, name) => {
const types = list.map((item) => visit(item.type, hashVisitor));
const typesAreEqual = types.every((type, _, arr) => type === arr[0]);
if (!typesAreEqual) {
typeMap.delete(name);
}
});
const deleteSelectors = Array.from(typeMap.values()).flatMap((list) => {
const sortedList = list.sort((a, b) => allPrograms.indexOf(a.program) - allPrograms.indexOf(b.program));
const [, ...sortedListTail] = sortedList;
return sortedListTail;
}).map(({ program, type }) => `[programNode]${program.name}.[definedTypeNode]${type.name}`);
if (deleteSelectors.length > 0) {
const newRoot = visit(root, deleteNodesVisitor(deleteSelectors));
assertIsNode(newRoot, "rootNode");
return newRoot;
}
return root;
});
}
function fillDefaultPdaSeedValuesVisitor(instructionPath, linkables, strictMode = false) {
const instruction = getLastNodeFromPath(instructionPath);
return pipe(
identityVisitor({ keys: INSTRUCTION_INPUT_VALUE_NODES }),
(v) => extendVisitor(v, {
visitPdaValue(node, { next }) {
const visitedNode = next(node);
assertIsNode(visitedNode, "pdaValueNode");
const foundPda = isNode(visitedNode.pda, "pdaNode") ? visitedNode.pda : linkables.get([...instructionPath, visitedNode.pda]);
if (!foundPda) return visitedNode;
const seeds = addDefaultSeedValuesFromPdaWhenMissing(instruction, foundPda, visitedNode.seeds ?? []);
if (strictMode && !allSeedsAreValid(instruction, foundPda, seeds)) {
throw new CodamaError(CODAMA_ERROR__VISITORS__INVALID_PDA_SEED_VALUES, {
instruction,
instructionName: instruction.name,
pda: foundPda,
pdaName: foundPda.name
});
}
return pdaValueNode(visitedNode.pda, seeds);
}
})
);
}
function addDefaultSeedValuesFromPdaWhenMissing(instruction, pda, existingSeeds) {
const existingSeedNames = new Set(existingSeeds.map((seed) => seed.name));
const defaultSeeds = getDefaultSeedValuesFromPda(instruction, pda).filter(
(seed) => !existingSeedNames.has(seed.name)
);
return [...existingSeeds, ...defaultSeeds];
}
function getDefaultSeedValuesFromPda(instruction, pda) {
return (pda.seeds ?? []).flatMap((seed) => {
if (!isNode(seed, "variablePdaSeedNode")) return [];
const hasMatchingAccount = (instruction.accounts ?? []).some((a) => a.name === seed.name);
if (isNode(seed.type, "publicKeyTypeNode") && hasMatchingAccount) {
return [pdaSeedValueNode(seed.name, accountValueNode(seed.name))];
}
const hasMatchingArgument = getAllInstructionArguments(instruction).some((a) => a.name === seed.name);
if (hasMatchingArgument) {
return [pdaSeedValueNode(seed.name, argumentValueNode(seed.name))];
}
return [];
});
}
function allSeedsAreValid(instruction, foundPda, seeds) {
const hasAllVariableSeeds = (foundPda.seeds ?? []).filter(isNodeFilter("variablePdaSeedNode")).length === seeds.length;
const allAccountsName = (instruction.accounts ?? []).map((a) => a.name);
const allArgumentsName = getAllInstructionArguments(instruction).map((a) => a.name);
const validSeeds = seeds.every((seed) => {
if (isNode(seed.value, "accountValueNode")) {
return allAccountsName.includes(seed.value.name);
}
if (isNode(seed.value, "argumentValueNode")) {
return allArgumentsName.includes(seed.value.name);
}
return true;
});
return hasAllVariableSeeds && validSeeds;
}
function flattenStructVisitor(map) {
return bottomUpTransformerVisitor(
Object.entries(map).map(([stack, options]) => ({
select: `${stack}.[structTypeNode]`,
transform: (node) => flattenStruct(node, options)
}))
);
}
var flattenStruct = (node, options = "*") => {
assertIsNode(node, "structTypeNode");
const camelCaseOptions = options === "*" ? options : options.map(camelCase);
const shouldInline = (field) => options === "*" || camelCaseOptions.includes(camelCase(field.name));
const inlinedFields = (node.fields ?? []).flatMap((field) => {
if (isNode(field.type, "structTypeNode") && shouldInline(field)) {
return field.type.fields ?? [];
}
return [field];
});
const inlinedFieldsNames = inlinedFields.map((arg) => arg.name);
const duplicates = inlinedFieldsNames.filter((e, i, a) => a.indexOf(e) !== i);
const uniqueDuplicates = [...new Set(duplicates)];
const hasConflictingNames = uniqueDuplicates.length > 0;
if (hasConflictingNames) {
throw new CodamaError(CODAMA_ERROR__VISITORS__CANNOT_FLATTEN_STRUCT_WITH_CONFLICTING_ATTRIBUTES, {
conflictingAttributes: uniqueDuplicates
});
}
return hasConflictingNames ? node : structTypeNode(inlinedFields);
};
function mergeHistograms(histograms) {
const result = {};
histograms.forEach((histogram) => {
Object.keys(histogram).forEach((key) => {
const mainCaseKey = key;
if (result[mainCaseKey] === void 0) {
result[mainCaseKey] = histogram[mainCaseKey];
} else {
result[mainCaseKey].total += histogram[mainCaseKey].total;
result[mainCaseKey].inAccounts += histogram[mainCaseKey].inAccounts;
result[mainCaseKey].inDefinedTypes += histogram[mainCaseKey].inDefinedTypes;
result[mainCaseKey].inEvents += histogram[mainCaseKey].inEvents;
result[mainCaseKey].inInstructionArgs += histogram[mainCaseKey].inInstructionArgs;
result[mainCaseKey].directlyAsInstructionArgs += histogram[mainCaseKey].directlyAsInstructionArgs;
}
});
});
return result;
}
function getDefinedTypeHistogramVisitor() {
const stack = new NodeStack();
let mode = null;
let stackLevel = 0;
return pipe(
mergeVisitor(
() => ({}),
(_, histograms) => mergeHistograms(histograms)
),
(v) => interceptVisitor(v, (node, next) => {
stackLevel += 1;
const newNode = next(node);
stackLevel -= 1;
return newNode;
}),
(v) => extendVisitor(v, {
visitAccount(node, { self }) {
mode = "account";
stackLevel = 0;
const histogram = visit(node.data, self);
mode = null;
return histogram;
},
visitDefinedType(node, { self }) {
mode = "definedType";
stackLevel = 0;
const histogram = visit(node.type, self);
mode = null;
return histogram;
},
visitDefinedTypeLink(node) {
const program = findProgramNodeFromPath(stack.getPath());
const key = program ? `${program.name}.${node.name}` : node.name;
return {
[key]: {
directlyAsInstructionArgs: Number(mode === "instruction" && stackLevel <= 1),
inAccounts: Number(mode === "account"),
inDefinedTypes: Number(mode === "definedType"),
inEvents: Number(mode === "event"),
inInstructionArgs: Number(mode === "instruction"),
total: 1
}
};
},
visitEvent(node, { self }) {
mode = "event";
stackLevel = 0;
const histogram = visit(node.data, self);
mode = null;
return histogram;
},
visitInstruction(node, { self }) {
mode = "instruction";
stackLevel = 0;
const dataHistograms = (node.arguments ?? []).map((arg) => visit(arg, self));
const extraHistograms = (node.extraArguments ?? []).map((arg) => visit(arg, self));
mode = null;
const subHistograms = (node.subInstructions ?? []).map((ix) => visit(ix, self));
return mergeHistograms([...dataHistograms, ...extraHistograms, ...subHistograms]);
}
}),
(v) => recordNodeStackVisitor(v, stack)
);
}
function setAccountDiscriminatorFromFieldVisitor(map) {
return bottomUpTransformerVisitor(
Object.entries(map).map(([selector, { field, value, offset }]) => ({
select: ["[accountNode]", selector],
transform: (node) => {
assertIsNode(node, "accountNode");
const accountData = resolveNestedTypeNode(node.data);
const accountFields = accountData.fields ?? [];
const fieldIndex = accountFields.findIndex((f) => f.name === field);
if (fieldIndex < 0) {
throw new CodamaError(CODAMA_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND, {
account: node,
missingField: camelCase(field),
name: node.name
});
}
const fieldNode = accountFields[fieldIndex];
return accountNode({
...node,
data: transformNestedTypeNode(
node.data,
() => structTypeNode([
...accountFields.slice(0, fieldIndex),
structFieldTypeNode({
...fieldNode,
defaultValue: value,
defaultValueStrategy: "omitted"
}),
...accountFields.slice(fieldIndex + 1)
])
),
discriminators: [fieldDiscriminatorNode(field, offset), ...node.discriminators ?? []]
});
}
}))
);
}
function setFixedAccountSizesVisitor() {
const linkables = new LinkableDictionary();
const visitor = topDownTransformerVisitor(
[
{
select: (path) => isNodePath(path, "accountNode") && getLastNodeFromPath(path).size === void 0,
transform: (node, stack) => {
assertIsNode(node, "accountNode");
const size = visit(node.data, getByteSizeVisitor(linkables, { stack }));
if (size === null) return node;
return accountNode({ ...node, size });
}
}
],
{ keys: ["rootNode", "programNode", "accountNode"] }
);
return pipe(visitor, (v) => recordLinkablesOnFirstVisitVisitor(v, linkables));
}
var getCommonInstructionAccountDefaultRules = () => [
{
account: /^(payer|feePayer)$/,
defaultValue: payerValueNode(),
ignoreIfOptional: true
},
{
account: /^(authority)$/,
defaultValue: identityValueNode(),
ignoreIfOptional: true
},
{
account: /^(programId)$/,
defaultValue: programIdValueNode(),
ignoreIfOptional: true
},
{
account: /^(systemProgram|splSystemProgram)$/,
defaultValue: publicKeyValueNode("11111111111111111111111111111111", "splSystem"),
ignoreIfOptional: true
},
{
account: /^(tokenProgram|splTokenProgram)$/,
defaultValue: publicKeyValueNode("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "splToken"),
ignoreIfOptional: true
},
{
account: /^(ataProgram|splAtaProgram)$/,
defaultValue: publicKeyValueNode("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", "splAssociatedToken"),
ignoreIfOptional: true
},
{
account: /^(tokenMetadataProgram|mplTokenMetadataProgram)$/,
defaultValue: publicKeyValueNode("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s", "mplTokenMetadata"),
ignoreIfOptional: true
},
{
account: /^(tokenAuth|mplTokenAuth|authorization|mplAuthorization|auth|mplAuth)RulesProgram$/,
defaultValue: publicKeyValueNode("auth9SigNpDKz4sJJ1DfCTuZrZNSAgh9sFD3rboVmgg", "mplTokenAuthRules"),
ignoreIfOptional: true
},
{
account: /^(candyMachineProgram|mplCandyMachineProgram)$/,
defaultValue: publicKeyValueNode("CndyV3LdqHUfDLmE5naZjVN8rBZz4tqhdefbAnjHG3JR", "mplCandyMachine"),
ignoreIfOptional: true
},
{
account: /^(candyGuardProgram|mplCandyGuardProgram)$/,
defaultValue: publicKeyValueNode("Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g", "mplCandyGuard"),
ignoreIfOptional: true
},
{
account: /^(clockSysvar|sysvarClock)$/,
defaultValue: publicKeyValueNode("SysvarC1ock11111111111111111111111111111111"),
ignoreIfOptional: true
},
{
account: /^(epochScheduleSysvar|sysvarEpochSchedule)$/,
defaultValue: publicKeyValueNode("SysvarEpochSchedu1e111111111111111111111111"),
ignoreIfOptional: true
},
{
account: /^(instructions?Sysvar|sysvarInstructions?)(Account)?$/,
defaultValue: publicKeyValueNode("Sysvar1nstructions1111111111111111111111111"),
ignoreIfOptional: true
},
{
account: /^(recentBlockhashesSysvar|sysvarRecentBlockhashes)$/,
defaultValue: publicKeyValueNode("SysvarRecentB1ockHashes11111111111111111111"),
ignoreIfOptional: true
},
{
account: /^(rent|rentSysvar|sysvarRent)$/,
defaultValue: publicKeyValueNode("SysvarRent111111111111111111111111111111111"),
ignoreIfOptional: true
},
{
account: /^(rewardsSysvar|sysvarRewards)$/,
defaultValue: publicKeyValueNode("SysvarRewards111111111111111111111111111111"),
ignoreIfOptional: true
},
{
account: /^(slotHashesSysvar|sysvarSlotHashes)$/,
defaultValue: publicKeyValueNode("SysvarS1otHashes111111111111111111111111111"),
ignoreIfOptional: true
},
{
account: /^(slotHistorySysvar|sysvarSlotHistory)$/,
defaultValue: publicKeyValueNode("SysvarS1otHistory11111111111111111111111111"),
ignoreIfOptional: true
},
{
account: /^(stakeHistorySysvar|sysvarStakeHistory)$/,
defaultValue: publicKeyValueNode("SysvarStakeHistory1111111111111111111111111"),
ignoreIfOptional: true
},
{
account: /^(mplCoreProgram)$/,
defaultValue: publicKeyValueNode("CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d", "mplCore"),
ignoreIfOptional: true
}
];
function setInstructionAccountDefaultValuesVisitor(rules) {
const linkables = new LinkableDictionary();
const stack = new NodeStack();
const sortedRules = rules.sort((a, b) => {
const ia = "instruction" in a;
const ib = "instruction" in b;
if (ia && ib || !a && !ib) return 0;
return ia ? -1 : 1;
});
function matchRule(instruction, account) {
return sortedRules.find((rule) => {
if ("instruction" in rule && rule.instruction && camelCase(rule.instruction) !== instruction.name) {
return false;
}
return typeof rule.account === "string" ? camelCase(rule.account) === account.name : rule.account.test(account.name);
});
}
return pipe(
nonNullableIdentityVisitor({ keys: ["rootNode", "programNode", "instructionNode"] }),
(v) => extendVisitor(v, {
visitInstruction(node) {
const instructionPath = stack.getPath("instructionNode");
const instructionAccounts = (node.accounts ?? []).map((account) => {
const rule = matchRule(node, account);
if (!rule) return account;
if ((rule.ignoreIfOptional ?? false) && (account.isOptional || !!account.defaultValue)) {
return account;
}
try {
return {
...account,
defaultValue: visit(
rule.defaultValue,
fillDefaultPdaSeedValuesVisitor(instructionPath, linkables, true)
)
};
} catch {
return account;
}
});
return instructionNode({
...node,
accounts: instructionAccounts
});
}
}),
(v) => recordNodeStackVisitor(v, stack),
(v) => recordLinkablesOnFirstVisitVisitor(v, linkables)
);
}
function setInstructionDiscriminatorsVisitor(map) {
return bottomUpTransformerVisitor(
Object.entries(map).map(([selector, discriminator]) => ({
select: ["[instructionNode]", selector],
transform: (node) => {
assertIsNode(node, "instructionNode");
const discriminatorArgument = instructionArgumentNode({
defaultValue: discriminator.value,
defaultValueStrategy: discriminator.strategy ?? "omitted",
docs: discriminator.docs ?? [],
name: discriminator.name ?? "discriminator",
type: discriminator.type ?? numberTypeNode("u8")
});
return instructionNode({
...node,
arguments: [discriminatorArgument, ...node.arguments ?? []],
discriminators: [
fieldDiscriminatorNode(discriminator.name ?? "discriminator"),
...node.discriminators ?? []
]
});
}
}))
);
}
function setNumberWrappersVisitor(map) {
return bottomUpTransformerVisitor(
Object.entries(map).map(([selectorStack, wrapper]) => ({
select: `${selectorStack}.[numberTypeNode]`,
transform: (node) => {
assertIsNestedTypeNode(node, "numberTypeNode");
switch (wrapper.kind) {
case "DateTime":
return dateTimeTypeNode(node);
case "SolAmount":
return solAmountTypeNode(node);
case "Amount":
return amountTypeNode(node, wrapper.decimals, wrapper.unit);
default:
throw new CodamaError(CODAMA_ERROR__VISITORS__INVALID_NUMBER_WRAPPER, { wrapper });
}
}
}))
);
}
function setStructDefaultValuesVisitor(map) {
return bottomUpTransformerVisitor(
Object.entries(map).flatMap(([stack, defaultValues]) => {
const camelCasedDefaultValues = Object.fromEntries(
Object.entries(defaultValues).map(([key, value]) => [camelCase(key), value])
);
return [
{
select: `${stack}.[structTypeNode]`,
transform: (node) => {
assertIsNode(node, "structTypeNode");
const fields = (node.fields ?? []).map((field) => {
const defaultValue = camelCasedDefaultValues[field.name];
if (defaultValue === void 0) return field;
if (defaultValue === null) {
return structFieldTypeNode({
...field,
defaultValue: void 0,
defaultValueStrategy: void 0
});
}
return structFieldTypeNode({
...field,
defaultValue: "kind" in defaultValue ? defaultValue : defaultValue.value,
defaultValueStrategy: "kind" in defaultValue ? void 0 : defaultValue.strategy
});
});
return structTypeNode(fields);
}
},
{
select: ["[instructionNode]", stack],
transform: (node) => {
assertIsNode(node, "instructionNode");
const transformArguments = (arg) => {
const defaultValue = camelCasedDefaultValues[arg.name];
if (defaultValue === void 0) return arg;
if (defaultValue === null) {
return instructionArgumentNode({
...arg,
defaultValue: void 0,
defaultValueStrategy: void 0
});
}
return instructionArgumentNode({
...arg,
defaultValue: "kind" in defaultValue ? defaultValue : defaultValue.value,
defaultValueStrategy: "kind" in defaultValue ? void 0 : defaultValue.strategy
});
};
return instructionNode({
...node,
arguments: (node.arguments ?? []).map(transformArguments),
extraArguments: node.extraArguments ? node.extraArguments.map(transformArguments) : void 0
});
}
}
];
})
);
}
function transformDefinedTypesIntoAccountsVisitor(definedTypes) {
return pipe(
nonNullableIdentityVisitor({ keys: ["rootNode", "programNode"] }),
(v) => extendVisitor(v, {
visitProgram(program) {
const typesToExtract = (program.definedTypes ?? []).filter((node) => definedTypes.includes(node.name));
const newDefinedTypes = (program.definedTypes ?? []).filter((node) => !definedTypes.includes(node.name));
const newAccounts = typesToExtract.map((node) => {
assertIsNode(node.type, "structTypeNode");
return accountNode({
...node,
data: node.type,
discriminators: [],
size: void 0
});
});
return programNode({
...program,
accounts: [...program.accounts ?? [], ...newAccounts],
definedTypes: newDefinedTypes
});
}
})
);
}
function transformU8ArraysToBytesVisitor(sizes = "*") {
const hasRequiredSize = (count) => {
if (!isNode(count, "fixedCountNode")) return false;
return sizes === "*" || sizes.includes(count.value);
};
return pipe(
nonNullableIdentityVisitor(),
(v) => extendVisitor(v, {
visitArrayType(node, { self }) {
const child = visit(node.item, self);
assertIsNode(child, TYPE_NODES);
if (isNode(child, "numberTypeNode") && child.format === "u8" && isNode(node.count, "fixedCountNode") && hasRequiredSize(node.count)) {
return fixedSizeTypeNode(bytesTypeNode(), node.count.value);
}
return arrayTypeNode(child, node.count);
}
})
);
}
function unwrapDefinedTypesVisitor(typesToInline = "*") {
const linkables = new LinkableDictionary();
const stack = new NodeStack();
const typesToInlineCamelCased = (typesToInline === "*" ? [] : typesToInline).map((fullPath) => {
if (!fullPath.includes(".")) return camelCase(fullPath);
const [programName, typeName] = fullPath.split(".");
return `${camelCase(programName)}.${camelCase(typeName)}`;
});
const shouldInline = (typeName, programName) => {
if (typesToInline === "*") return true;
const fullPath = `${programName}.${typeName}`;
if (!!programName && typesToInlineCamelCased.includes(fullPath)) return true;
return typesToInlineCamelCased.includes(typeName);
};
return pipe(
nonNullableIdentityVisitor(),
(v) => extendVisitor(v, {
visitDefinedTypeLink(linkType, { self }) {
const programName = linkType.program?.name ?? findProgramNodeFromPath(stack.getPath())?.name;
if (!shouldInline(linkType.name, programName)) {
return linkType;
}
const definedTypePath = linkables.getPathOrThrow(stack.getPath("definedTypeLinkNode"));
const definedType = getLastNodeFromPath(definedTypePath);
stack.pushPath(definedTypePath);
const result = visit(definedType.type, self);
stack.popPath();
return result;
},
visitProgram(program, { self }) {
return programNode({
...program,
accounts: (program.accounts ?? []).map((account) => visit(account, self)).filter(assertIsNodeFilter("accountNode")),
definedTypes: (program.definedTypes ?? []).filter((definedType) => !shouldInline(definedType.name, program.name)).map((type) => visit(type, self)).filter(assertIsNodeFilter("definedTypeNode")),
instructions: (program.instructions ?? []).map((instruction) => visit(instruction, self)).filter(assertIsNodeFilter("instructionNode"))
});
}
}),
(v) => recordNodeStackVisitor(v, stack),
(v) => recordLinkablesOnFirstVisitVisitor(v, linkables)
);
}
function unwrapInstructionArgsDefinedTypesVisitor() {
return rootNodeVisitor((root) => {
const histogram = visit(root, getDefinedTypeHistogramVisitor());
const linkables = new LinkableDictionary();
visit(root, getRecordLinkablesVisitor(linkables));
const definedTypesToInline = Object.keys(histogram).filter((key) => (histogram[key].total ?? 0) === 1 && (histogram[key].directlyAsInstructionArgs ?? 0) === 1).filter((key) => {
const names = key.split(".");
const link = names.length == 2 ? definedTypeLinkNode(names[1], names[0]) : definedTypeLinkNode(key);
const found = linkables.get([link]);
return found && !isNode(found.type, "enumTypeNode");
});
if (definedTypesToInline.length > 0) {
const inlineVisitor = unwrapDefinedTypesVisitor(definedTypesToInline);
const newRoot = visit(root, inlineVisitor);
assertIsNode(newRoot, "rootNode");
return newRoot;
}
return root;
});
}
function unwrapTupleEnumWithSingleStructVisitor(enumsOrVariantsToUnwrap = "*") {
const selectorFunctions = enumsOrVariantsToUnwrap === "*" ? [() => true] : enumsOrVariantsToUnwrap.map((selector) => getNodeSelectorFunction(selector));
const shouldUnwrap = (stack) => selectorFunctions.some((selector) => selector(stack.getPath(REGISTERED_NODE_KINDS)));
return rootNodeVisitor((root) => {
const typesToPotentiallyUnwrap = [];
const definedTypes = new Map(
getAllDefinedTypes(root).map((definedType) => [definedType.name, definedType])
);
let newRoot = visit(
root,
bottomUpTransformerVisitor([
{
select: "[enumTupleVariantTypeNode]",
transform: (node, stack) => {
assertIsNode(node, "enumTupleVariantTypeNode");
if (!shouldUnwrap(stack)) return node;
const tupleNode = resolveNestedTypeNode(node.tuple);
const tupleItems = tupleNode.items ?? [];
if (tupleItems.length !== 1) return node;
let item = tupleItems[0];
if (isNode(item, "definedTypeLinkNode")) {
const definedType = definedTypes.get(item.name);
if (!definedType) return node;
if (!isNode(definedType.type, "structTypeNode")) return node;
typesToPotentiallyUnwrap.push(item.name);
item = definedType.type;
}
if (!isNode(item, "structTypeNode")) return node;
const nestedStruct = transformNestedTypeNode(node.tuple, () => item);
return enumStructVariantTypeNode(node.name, nestedStruct);
}
}
])
);
assertIsNode(newRoot, "rootNode");
const histogram = visit(newRoot, getDefinedTypeHistogramVisitor());
const typesToUnwrap = typesToPotentiallyUnwrap.filter(
(type) => !histogram[type] || histogram[type].total === 0
);
newRoot = visit(newRoot, unwrapDefinedTypesVisitor(typesToUnwrap));
assertIsNode(newRoot, "rootNode");
return newRoot;
});
}
function unwrapTypeDefinedLinksVisitor(definedLinksType) {
const linkables = new LinkableDictionary();
const transformers = definedLinksType.map((selector) => ({
select: ["[definedTypeLinkNode]", selector],
transform: (_, stack) => {
const definedType = linkables.getOrThrow(stack.getPath("definedTypeLinkNode"));
return definedType.type;
}
}));
return pipe(bottomUpTransformerVisitor(transformers), (v) => recordLinkablesOnFirstVisitVisitor(v, linkables));
}
function renameStructNode(node, map) {
return structTypeNode(
(node.fields ?? []).map(
(field) => map[field.name] ? structFieldTypeNode({ ...field, name: map[field.name] }) : field
)
);
}
function renameEnumNode(node, map) {
return enumTypeNode(
(node.variants ?? []).map(
(variant) => map[variant.name] ? renameEnumVariant(variant, map[variant.name]) : variant
),
{ ...node }
);
}
function renameEnumVariant(variant, newName) {
if (isNode(variant, "enumStructVariantTypeNode")) {
return enumStructVariantTypeNode(newName, variant.struct);
}
if (isNode(variant, "enumTupleVariantTypeNode")) {
return enumTupleVariantTypeNode(newName, variant.tuple);
}
return enumEmptyVariantTypeNode(newName);
}
// src/updateAccountsVisitor.ts
function updateAccountsVisitor(map) {
return bottomUpTransformerVisitor(
Object.entries(map).flatMap(([selector, updates]) => {
const newName = typeof updates === "object" && "name" in updates && updates.name ? camelCase(updates.name) : void 0;
const pdasToUpsert = [];
const transformers = [
{
select: ["[accountNode]", selector],
transform: (node, stack) => {
assertIsNode(node, "accountNode");
if ("delete" in updates) return null;
const programNode6 = findProgramNodeFromPath(stack.getPath());
const { seeds, pda, ...assignableUpdates } = updates;
let newPda = node.pda;
if (pda && seeds !== void 0) {
newPda = pda;
pdasToUpsert.push({
pda: pdaNode({ name: pda.name, seeds }),
program: programNode6.name
});
} else if (pda) {
newPda = pda;
} else if (seeds !== void 0 && node.pda) {
pdasToUpsert.push({
pda: pdaNode({ name: node.pda.name, seeds }),
program: programNode6.name
});
} else if (seeds !== void 0) {
newPda = pdaLinkNode(newName ?? node.name);
pdasToUpsert.push({
pda: pdaNode({ name: newName ?? node.name, seeds }),
program: programNode6.name
});
}
return accountNode({
...node,
...assignableUpdates,
data: transformNestedTypeNode(
node.data,
(struct) => renameStructNode(struct, updates.data ?? {})
),
pda: newPda
});
}
},
{
select: `[programNode]`,
transform: (node) => {
assertIsNode(node, "programNode");
const pdasToUpsertForProgram = pdasToUpsert.filter((p) => p.program === node.name).map((p) => p.pda);
if (pdasToUpsertForProgram.length === 0) return node;
const existingPdaNames = new Set((node.pdas ?? []).map((pda) => pda.name));
const pdasToCreate = pdasToUpsertForProgram.filter((p) => !existingPdaNames.has(p.name));
const pdasToUpdate = new Map(
pdasToUpsertForProgram.filter((p) => existingPdaNames.has(p.name)).map((p) => [p.name, p])
);
const newPdas = [...(node.pdas ?? []).map((p) => pdasToUpdate.get(p.name) ?? p), ...pdasToCreate];
return programNode({ ...node, pdas: newPdas });
}
}
];
if (newName) {
transformers.push(
{
select: ["[accountLinkNode]", selector],
transform: (node) => {
assertIsNode(node, "accountLinkNode");
return accountLinkNode(newName);
}
},
{
select: ["[pdaNode]", selector],
transform: (node) => {
assertIsNode(node, "pdaNode");
return pdaNode({ name: newName, seeds: node.seeds });
}
},
{
select: ["[pdaLinkNode]", selector],
transform: (node) => {
assertIsNode(node, "pdaLinkNode");
return pdaLinkNode(newName);
}
}
);
}
return transformers;
})
);
}
function updateDefinedTypesVisitor(map) {
return bottomUpTransformerVisitor(
Object.entries(map).flatMap(([selector, updates]) => {
const newName = typeof updates === "object" && "name" in updates && updates.name ? camelCase(updates.name) : void 0;
const transformers = [
{
select: ["[definedTypeNode]", selector],
transform: (node) => {
assertIsNode(node, "definedTypeNode");
if ("delete" in updates) {
return null;
}
const { data: dataUpdates, ...otherUpdates } = updates;
let newType = node.type;
if (isNode(node.type, "structTypeNode")) {
newType = renameStructNode(node.type, dataUpdates ?? {});
} else if (isNode(node.type, "enumTypeNode")) {
newType = renameEnumNode(node.type, dataUpdates ?? {});
}
return definedTypeNode({
...node,
...otherUpdates,
name: newName ?? node.name,
type: newType
});
}
}
];
if (newName) {
transformers.push({
select: ["[definedTypeLinkNode]", selector],
transform: (node) => {
assertIsNode(node, "definedTypeLinkNode");
return definedTypeLinkNode(newName);
}
});
}
return transformers;
})
);
}
function updateErrorsVisitor(map) {
return bottomUpTransformerVisitor(
Object.entries(map).map(([name, updates]) => ({
select: `[errorNode]${name}`,
transform: (node) => {
assertIsNode(node, "errorNode");
if ("delete" in updates) return null;
return errorNode({ ...node, ...updates });
}
}))
);
}
function updateInstructionsVisitor(map) {
const linkables = new LinkableDictionary();
const stack = new NodeStack();
const transformers = Object.entries(map).map(([selector, updates]) => ({
select: ["[instructionNode]", selector],
transform: (node) => {
assertIsNode(node, "instructionNode");
if ("delete" in updates) {
return null;
}
const instructionPath = stack.getPath("instructionNode");
const { accounts: accountUpdates, arguments: argumentUpdates, ...metadataUpdates } = updates;
const { newArguments, newExtraArguments } = handleInstructionArguments(node, argumentUpdates ?? {});
const newAccounts = (node.accounts ?? []).map(
(account) => handleInstructionAccount(instructionPath, account, accountUpdates ?? {}, linkables)
);
return instructionNode({
...node,
...metadataUpdates,
accounts: newAccounts,
arguments: newArguments,
extraArguments: newExtraArguments.length > 0 ? newExtraArguments : void 0
});
}
}));
return pipe(
bottomUpTransformerVisitor(transformers),
(v) => recordNodeStackVisitor(v, stack),
(v) => recordLinkablesOnFirstVisitVisitor(v, linkables)
);
}
function handleInstructionAccount(instructionPath, account, accountUpdates, linkables) {
const accountUpdate = accountUpdates?.[account.name];
if (!accountUpdate) return account;
const { defaultValue, ...acountWithoutDefault } = {
...account,
...accountUpdate
};
if (!defaultValue) {
return instructionAccountNode(acountWithoutDefault);
}
return instructionAccountNode({
...acountWithoutDefault,
defaultValue: visit(defaultValue, fillDefaultPdaSeedValuesVisitor(instructionPath, linkables))
});
}
function handleInstructionArguments(instruction, argUpdates) {
const usedArguments = /* @__PURE__ */ new Set();
const newArguments = (instruction.arguments ?? []).map((node) => {
const argUpdate = argUpdates[node.name];
if (!argUpdate) return node;
usedArguments.add(node.name);
return instructionArgumentNode({
...node,
defaultValue: argUpdate.defaultValue ?? node.defaultValue,
defaultValueStrategy: argUpdate.defaultValueStrategy ?? node.defaultValueStrategy,
docs: argUpdate.docs ?? node.docs,
name: argUpdate.name ?? node.name,
type: argUpdate.type ?? node.type
});
});
const updatedExtraArguments = (instruction.extraArguments ?? []).map((node) => {
if (usedArguments.has(node.name)) return node;
const argUpdate = argUpdates[node.name];
if (!argUpdate) return node;
usedArguments.add(node.name);
return instructionArgumentNode({
...node,
defaultValue: argUpdate.defaultValue ?? node.defaultValue,
defaultValueStrategy: argUpdate.defaultValueStrategy ?? node.defaultValueStrategy,
docs: argUpdate.docs ?? node.docs,
name: argUpdate.name ?? node.name,
type: argUpdate.type ?? node.type
});
});
const newExtraArguments = [
...updatedExtraArguments,
...Object.entries(argUpdates).filter(([argName]) => !usedArguments.has(argName)).map(([argName, argUpdate]) => {
const { type } = argUpdate;
assertIsNode(type, TYPE_NODES);
return instructionArgumentNode({
defaultValue: argUpdate.defaultValue ?? void 0,
defaultValueStrategy: argUpdate.defaultValueStrategy ?? void 0,
docs: argUpdate.docs ?? [],
name: argUpdate.name ?? argName,
type
});
})
];
return { newArguments, newExtraArguments };
}
function updateProgramsVisitor(map) {
return bottomUpTransformerVisitor(
Object.entries(map).flatMap(([name, updates]) => {
const newName = typeof updates === "object" && "name" in updates && updates.name ? camelCase(updates.name) : void 0;
const transformers = [
{
select: `[programNode]${name}`,
transform: (node) => {
assertIsNode(node, "programNode");
if ("delete" in updates) return null;
return programNode({ ...node, ...updates });
}
}
];
if (newName) {
transformers.push({
select: `[programLinkNode]${name}`,
transform: (node) => {
assertIsNode(node, "programLinkNode");
return programLinkNode(newName);
}
});
}
return transformers;
})
);
}
export { addPdasVisitor, createSubInstructionsFromEnumArgsVisitor, deduplicateIdenticalDefinedTypesVisitor, fillDefaultPdaSeedValuesVisitor, flattenInstructionArguments, flattenInstructionDataArgumentsVisitor, flattenStruct, flattenStructVisitor, getCommonInstructionAccountDefaultRules, getDefinedTypeHistogramVisitor, setAccountDiscriminatorFromFieldVisitor, setFixedAccountSizesVisitor, setInstructionAccountDefaultValuesVisitor, setInstructionDiscriminatorsVisitor, setNumberWrappersVisitor, setStructDefaultValuesVisitor, transformDefinedTypesIntoAccountsVisitor, transformU8ArraysToBytesVisitor, unwrapDefinedTypesVisitor, unwrapInstructionArgsDefinedTypesVisitor, unwrapTupleEnumWithSingleStructVisitor, unwrapTypeDefinedLinksVisitor, updateAccountsVisitor, updateDefinedTypesVisitor, updateErrorsVisitor, updateInstructionsVisitor, updateProgramsVisitor };
//# sourceMappingURL=index.browser.mjs.map
//# sourceMappingURL=index.browser.mjs.map