UNPKG

@codama/visitors

Version:

All visitors for the Codama framework

1,120 lines (1,116 loc) 45.6 kB
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.react-native.mjs.map //# sourceMappingURL=index.react-native.mjs.map