@codama/renderers-js
Version:
JavaScript renderer compatible with the Solana Kit library
3,781 lines • 188 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var nodes = require('@codama/nodes');
var visitorsCore = require('@codama/visitors-core');
var codecsStrings = require('@solana/codecs-strings');
var renderersCore = require('@codama/renderers-core');
require('prettier');
var babelPlugin = require('prettier/plugins/babel');
var estreePlugin = require('prettier/plugins/estree');
var typeScriptPlugin = require('prettier/plugins/typescript');
var standalone = require('prettier/standalone');
var errors = require('@codama/errors');
var semver = require('semver');
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var babelPlugin__namespace = /*#__PURE__*/_interopNamespace(babelPlugin);
var estreePlugin__namespace = /*#__PURE__*/_interopNamespace(estreePlugin);
var typeScriptPlugin__namespace = /*#__PURE__*/_interopNamespace(typeScriptPlugin);
// src/utils/async.ts
function hasAsyncFunction(instructionNode, resolvedInputs, asyncResolvers) {
const hasByteDeltasAsync = (instructionNode.byteDeltas ?? []).some(
({ value }) => nodes.isNode(value, "resolverValueNode") && asyncResolvers.includes(value.name)
);
const hasRemainingAccountsAsync = (instructionNode.remainingAccounts ?? []).some(
({ value }) => nodes.isNode(value, "resolverValueNode") && asyncResolvers.includes(value.name)
);
return hasAsyncDefaultValues(resolvedInputs, asyncResolvers) || hasByteDeltasAsync || hasRemainingAccountsAsync;
}
function hasAsyncDefaultValues(resolvedInputs, asyncResolvers) {
return resolvedInputs.some(
(input) => !!input.defaultValue && isAsyncDefaultValue(input.defaultValue, asyncResolvers)
);
}
function isAsyncDefaultValue(defaultValue, asyncResolvers) {
switch (defaultValue.kind) {
case "pdaValueNode":
return true;
case "resolverValueNode":
return asyncResolvers.includes(defaultValue.name);
case "conditionalValueNode":
return isAsyncDefaultValue(defaultValue.condition, asyncResolvers) || (defaultValue.ifFalse == null ? false : isAsyncDefaultValue(defaultValue.ifFalse, asyncResolvers)) || (defaultValue.ifTrue == null ? false : isAsyncDefaultValue(defaultValue.ifTrue, asyncResolvers));
default:
return false;
}
}
function getInstructionDependencies(input, asyncResolvers, useAsync) {
if (nodes.isNode(input, "instructionNode")) {
return visitorsCore.deduplicateInstructionDependencies([
...(input.accounts ?? []).flatMap((x) => getInstructionDependencies(x, asyncResolvers, useAsync)),
...(input.arguments ?? []).flatMap((x) => getInstructionDependencies(x, asyncResolvers, useAsync)),
...(input.extraArguments ?? []).flatMap((x) => getInstructionDependencies(x, asyncResolvers, useAsync))
]);
}
if (!input.defaultValue) return [];
const getNestedDependencies = (defaultValue) => {
if (!defaultValue) return [];
return getInstructionDependencies({ ...input, defaultValue }, asyncResolvers, useAsync);
};
if (nodes.isNode(input.defaultValue, ["accountValueNode", "accountBumpValueNode"])) {
return [nodes.accountValueNode(input.defaultValue.name)];
}
if (nodes.isNode(input.defaultValue, ["argumentValueNode"])) {
return [nodes.argumentValueNode(input.defaultValue.name)];
}
if (nodes.isNode(input.defaultValue, "pdaValueNode")) {
const dependencies = /* @__PURE__ */ new Map();
(input.defaultValue.seeds ?? []).forEach((seed) => {
if (nodes.isNode(seed.value, ["accountValueNode", "argumentValueNode"])) {
dependencies.set(seed.value.name, { ...seed.value });
}
});
return [...dependencies.values()];
}
if (nodes.isNode(input.defaultValue, "resolverValueNode")) {
const isSynchronousResolver = !asyncResolvers.includes(input.defaultValue.name);
if (useAsync || isSynchronousResolver) {
return input.defaultValue.dependsOn ?? [];
}
}
if (nodes.isNode(input.defaultValue, "conditionalValueNode")) {
return visitorsCore.deduplicateInstructionDependencies([
...getNestedDependencies(input.defaultValue.condition),
...getNestedDependencies(input.defaultValue.ifTrue),
...getNestedDependencies(input.defaultValue.ifFalse)
]);
}
return [];
}
function getBytesFromBytesValueNode(node) {
switch (node.encoding) {
case "utf8":
return codecsStrings.getUtf8Encoder().encode(node.data);
case "base16":
return codecsStrings.getBase16Encoder().encode(node.data);
case "base58":
return codecsStrings.getBase58Encoder().encode(node.data);
case "base64":
default:
return codecsStrings.getBase64Encoder().encode(node.data);
}
}
var parseCustomDataOptions = (customDataOptions, defaultSuffix) => new Map(
customDataOptions.map((o) => {
const options = typeof o === "string" ? { name: o } : o;
const importAs = nodes.camelCase(options.importAs ?? `${options.name}${defaultSuffix}`);
const importFrom = options.importFrom ?? "hooked";
return [
nodes.camelCase(options.name),
{
extract: options.extract ?? false,
extractAs: options.extractAs ? nodes.camelCase(options.extractAs) : importAs,
importAs,
importFrom,
linkNode: nodes.definedTypeLinkNode(importAs)
}
];
})
);
var getDefinedTypeNodesToExtract = (nodes$1, parsedCustomDataOptions) => nodes$1.flatMap((node) => {
const options = parsedCustomDataOptions.get(node.name);
if (!options || !options.extract) return [];
if (nodes.isNode(node, "accountNode")) {
return [nodes.definedTypeNode({ name: options.extractAs, type: { ...node.data } })];
}
return [
nodes.definedTypeNode({
name: options.extractAs,
type: nodes.structTypeNodeFromInstructionArgumentNodes(node.arguments ?? [])
})
];
});
// src/utils/erasableSyntax.ts
function getEnumBody(variantNames, erasableSyntax) {
if (!erasableSyntax) return variantNames.join(", ");
const reverseEntries = variantNames.map((name, index) => `${index}: '${name}'`);
const forwardEntries = variantNames.map((name, index) => `${name}: ${index}`);
return [...reverseEntries, ...forwardEntries].join(", ");
}
var DEFAULT_PRETTIER_OPTIONS = {
plugins: [estreePlugin__namespace, typeScriptPlugin__namespace, babelPlugin__namespace]
};
async function getCodeFormatter(packageFolder, options) {
const shouldFormatCode = options.formatCode ?? true;
if (!shouldFormatCode) return (code) => Promise.resolve(code);
const prettierOptions = {
...DEFAULT_PRETTIER_OPTIONS,
...await resolvePrettierOptions(),
...options.prettierOptions
};
return (code, filepath) => standalone.format(code, { ...prettierOptions, filepath });
}
async function resolvePrettierOptions(packageFolder) {
{
return null;
}
}
function createFragment(content) {
return Object.freeze({ content, features: /* @__PURE__ */ new Set(), imports: createImportMap() });
}
function isFragment(value) {
return typeof value === "object" && value !== null && "content" in value;
}
function fragment(template, ...items) {
return renderersCore.createFragmentTemplate(template, items, isFragment, mergeFragments);
}
function mergeFragments(fragments, mergeContent) {
const filteredFragments = fragments.filter((f) => f !== void 0);
return Object.freeze({
content: mergeContent(filteredFragments.map((fragment2) => fragment2.content)),
features: new Set(filteredFragments.flatMap((f) => [...f.features])),
imports: mergeImportMaps(filteredFragments.map((f) => f.imports))
});
}
function use(importInput, module) {
const importInfo = parseImportInput(importInput);
return addFragmentImports(createFragment(importInfo.usedIdentifier), module, [importInput]);
}
function mergeFragmentImports(fragment2, importMaps) {
return Object.freeze({ ...fragment2, imports: mergeImportMaps([fragment2.imports, ...importMaps]) });
}
function addFragmentImports(fragment2, module, importInputs) {
return Object.freeze({ ...fragment2, imports: addToImportMap(fragment2.imports, module, importInputs) });
}
function removeFragmentImports(fragment2, module, usedIdentifiers) {
return Object.freeze({ ...fragment2, imports: removeFromImportMap(fragment2.imports, module, usedIdentifiers) });
}
function addFragmentFeatures(fragment2, features) {
return Object.freeze({ ...fragment2, features: /* @__PURE__ */ new Set([...fragment2.features, ...features]) });
}
function getExportAllFragment(module) {
return fragment`export * from '${module}';`;
}
function getDocblockFragment(lines, withLineJump = false) {
const lineJump = withLineJump ? "\n" : "";
if (lines.length === 0) return;
if (lines.length === 1) return fragment`/** ${lines[0]} */${lineJump}`;
const prefixedLines = lines.map((line) => line ? ` * ${line}` : " *");
return fragment`/**\n${prefixedLines.join("\n")}\n */${lineJump}`;
}
function getPageFragment(page, scope) {
const header = getDocblockFragment([
"This code was AUTOGENERATED using the Codama library.",
"Please DO NOT EDIT THIS FILE, instead use visitors",
"to add features, then rerun Codama to update it.",
"",
"@see https://github.com/codama-idl/codama"
]);
const imports = page.imports.size === 0 ? void 0 : fragment`${importMapToString(
page.imports,
scope.dependencyMap,
scope.kitImportStrategy,
scope.getImportPath
)}`;
return mergeFragments([header, imports, page], (cs) => cs.join("\n\n"));
}
function getImportFromFactory(overrides, customAccountData, customInstructionData) {
const customDataOverrides = Object.fromEntries(
[...customAccountData.values(), ...customInstructionData.values()].map(({ importFrom, importAs }) => [
importAs,
importFrom
])
);
const linkOverrides = {
accounts: overrides.accounts ?? {},
definedTypes: { ...customDataOverrides, ...overrides.definedTypes },
instructions: overrides.instructions ?? {},
pdas: overrides.pdas ?? {},
programs: overrides.programs ?? {},
resolvers: overrides.resolvers ?? {}
};
return (node) => {
const kind = node.kind;
switch (kind) {
case "accountLinkNode":
return linkOverrides.accounts[node.name] ?? "generatedAccounts";
case "definedTypeLinkNode":
return linkOverrides.definedTypes[node.name] ?? "generatedTypes";
case "instructionLinkNode":
return linkOverrides.instructions[node.name] ?? "generatedInstructions";
case "pdaLinkNode":
return linkOverrides.pdas[node.name] ?? "generatedPdas";
case "programLinkNode":
return linkOverrides.programs[node.name] ?? "generatedPrograms";
case "resolverValueNode":
return linkOverrides.resolvers[node.name] ?? "hooked";
default:
throw new errors.CodamaError(errors.CODAMA_ERROR__UNEXPECTED_NODE_KIND, {
expectedKinds: [
"accountLinkNode",
"definedTypeLinkNode",
"instructionLinkNode",
"pdaLinkNode",
"programLinkNode",
"resolverValueNode"
],
kind,
node
});
}
};
}
function getNameApi(transformers) {
const helpers = {
camelCase: nodes.camelCase,
capitalize: nodes.capitalize,
kebabCase: nodes.kebabCase,
pascalCase: nodes.pascalCase,
snakeCase: nodes.snakeCase,
titleCase: nodes.titleCase
};
return Object.fromEntries(
Object.entries(transformers).map(([key, transformer]) => [key, (name) => transformer(name, helpers)])
);
}
var DEFAULT_NAME_TRANSFORMERS = {
accountDecodeFunction: (name) => `decode${nodes.pascalCase(name)}`,
accountFetchAllFunction: (name) => `fetchAll${nodes.pascalCase(name)}`,
accountFetchAllMaybeFunction: (name) => `fetchAllMaybe${nodes.pascalCase(name)}`,
accountFetchFromSeedsFunction: (name) => `fetch${nodes.pascalCase(name)}FromSeeds`,
accountFetchFunction: (name) => `fetch${nodes.pascalCase(name)}`,
accountFetchMaybeFromSeedsFunction: (name) => `fetchMaybe${nodes.pascalCase(name)}FromSeeds`,
accountFetchMaybeFunction: (name) => `fetchMaybe${nodes.pascalCase(name)}`,
accountGetSizeFunction: (name) => `get${nodes.pascalCase(name)}Size`,
codecFunction: (name) => `get${nodes.pascalCase(name)}Codec`,
constant: (name) => nodes.snakeCase(name).toUpperCase(),
constantFunction: (name) => `get${nodes.pascalCase(name)}Bytes`,
dataArgsType: (name) => `${nodes.pascalCase(name)}Args`,
dataType: (name) => `${nodes.pascalCase(name)}`,
decoderFunction: (name) => `get${nodes.pascalCase(name)}Decoder`,
discriminatedUnionDiscriminator: () => "__kind",
discriminatedUnionFunction: (name) => `${nodes.camelCase(name)}`,
discriminatedUnionVariant: (name) => `${nodes.pascalCase(name)}`,
encoderFunction: (name) => `get${nodes.pascalCase(name)}Encoder`,
enumVariant: (name) => `${nodes.pascalCase(name)}`,
eventDataType: (name) => `${nodes.pascalCase(name)}Event`,
eventParseFunction: (name) => `parse${nodes.pascalCase(name)}Event`,
instructionAsyncFunction: (name) => `get${nodes.pascalCase(name)}InstructionAsync`,
instructionAsyncInputType: (name) => `${nodes.pascalCase(name)}AsyncInput`,
instructionDataType: (name) => `${nodes.pascalCase(name)}InstructionData`,
instructionExtraType: (name) => `${nodes.pascalCase(name)}InstructionExtra`,
instructionParseFunction: (name) => `parse${nodes.pascalCase(name)}Instruction`,
instructionParsedType: (name) => `Parsed${nodes.pascalCase(name)}Instruction`,
instructionSyncFunction: (name) => `get${nodes.pascalCase(name)}Instruction`,
instructionSyncInputType: (name) => `${nodes.pascalCase(name)}Input`,
instructionType: (name) => `${nodes.pascalCase(name)}Instruction`,
isDiscriminatedUnionFunction: (name) => `is${nodes.pascalCase(name)}`,
pdaFindFunction: (name) => `find${nodes.pascalCase(name)}Pda`,
pdaSeedsType: (name) => `${nodes.pascalCase(name)}Seeds`,
programAccountsEnum: (name) => `${nodes.pascalCase(name)}Account`,
programAccountsEnumVariant: (name) => `${nodes.pascalCase(name)}`,
programAccountsIdentifierFunction: (name) => `identify${nodes.pascalCase(name)}Account`,
programAddressConstant: (name) => `${nodes.snakeCase(name).toUpperCase()}_PROGRAM_ADDRESS`,
programErrorConstant: (name) => nodes.snakeCase(name).toUpperCase(),
programErrorConstantPrefix: (name) => `${nodes.snakeCase(name).toUpperCase()}_ERROR__`,
programErrorMessagesMap: (name) => `${nodes.camelCase(name)}ErrorMessages`,
programErrorUnion: (name) => `${nodes.pascalCase(name)}Error`,
programEventsEnum: (name) => `${nodes.pascalCase(name)}Event`,
programEventsEnumVariant: (name) => `${nodes.pascalCase(name)}`,
programEventsIdentifierFunction: (name) => `identify${nodes.pascalCase(name)}Event`,
programGetErrorMessageFunction: (name) => `get${nodes.pascalCase(name)}ErrorMessage`,
programInstructionsEnum: (name) => `${nodes.pascalCase(name)}Instruction`,
programInstructionsEnumVariant: (name) => `${nodes.pascalCase(name)}`,
programInstructionsIdentifierFunction: (name) => `identify${nodes.pascalCase(name)}Instruction`,
programInstructionsParseFunction: (name) => `parse${nodes.pascalCase(name)}Instruction`,
programInstructionsParsedUnionType: (name) => `Parsed${nodes.pascalCase(name)}Instruction`,
programIsErrorFunction: (name) => `is${nodes.pascalCase(name)}Error`,
programPluginAccountKey: (name) => `${nodes.camelCase(name)}`,
programPluginAccountsType: (name) => `${nodes.pascalCase(name)}PluginAccounts`,
programPluginFunction: (name) => `${nodes.camelCase(name)}Program`,
programPluginInstructionKey: (name) => `${nodes.camelCase(name)}`,
programPluginInstructionsType: (name) => `${nodes.pascalCase(name)}PluginInstructions`,
programPluginKey: (name) => `${nodes.camelCase(name)}`,
programPluginPdaKey: (name) => `${nodes.camelCase(name)}`,
programPluginPdasType: (name) => `${nodes.pascalCase(name)}PluginPdas`,
programPluginRequirementsType: (name) => `${nodes.pascalCase(name)}PluginRequirements`,
programPluginType: (name) => `${nodes.pascalCase(name)}Plugin`,
resolverFunction: (name) => `${nodes.camelCase(name)}`
};
// src/utils/options.ts
var DEFAULT_KIT_IMPORT_STRATEGY = "preferRoot";
var DEFAULT_DEPENDENCY_VERSIONS = {
"@solana/accounts": "^6.10.0",
"@solana/addresses": "^6.10.0",
"@solana/codecs": "^6.10.0",
"@solana/errors": "^6.10.0",
"@solana/instruction-plans": "^6.10.0",
"@solana/instructions": "^6.10.0",
"@solana/kit": "^6.10.0",
"@solana/plugin-core": "^6.10.0",
"@solana/plugin-interfaces": "^6.10.0",
"@solana/program-client-core": "^6.10.0",
"@solana/programs": "^6.10.0",
"@solana/rpc-api": "^6.10.0",
"@solana/rpc-types": "^6.10.0",
"@solana/signers": "^6.10.0"
};
async function syncPackageJson(renderMap, formatCode, packageFolder, options) {
const shouldSyncPackageJson = options.syncPackageJson ?? true;
const packageJsonPath = renderersCore.joinPath(packageFolder, "package.json");
const usedDependencies = getUsedDependencyVersions(
renderMap,
options.dependencyMap ?? {},
options.dependencyVersions ?? {},
options.kitImportStrategy ?? DEFAULT_KIT_IMPORT_STRATEGY
);
if (!shouldSyncPackageJson) {
if (renderersCore.fileExists(packageJsonPath)) {
checkExistingPackageJson(renderersCore.readJson(packageJsonPath), usedDependencies);
}
return;
}
if (renderersCore.fileExists(packageJsonPath)) {
const packageJson = updateExistingPackageJson(renderersCore.readJson(packageJsonPath), usedDependencies);
await writePackageJson(packageJson, packageJsonPath, formatCode);
} else {
const packageJson = createNewPackageJson(usedDependencies);
await writePackageJson(packageJson, packageJsonPath, formatCode);
}
}
function createNewPackageJson(dependencyVersions) {
return updateExistingPackageJson(
// oxlint-disable-next-line sort-keys -- Follows the conventional package.json field order instead.
{
name: "js-client",
version: "1.0.0",
description: "",
main: "src/index.ts",
files: ["./dist/src", "./dist/types", "./src/"],
scripts: { test: 'echo "Error: no test specified" && exit 1' },
keywords: [],
author: ""
},
dependencyVersions
);
}
function updateExistingPackageJson(packageJson, dependencyVersions) {
const updatedDependencies = { ...packageJson.dependencies };
const updatedPeerDependencies = { ...packageJson.peerDependencies };
const updatedDevDependencies = { ...packageJson.devDependencies };
for (const [dependency, requiredRange] of Object.entries(dependencyVersions)) {
let found = false;
if (updatedDependencies[dependency]) {
updateDependency(updatedDependencies, dependency, requiredRange);
found = true;
}
if (updatedPeerDependencies[dependency]) {
updateDependency(updatedPeerDependencies, dependency, requiredRange);
found = true;
}
if (updatedDevDependencies[dependency]) {
updateDependency(updatedDevDependencies, dependency, requiredRange);
found = true;
}
if (!found) {
const dependencyGroupToAdd = dependency === "@solana/kit" ? updatedPeerDependencies : updatedDependencies;
dependencyGroupToAdd[dependency] = requiredRange;
}
}
return {
...packageJson,
...Object.entries(updatedPeerDependencies).length > 0 ? { peerDependencies: updatedPeerDependencies } : {},
...Object.entries(updatedDependencies).length > 0 ? { dependencies: updatedDependencies } : {},
...Object.entries(updatedDevDependencies).length > 0 ? { devDependencies: updatedDevDependencies } : {}
};
}
function checkExistingPackageJson(packageJson, dependencyVersions) {
const missingDependencies = [];
const dependenciesToUpdate = [];
const existingDependencies = {
...packageJson.devDependencies,
...packageJson.peerDependencies,
...packageJson.dependencies
};
for (const [dependency, requiredRange] of Object.entries(dependencyVersions)) {
if (!existingDependencies[dependency]) {
missingDependencies.push(dependency);
} else if (shouldUpdateRange(dependency, existingDependencies[dependency], requiredRange)) {
dependenciesToUpdate.push(dependency);
}
}
if (missingDependencies.length === 0 && dependenciesToUpdate.length === 0) return;
const missingList = missingDependencies.map((d) => `- ${d} missing: ${dependencyVersions[d]}
`).join("");
const outdatedList = dependenciesToUpdate.map((d) => `- ${d} outdated: ${existingDependencies[d]} -> ${dependencyVersions[d]}
`).join("");
errors.logWarn(
`The following dependencies in your \`package.json\` are out-of-date or missing:
${missingList}${outdatedList}`
);
}
function getUsedDependencyVersions(renderMap, dependencyMap, dependencyVersions, kitImportStrategy) {
const dependencyVersionsWithDefaults = {
...DEFAULT_DEPENDENCY_VERSIONS,
...dependencyVersions
};
const fragment2 = mergeFragments([...renderMap.values()], () => "");
const usedDependencies = getExternalDependencies(fragment2.imports, dependencyMap, kitImportStrategy);
const [usedDependencyVersion, missingDependencies] = [...usedDependencies].reduce(
([acc, missingDependencies2], dependency) => {
const version = dependencyVersionsWithDefaults[dependency];
if (version) {
acc[dependency] = version;
} else {
missingDependencies2.add(dependency);
}
return [acc, missingDependencies2];
},
[{}, /* @__PURE__ */ new Set()]
);
if (missingDependencies.size > 0) {
throw new errors.CodamaError(errors.CODAMA_ERROR__RENDERERS__MISSING_DEPENDENCY_VERSIONS, {
dependencies: [...missingDependencies],
message: "Please add these dependencies to the `dependencyVersions` option."
});
}
return usedDependencyVersion;
}
function shouldUpdateRange(dependency, currentRange, requiredRange) {
try {
if (semver.subset(currentRange, requiredRange)) {
return false;
}
const minRequiredVersion = semver.minVersion(requiredRange);
const minCurrentVersion = semver.minVersion(currentRange);
if (!minCurrentVersion || !minRequiredVersion) {
throw new Error("Could not determine minimum versions.");
}
if (semver.lt(minCurrentVersion, minRequiredVersion)) {
return true;
}
return false;
} catch (error) {
console.warn(
`Could not parse the following ranges for dependency "${dependency}": [${currentRange}] and/or [${requiredRange}]. Caused by: ${error.message}`
);
return false;
}
}
function updateDependency(dependencyGroup, dependency, requiredRange) {
const currentRange = dependencyGroup[dependency];
if (!shouldUpdateRange(dependency, currentRange, requiredRange)) return;
dependencyGroup[dependency] = requiredRange;
}
async function writePackageJson(packageJson, packageJsonPath, formatCode) {
const packageJsonContent = JSON.stringify(packageJson, null, 2) + "\n";
const formattedContent = await formatCode(packageJsonContent, packageJsonPath);
renderersCore.writeFile(packageJsonPath, formattedContent);
}
// src/utils/typeManifest.ts
function typeManifest(input = {}) {
return Object.freeze({
decoder: fragment``,
encoder: fragment``,
isEnum: false,
looseType: fragment``,
strictType: fragment``,
value: fragment``,
...input
});
}
function mergeTypeManifests(manifests, options = {}) {
const { mergeTypes, mergeCodecs, mergeValues } = options;
const merge = (fragmentFn, mergeFn) => mergeFn ? mergeFragments(manifests.map(fragmentFn), mergeFn) : fragment``;
return Object.freeze({
decoder: merge((m) => m.decoder, mergeCodecs),
encoder: merge((m) => m.encoder, mergeCodecs),
isEnum: false,
looseType: merge((m) => m.looseType, mergeTypes),
strictType: merge((m) => m.strictType, mergeTypes),
value: merge((m) => m.value, mergeValues)
});
}
// src/utils/importMap.ts
var DEFAULT_EXTERNAL_MODULE_MAP = {
solanaAccounts: "@solana/kit",
solanaAddresses: "@solana/kit",
solanaCodecsCore: "@solana/kit",
solanaCodecsDataStructures: "@solana/kit",
solanaCodecsNumbers: "@solana/kit",
solanaCodecsStrings: "@solana/kit",
solanaErrors: "@solana/kit",
solanaInstructionPlans: "@solana/kit",
solanaInstructions: "@solana/kit",
solanaOptions: "@solana/kit",
solanaPluginCore: "@solana/kit",
solanaPluginInterfaces: "@solana/kit",
solanaProgramClientCore: "@solana/kit/program-client-core",
solanaPrograms: "@solana/kit",
solanaRpcApi: "@solana/kit",
solanaRpcTypes: "@solana/kit",
solanaSigners: "@solana/kit"
};
var DEFAULT_GRANULAR_EXTERNAL_MODULE_MAP = {
solanaAccounts: "@solana/accounts",
solanaAddresses: "@solana/addresses",
solanaCodecsCore: "@solana/codecs",
solanaCodecsDataStructures: "@solana/codecs",
solanaCodecsNumbers: "@solana/codecs",
solanaCodecsStrings: "@solana/codecs",
solanaErrors: "@solana/errors",
solanaInstructionPlans: "@solana/instruction-plans",
solanaInstructions: "@solana/instructions",
solanaOptions: "@solana/codecs",
solanaPluginCore: "@solana/plugin-core",
solanaPluginInterfaces: "@solana/plugin-interfaces",
solanaProgramClientCore: "@solana/program-client-core",
solanaPrograms: "@solana/programs",
solanaRpcApi: "@solana/rpc-api",
solanaRpcTypes: "@solana/rpc-types",
solanaSigners: "@solana/signers"
};
var RECOGNIZED_EXTENSION_REGEX = /\.(?:[mc]?[jt]sx?|json)$/;
function createImportMap() {
return Object.freeze(/* @__PURE__ */ new Map());
}
function parseImportInput(input) {
const matches = input.match(/^(type )?([^ ]+)(?: as (.+))?$/);
if (!matches) return Object.freeze({ importedIdentifier: input, isType: false, usedIdentifier: input });
const [_, isType, name, alias] = matches;
return Object.freeze({
importedIdentifier: name,
isType: !!isType,
usedIdentifier: alias ?? name
});
}
function addToImportMap(importMap, module, imports) {
const parsedImports = imports.map(parseImportInput).map((i) => [i.usedIdentifier, i]);
return mergeImportMaps([importMap, /* @__PURE__ */ new Map([[module, new Map(parsedImports)]])]);
}
function removeFromImportMap(importMap, module, usedIdentifiers) {
const newMap = new Map(importMap);
const newModuleMap = new Map(newMap.get(module));
usedIdentifiers.forEach((usedIdentifier) => {
newModuleMap.delete(usedIdentifier);
});
if (newModuleMap.size === 0) {
newMap.delete(module);
} else {
newMap.set(module, newModuleMap);
}
return Object.freeze(newMap);
}
function mergeImportMaps(importMaps) {
if (importMaps.length === 0) return createImportMap();
if (importMaps.length === 1) return importMaps[0];
const mergedMap = new Map(importMaps[0]);
for (const map of importMaps.slice(1)) {
for (const [module, imports] of map) {
const mergedModuleMap = mergedMap.get(module) ?? /* @__PURE__ */ new Map();
for (const [usedIdentifier, importInfo] of imports) {
const existingImportInfo = mergedModuleMap.get(usedIdentifier);
const shouldOverwriteTypeOnly = existingImportInfo && existingImportInfo.importedIdentifier === importInfo.importedIdentifier && existingImportInfo.isType && !importInfo.isType;
if (!existingImportInfo || shouldOverwriteTypeOnly) {
mergedModuleMap.set(usedIdentifier, importInfo);
}
}
mergedMap.set(module, mergedModuleMap);
}
}
return Object.freeze(mergedMap);
}
function importMapToString(importMap, dependencyMap = {}, kitImportStrategy = DEFAULT_KIT_IMPORT_STRATEGY, getImportPath = getImportPathFactory()) {
const resolvedMap = resolveImportMapModules(importMap, dependencyMap, kitImportStrategy, getImportPath);
return [...resolvedMap.entries()].sort(([a], [b]) => {
const relative = Number(a.startsWith(".")) - Number(b.startsWith("."));
if (relative !== 0) return relative;
return a.localeCompare(b);
}).map(([module, imports]) => {
const importInfos = [...imports.values()];
const isTypeOnlyModule = importInfos.length > 0 && importInfos.every(({ isType }) => isType);
const innerImports = importInfos.map((importInfo) => importInfoToString(importInfo, isTypeOnlyModule)).sort((a, b) => a.localeCompare(b)).join(", ");
return `import ${isTypeOnlyModule ? "type " : ""}{ ${innerImports} } from '${module}';`;
}).join("\n");
}
function getExternalDependencies(importMap, dependencyMap, kitImportStrategy) {
const resolvedImports = resolveImportMapModules(importMap, dependencyMap, kitImportStrategy);
return new Set(
[...resolvedImports.keys()].filter((module) => !module.startsWith(".")).map((module) => {
const subPathExportIndex = module.startsWith("@") ? 2 : 1;
return module.split("/").slice(0, subPathExportIndex).join("/");
})
);
}
function resolveImportMapModules(importMap, dependencyMap, kitImportStrategy, getImportPath = getImportPathFactory()) {
const defaultExternalModuleMap = kitImportStrategy === "granular" ? DEFAULT_GRANULAR_EXTERNAL_MODULE_MAP : DEFAULT_EXTERNAL_MODULE_MAP;
if (kitImportStrategy === "preferRoot") {
defaultExternalModuleMap["solanaProgramClientCore"] = "@solana/program-client-core";
}
const dependencyMapWithDefaults = {
...defaultExternalModuleMap,
...getDefaultInternalModuleMap(getImportPath),
...dependencyMap
};
return mergeImportMaps(
[...importMap.entries()].map(([module, imports]) => {
const resolvedModule = dependencyMapWithDefaults[module] ?? module;
return /* @__PURE__ */ new Map([[resolvedModule, imports]]);
})
);
}
function getDefaultInternalModuleMap(getImportPath) {
return {
errors: getImportPath("../errors", "directory"),
generated: getImportPath("..", "directory"),
generatedAccounts: getImportPath("../accounts", "directory"),
generatedErrors: getImportPath("../errors", "directory"),
generatedInstructions: getImportPath("../instructions", "directory"),
generatedPdas: getImportPath("../pdas", "directory"),
generatedPrograms: getImportPath("../programs", "directory"),
generatedTypes: getImportPath("../types", "directory"),
hooked: getImportPath("../../hooked", "directory"),
shared: getImportPath("../shared", "directory"),
types: getImportPath("../types", "directory")
};
}
function getImportPathFactory(importExtension) {
return (path, type) => {
if (!importExtension || !path.startsWith(".") || RECOGNIZED_EXTENSION_REGEX.test(path)) return path;
return type === "directory" ? `${path}/index.${importExtension}` : `${path}.${importExtension}`;
};
}
function importInfoToString({ importedIdentifier, isType, usedIdentifier }, omitTypeKeyword = false) {
const alias = importedIdentifier !== usedIdentifier ? ` as ${usedIdentifier}` : "";
return `${isType && !omitTypeKeyword ? "type " : ""}${importedIdentifier}${alias}`;
}
function getAccountFetchHelpersFragment(scope) {
const { accountPath, typeManifest: typeManifest2, nameApi, customAccountData } = scope;
const accountNode = visitorsCore.getLastNodeFromPath(accountPath);
const decodeFunction = nameApi.accountDecodeFunction(accountNode.name);
const fetchAllFunction = nameApi.accountFetchAllFunction(accountNode.name);
const fetchAllMaybeFunction = nameApi.accountFetchAllMaybeFunction(accountNode.name);
const fetchFunction = nameApi.accountFetchFunction(accountNode.name);
const fetchMaybeFunction = nameApi.accountFetchMaybeFunction(accountNode.name);
const hasCustomData = customAccountData.has(accountNode.name);
const accountType = hasCustomData ? typeManifest2.strictType : nameApi.dataType(accountNode.name);
const decoderFunction = hasCustomData ? typeManifest2.decoder : `${nameApi.decoderFunction(accountNode.name)}()`;
return visitorsCore.pipe(
fragment`export function ${decodeFunction}<TAddress extends string = string>(encodedAccount: EncodedAccount<TAddress>): Account<${accountType}, TAddress>;
export function ${decodeFunction}<TAddress extends string = string>(encodedAccount: MaybeEncodedAccount<TAddress>): MaybeAccount<${accountType}, TAddress>;
export function ${decodeFunction}<TAddress extends string = string>(encodedAccount: EncodedAccount<TAddress> | MaybeEncodedAccount<TAddress>): Account<${accountType}, TAddress> | MaybeAccount<${accountType}, TAddress> {
return decodeAccount(encodedAccount as MaybeEncodedAccount<TAddress>, ${decoderFunction});
}
export async function ${fetchFunction}<TAddress extends string = string>(
rpc: Parameters<typeof fetchEncodedAccount>[0],
address: Address<TAddress>,
config?: FetchAccountConfig,
): Promise<Account<${accountType}, TAddress>> {
const maybeAccount = await ${fetchMaybeFunction}(rpc, address, config);
assertAccountExists(maybeAccount);
return maybeAccount;
}
export async function ${fetchMaybeFunction}<TAddress extends string = string>(
rpc: Parameters<typeof fetchEncodedAccount>[0],
address: Address<TAddress>,
config?: FetchAccountConfig,
): Promise<MaybeAccount<${accountType}, TAddress>> {
const maybeAccount = await fetchEncodedAccount(rpc, address, config);
return ${decodeFunction}(maybeAccount);
}
export async function ${fetchAllFunction}(
rpc: Parameters<typeof fetchEncodedAccounts>[0],
addresses: Array<Address>,
config?: FetchAccountsConfig,
): Promise<Account<${accountType}>[]> {
const maybeAccounts = await ${fetchAllMaybeFunction}(rpc, addresses, config);
assertAccountsExist(maybeAccounts);
return maybeAccounts;
}
export async function ${fetchAllMaybeFunction}(
rpc: Parameters<typeof fetchEncodedAccounts>[0],
addresses: Array<Address>,
config?: FetchAccountsConfig,
): Promise<MaybeAccount<${accountType}>[]> {
const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
return maybeAccounts.map((maybeAccount) => ${decodeFunction}(maybeAccount));
}`,
(f) => addFragmentImports(f, "solanaAddresses", ["type Address"]),
(f) => addFragmentImports(f, "solanaAccounts", [
"type Account",
"assertAccountExists",
"assertAccountsExist",
"decodeAccount",
"type EncodedAccount",
"fetchEncodedAccount",
"fetchEncodedAccounts",
"type FetchAccountConfig",
"type FetchAccountsConfig",
"type MaybeAccount",
"type MaybeEncodedAccount"
])
);
}
function getAccountPdaHelpersFragment(scope) {
const { accountPath, nameApi, linkables, customAccountData, typeManifest: typeManifest2 } = scope;
const accountNode = visitorsCore.getLastNodeFromPath(accountPath);
const pdaNode = accountNode.pda ? linkables.get([...accountPath, accountNode.pda]) : void 0;
if (!pdaNode) return;
const accountType = customAccountData.has(accountNode.name) ? typeManifest2.strictType : nameApi.dataType(accountNode.name);
const importFrom = "generatedPdas";
const pdaSeedsType = nameApi.pdaSeedsType(pdaNode.name);
const findPdaFunction = nameApi.pdaFindFunction(pdaNode.name);
const hasVariableSeeds = (pdaNode.seeds ?? []).filter(nodes.isNodeFilter("variablePdaSeedNode")).length > 0;
const fetchFromSeedsFunction = nameApi.accountFetchFromSeedsFunction(accountNode.name);
const fetchMaybeFromSeedsFunction = nameApi.accountFetchMaybeFromSeedsFunction(accountNode.name);
const fetchMaybeFunction = nameApi.accountFetchMaybeFunction(accountNode.name);
return visitorsCore.pipe(
fragment`export async function ${fetchFromSeedsFunction}(
rpc: Parameters<typeof fetchEncodedAccount>[0],
${hasVariableSeeds ? `seeds: ${pdaSeedsType},` : ""}
config: FetchAccountConfig & { programAddress?: Address } = {},
): Promise<Account<${accountType}>> {
const maybeAccount = await ${fetchMaybeFromSeedsFunction}(rpc, ${hasVariableSeeds ? "seeds, " : ""}config);
assertAccountExists(maybeAccount);
return maybeAccount;
}
export async function ${fetchMaybeFromSeedsFunction}(
rpc: Parameters<typeof fetchEncodedAccount>[0],
${hasVariableSeeds ? `seeds: ${pdaSeedsType},` : ""}
config: FetchAccountConfig & { programAddress?: Address } = {},
): Promise<MaybeAccount<${accountType}>> {
const { programAddress, ...fetchConfig } = config;
const [address] = await ${findPdaFunction}(${hasVariableSeeds ? "seeds, " : ""}{ programAddress });
return await ${fetchMaybeFunction}(rpc, address, fetchConfig);
}`,
(f) => addFragmentImports(
f,
importFrom,
hasVariableSeeds ? [`type ${pdaSeedsType}`, findPdaFunction] : [findPdaFunction]
),
(f) => addFragmentImports(f, "solanaAddresses", ["type Address"]),
(f) => addFragmentImports(f, "solanaAccounts", [
"type Account",
"assertAccountExists",
"type FetchAccountConfig",
"type MaybeAccount"
])
);
}
function getAccountSizeHelpersFragment(scope) {
const { accountPath, nameApi } = scope;
const accountNode = visitorsCore.getLastNodeFromPath(accountPath);
if (accountNode.size == null) return;
const getSizeFunction = nameApi.accountGetSizeFunction(accountNode.name);
return fragment`export function ${getSizeFunction}(): number {
return ${accountNode.size};
}`;
}
// src/fragments/enumDeclaration.ts
function getEnumDeclarationFragment(scope) {
const { variantNames, erasableSyntax } = scope;
const body = getEnumBody(variantNames, erasableSyntax);
return getEnumDeclarationFromBodyFragment({ ...scope, body: fragment`{ ${body} }` });
}
function getEnumDeclarationFromBodyFragment(scope) {
const { name, body, docblock = fragment``, erasableSyntax } = scope;
if (!erasableSyntax) {
return fragment`${docblock}export enum ${name} ${body}`;
}
return fragment`${docblock}export const ${name} = ${body} as const;\n\n${docblock}export type ${name} = (typeof ${name})[Exclude<keyof typeof ${name}, number>];`;
}
// src/fragments/type.ts
function getTypeFragment(scope) {
const { name, manifest, nameApi, docs = [] } = scope;
const docblock = getDocblockFragment(docs, true);
const strictName = nameApi.dataType(name);
const looseName = nameApi.dataArgsType(name);
const aliasedLooseName = `export type ${looseName} = ${strictName};`;
if (manifest.isEnum) {
const enumDeclaration = getEnumDeclarationFromBodyFragment({
...scope,
body: manifest.strictType,
docblock,
name: strictName
});
return fragment`${enumDeclaration}\n\n${aliasedLooseName}`;
}
const looseExport = manifest.strictType.content === manifest.looseType.content ? aliasedLooseName : fragment`export type ${looseName} = ${manifest.looseType};`;
return fragment`${docblock}export type ${strictName} = ${manifest.strictType};\n\n${looseExport}`;
}
function getTypeDecoderFragment(scope) {
const { name, node, manifest, nameApi, docs = [] } = scope;
const decoderFunction = nameApi.decoderFunction(name);
const strictName = nameApi.dataType(name);
const docblock = getDocblockFragment(docs, true);
const decoderType = use(
typeof scope.size === "number" ? "type FixedSizeDecoder" : "type Decoder",
"solanaCodecsCore"
);
const useTypeCast = nodes.isNode(node, "enumTypeNode") && nodes.isDataEnum(node) && typeof scope.size === "number";
const typeCast = useTypeCast ? fragment` as ${decoderType}<${strictName}>` : "";
return fragment`${docblock}export function ${decoderFunction}(): ${decoderType}<${strictName}> {
return ${manifest.decoder}${typeCast};
}`;
}
function getTypeEncoderFragment(scope) {
const { name, node, manifest, nameApi, docs = [] } = scope;
const encoderFunction = nameApi.encoderFunction(name);
const looseName = nameApi.dataArgsType(name);
const docblock = getDocblockFragment(docs, true);
const encoderType = use(
typeof scope.size === "number" ? "type FixedSizeEncoder" : "type Encoder",
"solanaCodecsCore"
);
const useTypeCast = nodes.isNode(node, "enumTypeNode") && nodes.isDataEnum(node) && typeof scope.size === "number";
const typeCast = useTypeCast ? fragment` as ${encoderType}<${looseName}>` : "";
return fragment`${docblock}export function ${encoderFunction}(): ${encoderType}<${looseName}> {
return ${manifest.encoder}${typeCast};
}`;
}
// src/fragments/typeCodec.ts
function getTypeCodecFragment(scope) {
const { codecDocs = [], name, nameApi } = scope;
const codecFunction = nameApi.codecFunction(name);
const decoderFunction = nameApi.decoderFunction(name);
const encoderFunction = nameApi.encoderFunction(name);
const looseName = nameApi.dataArgsType(name);
const strictName = nameApi.dataType(name);
const docblock = getDocblockFragment(codecDocs, true);
const codecType = use(typeof scope.size === "number" ? "type FixedSizeCodec" : "type Codec", "solanaCodecsCore");
return mergeFragments(
[
getTypeEncoderFragment({ ...scope, docs: scope.encoderDocs }),
getTypeDecoderFragment({ ...scope, docs: scope.decoderDocs }),
fragment`${docblock}export function ${codecFunction}(): ${codecType}<${looseName}, ${strictName}> {
return ${use("combineCodec", "solanaCodecsCore")}(${encoderFunction}(), ${decoderFunction}());
}`
],
(renders) => renders.join("\n\n")
);
}
// src/fragments/typeWithCodec.ts
function getTypeWithCodecFragment(scope) {
return mergeFragments(
[getTypeFragment({ ...scope, docs: scope.typeDocs }), getTypeCodecFragment(scope)],
(renders) => renders.join("\n\n")
);
}
// src/fragments/accountType.ts
function getAccountTypeFragment(scope) {
const { accountPath, typeManifest: typeManifest2, nameApi, customAccountData } = scope;
const accountNode = visitorsCore.getLastNodeFromPath(accountPath);
if (customAccountData.has(accountNode.name)) return;
return getTypeWithCodecFragment({
codecDocs: [`Gets the codec for {@link ${nameApi.dataType(accountNode.name)}} account data.`],
decoderDocs: [`Gets the decoder for {@link ${nameApi.dataType(accountNode.name)}} account data.`],
encoderDocs: [`Gets the encoder for {@link ${nameApi.dataArgsType(accountNode.name)}} account data.`],
erasableSyntax: scope.erasableSyntax,
manifest: typeManifest2,
name: accountNode.name,
nameApi,
node: nodes.resolveNestedTypeNode(accountNode.data),
size: scope.size,
typeDocs: accountNode.docs
});
}
function getDiscriminatorConstantsFragment(scope) {
const fragments = scope.discriminatorNodes.map((node) => getDiscriminatorConstantFragment(node, scope)).filter(Boolean);
return mergeFragments(fragments, (c) => c.join("\n\n"));
}
function getDiscriminatorConstantFragment(discriminatorNode, scope) {
switch (discriminatorNode.kind) {
case "constantDiscriminatorNode":
return getConstantDiscriminatorConstantFragment(discriminatorNode, scope);
case "fieldDiscriminatorNode":
return getFieldDiscriminatorConstantFragment(discriminatorNode, scope);
default:
return null;
}
}
function getConstantDiscriminatorName(prefix, index) {
const suffix = index <= 0 ? "" : `_${index + 1}`;
return nodes.camelCase(`${prefix}_discriminator${suffix}`);
}
function getEventDiscriminatorPrefix(nameApi, eventName) {
return nodes.camelCase(nameApi.eventDataType(eventName));
}
function getConstantDiscriminatorConstantFragment(discriminatorNode, scope) {
const { discriminatorNodes, typeManifestVisitor, prefix } = scope;
const index = discriminatorNodes.filter(nodes.isNodeFilter("constantDiscriminatorNode")).indexOf(discriminatorNode);
const name = getConstantDiscriminatorName(prefix, index);
const typeManifest2 = visitorsCore.visit(discriminatorNode.constant.type, typeManifestVisitor);
const encoder = typeManifest2.encoder;
const { value, valueType } = resolveDiscriminatorValue(
visitorsCore.visit(discriminatorNode.constant.value, typeManifestVisitor).value,
nodes.isNode(discriminatorNode.constant.value, "numberValueNode"),
typeManifest2.strictType
);
return getConstantFragment({ ...scope, encoder, name, value, valueType });
}
function getFieldDiscriminatorConstantFragment(discriminatorNode, scope) {
const { fields, prefix, typeManifestVisitor } = scope;
const field = fields.find((f) => f.name === discriminatorNode.name);
if (!field || !field.defaultValue || !nodes.isNode(field.defaultValue, nodes.VALUE_NODES)) {
return null;
}
const name = nodes.camelCase(`${prefix}_${discriminatorNode.name}`);
const typeManifest2 = visitorsCore.visit(field.type, typeManifestVisitor);
const encoder = typeManifest2.encoder;
const { value, valueType } = resolveDiscriminatorValue(
visitorsCore.visit(field.defaultValue, typeManifestVisitor).value,
nodes.isNode(field.defaultValue, "numberValueNode"),
typeManifest2.strictType
);
return getConstantFragment({ ...scope, encoder, name, value, valueType });
}
function resolveDiscriminatorValue(rawValue, isNumberValue, strictType) {
const value = strictType.content === "bigint" && isNumberValue ? fragment`${rawValue}n` : rawValue;
const needsTypeAnnotation = !["string", "number", "boolean", "bigint"].includes(strictType.content);
return { value, valueType: needsTypeAnnotation ? strictType : void 0 };
}
function getConstantFragment(scope) {
const { encoder, name, nameApi, value, valueType } = scope;
const constantName = nameApi.constant(name);
const constantFunction = nameApi.constantFunction(name);
const readonlyUint8Array = use("type ReadonlyUint8Array", "solanaCodecsCore");
const typeAnnotation = valueType ? fragment`: ${valueType}` : fragment``;
return fragment`export const ${constantName}${typeAnnotation} = ${value};\n\nexport function ${constantFunction}(): ${readonlyUint8Array} { return ${encoder}.encode(${constantName}); }`;
}
// src/fragments/accountPage.ts
function getAccountPageFragment(scope) {
const node = visitorsCore.getLastNodeFromPath(scope.accountPath);
if (!visitorsCore.findProgramNodeFromPath(scope.accountPath)) {
throw new Error("Account must be visited inside a program.");
}
const typeManifest2 = visitorsCore.visit(node, scope.typeManifestVisitor);
const fields = nodes.resolveNestedTypeNode(node.data).fields ?? [];
return mergeFragments(
[
getDiscriminatorConstantsFragment({
...scope,
discriminatorNodes: node.discriminators ?? [],
fields,
prefix: node.name
}),
getAccountTypeFragment({ ...scope, typeManifest: typeManifest2 }),
getAccountFetchHelpersFragment({ ...scope, typeManifest: typeManifest2 }),
getAccountSizeHelpersFragment(scope),
getAccountPdaHelpersFragment({ ...scope, typeManifest: typeManifest2 })
],
(cs) => cs.join("\n\n")
);
}
function getConstantsPageFragment(scope) {
if (scope.nodes.length === 0) return;
return mergeFragments(
[...scope.nodes].sort((a, b) => a.name.localeCompare(b.name)).map((node) => getConstantFragment2(node, scope)),
(constants) => constants.join("\n\n")
);
}
function getConstantFragment2(node, scope) {
const typeManifest2 = visitorsCore.visit(node.type, scope.typeManifestVisitor);
const rawValue = visitorsCore.visit(node.value, scope.typeManifestVisitor).value;
const useBigInt = nodes.isNode(node.value, "numberValueNode") && resolvesToBigInt(node.type, scope);
const value = useBigInt ? fragment`${rawValue}n` : rawValue;
const valueType = nodes.isNode(node.value, "stringValueNode") ? fragment`string` : typeManifest2.strictType;
const docs = getDocblockFragment(node.docs ?? [], true);
return fragment`${docs}export const ${scope.nameApi.constant(node.name)}: ${valueType} = ${value};`;
}
function resolvesToBigInt(type, scope, path = scope.programPath, visited = /* @__PURE__ */ new Set()) {
if (visited.has(type)) return false;
if (visitorsCore.visit(type, scope.typeManifestVisitor).strictType.content === "bigint") return true;
if (!nodes.isNode(type, "definedTypeLinkNode")) return false;
const linkedPath = scope.linkables.getPath([...path, type]);
if (!linkedPath) return false;
const linkedType = visitorsCore.getLastNodeFromPath(linkedPath).type;
return resolvesToBigInt(linkedType, scope, linkedPath, /* @__PURE__ */ new Set([...visited, type]));
}
function getDiscriminatorConditionFragment(scope) {
return visitorsCore.pipe(
mergeFragments(
scope.discriminators.flatMap((discriminator) => {
if (nodes.isNode(discriminator, "sizeDiscriminatorNode")) {
return [getSizeConditionFragment(discriminator, scope)];
}
if (nodes.isNode(discriminator, "constantDiscriminatorNode")) {
return [getByteConditionFragment(discriminator, scope)];
}
if (nodes.isNode(discriminator, "fieldDiscriminatorNode")) {
return [getFieldConditionFragment(discriminator, scope)];
}
return [];
}),
(c) => c.join(" && ")
),
(f) => renderersCore.mapFragmentContent(f, (c) => `if (${c}) { ${scope.ifTrue} }`)
);
}
function getSizeConditionFragment(discriminator, scope) {
const { dataName } = scope;
return fragment`${dataName}.length === ${discriminator.size}`;
}
function getByteConditionFragment(discriminator, scope) {
const { dataName, typeManifestVisitor } = scope;
const constant = visitorsCore.visit(discriminator.constant, typeManifestVisitor).value;
return fragment`${use("containsBytes", "solanaCodecsCore")}(${dataName}, ${constant}, ${discriminator.offset})`;
}
function getFieldConditionFragment(discriminator, scope) {
const field = (scope.struct.fields ?? []).find((f) => f.name === discriminator.name);
if (!field || !field.defaultValue) {
throw new Error(
`Field discriminator "${discriminator.name}" does not have a matching argument with default value.`
);
}
const defaultValueItems = nodes.isNode(field.defaultValue, "arrayValueNode") ? field.defaultValue.items ?? [] : [];
if (nodes.isNode(field.type, "arrayTypeNode") && nodes.isNode(field.type.item, "numberTypeNode") && field.type.item.format === "u8" && nodes.isNode(field.type.count, "fixedCountNode") && nodes.isNode(field.defaultValue, "arrayValueNode") && defaultValueItems.every(nodes.isNodeFilter("numberValueNode"))) {
const base64Bytes = codecsStrings.getBase64Decoder().decode(new Uint8Array(defaultValueItems.map((node) => node.number)));
return getByteConditionFragment(
nodes.constantDiscriminatorNode(nodes.constantValueNodeFromBytes("base64", base64Bytes), discriminator.offset),
scope
);
}
return getByteConditionFragment(
nodes.constantDiscriminatorNode(nodes.constantValueNode(field.type, field.defaultValue), discriminator.offset),
scope
);
}
// src/fragments/errorPage.ts
function getErrorPageFragment(scope) {
return mergeFragments(
[
getConstantsFragment(scope),
getConstantUnionTypeFragment(scope),
getErrorMessagesFragment(scope),
getErrorMessageFunctionFragment(scope),
getIsErrorFunctionFragment(scope)
],
(cs) => cs.join("\n\n")
);
}
function getConstantsFragment(scope) {
const constantPrefix = scope.nameApi.programErrorConstantPrefix(scope.programNode.name);
return mergeFragments(
[...scope.programNode.errors ?? []].sort((a, b) => a.code - b.code).map((error) => {
const docs = getDocblockFragment(error.docs ?? [], true);
const name = constantPrefix + scope.nameApi.programErrorConstant(error.name);
return fragment`${docs}export const ${name} = 0x${error.code.toString(16)}; // ${error.code}`;
}),
(cs) => cs.join("\n")
);
}
function getConstantUnionTypeFragment(scope) {
const constantPrefix = scope.nameApi.programErrorConstantPrefix(scope.programNode.name);
const typeName = scope.nameApi.programErrorUnion(scope.programNode.name);
const errorTypes = mergeFragments(
[...scope.programNode.errors ?? []].sort((a, b) => a.name.localeCompare(b.name)).map((error) => fragment`typeof ${constantPrefix + scope.nameApi.programErrorConstant(error.name)}`),
(cs) => cs.join(" | ")
);
return fragment`export type ${typeName} = ${errorTypes};`;
}
function getErrorMessagesFragment(scope) {
const mapName = scope.nameApi.programErrorMessagesMap(scope.programNode.name);
const errorUnionType = scope.nameApi.programErrorUnion(scope.programNode.name);
const constantPrefix = scope.nameApi.programErrorConstantPrefix(scope.programNode.name);
const messageEntries = mergeFragments(
[...scope.programNode.errors ?? []].sort((a, b) => a.name.localeCompare(b.name)).map((error) => {
const constantName = constantPrefix + scope.nameApi.programErrorConstant(error.name);
const escapedMessage = error.message.replace(/`/g, "\\`");
return fragment`[${constantName}]: \`${escapedMessage}\``;
}),
(cs) => cs.join(", ")
);
return fragment`let ${mapName}: Record<${errorUnionType}, string> | undefined;
if (process.env['NODE_ENV'] !== 'production') {
${mapName} = { ${messageEntries} };
}`;
}
function getErrorMessageFunctionFragment(scope) {
const functionName = scope.nameApi.programGetErrorMessageFunction(scope.programNode.name);
const errorUnionType = scope.nameApi.programErrorUnion(scope.programNode.name);
const messageMapName = scope.nameApi.programErrorMessagesMap(scope.programNode.name);
return fragment`export function ${functionName}(code: ${errorUnionType}): string {
if (process.env['NODE_ENV'] !== 'production') {
return (${messageMapName} as Record<${errorUnionType}, string>)[code];
}
return 'Error message not available in production bundles.';
}`;
}
function getIsErrorFunctionFragment(scope) {
const { programNode, nameApi } = scope;
const programAddressConstant = use(nameApi.programAddressConstant(programNode.name), "generatedPrograms");
const functionName = nameApi.programIsErrorFunction(programNode.name);
const programErrorUnion = nameApi.programErrorUnion(programNode.name);
return fragment`export function ${functionName}<TProgramErrorCode extends ${programErrorUnion}>(
error: unknown,
transactionMessage: { instructions: Record<number, { programAddress: ${use("type Address", "solanaAddresses")} }> },
code?: TProgramErrorCode,
): error is ${use("type SolanaError", "solanaErrors")}<typeof ${use("type SOLANA_ERROR__INSTRUCTION_ERROR__CUSTOM", "solanaErrors")}> & Readonly<{ context: Readonly<{ code: TProgramErrorCode }> }> {
return ${use("isProgramError", "solanaPrograms")}<TProgramErrorCode>(error, transactionMessage, ${programAddressConstant}, code);
}`;
}
function getEventPageFragment(scope) {
const node = visitorsCore.getLastNodeFromPath(scope.eventPath);
if (!visitorsCore.findProgramNodeFromPath(scope.eventPath)) {
throw new Error("Event must be visited inside a program.");
}
const { nameApi } = scope;
const typeManifest2 = visitorsCore.visit(node, scope.typeManifestVisitor);
const eventDataName = nameApi.eventDataType(node.name);
const resolvedData = nodes.resolveNestedTypeNode(node.data);
const fields = nodes.isNode(resolvedData, "structTypeNode") ? resolvedData.fields ?? [] : [];
return mergeFragments(
[
getDiscriminatorConstantsFragment({
...scope,
discriminatorNodes: node.discriminators ?? [],
fields,
prefix: getEventDiscriminatorPrefix(nameApi, node.name)
}),
getTypeWithCodecFragment({
codecDocs: [`Gets the codec for {@link ${nameApi.dataType(eventDataName)}} event data.`],
decoderDocs: [`Gets the decoder for {@link ${nameApi.dataType(eventDataName)}} event data.`],
encoderDocs: [`Gets the encoder for {@link ${nameApi.dataArgsType(eventDataName)}} event data.`],
erasableSyntax: scope.erasableSyntax,
manifest: typeManifest2,
name: eventDataName,
nameApi,
node: node.data,
size: scope.size,
typeDocs: node.docs
}),
getEventParseFunctionFragment({ ...scope, node })
],
(cs) => cs.join("\n\n")
);
}
function getEventParseFunctionFragment(scope) {
const { nameApi, node } = scope;
const eventDataName = nameApi.eventDataType(node.name);
const strictName = nameApi.dataType(eventDataName);
const parseFunction = nameApi.eventParseFunction(node.name);
const decoderFunction = nameApi.decoderFunction(eventDataName);
const readonlyUint8Array = use("type ReadonlyUint8Array", "solanaCodecsCore");
const docblock = fragment`/** Parses the provided bytes into {@link ${strictName}} event data. */\n`;
const discriminators = node.discriminators ?? [];
if (discriminators.length === 0) {
return fragment`${docblock}export function ${parseFunction}(data: ${readonlyUint8Array} | Uint8Array): ${strictName} {
return ${decoderFunction}().decode(data);
}`;
}
const resolvedData = nodes.resolveNestedTypeNode(node.data);
const condition = getDiscriminatorConditionFragment({
...scope,
dataName: "data",
discriminators,
ifTrue: `return ${decoderFunction}().decode(data);`,
programNode: visitorsCore.findProgramNodeFromPath(scope.eventPath),
struct: nodes.isNode(resolvedData, "structTypeNode") ? resolvedData : nodes.structTypeNode([])
});
return fragment`${docblock}export function ${parseFunction}(data: ${readonlyUint8Array} | Uint8Array): ${strictName} {
${condition}
throw new Error('The provided data does not match the "${strictName}" event discriminators.');
}`;
}
function getIndexPageFragment(items, scope) {
if (items.length === 0) return;
const names = items.map((item) => nodes.camelCase(item.name)).sort((a, b) => a.localeCompare(b)).map((name) => getExportAllFragment(scope.getImportPath(`./${name}`, "file")));
return mergeFragments(names, (cs) => cs.join("\n"));
}
function getInstructionAccountMetaFragment(instructionAccountNode) {
const typeParam = `TAccount${nodes.pascalCase(instructionAccountNode.name)}`;
if (instructionAccountNode.isSigner === true && instructionAccountNode.isWritable) {
return fragment`${use("type WritableSignerAccount", "solanaInstructions")}<${typeParam}> & ${use("type AccountSignerMeta", "solanaSigners")}<${typeParam}>`;
}
if (instructionAccountNode.isSigner === true) {
return fragment`${use("type ReadonlySignerAccount", "solanaInstructions")}<${typeParam}> & ${use("type AccountSignerMeta", "solanaSigners")}<${typeParam}>`;
}
if (instructionAccountNode.isWritable) {
return fragment`${use("type WritableAccount", "solanaInstructions")}<${typeParam}>`;
}
return fragment`${use("type ReadonlyAccount", "solanaInstructions")}<${typeParam}>`;
}
function getInstructionAccountTypeParamFragment(scope) {
const { instructionAccountPath, linkables } = scope;
const instructionAccountNode = visitorsCore.getLastNodeFromPath(instructionAccountPath);
const instructionNode = visitorsCore.findInstructionNodeFromPath(instructionAccountPath);
const programNode = visitorsCore.findProgramNodeFromPath(instructionAccountPath);
const typeParam = `TAccount${nodes.pascalCase(instructionAccountNode.name)}`;
const accountMeta = fragment` | ${use("type AccountMeta", "solanaInstructions")}<string>` ;
if (instructionNode.optionalAccountStrategy === "omitted" && instructionAccountNode.isOptional) {
return fragment`${typeParam} extends string${accountMeta} | undefined = undefined`;
}
const defaultAddress = getDefaultAddress(instructionAccountNode.defaultValue, programNode.publicKey, linkables);
return fragment`${typeParam} extends string${accountMeta} = ${defaultAddress}`;
}
function getDefaultAddress(defaultValue, programId, linkables) {
switch (defaultValue?.kind) {
case "publicKeyValueNode":
return `"${defaultValue.publicKey}"`;
case "programLinkNode":
const programNode = linkables.get([defaultValue]);
return programNode ? `"${programNode.publicKey}"` : "string";
case "programIdValueNode":
return `"${programId}"`;
default:
return "string";
}
}
function getInstructionByteDeltaFragment(scope) {
const { byteDeltas } = visitorsCore.getLastNodeFromPath(scope.instructionPath);
const fragments = (byteDeltas ?? []).flatMap((c) => getByteDeltaFragment(c, scope));
if (fragments.length === 0) return;
return mergeFragments(
fragments,
(c) => `// Bytes created or reallocated by the instruction.
const byteDelta: number = [${c.join(",")}].reduce((a, b) => a + b, 0);`
);
}
function getByteDeltaFragment(byteDelta, scope) {
let bytesFragment = (() => {
if (nodes.isNode(byteDelta.value, "numberValueNode")) {
return getNumberValueNodeFragment(byteDelta);
}
if (nodes.isNode(byteDelta.value, "argumentValueNode")) {
return getArgumentValueNodeFragment(byteDelta);
}
if (nodes.isNode(byteDelta.value, "accountLinkNode")) {
return getAccountLinkNodeFragment(byteDelta, scope);
}
if (nodes.isNode(byteDelta.value, "resolverValueNode")) {
return getResolverValueNodeFragment(byteDelta, scope);
}
return null;
})();
if (bytesFragment === null) return [];
if (byteDelta.withHeader) {
bytesFragment = fragment`${bytesFragment} + ${use("BASE_ACCOUNT_SIZE", "solanaAccounts")}`;
}
if (byteDelta.subtract) {
bytesFragment = visitorsCore.pipe(bytesFragment, (f) => renderersCore.mapFragmentContent(f, (c) => `- (${c})`));
}
return [bytesFragment];
}
function getNumberValueNodeFragment(byteDelta) {
nodes.assertIsNode(byteDelta.value, "numberValueNode");
return fragment`${byteDelta.value.number}`;
}
function getArgumentValueNodeFragment(byteDelta) {
nodes.assertIsNode(byteDelta.value, "argumentValueNode");
const argumentName = nodes.camelCase(byteDelta.value.name);
return fragment`Number(args.${argumentName})`;
}
function getAccountLinkNodeFragment(byteDelta, scope) {
nodes.assertIsNode(byteDelta.value, "accountLinkNode");
const functionName = use(
scope.nameApi.accountGetSizeFunction(byteDelta.value.name),
scope.getImportFrom(byteDelta.value)
);
return fragment`${functionName}()`;
}
function getResolverValueNodeFragment(byteDelta, scope) {
nodes.assertIsNode(byteDelta.value, "resolverValueNode");
const isAsync = scope.asyncResolvers.includes(byteDelta.value.name);
if (!scope.useAsync && isAsync) return null;
const awaitKeyword = scope.useAsync && isAsync ? "await " : "";
const functionName = use(
scope.nameApi.resolverFunction(byteDelta.value.name),
scope.getImportFrom(byteDelta.value)
);
return visitorsCore.pipe(
fragment`${awaitKeyword}${functionName}(resolverScope)`,
(f) => addFragmentFeatures(f, ["instruction:resolverScopeVariable"])
);
}
function getInstructionDataFragment(scope) {
const { instructionPath, dataArgsManifest, nameApi, customInstructionData } = scope;
const instructionNode = visitorsCore.getLastNodeFromPath(instructionPath);
if ((instructionNode.arguments ?? []).length === 0 || customInstructionData.has(instructionNode.name)) return;
const instructionDataName = nameApi.instructionDataType(instructionNode.name);
return getTypeWithCodecFragment({
erasableSyntax: scope.erasableSyntax,
manifest: dataArgsManifest,
name: instructionDataName,
nameApi,
node: nodes.structTypeNodeFromInstructionArgumentNodes(instructionNode.arguments ?? []),
size: scope.size
});
}
function getInstructionExtraArgsFragment(scope) {
const { instructionPath, extraArgsManifest, nameApi } = scope;
const instructionNode = visitorsCore.getLastNodeFromPath(instructionPath);
if ((instructionNode.extraArguments ?? []).length === 0) return;
const instructionExtraName = nameApi.instructionExtraType(instructionNode.name);
const looseName = nameApi.dataArgsType(instructionExtraName);
return renderersCore.mapFragmentContent(extraArgsManifest.looseType, (c) => `export type ${looseName} = ${c};`);
}
function getInstructionInputDefaultFragment(scope) {
const {
input,
instructionPath,
optionalAccountStrategy,
asyncResolvers,
useAsync,
nameApi,
linkables,
typeManifestVisitor,
getImportFrom
} = scope;
if (!input.defaultValue) {
return fragment``;
}
if (!useAsync && isAsyncDefaultValue(input.defaultValue, asyncResolvers)) {
return fragment``;
}
const { defaultValue } = input;
const defaultFragment = (renderedValue, isWritable) => {
const inputName = nodes.camelCase(input.name);
if (input.kind === "instructionAccountNode" && nodes.isNode(defaultValue, "resolverValueNode")) {
return fragment`accounts.${inputName} = { ...accounts.${inputName}, ...${renderedValue} };`;
}
if (input.kind === "instructionAccountNode" && isWritable === void 0) {
return fragment`accounts.${inputName}.value = ${renderedValue};`;
}
if (input.kind === "instructionAccountNode") {
return fragment`accounts.${inputName}.value = ${renderedValue};\naccounts.${inputName}.isWritable = ${isWritable ? "true" : "false"}`;
}
return fragment`args.${inputName} = ${renderedValue};`;
};
const getNonNullResolvedInstructionInput = use("getNonNullResolvedInstructionInput", "solanaProgramClientCore");
const getResolvedInstructionAccountAsTransactionSigner = use(
"getResolvedInstructionAccountAsTransactionSigner",
"solanaProgramClientCore"
);
const getAddressFromResolvedInstructionAccount = use(
"getAddressFromResolvedInstructionAccount",
"solanaProgramClientCore"
);
const getResolvedInstructionAccountAsProgramDerivedAddress = use(
"getResolvedInstructionAccountAsProgramDerivedAddress",
"solanaProgramClientCore"
);
const addressType = use("type Address", "solanaAddresses");
switch (defaultValue.kind) {
case "accountValueNode":
const name = nodes.camelCase(defaultValue.name);
if (input.kind === "instructionAccountNode" && input.resolvedIsSigner && !input.isSigner) {
return defaultFragment(
fragment`${getResolvedInstructionAccountAsTransactionSigner}("${name}", accounts.${name}.value).address`
);
}
if (input.kind === "instructionAccountNode") {
return defaultFragment(
fragment`${getNonNullResolvedInstructionInput}("${name}", accounts.${name}.value)`
);
}
return defaultFragment(
fragment`${getAddressFromResolvedInstructionAccount}("${name}", accounts.${name}.value)`
);
case "pdaValueNode":
let pdaProgramValue;
if (nodes.isNode(defaultValue.programId, "accountValueNode")) {
const name2 = nodes.camelCase(defaultValue.programId.name);
pdaProgramValue = fragment`${getAddressFromResolvedInstructionAccount}("${name2}", accounts.${name2}.value)`;
}
if (nodes.isNode(defaultValue.programId, "argumentValueNode")) {
const name2 = nodes.camelCase(defaultValue.programId.name);
pdaProgramValue = fragment`${getAddressFromResolvedInstructionAccount}("${name2}", args.${name2})`;
}
if (nodes.isNode(defaultValue.pda, "pdaNode")) {
let pdaProgram = fragment`programAddress`;
if (pdaProgramValue) {
pdaProgram = pdaProgramValue;
} else if (defaultValue.pda.programId) {
pdaProgram = fragment`'${defaultValue.pda.programId}' as ${addressType}<'${defaultValue.pda.programId}'>`;
}
const pdaSeeds2 = (defaultValue.pda.seeds ?? []).flatMap((seed) => {
if (nodes.isNode(seed, "constantPdaSeedNode") && nodes.isNode(seed.value, "programIdValueNode")) {
return [fragment`${use("getAddressEncoder", "solanaAddresses")}().encode(${pdaProgram})`];
}
if (nodes.isNode(seed, "constantPdaSeedNode") && !nodes.isNode(seed.value, "programIdValueNode")) {
const typeManifest2 = visitorsCore.visit(seed.type, typeManifestVisitor);
const valueManifest2 = visitorsCore.visit(seed.value, typeManifestVisitor);
return [fragment`${typeManifest2.encoder}.encode(${valueManifest2.value})`];
}
if (nodes.isNode(seed, "variablePdaSeedNode")) {
const typeManifest2 = visitorsCore.visit(seed.type, typeManifestVisitor);
const valueSeed = (defaultValue.seeds ?? []).find((s) => s.name === seed.name)?.value;
if (!valueSeed) return [];
if (nodes.isNode(valueSeed, "accountValueNode")) {
const name2 = nodes.camelCase(valueSeed.name);
return [
fragment`${typeManifest2.encoder}.encode(${getAddressFromResolvedInstructionAccount}("${name2}", accounts.${name2}.value))`
];
}
if (nodes.isNode(valueSeed, "argumentValueNode")) {
const name2 = nodes.camelCase(valueSeed.name);
return [
fragment`${typeManifest2.encoder}.encode(${getNonNullResolvedInstructionInput}("${name2}", args.${name2}))`
];
}
const valueManifest2 = visitorsCore.visit(valueSeed, typeManifestVisitor);
return [fragment`${typeManifest2.encoder}.encode(${valueManifest2.value})`];
}
return [];
});
const getProgramDerivedAddress = use("getProgramDerivedAddress", "solanaAddresses");
const programAddress2 = pdaProgram.content === "programAddress" ? pdaProgram : fragment`programAddress: ${pdaProgram}`;
const seeds = mergeFragments(pdaSeeds2, (s) => s.join(", "));
return defaultFragment(
fragment`await ${getProgramDerivedAddress}({ ${programAddress2}, seeds: [${seeds}] })`
);
}
const pdaFunction = use(nameApi.pdaFindFunction(defaultValue.pda.name), getImportFrom(defaultValue.pda));
const pdaPath = linkables.getPath([...instructionPath, defaultValue.pda]);
const pdaArgs = [];
const pdaSeeds = (defaultValue.seeds ?? []).map((seed) => {
if (nodes.isNode(seed.value, "accountValueNode")) {
const name2 = nodes.camelCase(seed.value.name);
return fragment`${seed.name}: ${getAddressFromResolvedInstructionAccount}("${name2}", accounts.${name2}.value)`;
}
if (nodes.isNode(seed.value, "argumentValueNode")) {
const name2 = nodes.camelCase(seed.value.name);
return fragment`${seed.name}: ${getNonNullResolvedInstructionInput}("${name2}", args.${name2})`;
}
return visitorsCore.pipe(
visitorsCore.visit(seed.value, typeManifestVisitor).value,
(f) => renderersCore.mapFragmentContent(f, (c) => `${seed.name}: ${c}`)
);
});
const pdaSeedsFragment = visitorsCore.pipe(
mergeFragments(pdaSeeds, (renders) => renders.join(", ")),
(f) => renderersCore.mapFragmentContent(f, (c) => `{ ${c} }`)
);
if (pdaSeeds.length > 0 || hasVariablePdaSeeds(pdaPath)) {
pdaArgs.push(pdaSeedsFragment);
}
if (pdaProgramValue) {
pdaArgs.push(fragment`{ programAddress: ${pdaProgramValue} }`);
} else if (isPdaDerivedFromInstructionProgram(pdaPath, instructionPath)) {
pdaArgs.push(fragment`{ programAddress }`);
}
return defaultFragment(fragment`await ${pdaFunction}(${mergeFragments(pdaArgs, (c) => c.join(", "))})`);
case "publicKeyValueNode":
return defaultFragment(
fragment`'${defaultValue.publicKey}' as ${addressType}<'${defaultValue.publicKey}'>`
);
case "programLinkNode":
const programAddress = use(nameApi.programAddressConstant(defaultValue.name), getImportFrom(defaultValue));
return defaultFragment(programAddress, false);
case "programIdValueNode":
if (optionalAccountStrategy === "programId" && input.kind === "instructionAccountNode" && input.isOptional) {
return fragment``;
}
return defaultFragment(fragment`programAddress`, false);
case "identityValueNode":
case "payerValueNode":
return fragment``;
case "accountBumpValueNode":
return defaultFragment(
fragment`${getResolvedInstructionAccountAsProgramDerivedAddress}("${nodes.camelCase(defaultValue.name)}", accounts.${nodes.camelCase(defaultValue.name)}.value)[1]`
);
case "argumentValueNode":
return defaultFragment(
fragment`${getNonNullResolvedInstructionInput}("${nodes.camelCase(defaultValue.name)}", args.${nodes.camelCase(defaultValue.name)})`
);
case "resolverValueNode":
const resolverFunction = use(nameApi.resolverFunction(defaultValue.name), getImportFrom(defaultValue));
const resolverAwait = useAsync && asyncResolvers.includes(defaultValue.name) ? "await " : "";
return visitorsCore.pipe(
defaultFragment(fragment`${resolverAwait}${resolverFunction}(resolverScope)`),
(f) => addFragmentFeatures(f, ["instruction:resolverScopeVariable"])
);
case "conditionalValueNode":
const ifTrueRenderer = renderNestedInstructionDefault({
...scope,
defaultValue: defaultValue.ifTrue
});
const ifFalseRenderer = renderNestedInstructionDefault({
...scope,
defaultValue: defaultValue.ifFalse
});
if (!ifTrueRenderer && !ifFalseRenderer) {
return fragment``;
}
let conditionalFragment = fragment``;
if (ifTrueRenderer) {
conditionalFragment = mergeFragments([conditionalFragment, ifTrueRenderer], (c) => c[0]);
}
if (ifFalseRenderer) {
conditionalFragment = mergeFragments([conditionalFragment, ifFalseRenderer], (c) => c[0]);
}
const negatedCondition = !ifTrueRenderer;
let condition = "true";
if (nodes.isNode(defaultValue.condition, "resolverValueNode")) {
const conditionalResolverFunction = nameApi.resolverFunction(defaultValue.condition.name);
const module = getImportFrom(defaultValue.condition);
conditionalFragment = visitorsCore.pipe(
conditionalFragment,
(f) => addFragmentImports(f, module, [conditionalResolverFunction]),
(f) => addFragmentFeatures(f, ["instruction:resolverScopeVariable"])
);
const conditionalResolverAwait = useAsync && asyncResolvers.includes(defaultValue.condition.name) ? "await " : "";
condition = `${conditionalResolverAwait}${conditionalResolverFunction}(resolverScope)`;
condition = negatedCondition ? `!${condition}` : condition;
} else {
const comparedInputName = nodes.isNode(defaultValue.condition, "accountValueNode") ? `accounts.${nodes.camelCase(defaultValue.condition.name)}.value` : `args.${nodes.camelCase(defaultValue.condition.name)}`;
if (defaultValue.value) {
const comparedValue = visitorsCore.visit(defaultValue.value, typeManifestVisitor).value;
conditionalFragment = mergeFragments([conditionalFragment, comparedValue], (c) => c[0]);
const operator = negatedCondition ? "!==" : "===";
condition = `${comparedInputName} ${operator} ${comparedValue.content}`;
} else {
condition = negatedCondition ? `!${comparedInputName}` : comparedInputName;
}
}
if (ifTrueRenderer && ifFalseRenderer) {
return renderersCore.setFragmentContent(
conditionalFragment,
`if (${condition}) {
${ifTrueRenderer.content}
} else {
${ifFalseRenderer.content}
}`
);
}
return renderersCore.setFragmentContent(
conditionalFragment,
`if (${condition}) {
${ifTrueRenderer ? ifTrueRenderer.content : ifFalseRenderer?.content}
}`
);
case "accountFieldValueNode":
case "injectedValueNode":
throw new Error(`Unsupported instruction input default value node: [${defaultValue.kind}]`);
default:
const valueManifest = visitorsCore.visit(defaultValue, typeManifestVisitor).value;
return defaultFragment(valueManifest);
}
}
function renderNestedInstructionDefault(scope) {
const { input, defaultValue } = scope;
if (!defaultValue) return void 0;
return getInstructionInputDefaultFragment({
...scope,
input: { ...input, defaultValue }
});
}
function isPdaDerivedFromInstructionProgram(pdaPath, instructionPath) {
if (!pdaPath || visitorsCore.getLastNodeFromPath(pdaPath).programId) return false;
const pdaProgram = visitorsCore.findProgramNodeFromPath(pdaPath);
const instructionProgram = visitorsCore.findProgramNodeFromPath(instructionPath);
return !!pdaProgram && !!instructionProgram && pdaProgram.name === instructionProgram.name;
}
function hasVariablePdaSeeds(pdaPath) {
if (!pdaPath) return false;
return (visitorsCore.getLastNodeFromPath(pdaPath).seeds ?? []).some(nodes.isNodeFilter("variablePdaSeedNode"));
}
// src/fragments/instructionInputResolved.ts
function getInstructionInputResolvedFragment(scope) {
const instructionNode = visitorsCore.getLastNodeFromPath(scope.instructionPath);
const resolvedInputFragments = scope.resolvedInputs.flatMap((input) => {
const inputFragment = getInstructionInputDefaultFragment({
...scope,
input,
optionalAccountStrategy: nodes.parseOptionalAccountStrategy(instructionNode.optionalAccountStrategy)
});
if (!inputFragment.content) return [];
const camelName = nodes.camelCase(input.name);
return [
renderersCore.mapFragmentContent(
inputFragment,
(c) => nodes.isNode(input, "instructionArgumentNode") ? `if (!args.${camelName}) {
${c}
}` : `if (!accounts.${camelName}.value) {
${c}
}`
)
];
});
if (resolvedInputFragments.length === 0) {
return fragment``;
}
return mergeFragments([fragment`// Resolve default values.`, ...resolvedInputFragments], (c) => c.join("\n"));
}
function getInstructionInputTypeFragment(scope) {
const { instructionPath, useAsync, nameApi } = scope;
const instructionNode = visitorsCore.getLastNodeFromPath(instructionPath);
const instructionInputType = useAsync ? nameApi.instructionAsyncInputType(instructionNode.name) : nameApi.instructionSyncInputType(instructionNode.name);
const [dataArgumentsFragment, customDataArgumentsFragment] = getDataArgumentsFragments(scope);
let accountTypeParams = "";
if ((instructionNode.accounts ?? []).length > 0) {
accountTypeParams = (instructionNode.accounts ?? []).map((account) => `TAccount${nodes.pascalCase(account.name)} extends string = string`).join(", ");
accountTypeParams = `<${accountTypeParams}>`;
}
const typeBodyFragment = mergeFragments(
[
getAccountsFragment(scope),
dataArgumentsFragment,
getExtraArgumentsFragment(scope),
getRemainingAccountsFragment(instructionNode)
],
(c) => c.join("\n")
);
return fragment`export type ${instructionInputType}${accountTypeParams} = ${customDataArgumentsFragment} {
${typeBodyFragment}
}`;
}
function getAccountsFragment(scope) {
const { instructionPath, resolvedInputs, useAsync, asyncResolvers } = scope;
const instructionNode = visitorsCore.getLastNodeFromPath(instructionPath);
const fragments = (instructionNode.accounts ?? []).map((account) => {
const resolvedAccount = resolvedInputs.find(
(input) => input.kind === "instructionAccountNode" && input.name === account.name
);
const hasDefaultValue = !!resolvedAccount.defaultValue && !nodes.isNode(resolvedAccount.defaultValue, ["identityValueNode", "payerValueNode"]) && (useAsync || !isAsyncDefaultValue(resolvedAccount.defaultValue, asyncResolvers));
const docs = getDocblockFragment(account.docs ?? [], true);
const optionalSign = hasDefaultValue || resolvedAccount.isOptional ? "?" : "";
return fragment`${docs}${nodes.camelCase(account.name)}${optionalSign}: ${getAccountTypeFragment2(resolvedAccount)};`;
});
return mergeFragments(fragments, (c) => c.join("\n"));
}
function getAccountTypeFragment2(account) {
const typeParam = `TAccount${nodes.pascalCase(account.name)}`;
const address = use("type Address", "solanaAddresses");
const signer = use("type TransactionSigner", "solanaSigners");
const pda = use("type ProgramDerivedAddress", "solanaAddresses");
if (account.isPda && account.isSigner === false) return fragment`${pda}<${typeParam}>`;
if (account.isPda && account.isSigner === "either") return fragment`${pda}<${typeParam}> | ${signer}<${typeParam}>`;
if (account.isSigner === "either") return fragment`${address}<${typeParam}> | ${signer}<${typeParam}>`;
if (account.isSigner) return fragment`${signer}<${typeParam}>`;
return fragment`${address}<${typeParam}>`;
}
function getDataArgumentsFragments(scope) {
const { instructionPath, nameApi } = scope;
const instructionNode = visitorsCore.getLastNodeFromPath(instructionPath);
const customData = scope.customInstructionData.get(instructionNode.name);
if (customData) {
return [
void 0,
visitorsCore.pipe(
fragment`${nameApi.dataArgsType(customData.importAs)}`,
(f) => mergeFragmentImports(f, [scope.dataArgsManifest.looseType.imports]),
(f) => renderersCore.mapFragmentContent(f, (c) => `${c} & `)
)
];
}
const instructionDataName = nameApi.instructionDataType(instructionNode.name);
const dataArgsType = nameApi.dataArgsType(instructionDataName);
const fragments = (instructionNode.arguments ?? []).flatMap((arg) => {
const argFragment = getArgumentFragment(arg, dataArgsType, scope.resolvedInputs, scope.renamedArgs);
return argFragment ? [argFragment] : [];
});
return [fragments.length === 0 ? void 0 : mergeFragments(fragments, (c) => c.join("\n")), fragment``];
}
function getExtraArgumentsFragment(scope) {
const { instructionPath, nameApi } = scope;
const instructionNode = visitorsCore.getLastNodeFromPath(instructionPath);
const instructionExtraName = nameApi.instructionExtraType(instructionNode.name);
const extraArgsType = nameApi.dataArgsType(instructionExtraName);
const fragments = (instructionNode.extraArguments ?? []).flatMap((arg) => {
const argFragment = getArgumentFragment(arg, extraArgsType, scope.resolvedInputs, scope.renamedArgs);
return argFragment ? [argFragment] : [];
});
if (fragments.length === 0) return;
return mergeFragments(fragments, (c) => c.join("\n"));
}
function getArgumentFragment(arg, argsType, resolvedInputs, renamedArgs) {
const resolvedArg = resolvedInputs.find(
(input) => nodes.isNode(input, "instructionArgumentNode") && input.name === arg.name
);
if (arg.defaultValue && arg.defaultValueStrategy === "omitted") return null;
const renamedName = renamedArgs.get(arg.name) ?? arg.name;
const optionalSign = arg.defaultValue || resolvedArg?.defaultValue ? "?" : "";
return fragment`${nodes.camelCase(renamedName)}${optionalSign}: ${argsType}["${nodes.camelCase(arg.name)}"];`;
}
function getRemainingAccountsFragment(instructionNode) {
const fragments = (instructionNode.remainingAccounts ?? []).flatMap((remainingAccountsNode) => {
if (nodes.isNode(remainingAccountsNode.value, "resolverValueNode")) return [];
const { name } = remainingAccountsNode.value;
const allArguments = nodes.getAllInstructionArguments(instructionNode);
const argumentExists = allArguments.some((arg) => arg.name === name);
if (argumentExists) return [];
const isSigner = remainingAccountsNode.isSigner ?? false;
const optionalSign = remainingAccountsNode.isOptional ?? false ? "?" : "";
const signerFragment = use("type TransactionSigner", "solanaSigners");
const addressFragment = use("type Address", "solanaAddresses");
const typeFragment = (() => {
if (isSigner === "either") return fragment`${signerFragment} | ${addressFragment}`;
return isSigner ? signerFragment : addressFragment;
})();
return fragment`${nodes.camelCase(name)}${optionalSign}: Array<${typeFragment}>;`;
});
if (fragments.length === 0) return;
return mergeFragments(fragments, (c) => c.join("\n"));
}
function getInstructionRemainingAccountsFragment(scope) {
const { remainingAccounts } = visitorsCore.getLastNodeFromPath(scope.instructionPath);
const fragments = (remainingAccounts ?? []).flatMap((a) => getRemainingAccountsFragment2(a, scope));
if (fragments.length === 0) return;
return visitorsCore.pipe(
mergeFragments(
fragments,
(c) => `// Remaining accounts.
const remainingAccounts: AccountMeta[] = ${c.length === 1 ? c[0] : `[...${c.join(", ...")}]`}`
),
(f) => addFragmentImports(f, "solanaInstructions", ["type AccountMeta"])
);
}
function getRemainingAccountsFragment2(remainingAccounts, scope) {
const remainingAccountsFragment = (() => {
if (nodes.isNode(remainingAccounts.value, "argumentValueNode")) {
return getArgumentValueNodeFragment2(remainingAccounts, scope);
}
if (nodes.isNode(remainingAccounts.value, "resolverValueNode")) {
return getResolverValueNodeFragment2(remainingAccounts, scope);
}
return null;
})();
if (remainingAccountsFragment === null) return [];
return [remainingAccountsFragment];
}
function getArgumentValueNodeFragment2(remainingAccounts, scope) {
const instructionNode = visitorsCore.getLastNodeFromPath(scope.instructionPath);
nodes.assertIsNode(remainingAccounts.value, "argumentValueNode");
const argumentName = nodes.camelCase(remainingAccounts.value.name);
const isOptional = remainingAccounts.isOptional ?? false;
const isSigner = remainingAccounts.isSigner ?? false;
const isWritable = remainingAccounts.isWritable ?? false;
const accountRole = use("AccountRole", "solanaInstructions");
const nonSignerRole = isWritable ? fragment`${accountRole}.WRITABLE` : fragment`${accountRole}.READONLY`;
const signerRole = isWritable ? fragment`${accountRole}.WRITABLE_SIGNER` : fragment`${accountRole}.READONLY_SIGNER`;
const role = isSigner === true ? signerRole : nonSignerRole;
const argumentArray = isOptional ? `(args.${argumentName} ?? [])` : `args.${argumentName}`;
const allArguments = nodes.getAllInstructionArguments(instructionNode);
const argumentExists = allArguments.some((arg) => arg.name === remainingAccounts.value.name);
if (argumentExists || isSigner === false) {
return fragment`${argumentArray}.map((address) => ({ address, role: ${role} }))`;
}
if (isSigner === "either") {
return fragment`${argumentArray}.map((addressOrSigner) => (typeof addressOrSigner === "string" ? { address: addressOrSigner, role: ${role} } : { address: addressOrSigner.address, role: ${role}, signer: addressOrSigner }))`;
}
return fragment`${argumentArray}.map((signer) => ({ address: signer.address, role: ${signerRole}, signer }))`;
}
function getResolverValueNodeFragment2(remainingAccounts, scope) {
nodes.assertIsNode(remainingAccounts.value, "resolverValueNode");
const isAsync = scope.asyncResolvers.includes(remainingAccounts.value.name);
if (!scope.useAsync && isAsync) return null;
const awaitKeyword = scope.useAsync && isAsync ? "await " : "";
const functionName = use(
scope.nameApi.resolverFunction(remainingAccounts.value.name),
scope.getImportFrom(remainingAccounts.value)
);
return visitorsCore.pipe(
fragment`${awaitKeyword}${functionName}(resolverScope)`,
(f) => addFragmentFeatures(f, ["instruction:resolverScopeVariable"])
);
}
// src/fragments/instructionFunction.ts
function getInstructionFunctionFragment(scope) {
const { useAsync, instructionPath, resolvedInputs, renamedArgs, asyncResolvers, nameApi, customInstructionData } = scope;
const instructionNode = visitorsCore.getLastNodeFromPath(instructionPath);
const programNode = visitorsCore.findProgramNodeFromPath(instructionPath);
if (useAsync && !hasAsyncFunction(instructionNode, resolvedInputs, asyncResolvers)) return;
const customData = customInstructionData.get(instructionNode.name);
const hasAccounts = (instructionNode.accounts ?? []).length > 0;
const instructionDependencies = getInstructionDependencies(instructionNode, asyncResolvers, useAsync);
const argDependencies = instructionDependencies.filter(nodes.isNodeFilter("argumentValueNode")).map((node) => node.name);
const hasData = !!customData || (instructionNode.arguments ?? []).length > 0;
const argIsNotOmitted = (arg) => !(arg.defaultValue && arg.defaultValueStrategy === "omitted");
const argIsDependent = (arg) => argDependencies.includes(arg.name);
const argHasDefaultValue = (arg) => {
if (!arg.defaultValue) return false;
if (useAsync) return true;
return !isAsyncDefaultValue(arg.defaultValue, asyncResolvers);
};
const hasDataArgs = !!customData || (instructionNode.arguments ?? []).filter(argIsNotOmitted).length > 0;
const hasExtraArgs = (instructionNode.extraArguments ?? []).filter(
(field) => argIsNotOmitted(field) && (argIsDependent(field) || argHasDefaultValue(field))
).length > 0;
const hasRemainingAccountArgs = (instructionNode.remainingAccounts ?? []).filter(({ value }) => nodes.isNode(value, "argumentValueNode")).length > 0;
const hasAnyArgs = hasDataArgs || hasExtraArgs || hasRemainingAccountArgs;
const hasInput = hasAccounts || hasAnyArgs;
const programAddressConstant = use(nameApi.programAddressConstant(programNode.name), "generatedPrograms");
const functionName = useAsync ? nameApi.instructionAsyncFunction(instructionNode.name) : nameApi.instructionSyncFunction(instructionNode.name);
const resolvedInputsFragment = getInstructionInputResolvedFragment(scope);
const remainingAccountsFragment = getInstructionRemainingAccountsFragment(scope);
const byteDeltaFragment = getInstructionByteDeltaFragment(scope);
const resolvedInputFragment = mergeFragments(
[resolvedInputsFragment, remainingAccountsFragment, byteDeltaFragment],
(content) => content.join("\n\n")
);
const hasRemainingAccounts = !!remainingAccountsFragment;
const hasByteDeltas = !!byteDeltaFragment;
const hasResolver = resolvedInputFragment.features.has("instruction:resolverScopeVariable");
const instructionTypeFragment = getInstructionTypeFragment(scope);
const typeParams = getTypeParamsFragment(instructionNode, programAddressConstant);
const returnType = getReturnTypeFragment(instructionTypeFragment, hasByteDeltas, useAsync);
const inputType = getInstructionInputTypeFragment(scope);
const inputArg = renderersCore.mapFragmentContent(getInputTypeCallFragment(scope), (c) => hasInput ? `input: ${c}, ` : "");
const functionBody = mergeFragments(
[
getProgramAddressInitializationFragment(programAddressConstant),
getAccountsInitializationFragment(instructionNode),
getArgumentsInitializationFragment(hasAnyArgs, renamedArgs),
getResolverScopeInitializationFragment(hasResolver, hasAccounts, hasAnyArgs),
resolvedInputFragment,
getReturnStatementFragment({
...scope,
hasByteDeltas,
hasData,
hasDataArgs,
hasRemainingAccounts,
instructionNode,
syncReturnTypeFragment: getReturnTypeFragment(instructionTypeFragment, hasByteDeltas, false)
})
],
(cs) => cs.join("\n\n")
);
return fragment`${inputType}\n\nexport ${useAsync ? "async " : ""}function ${functionName}${typeParams}(${inputArg}config?: { programAddress?: TProgramAddress } ): ${returnType} {
${functionBody}
}`;
}
function getProgramAddressInitializationFragment(programAddressConstant) {
return fragment`// Program address.
const programAddress = config?.programAddress ?? ${programAddressConstant};`;
}
function getAccountsInitializationFragment(instructionNode) {
if ((instructionNode.accounts ?? []).length === 0) return;
const accounts = mergeFragments(
(instructionNode.accounts ?? []).map((account) => {
const name = nodes.camelCase(account.name);
const isWritable = account.isWritable ? "true" : "false";
return fragment`${name}: { value: input.${name} ?? null, isWritable: ${isWritable} }`;
}),
(cs) => cs.join(", ")
);
return fragment` // Original accounts.
const originalAccounts = { ${accounts} }
const accounts = originalAccounts as Record<keyof typeof originalAccounts, ${use("type ResolvedInstructionAccount", "solanaProgramClientCore")}>;
`;
}
function getArgumentsInitializationFragment(hasAnyArgs, renamedArgs) {
if (!hasAnyArgs) return;
const renamedArgsText = [...renamedArgs.entries()].map(([k, v]) => `${k}: input.${v}`).join(", ");
return fragment`// Original args.
const args = { ...input, ${renamedArgsText} };
`;
}
function getResolverScopeInitializationFragment(hasResolver, hasAccounts, hasAnyArgs) {
if (!hasResolver) return;
const resolverAttributes = [
"programAddress",
...hasAccounts ? ["accounts"] : [],
...hasAnyArgs ? ["args"] : []
].join(", ");
return fragment`// Resolver scope.
const resolverScope = { ${resolverAttributes} };`;
}
function getReturnStatementFragment(scope) {
const { instructionNode, hasByteDeltas, hasData, hasDataArgs, hasRemainingAccounts, nameApi } = scope;
const optionalAccountStrategy = instructionNode.optionalAccountStrategy ?? "programId";
const hasAccounts = (instructionNode.accounts ?? []).length > 0;
const hasLegacyOptionalAccounts = instructionNode.optionalAccountStrategy === "omitted" && (instructionNode.accounts ?? []).some((account) => account.isOptional);
const getAccountMeta = hasAccounts ? fragment`const getAccountMeta = ${use("getAccountMetaFactory", "solanaProgramClientCore")}(programAddress, '${optionalAccountStrategy}');` : "";
const accountItems = [
...(instructionNode.accounts ?? []).map(
(account) => `getAccountMeta("${nodes.camelCase(account.name)}", accounts.${nodes.camelCase(account.name)})`
),
...hasRemainingAccounts ? ["...remainingAccounts"] : []
].join(", ");
let accounts;
if (hasAccounts && hasLegacyOptionalAccounts) {
accounts = fragment`accounts: [${accountItems}].filter(<T>(x: T | undefined): x is T => x !== undefined)`;
} else if (hasAccounts) {
accounts = fragment`accounts: [${accountItems}]`;
} else if (hasRemainingAccounts) {
accounts = fragment`accounts: remainingAccounts`;
}
const customData = scope.customInstructionData.get(instructionNode.name);
const instructionDataName = nameApi.instructionDataType(instructionNode.name);
const encoderFunctionFragment = customData ? scope.dataArgsManifest.encoder : `${nameApi.encoderFunction(instructionDataName)}()`;
const argsTypeFragment = customData ? scope.dataArgsManifest.looseType : nameApi.dataArgsType(instructionDataName);
let data;
if (hasDataArgs) {
data = fragment`data: ${encoderFunctionFragment}.encode(args as ${argsTypeFragment})`;
} else if (hasData) {
data = fragment`data: ${encoderFunctionFragment}.encode({})`;
}
const instructionAttributes = visitorsCore.pipe(
[accounts, hasByteDeltas ? fragment`byteDelta` : void 0, data, fragment`programAddress`],
(fs) => mergeFragments(fs, (cs) => cs.join(", "))
);
return fragment`${getAccountMeta}\nreturn Object.freeze({ ${instructionAttributes} } as ${scope.syncReturnTypeFragment});`;
}
function getReturnTypeFragment(instructionTypeFragment, hasByteDeltas, useAsync) {
return visitorsCore.pipe(
instructionTypeFragment,
(f) => hasByteDeltas ? fragment`${f} & ${use("type InstructionWithByteDelta", "solanaProgramClientCore")}` : f,
(f) => useAsync ? fragment`Promise<${f}>` : f
);
}
function getTypeParamsFragment(instructionNode, programAddressConstant) {
return mergeFragments(
[
...(instructionNode.accounts ?? []).map(
(account) => fragment`TAccount${nodes.pascalCase(account.name)} extends string`
),
fragment`TProgramAddress extends ${use("type Address", "solanaAddresses")} = typeof ${programAddressConstant}`
],
(cs) => `<${cs.join(", ")}>`
);
}
function getInstructionTypeFragment(scope) {
const { instructionPath, nameApi } = scope;
const instructionNode = visitorsCore.getLastNodeFromPath(instructionPath);
const instructionTypeName = nameApi.instructionType(instructionNode.name);
const accountTypeParamsFragments = (instructionNode.accounts ?? []).map((account) => {
const typeParam = fragment`TAccount${nodes.pascalCase(account.name)}`;
const camelName = nodes.camelCase(account.name);
if (account.isSigner === "either") {
const signerRole = use(
account.isWritable ? "type WritableSignerAccount" : "type ReadonlySignerAccount",
"solanaInstructions"
);
return visitorsCore.pipe(
fragment`typeof input["${camelName}"] extends TransactionSigner<${typeParam}> ? ${signerRole}<${typeParam}> & AccountSignerMeta<${typeParam}> : ${typeParam}`,
(f) => addFragmentImports(f, "solanaSigners", ["type AccountSignerMeta", "type TransactionSigner"])
);
}
return typeParam;
});
return visitorsCore.pipe(
mergeFragments([fragment`TProgramAddress`, ...accountTypeParamsFragments], (c) => c.join(", ")),
(f) => renderersCore.mapFragmentContent(f, (c) => `${instructionTypeName}<${c}>`)
);
}
function getInputTypeCallFragment(scope) {
const { instructionPath, useAsync, nameApi } = scope;
const instructionNode = visitorsCore.getLastNodeFromPath(instructionPath);
const inputTypeName = useAsync ? nameApi.instructionAsyncInputType(instructionNode.name) : nameApi.instructionSyncInputType(instructionNode.name);
if ((instructionNode.accounts ?? []).length === 0) return fragment`${inputTypeName}`;
const accountTypeParams = (instructionNode.accounts ?? []).map((account) => `TAccount${nodes.pascalCase(account.name)}`).join(", ");
return fragment`${inputTypeName}<${accountTypeParams}>`;
}
function getInstructionParseFunctionFragment(scope) {
const instructionNode = visitorsCore.getLastNodeFromPath(scope.instructionPath);
const programNode = visitorsCore.findProgramNodeFromPath(scope.instructionPath);
const programAddressConstant = use(scope.nameApi.programAddressConstant(programNode.name), "generatedPrograms");
const childScope = { ...scope, instructionNode, programAddressConstant };
return mergeFragments([getTypeFragment2(childScope), getFunctionFragment(childScope)], (cs) => cs.join("\n\n"));
}
function getTypeFragment2(scope) {
const customData = scope.customInstructionData.get(scope.instructionNode.name);
const instructionParsedType = scope.nameApi.instructionParsedType(scope.instructionNode.name);
const instructionDataName = scope.nameApi.instructionDataType(scope.instructionNode.name);
const hasData = !!customData || (scope.instructionNode.arguments ?? []).length > 0;
const hasAccounts = (scope.instructionNode.accounts ?? []).length > 0;
const typeParamDeclarations = mergeFragments(
[
fragment`TProgram extends string = typeof ${scope.programAddressConstant}`,
hasAccounts ? fragment`TAccountMetas extends readonly ${use("type AccountMeta", "solanaInstructions")}[] = readonly AccountMeta[]` : void 0
],
(cs) => cs.join(", ")
);
const accounts = mergeFragments(
(scope.instructionNode.accounts ?? []).map((account, i) => {
const docs = getDocblockFragment(account.docs ?? [], true);
const name = nodes.camelCase(account.name);
return fragment`${docs}${name}${account.isOptional ? "?" : ""}: TAccountMetas[${i}]${account.isOptional ? " | undefined" : ""};`;
}),
(cs) => hasAccounts ? `
accounts: {
${cs.join("\n")}
};` : ""
);
const dataTypeFragment = customData ? scope.dataArgsManifest.strictType : fragment`${scope.nameApi.dataType(instructionDataName)}`;
const data = hasData ? fragment`\ndata: ${dataTypeFragment};` : fragment``;
return fragment`export type ${instructionParsedType}<${typeParamDeclarations}> = { programAddress: ${use("type Address", "solanaAddresses")}<TProgram>;${accounts}${data} };`;
}
function getFunctionFragment(scope) {
const customData = scope.customInstructionData.get(scope.instructionNode.name);
const instructionParsedType = scope.nameApi.instructionParsedType(scope.instructionNode.name);
const instructionParseFunction = scope.nameApi.instructionParseFunction(scope.instructionNode.name);
const instructionDataName = scope.nameApi.instructionDataType(scope.instructionNode.name);
const decoderFunction = customData ? scope.dataArgsManifest.decoder : fragment`${scope.nameApi.decoderFunction(instructionDataName)}()`;
const hasData = !!customData || (scope.instructionNode.arguments ?? []).length > 0;
const hasAccounts = (scope.instructionNode.accounts ?? []).length > 0;
const hasOptionalAccounts = (scope.instructionNode.accounts ?? []).some((account) => account.isOptional);
const minimumNumberOfAccounts = scope.instructionNode.optionalAccountStrategy === "omitted" ? (scope.instructionNode.accounts ?? []).filter((account) => !account.isOptional).length : (scope.instructionNode.accounts ?? []).length;
const typeParams = ["TProgram", hasAccounts ? "TAccountMetas" : void 0].filter(Boolean).join(", ");
const typeParamDeclarations = mergeFragments(
[
fragment`TProgram extends string`,
hasAccounts ? fragment`TAccountMetas extends readonly ${use("type AccountMeta", "solanaInstructions")}[]` : void 0
],
(cs) => cs.join(", ")
);
const instructionType = mergeFragments(
[
fragment`${use("type Instruction", "solanaInstructions")}<TProgram>`,
hasAccounts ? fragment`${use("type InstructionWithAccounts", "solanaInstructions")}<TAccountMetas>` : void 0,
hasData ? visitorsCore.pipe(
fragment`InstructionWithData<ReadonlyUint8Array>`,
(f) => addFragmentImports(f, "solanaInstructions", ["type InstructionWithData"]),
(f) => addFragmentImports(f, "solanaCodecsCore", ["type ReadonlyUint8Array"])
) : void 0
],
(cs) => cs.join(" & ")
);
let accountHelpers;
if (hasAccounts) {
const solanaError = use("SolanaError", "solanaErrors");
const solanaErrorCode = use("SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS", "solanaErrors");
accountHelpers = fragment`if (instruction.accounts.length < ${minimumNumberOfAccounts}) {
throw new ${solanaError}(${solanaErrorCode}, { actualAccountMetas: instruction.accounts.length, expectedAccountMetas: ${minimumNumberOfAccounts} });
}
let accountIndex = 0;
const getNextAccount = () => {
const accountMeta = (instruction.accounts as TAccountMetas)[accountIndex]!;
accountIndex += 1;
return accountMeta;
}`;
}
if (hasOptionalAccounts && scope.instructionNode.optionalAccountStrategy === "omitted") {
accountHelpers = fragment`${accountHelpers}
let optionalAccountsRemaining = instruction.accounts.length - ${minimumNumberOfAccounts};
const getNextOptionalAccount = () => {
if (optionalAccountsRemaining === 0) return undefined;
optionalAccountsRemaining -= 1;
return getNextAccount();
};`;
} else if (hasOptionalAccounts) {
accountHelpers = fragment`${accountHelpers}
const getNextOptionalAccount = () => {
const accountMeta = getNextAccount();
return accountMeta.address === ${scope.programAddressConstant} ? undefined : accountMeta;
};`;
}
const accounts = mergeFragments(
(scope.instructionNode.accounts ?? []).map(
(account) => account.isOptional ? fragment`${nodes.camelCase(account.name)}: getNextOptionalAccount()` : fragment`${nodes.camelCase(account.name)}: getNextAccount()`
),
(cs) => hasAccounts ? `, accounts: { ${cs.join(", ")} }` : ""
);
const data = hasData ? fragment`, data: ${decoderFunction}.decode(instruction.data)` : fragment``;
return fragment`export function ${instructionParseFunction}<${typeParamDeclarations}>(instruction: ${instructionType}): ${instructionParsedType}<${typeParams}> {
${accountHelpers}
return { programAddress: instruction.programAddress${accounts}${data} };
}`;
}
function getInstructionTypeFragment2(scope) {
const { instructionPath, nameApi, customInstructionData } = scope;
const instructionNode = visitorsCore.getLastNodeFromPath(instructionPath);
const programNode = visitorsCore.findProgramNodeFromPath(instructionPath);
const hasAccounts = (instructionNode.accounts ?? []).length > 0;
const customData = customInstructionData.get(instructionNode.name);
const hasData = !!customData || (instructionNode.arguments ?? []).length > 0;
const instructionType = nameApi.instructionType(instructionNode.name);
const programAddressConstant = use(nameApi.programAddressConstant(programNode.name), "generatedPrograms");
const accountTypeParams = mergeFragments(
(instructionNode.accounts ?? []).map(
(account) => getInstructionAccountTypeParamFragment({
...scope,
instructionAccountPath: [...instructionPath, account]
})
),
(cs) => cs.length > 0 ? `${cs.join(", ")}, ` : ""
);
const data = hasData ? fragment` & ${use("type InstructionWithData", "solanaInstructions")}<${use("type ReadonlyUint8Array", "solanaCodecsCore")}>` : void 0;
const usesLegacyOptionalAccounts = instructionNode.optionalAccountStrategy === "omitted";
const accountMetasFragment = mergeFragments(
(instructionNode.accounts ?? []).map(
(account) => renderersCore.mapFragmentContent(getInstructionAccountMetaFragment(account), (c) => {
const typeParam = `TAccount${nodes.pascalCase(account.name)}`;
const isLegacyOptional = account.isOptional && usesLegacyOptionalAccounts;
const type = `${typeParam} extends string ? ${c} : ${typeParam}`;
if (!isLegacyOptional) return type;
return `...(${typeParam} extends undefined ? [] : [${type}])`;
})
),
(c) => c.join(", ")
);
const instructionWithAccounts = use("type InstructionWithAccounts", "solanaInstructions");
const accounts = hasAccounts ? fragment` & ${instructionWithAccounts}<[${accountMetasFragment}, ...TRemainingAccounts]>` : fragment` & ${instructionWithAccounts}<TRemainingAccounts>`;
return fragment`export type ${instructionType}<TProgram extends string = typeof ${programAddressConstant}, ${accountTypeParams}TRemainingAccounts extends readonly ${use("type AccountMeta", "solanaInstructions")}<string>[] = []> =
${use("type Instruction", "solanaInstructions")}<TProgram>${data}${accounts};`;
}
// src/fragments/instructionPage.ts
function getInstructionPageFragment(scope) {
const node = visitorsCore.getLastNodeFromPath(scope.instructionPath);
if (!visitorsCore.findProgramNodeFromPath(scope.instructionPath)) {
throw new Error("Instruction must be visited inside a program.");
}
const childScope = {
...scope,
dataArgsManifest: visitorsCore.visit(node, scope.typeManifestVisitor),
extraArgsManifest: visitorsCore.visit(
nodes.definedTypeNode({
name: scope.nameApi.instructionExtraType(node.name),
type: nodes.structTypeNodeFromInstructionArgumentNodes(node.extraArguments ?? [])
}),
scope.typeManifestVisitor
),
renamedArgs: getRenamedArgsMap(node)
};
return mergeFragments(
[
getDiscriminatorConstantsFragment({
...childScope,
discriminatorNodes: node.discriminators ?? [],
fields: node.arguments ?? [],
prefix: node.name
}),
getInstructionTypeFragment2(childScope),
getInstructionDataFragment(childScope),
getInstructionExtraArgsFragment(childScope),
getInstructionFunctionFragment({ ...childScope, useAsync: true }),
getInstructionFunctionFragment({ ...childScope, useAsync: false }),
getInstructionParseFunctionFragment(childScope)
],
(cs) => cs.join("\n\n")
);
}
function getRenamedArgsMap(instruction) {
const argNames = [
...(instruction.arguments ?? []).map((a) => a.name),
...(instruction.extraArguments ?? []).map((a) => a.name)
];
const duplicateArgs = argNames.filter((e, i, a) => a.indexOf(e) !== i);
if (duplicateArgs.length > 0) {
throw new Error(`Duplicate args found: [${duplicateArgs.join(", ")}] in instruction [${instruction.name}].`);
}
const allNames = [...(instruction.accounts ?? []).map((account) => account.name), ...argNames];
const duplicates = allNames.filter((e, i, a) => a.indexOf(e) !== i);
if (duplicates.length === 0) return /* @__PURE__ */ new Map();
errors.logWarn(
`[JavaScript] Accounts and args of instruction [${instruction.name}] have the following conflicting attributes [${duplicates.join(", ")}]. Thus, the arguments have been renamed to avoid conflicts in the input type.`
);
return new Map(duplicates.map((name) => [nodes.camelCase(name), nodes.camelCase(`${name}Arg`)]));
}
function getPdaFunctionFragment(scope) {
const pdaNode = visitorsCore.getLastNodeFromPath(scope.pdaPath);
const seeds = parsePdaSeedNodes(pdaNode.seeds ?? [], scope);
return mergeFragments(
[getSeedInputTypeFragment(seeds, scope), getFunctionFragment2(seeds, scope)],
(cs) => cs.join("\n\n")
);
}
function getSeedInputTypeFragment(seeds, scope) {
const variableSeeds = seeds.filter(nodes.isNodeFilter("variablePdaSeedNode"));
if (variableSeeds.length === 0) return;
const pdaNode = visitorsCore.getLastNodeFromPath(scope.pdaPath);
const seedTypeName = scope.nameApi.pdaSeedsType(pdaNode.name);
const seedAttributes = mergeFragments(
variableSeeds.map((seed) => seed.inputAttribute),
(cs) => cs.join("\n")
);
return fragment`export type ${seedTypeName} = {\n${seedAttributes}\n};`;
}
function getFunctionFragment2(seeds, scope) {
const pdaNode = visitorsCore.getLastNodeFromPath(scope.pdaPath);
const programNode = visitorsCore.findProgramNodeFromPath(scope.pdaPath);
const addressType = use("type Address", "solanaAddresses");
const pdaType = use("type ProgramDerivedAddress", "solanaAddresses");
const getPdaFunction = use("getProgramDerivedAddress", "solanaAddresses");
const seedTypeName = scope.nameApi.pdaSeedsType(pdaNode.name);
const findPdaFunction = scope.nameApi.pdaFindFunction(pdaNode.name);
const docs = getDocblockFragment(pdaNode.docs ?? [], true);
const hasVariableSeeds = seeds.filter(nodes.isNodeFilter("variablePdaSeedNode")).length > 0;
const seedArgument = hasVariableSeeds ? `seeds: ${seedTypeName}, ` : "";
const programAddress = pdaNode.programId ?? programNode.publicKey;
const encodedSeeds = mergeFragments(
seeds.map((s) => s.encodedValue),
(cs) => cs.join(", ")
);
return fragment`${docs}export async function ${findPdaFunction}(${seedArgument}config: { programAddress?: ${addressType} | undefined } = {}): Promise<${pdaType}> {
const { programAddress = '${programAddress}' as ${addressType}<'${programAddress}'> } = config;
return await ${getPdaFunction}({ programAddress, seeds: [${encodedSeeds}]});
}`;
}
function parsePdaSeedNodes(seeds, scope) {
return seeds.map((seed) => {
if (nodes.isNode(seed, "variablePdaSeedNode")) {
const name = nodes.camelCase(seed.name);
const docs = getDocblockFragment(seed.docs ?? [], true);
const { encoder: encoder2, looseType } = visitorsCore.visit(seed.type, scope.typeManifestVisitor);
return {
...seed,
encodedValue: fragment`${encoder2}.encode(seeds.${name})`,
inputAttribute: fragment`${docs}${name}: ${looseType};`
};
}
if (nodes.isNode(seed.value, "programIdValueNode")) {
const addressEncoder = use("getAddressEncoder", "solanaAddresses");
return { ...seed, encodedValue: fragment`${addressEncoder}().encode(programAddress)` };
}
const { encoder } = visitorsCore.visit(seed.type, scope.typeManifestVisitor);
const { value } = visitorsCore.visit(seed.value, scope.typeManifestVisitor);
return { ...seed, encodedValue: fragment`${encoder}.encode(${value})` };
});
}
function getPdaPageFragment(scope) {
if (!visitorsCore.findProgramNodeFromPath(scope.pdaPath)) {
throw new Error("PDA must be visited inside a program.");
}
return getPdaFunctionFragment(scope);
}
function getProgramAccountsFragment(scope) {
if ((scope.programNode.accounts ?? []).length === 0) return;
return mergeFragments(
[getProgramAccountsEnumFragment(scope), getProgramAccountsIdentifierFunctionFragment(scope)],
(c) => c.join("\n\n")
);
}
function getProgramAccountsEnumFragment(scope) {
const { programNode, nameApi } = scope;
return getEnumDeclarationFragment({
...scope,
name: nameApi.programAccountsEnum(programNode.name),
variantNames: (programNode.accounts ?? []).map((account) => nameApi.programAccountsEnumVariant(account.name))
});
}
function getProgramAccountsIdentifierFunctionFragment(scope) {
const { programNode, nameApi } = scope;
const accountsWithDiscriminators = (programNode.accounts ?? []).filter(
(account) => (account.discriminators ?? []).length > 0
);
const hasAccountDiscriminators = accountsWithDiscriminators.length > 0;
if (!hasAccountDiscriminators) return;
const programAccountsEnum = nameApi.programAccountsEnum(programNode.name);
const programAccountsIdentifierFunction = nameApi.programAccountsIdentifierFunction(programNode.name);
const discriminatorsFragment = mergeFragments(
accountsWithDiscriminators.map((account) => {
const variant = nameApi.programAccountsEnumVariant(account.name);
return getDiscriminatorConditionFragment({
...scope,
dataName: "data",
discriminators: account.discriminators ?? [],
ifTrue: `return ${programAccountsEnum}.${variant};`,
struct: nodes.resolveNestedTypeNode(account.data)
});
}),
(c) => c.join("\n")
);
const readonlyUint8Array = use("type ReadonlyUint8Array", "solanaCodecsCore");
const solanaError = use("SolanaError", "solanaErrors");
const solanaErrorCode = use("SOLANA_ERROR__PROGRAM_CLIENTS__FAILED_TO_IDENTIFY_ACCOUNT", "solanaErrors");
return fragment`export function ${programAccountsIdentifierFunction}(account: { data: ${readonlyUint8Array} } | ${readonlyUint8Array}): ${programAccountsEnum} {
const data = 'data' in account ? account.data : account;
${discriminatorsFragment}
throw new ${solanaError}(${solanaErrorCode}, { accountData: data, programName: "${programNode.name}" });
}`;
}
function getProgramConstantFragment(scope) {
const { programNode, nameApi } = scope;
const programAddressConstant = nameApi.programAddressConstant(programNode.name);
return visitorsCore.pipe(
fragment`export const ${programAddressConstant} = '${programNode.publicKey}' as Address<'${programNode.publicKey}'>;`,
(f) => addFragmentImports(f, "solanaAddresses", ["type Address"])
);
}
function getProgramEventsFragment(scope) {
if ((scope.programNode.events ?? []).length === 0) return;
return mergeFragments(
[getProgramEventsEnumFragment(scope), getProgramEventsIdentifierFunctionFragment(scope)],
(c) => c.join("\n\n")
);
}
function getProgramEventsEnumFragment(scope) {
const { programNode, nameApi } = scope;
return getEnumDeclarationFragment({
...scope,
name: nameApi.programEventsEnum(programNode.name),
variantNames: (programNode.events ?? []).map((event) => nameApi.programEventsEnumVariant(event.name))
});
}
function getProgramEventsIdentifierFunctionFragment(scope) {
const { programNode, nameApi } = scope;
const eventsWithDiscriminators = (programNode.events ?? []).filter(
(event) => (event.discriminators ?? []).length > 0
);
if (eventsWithDiscriminators.length === 0) return;
const programEventsEnum = nameApi.programEventsEnum(programNode.name);
const programEventsIdentifierFunction = nameApi.programEventsIdentifierFunction(programNode.name);
const discriminatorsFragment = mergeFragments(
eventsWithDiscriminators.map((event) => {
const variant = nameApi.programEventsEnumVariant(event.name);
const resolvedData = nodes.resolveNestedTypeNode(event.data);
return getDiscriminatorConditionFragment({
...scope,
dataName: "data",
discriminators: event.discriminators ?? [],
ifTrue: `return ${programEventsEnum}.${variant};`,
struct: nodes.isNode(resolvedData, "structTypeNode") ? resolvedData : nodes.structTypeNode([])
});
}),
(c) => c.join("\n")
);
const readonlyUint8Array = use("type ReadonlyUint8Array", "solanaCodecsCore");
return fragment`export function ${programEventsIdentifierFunction}(event: { data: ${readonlyUint8Array} } | ${readonlyUint8Array}): ${programEventsEnum} {
const data = 'data' in event ? event.data : event;
${discriminatorsFragment}
throw new Error('The provided event could not be identified as a ${programNode.name} event.');
}`;
}
function getProgramInstructionsFragment(scope) {
if ((scope.programNode.instructions ?? []).length === 0) return;
const allInstructions = nodes.getAllInstructionsWithSubs(scope.programNode, {
leavesOnly: !scope.renderParentInstructions,
subInstructionsFirst: true
});
const scopeWithInstructions = { ...scope, allInstructions };
return mergeFragments(
[
getProgramInstructionsEnumFragment(scopeWithInstructions),
getProgramInstructionsIdentifierFunctionFragment(scopeWithInstructions),
getProgramInstructionsParsedUnionTypeFragment(scopeWithInstructions),
getProgramInstructionsParseFunctionFragment(scopeWithInstructions)
],
(c) => c.join("\n\n")
);
}
function getProgramInstructionsEnumFragment(scope) {
const { programNode, allInstructions, nameApi } = scope;
return getEnumDeclarationFragment({
...scope,
name: nameApi.programInstructionsEnum(programNode.name),
variantNames: allInstructions.map((instruction) => nameApi.programInstructionsEnumVariant(instruction.name))
});
}
function getProgramInstructionsIdentifierFunctionFragment(scope) {
const { programNode, nameApi, allInstructions } = scope;
const instructionsWithDiscriminators = allInstructions.filter(
(instruction) => (instruction.discriminators ?? []).length > 0
);
const hasInstructionDiscriminators = instructionsWithDiscriminators.length > 0;
if (!hasInstructionDiscriminators) return;
const programInstructionsEnum = nameApi.programInstructionsEnum(programNode.name);
const programInstructionsIdentifierFunction = nameApi.programInstructionsIdentifierFunction(programNode.name);
const discriminatorsFragment = mergeFragments(
instructionsWithDiscriminators.map((instruction) => {
const variant = nameApi.programInstructionsEnumVariant(instruction.name);
return getDiscriminatorConditionFragment({
...scope,
dataName: "data",
discriminators: instruction.discriminators ?? [],
ifTrue: `return ${programInstructionsEnum}.${variant};`,
struct: nodes.structTypeNodeFromInstructionArgumentNodes(instruction.arguments ?? [])
});
}),
(c) => c.join("\n")
);
const readonlyUint8Array = use("type ReadonlyUint8Array", "solanaCodecsCore");
const solanaError = use("SolanaError", "solanaErrors");
const solanaErrorCode = use("SOLANA_ERROR__PROGRAM_CLIENTS__FAILED_TO_IDENTIFY_INSTRUCTION", "solanaErrors");
return fragment`export function ${programInstructionsIdentifierFunction}(instruction: { data: ${readonlyUint8Array} } | ${readonlyUint8Array}): ${programInstructionsEnum} {
const data = 'data' in instruction ? instruction.data : instruction;
${discriminatorsFragment}
throw new ${solanaError}(${solanaErrorCode}, { instructionData: data, programName: "${programNode.name}" });
}`;
}
function getProgramInstructionsParsedUnionTypeFragment(scope) {
const { programNode, allInstructions, nameApi, erasableSyntax } = scope;
const programAddress = programNode.publicKey;
const programInstructionsType = nameApi.programInstructionsParsedUnionType(programNode.name);
const programInstructionsEnum = nameApi.programInstructionsEnum(programNode.name);
const typeVariants = allInstructions.map((instruction) => {
const instructionEnumVariant = nameApi.programInstructionsEnumVariant(instruction.name);
const parsedInstructionType = use(
`type ${nameApi.instructionParsedType(instruction.name)}`,
"generatedInstructions"
);
const memberType = erasableSyntax ? `typeof ${programInstructionsEnum}.${instructionEnumVariant}` : `${programInstructionsEnum}.${instructionEnumVariant}`;
return fragment`| { instructionType: ${memberType} } & ${parsedInstructionType}<TProgram>`;
});
return mergeFragments(
[
fragment`export type ${programInstructionsType}<TProgram extends string = '${programAddress}'> =`,
...typeVariants
],
(c) => c.join("\n")
);
}
function getProgramInstructionsParseFunctionFragment(scope) {
const { programNode, nameApi, allInstructions } = scope;
const instructionsWithDiscriminators = allInstructions.filter(
(instruction) => (instruction.discriminators ?? []).length > 0
);
if (instructionsWithDiscriminators.length === 0) return;
const programInstructionsEnum = nameApi.programInstructionsEnum(programNode.name);
const programInstructionsIdentifierFunction = nameApi.programInstructionsIdentifierFunction(programNode.name);
const programInstructionsParsedUnionType = nameApi.programInstructionsParsedUnionType(programNode.name);
const parseFunction = nameApi.programInstructionsParseFunction(programNode.name);
const switchCases = mergeFragments(
allInstructions.map((instruction) => {
const enumVariant = nameApi.programInstructionsEnumVariant(instruction.name);
const parseFunction2 = use(nameApi.instructionParseFunction(instruction.name), "generatedInstructions");
const assertIsInstructionWithAccounts = use("assertIsInstructionWithAccounts", "solanaInstructions");
const hasAccounts = (instruction.accounts ?? []).length > 0;
const assertionsCode = hasAccounts ? fragment`${assertIsInstructionWithAccounts}(instruction);\n` : fragment``;
return fragment`case ${programInstructionsEnum}.${enumVariant}: { ${assertionsCode}return { instructionType: ${programInstructionsEnum}.${enumVariant}, ...${parseFunction2}(instruction) }; }`;
}),
(c) => c.join("\n")
);
const solanaError = use("SolanaError", "solanaErrors");
const solanaErrorCode = use("SOLANA_ERROR__PROGRAM_CLIENTS__UNRECOGNIZED_INSTRUCTION_TYPE", "solanaErrors");
return fragment`
export function ${parseFunction}<TProgram extends string>(
instruction: ${use("type Instruction", "solanaInstructions")}<TProgram>
& ${use("type InstructionWithData", "solanaInstructions")}<${use("type ReadonlyUint8Array", "solanaCodecsCore")}>
): ${programInstructionsParsedUnionType}<TProgram> {
const instructionType = ${programInstructionsIdentifierFunction}(instruction);
switch (instructionType) {
${switchCases}
default: throw new ${solanaError}(${solanaErrorCode}, { instructionType: instructionType as string, programName: "${programNode.name}" });
}
}`;
}
function getProgramPluginFragment(scope) {
if ((scope.programNode.accounts ?? []).length === 0 && (scope.programNode.instructions ?? []).length === 0 && (scope.programNode.pdas ?? []).length === 0)
return;
const resolvedInstructionInputVisitor = visitorsCore.getResolvedInstructionInputsVisitor();
const asyncInstructions = (scope.programNode.instructions ?? []).filter(
(instruction) => hasAsyncFunction(instruction, visitorsCore.visit(instruction, resolvedInstructionInputVisitor), scope.asyncResolvers)
).map((i) => i.name);
const extendedScope = { ...scope, asyncInstructions };
return mergeFragments(
[
getProgramPluginTypeFragment(extendedScope),
getProgramPluginAccountsTypeFragment(scope),
getProgramPluginInstructionsTypeFragment(extendedScope),
getProgramPluginPdasTypeFragment(scope),
getProgramPluginRequirementsTypeFragment(scope),
getProgramPluginFunctionFragment(extendedScope),
getMakeOptionalHelperTypeFragment(scope)
],
(c) => c.join("\n\n")
);
}
function hasAccountIdentifier(programNode) {
return (programNode.accounts ?? []).some((account) => (account.discriminators ?? []).length > 0);
}
function hasInstructionIdentifier(programNode, renderParentInstructions) {
const allInstructions = nodes.getAllInstructionsWithSubs(programNode, {
leavesOnly: !renderParentInstructions,
subInstructionsFirst: true
});
return allInstructions.some((instruction) => (instruction.discriminators ?? []).length > 0);
}
function getProgramPluginTypeFragment(scope) {
const { programNode, nameApi, renderParentInstructions } = scope;
const programPluginType = nameApi.programPluginType(programNode.name);
const programPluginAccountsType = nameApi.programPluginAccountsType(programNode.name);
const programPluginInstructionsType = nameApi.programPluginInstructionsType(programNode.name);
const programPluginPdasType = nameApi.programPluginPdasType(programNode.name);
const accountsIdentifierFunction = nameApi.programAccountsIdentifierFunction(programNode.name);
const instructionsIdentifierFunction = nameApi.programInstructionsIdentifierFunction(programNode.name);
const instructionsParseFunction = nameApi.programInstructionsParseFunction(programNode.name);
const fields = mergeFragments(
[
(programNode.accounts ?? []).length > 0 ? fragment`accounts: ${programPluginAccountsType};` : void 0,
(programNode.instructions ?? []).length > 0 ? fragment`instructions: ${programPluginInstructionsType};` : void 0,
(programNode.pdas ?? []).length > 0 ? fragment`pdas: ${programPluginPdasType};` : void 0,
hasAccountIdentifier(programNode) ? fragment`identifyAccount: typeof ${accountsIdentifierFunction};` : void 0,
hasInstructionIdentifier(programNode, renderParentInstructions) ? fragment`identifyInstruction: typeof ${instructionsIdentifierFunction}; parseInstruction: typeof ${instructionsParseFunction};` : void 0
],
(c) => c.join(" ")
);
return fragment`export type ${programPluginType} = { ${fields} }`;
}
function getProgramPluginAccountsTypeFragment(scope) {
const { programNode, nameApi } = scope;
if ((programNode.accounts ?? []).length === 0) return;
const programPluginAccountsType = nameApi.programPluginAccountsType(programNode.name);
const selfFetchFunctions = use("type SelfFetchFunctions", "solanaProgramClientCore");
const fields = mergeFragments(
(programNode.accounts ?? []).map((account) => {
const name = nameApi.programPluginAccountKey(account.name);
const codecFunction = use("type " + nameApi.codecFunction(account.name), "generatedAccounts");
const fromType = use("type " + nameApi.dataArgsType(account.name), "generatedAccounts");
const toType = use("type " + nameApi.dataType(account.name), "generatedAccounts");
return fragment`${name}: ReturnType<typeof ${codecFunction}> & ${selfFetchFunctions}<${fromType}, ${toType}>;`;
}),
(c) => c.join(" ")
);
return fragment`export type ${programPluginAccountsType} = { ${fields} }`;
}
function getProgramPluginInstructionsTypeFragment(scope) {
const { programNode, asyncInstructions, nameApi } = scope;
if ((programNode.instructions ?? []).length === 0) return;
const programPluginInstructionsType = nameApi.programPluginInstructionsType(programNode.name);
const selfPlanAndSendFunctions = use("type SelfPlanAndSendFunctions", "solanaProgramClientCore");
const fields = mergeFragments(
(programNode.instructions ?? []).map((instruction) => {
const name = nameApi.programPluginInstructionKey(instruction.name);
const isAsync = asyncInstructions.includes(instruction.name);
let instructionInputType = isAsync ? use("type " + nameApi.instructionAsyncInputType(instruction.name), "generatedInstructions") : use("type " + nameApi.instructionSyncInputType(instruction.name), "generatedInstructions");
const instructionFunction = isAsync ? use("type " + nameApi.instructionAsyncFunction(instruction.name), "generatedInstructions") : use("type " + nameApi.instructionSyncFunction(instruction.name), "generatedInstructions");
const payerDefaultValues = getPayerDefaultValues(instruction);
if (payerDefaultValues.length > 0) {
const fieldStringUnion = payerDefaultValues.map(({ name: name2 }) => `"${name2}"`).join(" | ");
instructionInputType = fragment`MakeOptional<${instructionInputType}, ${fieldStringUnion}>`;
}
return fragment`${name}: (input: ${instructionInputType}) => ReturnType<typeof ${instructionFunction}> & ${selfPlanAndSendFunctions};`;
}),
(c) => c.join(" ")
);
return fragment`export type ${programPluginInstructionsType} = { ${fields} }`;
}
function getProgramPluginPdasTypeFragment(scope) {
const { programNode, nameApi } = scope;
if ((programNode.pdas ?? []).length === 0) return;
const programPluginPdasType = nameApi.programPluginPdasType(programNode.name);
const fields = mergeFragments(
(programNode.pdas ?? []).map((pda) => {
const name = nameApi.programPluginPdaKey(pda.name);
const pdaFindFunction = use("type " + nameApi.pdaFindFunction(pda.name), "generatedPdas");
return fragment`${name}: typeof ${pdaFindFunction};`;
}),
(c) => c.join(" ")
);
return fragment`export type ${programPluginPdasType} = { ${fields} }`;
}
function getProgramPluginPdasObjectFragment(scope) {
const { programNode, nameApi } = scope;
if ((programNode.pdas ?? []).length === 0) return;
const fields = mergeFragments(
(programNode.pdas ?? []).map((pda) => {
const name = nameApi.programPluginPdaKey(pda.name);
const pdaFindFunction = use(nameApi.pdaFindFunction(pda.name), "generatedPdas");
return fragment`${name}: ${pdaFindFunction}`;
}),
(c) => c.join(", ")
);
return fragment`pdas: { ${fields} }`;
}
function getProgramPluginRequirementsTypeFragment(scope) {
const { programNode, nameApi } = scope;
const programRequirementsType = nameApi.programPluginRequirementsType(programNode.name);
const clientWithRpc = fragment`${use("type ClientWithRpc", "solanaPluginInterfaces")}<${use("type GetAccountInfoApi", "solanaRpcApi")} & ${use("type GetMultipleAccountsApi", "solanaRpcApi")}>`;
const clientWithPayer = use("type ClientWithPayer", "solanaPluginInterfaces");
const clientWithTransactionPlanning = use("type ClientWithTransactionPlanning", "solanaPluginInterfaces");
const clientWithTransactionSending = use("type ClientWithTransactionSending", "solanaPluginInterfaces");
const hasAccounts = (programNode.accounts ?? []).length > 0;
const hasInstructions = (programNode.instructions ?? []).length > 0;
const requirementList = [
hasAccounts ? clientWithRpc : void 0,
hasPayerDefaultValues(programNode) ? clientWithPayer : void 0,
hasInstructions ? clientWithTransactionPlanning : void 0,
hasInstructions ? clientWithTransactionSending : void 0
].filter((r) => r !== void 0);
if (requirementList.length === 0) {
return fragment`export type ${programRequirementsType} = object`;
}
const requirements = mergeFragments(requirementList, (c) => c.join(" & "));
return fragment`export type ${programRequirementsType} = ${requirements}`;
}
function getProgramPluginFunctionFragment(scope) {
const { programNode, nameApi, renderParentInstructions, erasableSyntax } = scope;
const programPluginFunction = nameApi.programPluginFunction(programNode.name);
const programPluginType = nameApi.programPluginType(programNode.name);
const programPluginRequirementsType = nameApi.programPluginRequirementsType(programNode.name);
const programPluginKey = nameApi.programPluginKey(programNode.name);
const extendClient = use("extendClient", "solanaPluginCore");
const extendedClient = use("type ExtendedClient", "solanaPluginCore");
const accountsIdentifierFunction = nameApi.programAccountsIdentifierFunction(programNode.name);
const instructionsIdentifierFunction = nameApi.programInstructionsIdentifierFunction(programNode.name);
const instructionsParseFunction = nameApi.programInstructionsParseFunction(programNode.name);
const fields = mergeFragments(
[
getProgramPluginAccountsObjectFragment(scope),
getProgramPluginInstructionsObjectFragment(scope),
getProgramPluginPdasObjectFragment(scope),
hasAccountIdentifier(programNode) ? fragment`identifyAccount: ${accountsIdentifierFunction}` : void 0,
hasInstructionIdentifier(programNode, renderParentInstructions) ? fragment`identifyInstruction: ${instructionsIdentifierFunction}, parseInstruction: ${instructionsParseFunction}` : void 0
],
(c) => c.join(", ")
);
const pluginObject = erasableSyntax ? fragment`{ ${fields} } as ${programPluginType}` : fragment`<${programPluginType}>{ ${fields} }`;
return fragment`export function ${programPluginFunction}() {
return <T extends ${programPluginRequirementsType}>(client: T): ${extendedClient}<T, { ${programPluginKey}: ${programPluginType} }> => {
return ${extendClient}(client, { ${programPluginKey}: ${pluginObject} });
};
}`;
}
function getProgramPluginAccountsObjectFragment(scope) {
const { programNode, nameApi } = scope;
if ((programNode.accounts ?? []).length === 0) return;
const fields = mergeFragments(
(programNode.accounts ?? []).map((account) => {
const name = nameApi.programPluginAccountKey(account.name);
const addSelfFetchFunctions = use("addSelfFetchFunctions", "solanaProgramClientCore");
const codecFunction = use(nameApi.codecFunction(account.name), "generatedAccounts");
return fragment`${name}: ${addSelfFetchFunctions}(client, ${codecFunction}())`;
}),
(c) => c.join(", ")
);
return fragment`accounts: { ${fields} }`;
}
function getProgramPluginInstructionsObjectFragment(scope) {
const { programNode, nameApi, asyncInstructions } = scope;
if ((programNode.instructions ?? []).length === 0) return;
const fields = mergeFragments(
(programNode.instructions ?? []).map((instruction) => {
const name = nameApi.programPluginInstructionKey(instruction.name);
const isAsync = asyncInstructions.includes(instruction.name);
const instructionFunction = isAsync ? use(nameApi.instructionAsyncFunction(instruction.name), "generatedInstructions") : use(nameApi.instructionSyncFunction(instruction.name), "generatedInstructions");
const addSelfPlanAndSendFunctions = use("addSelfPlanAndSendFunctions", "solanaProgramClientCore");
const payerDefaultValues = getPayerDefaultValues(instruction);
let input = fragment`input`;
if (payerDefaultValues.length > 0) {
const fieldOverrides = mergeFragments(
payerDefaultValues.map(({ name: name2, signer }) => {
const signerDefault = signer ? "client.payer" : "client.payer.address";
return fragment`${name2}: input.${name2} ?? ${signerDefault}`;
}),
(c) => c.join(", ")
);
input = fragment`{ ...input, ${fieldOverrides} }`;
}
return fragment`${name}: input => ${addSelfPlanAndSendFunctions}(client, ${instructionFunction}(${input}))`;
}),
(c) => c.join(", ")
);
return fragment`instructions: { ${fields} }`;
}
function getMakeOptionalHelperTypeFragment(scope) {
if (!hasPayerDefaultValues(scope.programNode)) return;
return fragment`type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;`;
}
function hasPayerDefaultValues(programNode) {
return (programNode.instructions ?? []).some((instruction) => getPayerDefaultValueNodes(instruction).length > 0);
}
function getPayerDefaultValues(instructionNode) {
const renamedArgs = getRenamedArgsMap(instructionNode);
return getPayerDefaultValueNodes(instructionNode).map((inputNode) => {
return nodes.isNode(inputNode, "instructionAccountNode") ? { name: inputNode.name, signer: inputNode.isSigner !== false } : { name: renamedArgs.get(inputNode.name) ?? inputNode.name, signer: false };
});
}
function getPayerDefaultValueNodes(instructionNode) {
return [
...(instructionNode.accounts ?? []).filter((a) => !a.isOptional && nodes.isNode(a.defaultValue, "payerValueNode")),
...(instructionNode.arguments ?? []).filter((a) => nodes.isNode(a.defaultValue, "payerValueNode"))
];
}
// src/fragments/programPage.ts
function getProgramPageFragment(scope) {
return mergeFragments(
[
getProgramConstantFragment(scope),
getProgramAccountsFragment(scope),
getProgramEventsFragment(scope),
getProgramInstructionsFragment(scope),
getProgramPluginFragment(scope)
],
(cs) => cs.join("\n\n")
);
}
// src/fragments/rootIndexPage.ts
function getRootIndexPageFragment(scope) {
const hasAnythingToExport = scope.programsToExport.length > 0 || scope.accountsToExport.length > 0 || scope.programsWithConstantsToExport.length > 0 || scope.eventsToExport.length > 0 || scope.instructionsToExport.length > 0 || scope.definedTypesToExport.length > 0;
if (!hasAnythingToExport) {
return fragment`export default {};`;
}
const programsWithErrorsToExport = scope.programsToExport.filter((p) => (p.errors ?? []).length > 0);
return mergeFragments(
[
scope.accountsToExport.length > 0 ? getExportAllFragment(scope.getImportPath("./accounts", "directory")) : void 0,
scope.programsWithConstantsToExport.length > 0 ? getExportAllFragment(scope.getImportPath("./constants", "directory")) : void 0,
programsWithErrorsToExport.length > 0 ? getExportAllFragment(scope.getImportPath("./errors", "directory")) : void 0,
scope.eventsToExport.length > 0 ? getExportAllFragment(scope.getImportPath("./events", "directory")) : void 0,
scope.instructionsToExport.length > 0 ? getExportAllFragment(scope.getImportPath("./instructions", "directory")) : void 0,
scope.pdasToExport.length > 0 ? getExportAllFragment(scope.getImportPath("./pdas", "directory")) : void 0,
scope.programsToExport.length > 0 ? getExportAllFragment(scope.getImportPath("./programs", "directory")) : void 0,
scope.definedTypesToExport.length > 0 ? getExportAllFragment(scope.getImportPath("./types", "directory")) : void 0
],
(cs) => cs.join("\n")
);
}
function getTypeDiscriminatedUnionHelpersFragment(scope) {
const { name, typeNode, nameApi } = scope;
const isDiscriminatedUnion = nodes.isNode(typeNode, "enumTypeNode") && nodes.isDataEnum(typeNode);
if (!isDiscriminatedUnion) return;
const functionName = nameApi.discriminatedUnionFunction(name);
const isDiscriminatedUnionFunctionName = nameApi.isDiscriminatedUnionFunction(name);
const discriminatorName = nameApi.discriminatedUnionDiscriminator(name);
const strictName = nameApi.dataType(name);
const looseName = nameApi.dataArgsType(name);
const getVariantContentType = use("type GetDiscriminatedUnionVariantContent", "solanaCodecsDataStructures");
const getVariantType = use("type GetDiscriminatedUnionVariant", "solanaCodecsDataStructures");
const variantSignatures = mergeFragments(
(typeNode.variants ?? []).map((variant) => {
const variantName = nameApi.discriminatedUnionVariant(variant.name);
if (nodes.isNode(variant, "enumStructVariantTypeNode")) {
return fragment`export function ${functionName}(kind: '${variantName}', data: ${getVariantContentType}<${looseName}, '${discriminatorName}', '${variantName}'>): ${getVariantType}<${looseName}, '${discriminatorName}', '${variantName}'>;`;
}
if (nodes.isNode(variant, "enumTupleVariantTypeNode")) {
return fragment`export function ${functionName}(kind: '${variantName}', data: ${getVariantContentType}<${looseName}, '${discriminatorName}', '${variantName}'>['fields']): ${getVariantType}<${looseName}, '${discriminatorName}', '${variantName}'>;`;
}
return fragment`export function ${functionName}(kind: '${variantName}'): ${getVariantType}<${looseName}, '${discriminatorName}', '${variantName}'>;`;
}),
(cs) => cs.length > 0 ? `${cs.join("\n")}
` : ""
);
return fragment`// Data Enum Helpers.
${variantSignatures}export function ${functionName}<K extends ${looseName}['${discriminatorName}'], Data>(kind: K, data?: Data) {
return Array.isArray(data) ? { ${discriminatorName}: kind, fields: data } : { ${discriminatorName}: kind, ...(data ?? {}) };
}
export function ${isDiscriminatedUnionFunctionName}<K extends ${strictName}['${discriminatorName}']>(kind: K, value: ${strictName}): value is ${strictName} & { ${discriminatorName}: K } {
return value.${discriminatorName} === kind;
};
`;
}
function getTypePageFragment(scope) {
const node = scope.node;
const manifest = visitorsCore.visit(node, scope.typeManifestVisitor);
return visitorsCore.pipe(
mergeFragments(
[
getTypeWithCodecFragment({ ...scope, manifest, name: node.name, node: node.type, typeDocs: node.docs }),
getTypeDiscriminatedUnionHelpersFragment({ ...scope, name: node.name, typeNode: node.type })
],
(cs) => cs.join("\n\n")
),
(f) => removeFragmentImports(f, "generatedTypes", [
scope.nameApi.dataType(node.name),
scope.nameApi.dataArgsType(node.name),
scope.nameApi.encoderFunction(node.name),
scope.nameApi.decoderFunction(node.name),
scope.nameApi.codecFunction(node.name)
])
);
}
function getTypeManifestVisitor(input) {
const {
nameApi,
linkables,
nonScalarEnums,
customAccountData,
customInstructionData,
getImportFrom,
erasableSyntax = false
} = input;
const stack = input.stack ?? new visitorsCore.NodeStack();
let parentName = null;
const getEventDiscriminatorConstantName = (constant) => {
const eventNode = visitorsCore.findLastNodeFromPath(stack.getPath(), "eventNode");
if (!eventNode) return null;
const constantDiscriminators = (eventNode.discriminators ?? []).filter(
nodes.isNodeFilter("constantDiscriminatorNode")
);
const serializedConstant = JSON.stringify(constant);
const index = constantDiscriminators.findIndex(
(discriminator) => JSON.stringify(discriminator.constant) === serializedConstant
);
if (index < 0) return null;
const prefix = getEventDiscriminatorPrefix(nameApi, eventNode.name);
return nameApi.constant(getConstantDiscriminatorName(prefix, index));
};
return visitorsCore.pipe(
visitorsCore.staticVisitor(() => typeManifest(), {
keys: [
...nodes.REGISTERED_TYPE_NODE_KINDS,
...nodes.REGISTERED_VALUE_NODE_KINDS,
"definedTypeLinkNode",
"definedTypeNode",
"accountNode",
"eventNode",
"instructionNode"
]
}),
(visitor) => visitorsCore.extendVisitor(visitor, {
visitAccount(account, { self }) {
parentName = {
loose: nameApi.dataArgsType(account.name),
strict: nameApi.dataType(account.name)
};
const link = customAccountData.get(account.name)?.linkNode;
const manifest = link ? visitorsCore.visit(link, self) : visitorsCore.visit(account.data, self);
parentName = null;
return manifest;
},
visitAmountType(amountType, { self }) {
return visitorsCore.visit(amountType.number, self);
},
visitArrayType(arrayType, { self }) {
const childManifest = visitorsCore.visit(arrayType.item, self);
const sizeManifest = getArrayLikeSizeOption(arrayType.count, self);
const encoderOptions = sizeManifest.encoder ? fragment`, { ${sizeManifest.encoder} }` : "";
const decoderOptions = sizeManifest.decoder ? fragment`, { ${sizeManifest.decoder} }` : "";
return typeManifest({
...childManifest,
decoder: fragment`${use("getArrayDecoder", "solanaCodecsDataStructures")}(${childManifest.decoder}${decoderOptions})`,
encoder: fragment`${use("getArrayEncoder", "solanaCodecsDataStructures")}(${childManifest.encoder}${encoderOptions})`,
looseType: fragment`Array<${childManifest.looseType}>`,
strictType: fragment`Array<${childManifest.strictType}>`
});
},
visitArrayValue(node, { self }) {
return mergeTypeManifests(
(node.items ?? []).map((v) => visitorsCore.visit(v, self)),
{ mergeValues: (renders) => `[${renders.join(", ")}]` }
);
},
visitBooleanType(booleanType, { self }) {
let sizeEncoder = fragment``;
let sizeDecoder = fragment``;
const resolvedSize = nodes.resolveNestedTypeNode(booleanType.size);
if (resolvedSize.format !== "u8" || resolvedSize.endian !== "le") {
const size = visitorsCore.visit(booleanType.size, self);
sizeEncoder = fragment`{ size: ${size.encoder} }`;
sizeDecoder = fragment`{ size: ${size.decoder} }`;
}
return typeManifest({
decoder: fragment`${use("getBooleanDecoder", "solanaCodecsDataStructures")}(${sizeDecoder})`,
encoder: fragment`${use("getBooleanEncoder", "solanaCodecsDataStructures")}(${sizeEncoder})`,
looseType: fragment`boolean`,
strictType: fragment`boolean`
});
},
visitBooleanValue(node) {
return typeManifest({ value: fragment`${JSON.stringify(node.boolean)}` });
},
visitBytesType() {
const readonlyUint8Array = use("type ReadonlyUint8Array", "solanaCodecsCore");
return typeManifest({
decoder: fragment`${use("getBytesDecoder", "solanaCodecsDataStructures")}()`,
encoder: fragment`${use("getBytesEncoder", "solanaCodecsDataStructures")}()`,
looseType: readonlyUint8Array,
strictType: readonlyUint8Array
});
},
visitBytesValue(node) {
const bytes = getBytesFromBytesValueNode(node);
return typeManifest({ value: fragment`new Uint8Array([${Array.from(bytes).join(", ")}])` });
},
visitConstantValue(node, { self }) {
const discriminatorConstantName = getEventDiscriminatorConstantName(node);
if (discriminatorConstantName) {
return typeManifest({ value: fragment`${discriminatorConstantName}` });
}
if (nodes.isNode(node.type, "bytesTypeNode") && nodes.isNode(node.value, "bytesValueNode")) {
return visitorsCore.visit(node.value, self);
}
return typeManifest({
value: fragment`${visitorsCore.visit(node.type, self).encoder}.encode(${visitorsCore.visit(node.value, self).value})`
});
},
visitDateTimeType(dateTimeType, { self }) {
return visitorsCore.visit(dateTimeType.number, self);
},
visitDefinedType(definedType, { self }) {
parentName = {
loose: nameApi.dataArgsType(definedType.name),
strict: nameApi.dataType(definedType.name)
};
const manifest = visitorsCore.visit(definedType.type, self);
parentName = null;
return manifest;
},
visitDefinedTypeLink(node) {
const strictName = nameApi.dataType(node.name);
const looseName = nameApi.dataArgsType(node.name);
const encoderFunction = nameApi.encoderFunction(node.name);
const decoderFunction = nameApi.decoderFunction(node.name);
const importFrom = getImportFrom(node);
return typeManifest({
decoder: fragment`${use(decoderFunction, importFrom)}()`,
encoder: fragment`${use(encoderFunction, importFrom)}()`,
looseType: use(`type ${looseName}`, importFrom),
strictType: use(`type ${strictName}`, importFrom)
});
},
visitEnumEmptyVariantType(enumEmptyVariantType) {
const discriminator = nameApi.discriminatedUnionDiscriminator(nodes.camelCase(parentName?.strict ?? ""));
const name = nameApi.discriminatedUnionVariant(enumEmptyVariantType.name);
const kindAttribute = `${discriminator}: "${name}"`;
return typeManifest({
decoder: fragment`['${name}', ${use("getUnitDecoder", "solanaCodecsDataStructures")}()]`,
encoder: fragment`['${name}', ${use("getUnitEncoder", "solanaCodecsDataStructures")}()]`,
looseType: fragment`{ ${kindAttribute} }`,
strictType: fragment`{ ${kindAttribute} }`
});
},
visitEnumStructVariantType(enumStructVariantType, { self }) {
const currentParentName = parentName;
const discriminator = nameApi.discriminatedUnionDiscriminator(
nodes.camelCase(currentParentName?.strict ?? "")
);
const name = nameApi.discriminatedUnionVariant(enumStructVariantType.name);
const kindAttribute = `${discriminator}: "${name}"`;
parentName = null;
const structManifest = visitorsCore.visit(enumStructVariantType.struct, self);
parentName = currentParentName;
return typeManifest({
...structManifest,
decoder: fragment`['${name}', ${structManifest.decoder}]`,
encoder: fragment`['${name}', ${structManifest.encoder}]`,
looseType: visitorsCore.pipe(
structManifest.looseType,
(f) => renderersCore.mapFragmentContent(f, (c) => `{ ${kindAttribute},${c.slice(1, -1)}}`)
),
strictType: visitorsCore.pipe(
structManifest.strictType,
(f) => renderersCore.mapFragmentContent(f, (c) => `{ ${kindAttribute},${c.slice(1, -1)}}`)
)
});
},
visitEnumTupleVariantType(enumTupleVariantType, { self }) {
const currentParentName = parentName;
const discriminator = nameApi.discriminatedUnionDiscriminator(
nodes.camelCase(currentParentName?.strict ?? "")
);
const name = nameApi.discriminatedUnionVariant(enumTupleVariantType.name);
const kindAttribute = `${discriminator}: "${name}"`;
const struct = nodes.structTypeNode([
nodes.structFieldTypeNode({
name: "fields",
type: enumTupleVariantType.tuple
})
]);
parentName = null;
const structManifest = visitorsCore.visit(struct, self);
parentName = currentParentName;
return typeManifest({
...structManifest,
decoder: fragment`['${name}', ${structManifest.decoder}]`,
encoder: fragment`['${name}', ${structManifest.encoder}]`,
looseType: visitorsCore.pipe(
structManifest.looseType,
(f) => renderersCore.mapFragmentContent(f, (c) => `{ ${kindAttribute},${c.slice(1, -1)}}`)
),
strictType: visitorsCore.pipe(
structManifest.strictType,
(f) => renderersCore.mapFragmentContent(f, (c) => `{ ${kindAttribute},${c.slice(1, -1)}}`)
)
});
},
visitEnumType(enumType, { self }) {
const currentParentName = parentName;
const encoderOptions = [];
const decoderOptions = [];
const enumSize = nodes.resolveNestedTypeNode(enumType.size);
if (enumSize.format !== "u8" || enumSize.endian !== "le") {
const sizeManifest = visitorsCore.visit(enumType.size, self);
encoderOptions.push(fragment`size: ${sizeManifest.encoder}`);
decoderOptions.push(fragment`size: ${sizeManifest.decoder}`);
}
const discriminator = nameApi.discriminatedUnionDiscriminator(
nodes.camelCase(currentParentName?.strict ?? "")
);
if (!nodes.isScalarEnum(enumType) && discriminator !== "__kind") {
encoderOptions.push(fragment`discriminator: '${discriminator}'`);
decoderOptions.push(fragment`discriminator: '${discriminator}'`);
}
const encoderOptionsFragment = mergeFragments(
encoderOptions,
(cs) => cs.length > 0 ? `, { ${cs.join(", ")} }` : ""
);
const decoderOptionsFragment = mergeFragments(
decoderOptions,
(cs) => cs.length > 0 ? `, { ${cs.join(", ")} }` : ""
);
if (nodes.isScalarEnum(enumType)) {
if (currentParentName === null) {
throw new Error(
"Scalar enums cannot be inlined and must be introduced via a defined type. Ensure you are not inlining a defined type that is a scalar enum through a visitor."
);
}
const variantNames = (enumType.variants ?? []).map(({ name }) => nameApi.enumVariant(name));
const body = getEnumBody(variantNames, erasableSyntax);
const decoderConstructor = erasableSyntax ? fragment`${currentParentName.strict} as Omit<typeof ${currentParentName.strict}, number>` : fragment`${currentParentName.strict}`;
return typeManifest({
decoder: fragment`${use("getEnumDecoder", "solanaCodecsDataStructures")}(${decoderConstructor}${decoderOptionsFragment})`,
encoder: fragment`${use("getEnumEncoder", "solanaCodecsDataStructures")}(${currentParentName.strict}${encoderOptionsFragment})`,
isEnum: true,
looseType: fragment`{ ${body} }`,
strictType: fragment`{ ${body} }`
});
}
const mergedManifest = mergeTypeManifests(
(enumType.variants ?? []).map((variant) => visitorsCore.visit(variant, self)),
{
mergeCodecs: (renders) => renders.join(", "),
mergeTypes: (renders) => renders.join(" | ")
}
);
return typeManifest({
...mergedManifest,
decoder: fragment`${use("getDiscriminatedUnionDecoder", "solanaCodecsDataStructures")}([${mergedManifest.decoder}]${decoderOptionsFragment})`,
encoder: fragment`${use("getDiscriminatedUnionEncoder", "solanaCodecsDataStructures")}([${mergedManifest.encoder}]${encoderOptionsFragment})`
});
},
visitEnumValue(node, { self }) {
const manifest = typeManifest();
const enumName = nameApi.dataType(node.enum.name);
const enumFunction = nameApi.discriminatedUnionFunction(node.enum.name);
const importFrom = getImportFrom(node.enum);
const enumNode = linkables.get([...stack.getPath(), node.enum])?.type;
const isScalar = enumNode && nodes.isNode(enumNode, "enumTypeNode") ? nodes.isScalarEnum(enumNode) : !nonScalarEnums.includes(node.enum.name);
if (!node.value && isScalar) {
const variantName2 = nameApi.enumVariant(node.variant);
return typeManifest({
...manifest,
value: visitorsCore.pipe(
manifest.value,
(f) => renderersCore.setFragmentContent(f, `${enumName}.${variantName2}`),
(f) => addFragmentImports(f, importFrom, [enumName])
)
});
}
const variantName = nameApi.discriminatedUnionVariant(node.variant);
if (!node.value) {
return typeManifest({
...manifest,
value: visitorsCore.pipe(
manifest.value,
(f) => renderersCore.setFragmentContent(f, `${enumFunction}('${variantName}')`),
(f) => addFragmentImports(f, importFrom, [enumFunction])
)
});
}
return typeManifest({
...manifest,
value: visitorsCore.pipe(
visitorsCore.visit(node.value, self).value,
(f) => renderersCore.mapFragmentContent(f, (c) => `${enumFunction}('${variantName}', ${c})`),
(f) => addFragmentImports(f, importFrom, [enumFunction])
)
});
},
visitEvent(event, { self }) {
const eventDataName = nameApi.eventDataType(event.name);
parentName = {
loose: nameApi.dataArgsType(eventDataName),
strict: nameApi.dataType(eventDataName)
};
const manifest = visitorsCore.visit(event.data, self);
parentName = null;
return manifest;
},
visitFixedSizeType(node, { self }) {
const manifest = visitorsCore.visit(node.type, self);
return typeManifest({
...manifest,
decoder: fragment`${use("fixDecoderSize", "solanaCodecsCore")}(${manifest.decoder}, ${node.size})`,
encoder: fragment`${use("fixEncoderSize", "solanaCodecsCore")}(${manifest.encoder}, ${node.size})`
});
},
visitHiddenPrefixType(node, { self }) {
const manifest = visitorsCore.visit(node.type, self);
const prefixes = (node.prefix ?? []).map((c) => visitorsCore.visit(c, self).value);
const prefixEncoders = visitorsCore.pipe(
mergeFragments(prefixes, (cs) => cs.map((c) => `getConstantEncoder(${c})`).join(", ")),
(f) => addFragmentImports(f, "solanaCodecsCore", ["getConstantEncoder"])
);
const prefixDecoders = visitorsCore.pipe(
mergeFragments(prefixes, (cs) => cs.map((c) => `getConstantDecoder(${c})`).join(", ")),
(f) => addFragmentImports(f, "solanaCodecsCore", ["getConstantDecoder"])
);
return typeManifest({
...manifest,
decoder: fragment`${use("getHiddenPrefixDecoder", "solanaCodecsDataStructures")}(${manifest.decoder}, [${prefixDecoders}])`,
encoder: fragment`${use("getHiddenPrefixEncoder", "solanaCodecsDataStructures")}(${manifest.encoder}, [${prefixEncoders}])`
});
},
visitHiddenSuffixType(node, { self }) {
const manifest = visitorsCore.visit(node.type, self);
const suffixes = (node.suffix ?? []).map((c) => visitorsCore.visit(c, self).value);
const suffixEncoders = visitorsCore.pipe(
mergeFragments(suffixes, (cs) => cs.map((c) => `getConstantEncoder(${c})`).join(", ")),
(f) => addFragmentImports(f, "solanaCodecsCore", ["getConstantEncoder"])
);
const suffixDecoders = visitorsCore.pipe(
mergeFragments(suffixes, (cs) => cs.map((c) => `getConstantDecoder(${c})`).join(", ")),
(f) => addFragmentImports(f, "solanaCodecsCore", ["getConstantDecoder"])
);
return typeManifest({
...manifest,
decoder: fragment`${use("getHiddenSuffixDecoder", "solanaCodecsDataStructures")}(${manifest.decoder}, [${suffixDecoders}])`,
encoder: fragment`${use("getHiddenSuffixEncoder", "solanaCodecsDataStructures")}(${manifest.encoder}, [${suffixEncoders}])`
});
},
visitInstruction(instruction, { self }) {
const instructionDataName = nameApi.instructionDataType(instruction.name);
parentName = {
loose: nameApi.dataArgsType(instructionDataName),
strict: nameApi.dataType(instructionDataName)
};
const link = customInstructionData.get(instruction.name)?.linkNode;
const struct = nodes.structTypeNodeFromInstructionArgumentNodes(instruction.arguments ?? []);
const manifest = link ? visitorsCore.visit(link, self) : visitorsCore.visit(struct, self);
parentName = null;
return manifest;
},
visitMapEntryValue(node, { self }) {
return mergeTypeManifests([visitorsCore.visit(node.key, self), visitorsCore.visit(node.value, self)], {
mergeValues: (renders) => `[${renders.join(", ")}]`
});
},
visitMapType(mapType, { self }) {
const key = visitorsCore.visit(mapType.key, self);
const value = visitorsCore.visit(mapType.value, self);
const mergedManifest = mergeTypeManifests([key, value], {
mergeCodecs: ([k, v]) => `${k}, ${v}`,
mergeTypes: ([k, v]) => `Map<${k}, ${v}>`
});
const sizeManifest = getArrayLikeSizeOption(mapType.count, self);
const encoderOptions = sizeManifest.encoder ? fragment`, { ${sizeManifest.encoder} }` : "";
const decoderOptions = sizeManifest.decoder ? fragment`, { ${sizeManifest.decoder} }` : "";
return typeManifest({
...mergedManifest,
decoder: fragment`${use("getMapDecoder", "solanaCodecsDataStructures")}(${mergedManifest.decoder}${decoderOptions})`,
encoder: fragment`${use("getMapEncoder", "solanaCodecsDataStructures")}(${mergedManifest.encoder}${encoderOptions})`
});
},
visitMapValue(node, { self }) {
const entryFragments = (node.entries ?? []).map((entry) => visitorsCore.visit(entry, self));
return mergeTypeManifests(entryFragments, {
mergeValues: (renders) => `new Map([${renders.join(", ")}])`
});
},
visitNoneValue() {
return typeManifest({
value: fragment`${use("none", "solanaOptions")}()`
});
},
visitNumberType(numberType) {
const encoderFunction = use(nameApi.encoderFunction(numberType.format), "solanaCodecsNumbers");
const decoderFunction = use(nameApi.decoderFunction(numberType.format), "solanaCodecsNumbers");
const isBigNumber = ["u64", "u128", "i64", "i128"].includes(numberType.format);
const endianness = numberType.endian === "be" ? fragment`{ endian: ${use("Endian", "solanaCodecsNumbers")}.Big }` : "";
return typeManifest({
decoder: fragment`${decoderFunction}(${endianness})`,
encoder: fragment`${encoderFunction}(${endianness})`,
looseType: fragment`${isBigNumber ? "number | bigint" : "number"}`,
strictType: fragment`${isBigNumber ? "bigint" : "number"}`
});
},
visitNumberValue(node) {
return typeManifest({ value: fragment`${JSON.stringify(node.number)}` });
},
visitOptionType(optionType, { self }) {
const childManifest = visitorsCore.visit(optionType.item, self);
const encoderOptions = [];
const decoderOptions = [];
const optionPrefix = nodes.resolveNestedTypeNode(optionType.prefix);
if (optionPrefix.format !== "u8" || optionPrefix.endian !== "le") {
const prefixManifest = visitorsCore.visit(optionType.prefix, self);
encoderOptions.push(fragment`prefix: ${prefixManifest.encoder}`);
decoderOptions.push(fragment`prefix: ${prefixManifest.decoder}`);
}
if (optionType.fixed) {
encoderOptions.push(fragment`noneValue: "zeroes"`);
decoderOptions.push(fragment`noneValue: "zeroes"`);
}
const encoderOptionsFragment = mergeFragments(
encoderOptions,
(cs) => cs.length > 0 ? `, { ${cs.join(", ")} }` : ""
);
const decoderOptionsFragment = mergeFragments(
decoderOptions,
(cs) => cs.length > 0 ? `, { ${cs.join(", ")} }` : ""
);
return typeManifest({
...childManifest,
decoder: fragment`${use("getOptionDecoder", "solanaOptions")}(${childManifest.decoder}${decoderOptionsFragment})`,
encoder: fragment`${use("getOptionEncoder", "solanaOptions")}(${childManifest.encoder}${encoderOptionsFragment})`,
looseType: fragment`${use("type OptionOrNullable", "solanaOptions")}<${childManifest.looseType}>`,
strictType: fragment`${use("type Option", "solanaOptions")}<${childManifest.strictType}>`
});
},
visitPostOffsetType(node, { self }) {
const manifest = visitorsCore.visit(node.type, self);
if (node.strategy === "padded") {
return typeManifest({
...manifest,
decoder: fragment`${use("padRightDecoder", "solanaCodecsCore")}(${manifest.decoder}, ${node.offset})`,
encoder: fragment`${use("padRightEncoder", "solanaCodecsCore")}(${manifest.encoder}, ${node.offset})`
});
}
const fn = (() => {
switch (node.strategy) {
case "absolute":
return node.offset < 0 ? `({ wrapBytes }) => wrapBytes(${node.offset})` : `() => ${node.offset}`;
case "preOffset":
return node.offset < 0 ? `({ preOffset }) => preOffset ${node.offset}` : `({ preOffset }) => preOffset + ${node.offset}`;
case "relative":
default:
return node.offset < 0 ? `({ postOffset }) => postOffset ${node.offset}` : `({ postOffset }) => postOffset + ${node.offset}`;
}
})();
return typeManifest({
...manifest,
decoder: fragment`${use("offsetDecoder", "solanaCodecsCore")}(${manifest.decoder}, { postOffset: ${fn} })`,
encoder: fragment`${use("offsetEncoder", "solanaCodecsCore")}(${manifest.encoder}, { postOffset: ${fn} })`
});
},
visitPreOffsetType(node, { self }) {
const manifest = visitorsCore.visit(node.type, self);
if (node.strategy === "padded") {
return typeManifest({
...manifest,
decoder: fragment`${use("padLeftDecoder", "solanaCodecsCore")}(${manifest.decoder}, ${node.offset})`,
encoder: fragment`${use("padLeftEncoder", "solanaCodecsCore")}(${manifest.encoder}, ${node.offset})`
});
}
const fn = (() => {
switch (node.strategy) {
case "absolute":
return node.offset < 0 ? `({ wrapBytes }) => wrapBytes(${node.offset})` : `() => ${node.offset}`;
case "relative":
default:
return node.offset < 0 ? `({ preOffset }) => preOffset ${node.offset}` : `({ preOffset }) => preOffset + ${node.offset}`;
}
})();
return typeManifest({
...manifest,
decoder: fragment`${use("offsetDecoder", "solanaCodecsCore")}(${manifest.decoder}, { preOffset: ${fn} })`,
encoder: fragment`${use("offsetEncoder", "solanaCodecsCore")}(${manifest.encoder}, { preOffset: ${fn} })`
});
},
visitPublicKeyType() {
return typeManifest({
decoder: fragment`${use("getAddressDecoder", "solanaAddresses")}()`,
encoder: fragment`${use("getAddressEncoder", "solanaAddresses")}()`,
looseType: use("type Address", "solanaAddresses"),
strictType: use("type Address", "solanaAddresses")
});
},
visitPublicKeyValue(node) {
return typeManifest({
value: fragment`${use("address", "solanaAddresses")}("${node.publicKey}")`
});
},
visitRemainderOptionType(node, { self }) {
const childManifest = visitorsCore.visit(node.item, self);
const encoderOptions = ["prefix: null"];
const decoderOptions = ["prefix: null"];
const encoderOptionsAsString = encoderOptions.length > 0 ? `, { ${encoderOptions.join(", ")} }` : "";
const decoderOptionsAsString = decoderOptions.length > 0 ? `, { ${decoderOptions.join(", ")} }` : "";
return typeManifest({
...childManifest,
decoder: fragment`${use("getOptionDecoder", "solanaOptions")}(${childManifest.decoder}${decoderOptionsAsString})`,
encoder: fragment`${use("getOptionEncoder", "solanaOptions")}(${childManifest.encoder}${encoderOptionsAsString})`,
looseType: fragment`${use("type OptionOrNullable", "solanaOptions")}<${childManifest.looseType}>`,
strictType: fragment`${use("type Option", "solanaOptions")}<${childManifest.strictType}>`
});
},
visitSentinelType(node, { self }) {
const manifest = visitorsCore.visit(node.type, self);
const sentinel = visitorsCore.visit(node.sentinel, self).value;
return typeManifest({
...manifest,
decoder: fragment`${use("addDecoderSentinel", "solanaCodecsCore")}(${manifest.decoder}, ${sentinel})`,
encoder: fragment`${use("addEncoderSentinel", "solanaCodecsCore")}(${manifest.encoder}, ${sentinel})`
});
},
visitSetType(setType, { self }) {
const childManifest = visitorsCore.visit(setType.item, self);
const sizeManifest = getArrayLikeSizeOption(setType.count, self);
const encoderOptions = sizeManifest.encoder ? fragment`, { ${sizeManifest.encoder} }` : "";
const decoderOptions = sizeManifest.decoder ? fragment`, { ${sizeManifest.decoder} }` : "";
return typeManifest({
...childManifest,
decoder: fragment`${use("getSetDecoder", "solanaCodecsDataStructures")}(${childManifest.decoder}${decoderOptions})`,
encoder: fragment`${use("getSetEncoder", "solanaCodecsDataStructures")}(${childManifest.encoder}${encoderOptions})`,
looseType: fragment`Set<${childManifest.looseType}>`,
strictType: fragment`Set<${childManifest.strictType}>`
});
},
visitSetValue(node, { self }) {
return mergeTypeManifests(
(node.items ?? []).map((v) => visitorsCore.visit(v, self)),
{ mergeValues: (renders) => `new Set([${renders.join(", ")}])` }
);
},
visitSizePrefixType(node, { self }) {
const manifest = visitorsCore.visit(node.type, self);
const prefix = visitorsCore.visit(node.prefix, self);
return typeManifest({
...manifest,
decoder: fragment`${use("addDecoderSizePrefix", "solanaCodecsCore")}(${manifest.decoder}, ${prefix.decoder})`,
encoder: fragment`${use("addEncoderSizePrefix", "solanaCodecsCore")}(${manifest.encoder}, ${prefix.encoder})`
});
},
visitSolAmountType({ number }, { self }) {
const numberManifest = visitorsCore.visit(number, self);
return typeManifest({
...numberManifest,
decoder: fragment`${use("getLamportsDecoder", "solanaRpcTypes")}(${numberManifest.decoder})`,
encoder: fragment`${use("getLamportsEncoder", "solanaRpcTypes")}(${numberManifest.encoder})`,
looseType: use("type Lamports", "solanaRpcTypes"),
strictType: use("type Lamports", "solanaRpcTypes")
});
},
visitSomeValue(node, { self }) {
const innerValue = visitorsCore.visit(node.value, self).value;
return typeManifest({
value: fragment`${use("some", "solanaOptions")}(${innerValue})`
});
},
visitStringType(stringType) {
const [encoder, decoder] = (() => {
switch (stringType.encoding) {
case "base16":
return ["getBase16Encoder", "getBase16Decoder"];
case "base58":
return ["getBase58Encoder", "getBase58Decoder"];
case "base64":
return ["getBase64Encoder", "getBase64Decoder"];
case "utf8":
return ["getUtf8Encoder", "getUtf8Decoder"];
default:
throw new Error(`Unsupported string encoding: ${stringType.encoding}`);
}
})();
return typeManifest({
decoder: fragment`${use(decoder, "solanaCodecsStrings")}()`,
encoder: fragment`${use(encoder, "solanaCodecsStrings")}()`,
looseType: fragment`string`,
strictType: fragment`string`
});
},
visitStringValue(node) {
return typeManifest({
value: fragment`${JSON.stringify(node.string)}`
});
},
visitStructFieldType(structFieldType, { self }) {
const name = nodes.camelCase(structFieldType.name);
const originalChildManifest = visitorsCore.visit(structFieldType.type, self);
let docs = getDocblockFragment(structFieldType.docs ?? [], true);
docs = docs ? fragment`\n${docs}` : docs;
const childManifest = typeManifest({
...originalChildManifest,
decoder: fragment`['${name}', ${originalChildManifest.decoder}]`,
encoder: fragment`['${name}', ${originalChildManifest.encoder}]`,
looseType: fragment`${docs}${name}: ${originalChildManifest.looseType}; `,
strictType: fragment`${docs}${name}: ${originalChildManifest.strictType}; `
});
if (!structFieldType.defaultValue) {
return childManifest;
}
if (structFieldType.defaultValueStrategy !== "omitted") {
return typeManifest({
...childManifest,
looseType: fragment`${docs}${name}?: ${originalChildManifest.looseType}; `
});
}
return typeManifest({ ...childManifest, looseType: fragment`` });
},
visitStructFieldValue(node, { self }) {
const innerValue = visitorsCore.visit(node.value, self).value;
return typeManifest({
value: fragment`${node.name}: ${innerValue}`
});
},
visitStructType(structType, { self }) {
const optionalFields = (structType.fields ?? []).filter((f) => !!f.defaultValue);
const mergedManifest = visitorsCore.pipe(
mergeTypeManifests(
(structType.fields ?? []).map((field) => visitorsCore.visit(field, self)),
{
mergeCodecs: (renders) => `([${renders.join(", ")}])`,
mergeTypes: (renders) => `{ ${renders.join("")} }`
}
),
(manifest) => typeManifest({
...manifest,
decoder: fragment`${use("getStructDecoder", "solanaCodecsDataStructures")}${manifest.decoder}`,
encoder: fragment`${use("getStructEncoder", "solanaCodecsDataStructures")}${manifest.encoder}`
})
);
if (optionalFields.length === 0) {
return mergedManifest;
}
const parentPath = stack.getPath();
const instructionNode = visitorsCore.findLastNodeFromPath(parentPath, "instructionNode");
const accountNode = visitorsCore.findLastNodeFromPath(parentPath, "accountNode");
const eventNode = visitorsCore.findLastNodeFromPath(parentPath, "eventNode");
const discriminatorParent = instructionNode ?? accountNode ?? eventNode;
const discriminatorPrefix = discriminatorParent && nodes.isNode(discriminatorParent, "eventNode") ? getEventDiscriminatorPrefix(nameApi, discriminatorParent.name) : discriminatorParent?.name;
const discriminators = discriminatorParent?.discriminators ?? [];
const fieldDiscriminators = discriminators.filter(nodes.isNodeFilter("fieldDiscriminatorNode"));
const defaultValues = mergeFragments(
optionalFields.map((f) => {
const key = nodes.camelCase(f.name);
if (fieldDiscriminators.some((d) => d.name === f.name)) {
const constantName = nameApi.constant(nodes.camelCase(`${discriminatorPrefix}_${f.name}`));
return f.defaultValueStrategy === "omitted" ? fragment`${key}: ${constantName}` : fragment`${key}: value.${key} ?? ${constantName}`;
}
const defaultValue = f.defaultValue;
const value = visitorsCore.visit(defaultValue, self).value;
return f.defaultValueStrategy === "omitted" ? fragment`${key}: ${value}` : fragment`${key}: value.${key} ?? ${value}`;
}),
(cs) => cs.join(", ")
);
return typeManifest({
...mergedManifest,
encoder: fragment`${use("transformEncoder", "solanaCodecsCore")}(${mergedManifest.encoder}, (value) => ({ ...value, ${defaultValues} }))`
});
},
visitStructValue(node, { self }) {
return mergeTypeManifests(
(node.fields ?? []).map((field) => visitorsCore.visit(field, self)),
{ mergeValues: (renders) => `{ ${renders.join(", ")} }` }
);
},
visitTupleType(tupleType, { self }) {
const items = (tupleType.items ?? []).map((item) => visitorsCore.visit(item, self));
const mergedManifest = mergeTypeManifests(items, {
mergeCodecs: (codecs) => `[${codecs.join(", ")}]`,
mergeTypes: (types) => `readonly [${types.join(", ")}]`
});
return typeManifest({
...mergedManifest,
decoder: fragment`${use("getTupleDecoder", "solanaCodecsDataStructures")}(${mergedManifest.decoder})`,
encoder: fragment`${use("getTupleEncoder", "solanaCodecsDataStructures")}(${mergedManifest.encoder})`
});
},
visitTupleValue(node, { self }) {
return mergeTypeManifests(
(node.items ?? []).map((v) => visitorsCore.visit(v, self)),
{ mergeValues: (renders) => `[${renders.join(", ")}]` }
);
},
visitZeroableOptionType(node, { self }) {
const childManifest = visitorsCore.visit(node.item, self);
const encoderOptions = [fragment`prefix: null`];
const decoderOptions = [fragment`prefix: null`];
if (node.zeroValue) {
const zeroValueManifest = visitorsCore.visit(node.zeroValue, self);
encoderOptions.push(fragment`noneValue: ${zeroValueManifest.value}`);
decoderOptions.push(fragment`noneValue: ${zeroValueManifest.value}`);
} else {
encoderOptions.push(fragment`noneValue: "zeroes"`);
decoderOptions.push(fragment`noneValue: "zeroes"`);
}
const encoderOptionsFragment = mergeFragments(
encoderOptions,
(cs) => cs.length > 0 ? `, { ${cs.join(", ")} }` : ""
);
const decoderOptionsFragment = mergeFragments(
decoderOptions,
(cs) => cs.length > 0 ? `, { ${cs.join(", ")} }` : ""
);
return typeManifest({
...childManifest,
decoder: fragment`${use("getOptionDecoder", "solanaOptions")}(${childManifest.decoder}${decoderOptionsFragment})`,
encoder: fragment`${use("getOptionEncoder", "solanaOptions")}(${childManifest.encoder}${encoderOptionsFragment})`,
looseType: fragment`${use("type OptionOrNullable", "solanaOptions")}<${childManifest.looseType}>`,
strictType: fragment`${use("type Option", "solanaOptions")}<${childManifest.strictType}>`
});
}
}),
(visitor) => visitorsCore.recordNodeStackVisitor(visitor, stack)
);
}
function getArrayLikeSizeOption(count, visitor) {
if (nodes.isNode(count, "fixedCountNode")) {
return {
decoder: fragment`size: ${count.value}`,
encoder: fragment`size: ${count.value}`
};
}
if (nodes.isNode(count, "remainderCountNode")) {
return {
decoder: fragment`size: 'remainder'`,
encoder: fragment`size: 'remainder'`
};
}
const prefix = nodes.resolveNestedTypeNode(count.prefix);
if (prefix.format === "u32" && prefix.endian === "le") {
return { decoder: void 0, encoder: void 0 };
}
const prefixManifest = visitorsCore.visit(count.prefix, visitor);
return {
decoder: visitorsCore.pipe(prefixManifest.decoder, (f) => renderersCore.mapFragmentContent(f, (c) => `size: ${c}`)),
encoder: visitorsCore.pipe(prefixManifest.encoder, (f) => renderersCore.mapFragmentContent(f, (c) => `size: ${c}`))
};
}
// src/visitors/getRenderMapVisitor.ts
function getRenderMapVisitor(options = {}) {
const linkables = new visitorsCore.LinkableDictionary();
const stack = new visitorsCore.NodeStack();
const customAccountData = parseCustomDataOptions(options.customAccountData ?? [], "AccountData");
const customInstructionData = parseCustomDataOptions(options.customInstructionData ?? [], "InstructionData");
const renderScopeWithTypeManifestVisitor = {
asyncResolvers: (options.asyncResolvers ?? []).map(nodes.camelCase),
customAccountData,
customInstructionData,
dependencyMap: options.dependencyMap ?? {},
dependencyVersions: options.dependencyVersions ?? {},
erasableSyntax: options.erasableSyntax ?? false,
getImportFrom: getImportFromFactory(options.linkOverrides ?? {}, customAccountData, customInstructionData),
getImportPath: getImportPathFactory(options.importExtension),
kitImportStrategy: options.kitImportStrategy ?? DEFAULT_KIT_IMPORT_STRATEGY,
linkables,
nameApi: getNameApi({ ...DEFAULT_NAME_TRANSFORMERS, ...options.nameTransformers }),
nonScalarEnums: (options.nonScalarEnums ?? []).map(nodes.camelCase),
renderParentInstructions: options.renderParentInstructions ?? false
};
const typeManifestVisitor = getTypeManifestVisitor({ ...renderScopeWithTypeManifestVisitor, stack });
const renderScope = { ...renderScopeWithTypeManifestVisitor, typeManifestVisitor };
const internalNodes = (options.internalNodes ?? []).map(nodes.camelCase);
const isNotInternal = (node) => !internalNodes.includes(node.name);
const resolvedInstructionInputVisitor = visitorsCore.getResolvedInstructionInputsVisitor();
const byteSizeVisitor = visitorsCore.getByteSizeVisitor(linkables, { stack });
const asPage = (fragment2, pageDependencyMap = {}) => {
if (!fragment2) return void 0;
return getPageFragment(fragment2, {
...renderScope,
dependencyMap: { ...renderScope.dependencyMap, ...pageDependencyMap }
});
};
return visitorsCore.pipe(
visitorsCore.staticVisitor(() => renderersCore.createRenderMap(), {
keys: [
"rootNode",
"programNode",
"pdaNode",
"accountNode",
"definedTypeNode",
"eventNode",
"instructionNode"
]
}),
(v) => visitorsCore.extendVisitor(v, {
visitAccount(node) {
return renderersCore.createRenderMap(
`accounts/${nodes.camelCase(node.name)}.ts`,
asPage(
getAccountPageFragment({
...renderScope,
accountPath: stack.getPath("accountNode"),
size: visitorsCore.visit(node, byteSizeVisitor)
})
)
);
},
visitDefinedType(node) {
return renderersCore.createRenderMap(
`types/${nodes.camelCase(node.name)}.ts`,
asPage(getTypePageFragment({ ...renderScope, node, size: visitorsCore.visit(node, byteSizeVisitor) }), {
generatedTypes: renderScope.getImportPath(".", "directory")
})
);
},
visitEvent(node) {
return renderersCore.createRenderMap(
`events/${nodes.camelCase(node.name)}.ts`,
asPage(
getEventPageFragment({
...renderScope,
eventPath: stack.getPath("eventNode"),
size: visitorsCore.visit(node.data, byteSizeVisitor)
})
)
);
},
visitInstruction(node) {
return renderersCore.createRenderMap(
`instructions/${nodes.camelCase(node.name)}.ts`,
asPage(
getInstructionPageFragment({
...renderScope,
instructionPath: stack.getPath("instructionNode"),
resolvedInputs: visitorsCore.visit(node, resolvedInstructionInputVisitor),
size: visitorsCore.visit(node, byteSizeVisitor)
})
)
);
},
visitPda(node) {
return renderersCore.createRenderMap(
`pdas/${nodes.camelCase(node.name)}.ts`,
asPage(getPdaPageFragment({ ...renderScope, pdaPath: stack.getPath("pdaNode") }))
);
},
visitProgram(node, { self }) {
const constantsToExport = (node.constants ?? []).filter(isNotInternal);
const customDataDefinedType = [
...getDefinedTypeNodesToExtract(node.accounts ?? [], customAccountData),
...getDefinedTypeNodesToExtract(node.instructions ?? [], customInstructionData)
];
const scope = { ...renderScope, programNode: node };
return renderersCore.mergeRenderMaps([
renderersCore.createRenderMap({
[`constants/${nodes.camelCase(node.name)}.ts`]: constantsToExport.length > 0 ? asPage(
getConstantsPageFragment({
...renderScope,
nodes: constantsToExport,
programPath: stack.getPath("programNode")
}),
{ generatedTypes: renderScope.getImportPath("../types", "directory") }
) : void 0,
[`programs/${nodes.camelCase(node.name)}.ts`]: asPage(getProgramPageFragment(scope)),
[`errors/${nodes.camelCase(node.name)}.ts`]: (node.errors ?? []).length > 0 ? asPage(getErrorPageFragment(scope)) : void 0
}),
...(node.pdas ?? []).map((p) => visitorsCore.visit(p, self)),
...(node.accounts ?? []).map((a) => visitorsCore.visit(a, self)),
...(node.events ?? []).map((e) => visitorsCore.visit(e, self)),
...(node.definedTypes ?? []).map((t) => visitorsCore.visit(t, self)),
...customDataDefinedType.map((t) => visitorsCore.visit(t, self)),
...nodes.getAllInstructionsWithSubs(node, { leavesOnly: !renderScope.renderParentInstructions }).map(
(i) => visitorsCore.visit(i, self)
)
]);
},
visitRoot(node, { self }) {
const programsToExport = nodes.getAllPrograms(node).filter(isNotInternal);
const programsWithConstantsToExport = programsToExport.filter(
(program) => (program.constants ?? []).some(isNotInternal)
);
const programsWithErrorsToExport = programsToExport.filter((p) => (p.errors ?? []).length > 0);
const pdasToExport = nodes.getAllPdas(node);
const accountsToExport = nodes.getAllAccounts(node).filter(isNotInternal);
const eventsToExport = nodes.getAllEvents(node).filter(isNotInternal);
const instructionsToExport = nodes.getAllInstructionsWithSubs(node, {
leavesOnly: !renderScope.renderParentInstructions
}).filter(isNotInternal);
const definedTypesToExport = nodes.getAllDefinedTypes(node).filter(isNotInternal);
const scope = {
...renderScope,
accountsToExport,
definedTypesToExport,
eventsToExport,
instructionsToExport,
pdasToExport,
programsToExport,
programsWithConstantsToExport
};
return renderersCore.mergeRenderMaps([
renderersCore.createRenderMap({
["accounts/index.ts"]: asPage(getIndexPageFragment(accountsToExport, renderScope)),
["constants/index.ts"]: asPage(
getIndexPageFragment(programsWithConstantsToExport, renderScope)
),
["errors/index.ts"]: asPage(getIndexPageFragment(programsWithErrorsToExport, renderScope)),
["events/index.ts"]: asPage(getIndexPageFragment(eventsToExport, renderScope)),
["index.ts"]: asPage(getRootIndexPageFragment(scope)),
["instructions/index.ts"]: asPage(getIndexPageFragment(instructionsToExport, renderScope)),
["pdas/index.ts"]: asPage(getIndexPageFragment(pdasToExport, renderScope)),
["programs/index.ts"]: asPage(getIndexPageFragment(programsToExport, renderScope)),
["types/index.ts"]: asPage(getIndexPageFragment(definedTypesToExport, renderScope))
}),
...nodes.getAllPrograms(node).map((p) => visitorsCore.visit(p, self))
]);
}
}),
(v) => visitorsCore.recordNodeStackVisitor(v, stack),
(v) => visitorsCore.recordLinkablesOnFirstVisitVisitor(v, linkables)
);
}
function renderVisitor(packageFolder, options = {}) {
return visitorsCore.rootNodeVisitor(async (root) => {
const generatedFolder = renderersCore.joinPath(packageFolder, options.generatedFolder ?? "src/generated");
if (options.deleteFolderBeforeRendering ?? true) {
renderersCore.deleteDirectory(generatedFolder);
}
let renderMap = visitorsCore.visit(root, getRenderMapVisitor(options));
const formatCode = await getCodeFormatter(packageFolder, options);
renderMap = await renderersCore.mapRenderMapContentAsync(renderMap, formatCode);
await syncPackageJson(renderMap, formatCode, packageFolder, options);
renderersCore.writeRenderMap(renderMap, generatedFolder);
});
}
exports.DEFAULT_KIT_IMPORT_STRATEGY = DEFAULT_KIT_IMPORT_STRATEGY;
exports.DEFAULT_NAME_TRANSFORMERS = DEFAULT_NAME_TRANSFORMERS;
exports.addToImportMap = addToImportMap;
exports.createImportMap = createImportMap;
exports.default = renderVisitor;
exports.getExternalDependencies = getExternalDependencies;
exports.getImportPathFactory = getImportPathFactory;
exports.getNameApi = getNameApi;
exports.getRenderMapVisitor = getRenderMapVisitor;
exports.getTypeManifestVisitor = getTypeManifestVisitor;
exports.importMapToString = importMapToString;
exports.mergeImportMaps = mergeImportMaps;
exports.mergeTypeManifests = mergeTypeManifests;
exports.parseImportInput = parseImportInput;
exports.removeFromImportMap = removeFromImportMap;
exports.renderVisitor = renderVisitor;
exports.typeManifest = typeManifest;
//# sourceMappingURL=index.browser.cjs.map
//# sourceMappingURL=index.browser.cjs.map