UNPKG

@codama/renderers-js

Version:

JavaScript renderer compatible with the Solana Kit library

3,739 lines 183 kB
import { titleCase, snakeCase, pascalCase, kebabCase, capitalize, camelCase, REGISTERED_TYPE_NODE_KINDS, REGISTERED_VALUE_NODE_KINDS, isNode, isNodeFilter, resolveNestedTypeNode, structTypeNodeFromInstructionArgumentNodes, isScalarEnum, structTypeNode, structFieldTypeNode, getAllPrograms, getAllPdas, getAllAccounts, getAllEvents, getAllInstructionsWithSubs, getAllDefinedTypes, definedTypeLinkNode, definedTypeNode, isDataEnum, accountValueNode, argumentValueNode, parseOptionalAccountStrategy, VALUE_NODES, getAllInstructionArguments, constantDiscriminatorNode, constantValueNodeFromBytes, constantValueNode, assertIsNode } from '@codama/nodes';
import { NodeStack, pipe, staticVisitor, extendVisitor, visit, findLastNodeFromPath, recordNodeStackVisitor, LinkableDictionary, getResolvedInstructionInputsVisitor, getByteSizeVisitor, recordLinkablesOnFirstVisitVisitor, rootNodeVisitor, findProgramNodeFromPath, getLastNodeFromPath, findInstructionNodeFromPath, deduplicateInstructionDependencies } from '@codama/visitors-core';
import { getBase64Encoder, getBase58Encoder, getBase16Encoder, getUtf8Encoder, getBase64Decoder } from '@solana/codecs-strings';
import { setFragmentContent, mapFragmentContent, createRenderMap, mergeRenderMaps, joinPath, deleteDirectory, mapRenderMapContentAsync, writeRenderMap, createFragmentTemplate, fileExists, readJson, writeFile } from '@codama/renderers-core';
import { resolveConfig } from 'prettier';
import * as babelPlugin from 'prettier/plugins/babel';
import * as estreePlugin from 'prettier/plugins/estree';
import * as typeScriptPlugin from 'prettier/plugins/typescript';
import { format } from 'prettier/standalone';
import { CodamaError, CODAMA_ERROR__UNEXPECTED_NODE_KIND, logWarn, CODAMA_ERROR__RENDERERS__MISSING_DEPENDENCY_VERSIONS } from '@codama/errors';
import { subset, minVersion, lt } from 'semver';

// src/utils/async.ts
function hasAsyncFunction(instructionNode, resolvedInputs, asyncResolvers) {
  const hasByteDeltasAsync = (instructionNode.byteDeltas ?? []).some(
    ({ value }) => isNode(value, "resolverValueNode") && asyncResolvers.includes(value.name)
  );
  const hasRemainingAccountsAsync = (instructionNode.remainingAccounts ?? []).some(
    ({ value }) => 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 (isNode(input, "instructionNode")) {
    return 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 (isNode(input.defaultValue, ["accountValueNode", "accountBumpValueNode"])) {
    return [accountValueNode(input.defaultValue.name)];
  }
  if (isNode(input.defaultValue, ["argumentValueNode"])) {
    return [argumentValueNode(input.defaultValue.name)];
  }
  if (isNode(input.defaultValue, "pdaValueNode")) {
    const dependencies = /* @__PURE__ */ new Map();
    (input.defaultValue.seeds ?? []).forEach((seed) => {
      if (isNode(seed.value, ["accountValueNode", "argumentValueNode"])) {
        dependencies.set(seed.value.name, { ...seed.value });
      }
    });
    return [...dependencies.values()];
  }
  if (isNode(input.defaultValue, "resolverValueNode")) {
    const isSynchronousResolver = !asyncResolvers.includes(input.defaultValue.name);
    if (useAsync || isSynchronousResolver) {
      return input.defaultValue.dependsOn ?? [];
    }
  }
  if (isNode(input.defaultValue, "conditionalValueNode")) {
    return deduplicateInstructionDependencies([
      ...getNestedDependencies(input.defaultValue.condition),
      ...getNestedDependencies(input.defaultValue.ifTrue),
      ...getNestedDependencies(input.defaultValue.ifFalse)
    ]);
  }
  return [];
}
function getBytesFromBytesValueNode(node) {
  switch (node.encoding) {
    case "utf8":
      return getUtf8Encoder().encode(node.data);
    case "base16":
      return getBase16Encoder().encode(node.data);
    case "base58":
      return getBase58Encoder().encode(node.data);
    case "base64":
    default:
      return getBase64Encoder().encode(node.data);
  }
}
var parseCustomDataOptions = (customDataOptions, defaultSuffix) => new Map(
  customDataOptions.map((o) => {
    const options = typeof o === "string" ? { name: o } : o;
    const importAs = camelCase(options.importAs ?? `${options.name}${defaultSuffix}`);
    const importFrom = options.importFrom ?? "hooked";
    return [
      camelCase(options.name),
      {
        extract: options.extract ?? false,
        extractAs: options.extractAs ? camelCase(options.extractAs) : importAs,
        importAs,
        importFrom,
        linkNode: definedTypeLinkNode(importAs)
      }
    ];
  })
);
var getDefinedTypeNodesToExtract = (nodes, parsedCustomDataOptions) => nodes.flatMap((node) => {
  const options = parsedCustomDataOptions.get(node.name);
  if (!options || !options.extract) return [];
  if (isNode(node, "accountNode")) {
    return [definedTypeNode({ name: options.extractAs, type: { ...node.data } })];
  }
  return [
    definedTypeNode({
      name: options.extractAs,
      type: 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, typeScriptPlugin, babelPlugin]
};
async function getCodeFormatter(packageFolder, options) {
  const shouldFormatCode = options.formatCode ?? true;
  if (!shouldFormatCode) return (code) => Promise.resolve(code);
  const prettierOptions = {
    ...DEFAULT_PRETTIER_OPTIONS,
    ...await resolvePrettierOptions(packageFolder),
    ...options.prettierOptions
  };
  return (code, filepath) => format(code, { ...prettierOptions, filepath });
}
async function resolvePrettierOptions(packageFolder) {
  if (!packageFolder) return null;
  const filePathToResolve = joinPath(packageFolder, "package.json");
  return await resolveConfig(filePathToResolve);
}
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 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 CodamaError(CODAMA_ERROR__UNEXPECTED_NODE_KIND, {
          expectedKinds: [
            "accountLinkNode",
            "definedTypeLinkNode",
            "instructionLinkNode",
            "pdaLinkNode",
            "programLinkNode",
            "resolverValueNode"
          ],
          kind,
          node
        });
    }
  };
}
function getNameApi(transformers) {
  const helpers = {
    camelCase: camelCase,
    capitalize,
    kebabCase,
    pascalCase,
    snakeCase,
    titleCase
  };
  return Object.fromEntries(
    Object.entries(transformers).map(([key, transformer]) => [key, (name) => transformer(name, helpers)])
  );
}
var DEFAULT_NAME_TRANSFORMERS = {
  accountDecodeFunction: (name) => `decode${pascalCase(name)}`,
  accountFetchAllFunction: (name) => `fetchAll${pascalCase(name)}`,
  accountFetchAllMaybeFunction: (name) => `fetchAllMaybe${pascalCase(name)}`,
  accountFetchFromSeedsFunction: (name) => `fetch${pascalCase(name)}FromSeeds`,
  accountFetchFunction: (name) => `fetch${pascalCase(name)}`,
  accountFetchMaybeFromSeedsFunction: (name) => `fetchMaybe${pascalCase(name)}FromSeeds`,
  accountFetchMaybeFunction: (name) => `fetchMaybe${pascalCase(name)}`,
  accountGetSizeFunction: (name) => `get${pascalCase(name)}Size`,
  codecFunction: (name) => `get${pascalCase(name)}Codec`,
  constant: (name) => snakeCase(name).toUpperCase(),
  constantFunction: (name) => `get${pascalCase(name)}Bytes`,
  dataArgsType: (name) => `${pascalCase(name)}Args`,
  dataType: (name) => `${pascalCase(name)}`,
  decoderFunction: (name) => `get${pascalCase(name)}Decoder`,
  discriminatedUnionDiscriminator: () => "__kind",
  discriminatedUnionFunction: (name) => `${camelCase(name)}`,
  discriminatedUnionVariant: (name) => `${pascalCase(name)}`,
  encoderFunction: (name) => `get${pascalCase(name)}Encoder`,
  enumVariant: (name) => `${pascalCase(name)}`,
  eventDataType: (name) => `${pascalCase(name)}Event`,
  eventParseFunction: (name) => `parse${pascalCase(name)}Event`,
  instructionAsyncFunction: (name) => `get${pascalCase(name)}InstructionAsync`,
  instructionAsyncInputType: (name) => `${pascalCase(name)}AsyncInput`,
  instructionDataType: (name) => `${pascalCase(name)}InstructionData`,
  instructionExtraType: (name) => `${pascalCase(name)}InstructionExtra`,
  instructionParseFunction: (name) => `parse${pascalCase(name)}Instruction`,
  instructionParsedType: (name) => `Parsed${pascalCase(name)}Instruction`,
  instructionSyncFunction: (name) => `get${pascalCase(name)}Instruction`,
  instructionSyncInputType: (name) => `${pascalCase(name)}Input`,
  instructionType: (name) => `${pascalCase(name)}Instruction`,
  isDiscriminatedUnionFunction: (name) => `is${pascalCase(name)}`,
  pdaFindFunction: (name) => `find${pascalCase(name)}Pda`,
  pdaSeedsType: (name) => `${pascalCase(name)}Seeds`,
  programAccountsEnum: (name) => `${pascalCase(name)}Account`,
  programAccountsEnumVariant: (name) => `${pascalCase(name)}`,
  programAccountsIdentifierFunction: (name) => `identify${pascalCase(name)}Account`,
  programAddressConstant: (name) => `${snakeCase(name).toUpperCase()}_PROGRAM_ADDRESS`,
  programErrorConstant: (name) => snakeCase(name).toUpperCase(),
  programErrorConstantPrefix: (name) => `${snakeCase(name).toUpperCase()}_ERROR__`,
  programErrorMessagesMap: (name) => `${camelCase(name)}ErrorMessages`,
  programErrorUnion: (name) => `${pascalCase(name)}Error`,
  programEventsEnum: (name) => `${pascalCase(name)}Event`,
  programEventsEnumVariant: (name) => `${pascalCase(name)}`,
  programEventsIdentifierFunction: (name) => `identify${pascalCase(name)}Event`,
  programGetErrorMessageFunction: (name) => `get${pascalCase(name)}ErrorMessage`,
  programInstructionsEnum: (name) => `${pascalCase(name)}Instruction`,
  programInstructionsEnumVariant: (name) => `${pascalCase(name)}`,
  programInstructionsIdentifierFunction: (name) => `identify${pascalCase(name)}Instruction`,
  programInstructionsParseFunction: (name) => `parse${pascalCase(name)}Instruction`,
  programInstructionsParsedUnionType: (name) => `Parsed${pascalCase(name)}Instruction`,
  programIsErrorFunction: (name) => `is${pascalCase(name)}Error`,
  programPluginAccountKey: (name) => `${camelCase(name)}`,
  programPluginAccountsType: (name) => `${pascalCase(name)}PluginAccounts`,
  programPluginFunction: (name) => `${camelCase(name)}Program`,
  programPluginInstructionKey: (name) => `${camelCase(name)}`,
  programPluginInstructionsType: (name) => `${pascalCase(name)}PluginInstructions`,
  programPluginKey: (name) => `${camelCase(name)}`,
  programPluginPdaKey: (name) => `${camelCase(name)}`,
  programPluginPdasType: (name) => `${pascalCase(name)}PluginPdas`,
  programPluginRequirementsType: (name) => `${pascalCase(name)}PluginRequirements`,
  programPluginType: (name) => `${pascalCase(name)}Plugin`,
  resolverFunction: (name) => `${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 = joinPath(packageFolder, "package.json");
  const usedDependencies = getUsedDependencyVersions(
    renderMap,
    options.dependencyMap ?? {},
    options.dependencyVersions ?? {},
    options.kitImportStrategy ?? DEFAULT_KIT_IMPORT_STRATEGY
  );
  if (!shouldSyncPackageJson) {
    if (fileExists(packageJsonPath)) {
      checkExistingPackageJson(readJson(packageJsonPath), usedDependencies);
    }
    return;
  }
  if (fileExists(packageJsonPath)) {
    const packageJson = updateExistingPackageJson(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("");
  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 CodamaError(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 (subset(currentRange, requiredRange)) {
      return false;
    }
    const minRequiredVersion = minVersion(requiredRange);
    const minCurrentVersion = minVersion(currentRange);
    if (!minCurrentVersion || !minRequiredVersion) {
      throw new Error("Could not determine minimum versions.");
    }
    if (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);
  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 = 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 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 = 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(isNodeFilter("variablePdaSeedNode")).length > 0;
  const fetchFromSeedsFunction = nameApi.accountFetchFromSeedsFunction(accountNode.name);
  const fetchMaybeFromSeedsFunction = nameApi.accountFetchMaybeFromSeedsFunction(accountNode.name);
  const fetchMaybeFunction = nameApi.accountFetchMaybeFunction(accountNode.name);
  return 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 = 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 = isNode(node, "enumTypeNode") && 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 = isNode(node, "enumTypeNode") && 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 = 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: 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 camelCase(`${prefix}_discriminator${suffix}`);
}
function getEventDiscriminatorPrefix(nameApi, eventName) {
  return camelCase(nameApi.eventDataType(eventName));
}
function getConstantDiscriminatorConstantFragment(discriminatorNode, scope) {
  const { discriminatorNodes, typeManifestVisitor, prefix } = scope;
  const index = discriminatorNodes.filter(isNodeFilter("constantDiscriminatorNode")).indexOf(discriminatorNode);
  const name = getConstantDiscriminatorName(prefix, index);
  const typeManifest2 = visit(discriminatorNode.constant.type, typeManifestVisitor);
  const encoder = typeManifest2.encoder;
  const { value, valueType } = resolveDiscriminatorValue(
    visit(discriminatorNode.constant.value, typeManifestVisitor).value,
    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 || !isNode(field.defaultValue, VALUE_NODES)) {
    return null;
  }
  const name = camelCase(`${prefix}_${discriminatorNode.name}`);
  const typeManifest2 = visit(field.type, typeManifestVisitor);
  const encoder = typeManifest2.encoder;
  const { value, valueType } = resolveDiscriminatorValue(
    visit(field.defaultValue, typeManifestVisitor).value,
    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 = getLastNodeFromPath(scope.accountPath);
  if (!findProgramNodeFromPath(scope.accountPath)) {
    throw new Error("Account must be visited inside a program.");
  }
  const typeManifest2 = visit(node, scope.typeManifestVisitor);
  const fields = 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 = visit(node.type, scope.typeManifestVisitor);
  const rawValue = visit(node.value, scope.typeManifestVisitor).value;
  const useBigInt = isNode(node.value, "numberValueNode") && resolvesToBigInt(node.type, scope);
  const value = useBigInt ? fragment`${rawValue}n` : rawValue;
  const valueType = 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 (visit(type, scope.typeManifestVisitor).strictType.content === "bigint") return true;
  if (!isNode(type, "definedTypeLinkNode")) return false;
  const linkedPath = scope.linkables.getPath([...path, type]);
  if (!linkedPath) return false;
  const linkedType = getLastNodeFromPath(linkedPath).type;
  return resolvesToBigInt(linkedType, scope, linkedPath, /* @__PURE__ */ new Set([...visited, type]));
}
function getDiscriminatorConditionFragment(scope) {
  return pipe(
    mergeFragments(
      scope.discriminators.flatMap((discriminator) => {
        if (isNode(discriminator, "sizeDiscriminatorNode")) {
          return [getSizeConditionFragment(discriminator, scope)];
        }
        if (isNode(discriminator, "constantDiscriminatorNode")) {
          return [getByteConditionFragment(discriminator, scope)];
        }
        if (isNode(discriminator, "fieldDiscriminatorNode")) {
          return [getFieldConditionFragment(discriminator, scope)];
        }
        return [];
      }),
      (c) => c.join(" && ")
    ),
    (f) => 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 = 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 = isNode(field.defaultValue, "arrayValueNode") ? field.defaultValue.items ?? [] : [];
  if (isNode(field.type, "arrayTypeNode") && isNode(field.type.item, "numberTypeNode") && field.type.item.format === "u8" && isNode(field.type.count, "fixedCountNode") && isNode(field.defaultValue, "arrayValueNode") && defaultValueItems.every(isNodeFilter("numberValueNode"))) {
    const base64Bytes = getBase64Decoder().decode(new Uint8Array(defaultValueItems.map((node) => node.number)));
    return getByteConditionFragment(
      constantDiscriminatorNode(constantValueNodeFromBytes("base64", base64Bytes), discriminator.offset),
      scope
    );
  }
  return getByteConditionFragment(
    constantDiscriminatorNode(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 = getLastNodeFromPath(scope.eventPath);
  if (!findProgramNodeFromPath(scope.eventPath)) {
    throw new Error("Event must be visited inside a program.");
  }
  const { nameApi } = scope;
  const typeManifest2 = visit(node, scope.typeManifestVisitor);
  const eventDataName = nameApi.eventDataType(node.name);
  const resolvedData = resolveNestedTypeNode(node.data);
  const fields = 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 = resolveNestedTypeNode(node.data);
  const condition = getDiscriminatorConditionFragment({
    ...scope,
    dataName: "data",
    discriminators,
    ifTrue: `return ${decoderFunction}().decode(data);`,
    programNode: findProgramNodeFromPath(scope.eventPath),
    struct: isNode(resolvedData, "structTypeNode") ? resolvedData : 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) => 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${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 = getLastNodeFromPath(instructionAccountPath);
  const instructionNode = findInstructionNodeFromPath(instructionAccountPath);
  const programNode = findProgramNodeFromPath(instructionAccountPath);
  const typeParam = `TAccount${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 } = 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 (isNode(byteDelta.value, "numberValueNode")) {
      return getNumberValueNodeFragment(byteDelta);
    }
    if (isNode(byteDelta.value, "argumentValueNode")) {
      return getArgumentValueNodeFragment(byteDelta);
    }
    if (isNode(byteDelta.value, "accountLinkNode")) {
      return getAccountLinkNodeFragment(byteDelta, scope);
    }
    if (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 = pipe(bytesFragment, (f) => mapFragmentContent(f, (c) => `- (${c})`));
  }
  return [bytesFragment];
}
function getNumberValueNodeFragment(byteDelta) {
  assertIsNode(byteDelta.value, "numberValueNode");
  return fragment`${byteDelta.value.number}`;
}
function getArgumentValueNodeFragment(byteDelta) {
  assertIsNode(byteDelta.value, "argumentValueNode");
  const argumentName = camelCase(byteDelta.value.name);
  return fragment`Number(args.${argumentName})`;
}
function getAccountLinkNodeFragment(byteDelta, scope) {
  assertIsNode(byteDelta.value, "accountLinkNode");
  const functionName = use(
    scope.nameApi.accountGetSizeFunction(byteDelta.value.name),
    scope.getImportFrom(byteDelta.value)
  );
  return fragment`${functionName}()`;
}
function getResolverValueNodeFragment(byteDelta, scope) {
  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 pipe(
    fragment`${awaitKeyword}${functionName}(resolverScope)`,
    (f) => addFragmentFeatures(f, ["instruction:resolverScopeVariable"])
  );
}
function getInstructionDataFragment(scope) {
  const { instructionPath, dataArgsManifest, nameApi, customInstructionData } = scope;
  const instructionNode = 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: structTypeNodeFromInstructionArgumentNodes(instructionNode.arguments ?? []),
    size: scope.size
  });
}
function getInstructionExtraArgsFragment(scope) {
  const { instructionPath, extraArgsManifest, nameApi } = scope;
  const instructionNode = getLastNodeFromPath(instructionPath);
  if ((instructionNode.extraArguments ?? []).length === 0) return;
  const instructionExtraName = nameApi.instructionExtraType(instructionNode.name);
  const looseName = nameApi.dataArgsType(instructionExtraName);
  return 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 = camelCase(input.name);
    if (input.kind === "instructionAccountNode" && 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 = 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 (isNode(defaultValue.programId, "accountValueNode")) {
        const name2 = camelCase(defaultValue.programId.name);
        pdaProgramValue = fragment`${getAddressFromResolvedInstructionAccount}("${name2}", accounts.${name2}.value)`;
      }
      if (isNode(defaultValue.programId, "argumentValueNode")) {
        const name2 = camelCase(defaultValue.programId.name);
        pdaProgramValue = fragment`${getAddressFromResolvedInstructionAccount}("${name2}", args.${name2})`;
      }
      if (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 (isNode(seed, "constantPdaSeedNode") && isNode(seed.value, "programIdValueNode")) {
            return [fragment`${use("getAddressEncoder", "solanaAddresses")}().encode(${pdaProgram})`];
          }
          if (isNode(seed, "constantPdaSeedNode") && !isNode(seed.value, "programIdValueNode")) {
            const typeManifest2 = visit(seed.type, typeManifestVisitor);
            const valueManifest2 = visit(seed.value, typeManifestVisitor);
            return [fragment`${typeManifest2.encoder}.encode(${valueManifest2.value})`];
          }
          if (isNode(seed, "variablePdaSeedNode")) {
            const typeManifest2 = visit(seed.type, typeManifestVisitor);
            const valueSeed = (defaultValue.seeds ?? []).find((s) => s.name === seed.name)?.value;
            if (!valueSeed) return [];
            if (isNode(valueSeed, "accountValueNode")) {
              const name2 = camelCase(valueSeed.name);
              return [
                fragment`${typeManifest2.encoder}.encode(${getAddressFromResolvedInstructionAccount}("${name2}", accounts.${name2}.value))`
              ];
            }
            if (isNode(valueSeed, "argumentValueNode")) {
              const name2 = camelCase(valueSeed.name);
              return [
                fragment`${typeManifest2.encoder}.encode(${getNonNullResolvedInstructionInput}("${name2}", args.${name2}))`
              ];
            }
            const valueManifest2 = 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 (isNode(seed.value, "accountValueNode")) {
          const name2 = camelCase(seed.value.name);
          return fragment`${seed.name}: ${getAddressFromResolvedInstructionAccount}("${name2}", accounts.${name2}.value)`;
        }
        if (isNode(seed.value, "argumentValueNode")) {
          const name2 = camelCase(seed.value.name);
          return fragment`${seed.name}: ${getNonNullResolvedInstructionInput}("${name2}", args.${name2})`;
        }
        return pipe(
          visit(seed.value, typeManifestVisitor).value,
          (f) => mapFragmentContent(f, (c) => `${seed.name}: ${c}`)
        );
      });
      const pdaSeedsFragment = pipe(
        mergeFragments(pdaSeeds, (renders) => renders.join(", ")),
        (f) => 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}("${camelCase(defaultValue.name)}", accounts.${camelCase(defaultValue.name)}.value)[1]`
      );
    case "argumentValueNode":
      return defaultFragment(
        fragment`${getNonNullResolvedInstructionInput}("${camelCase(defaultValue.name)}", args.${camelCase(defaultValue.name)})`
      );
    case "resolverValueNode":
      const resolverFunction = use(nameApi.resolverFunction(defaultValue.name), getImportFrom(defaultValue));
      const resolverAwait = useAsync && asyncResolvers.includes(defaultValue.name) ? "await " : "";
      return 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 (isNode(defaultValue.condition, "resolverValueNode")) {
        const conditionalResolverFunction = nameApi.resolverFunction(defaultValue.condition.name);
        const module = getImportFrom(defaultValue.condition);
        conditionalFragment = 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 = isNode(defaultValue.condition, "accountValueNode") ? `accounts.${camelCase(defaultValue.condition.name)}.value` : `args.${camelCase(defaultValue.condition.name)}`;
        if (defaultValue.value) {
          const comparedValue = 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 setFragmentContent(
          conditionalFragment,
          `if (${condition}) {
${ifTrueRenderer.content}
} else {
${ifFalseRenderer.content}
}`
        );
      }
      return 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 = 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 || getLastNodeFromPath(pdaPath).programId) return false;
  const pdaProgram = findProgramNodeFromPath(pdaPath);
  const instructionProgram = findProgramNodeFromPath(instructionPath);
  return !!pdaProgram && !!instructionProgram && pdaProgram.name === instructionProgram.name;
}
function hasVariablePdaSeeds(pdaPath) {
  if (!pdaPath) return false;
  return (getLastNodeFromPath(pdaPath).seeds ?? []).some(isNodeFilter("variablePdaSeedNode"));
}

// src/fragments/instructionInputResolved.ts
function getInstructionInputResolvedFragment(scope) {
  const instructionNode = getLastNodeFromPath(scope.instructionPath);
  const resolvedInputFragments = scope.resolvedInputs.flatMap((input) => {
    const inputFragment = getInstructionInputDefaultFragment({
      ...scope,
      input,
      optionalAccountStrategy: parseOptionalAccountStrategy(instructionNode.optionalAccountStrategy)
    });
    if (!inputFragment.content) return [];
    const camelName = camelCase(input.name);
    return [
      mapFragmentContent(
        inputFragment,
        (c) => 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 = 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${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 = getLastNodeFromPath(instructionPath);
  const fragments = (instructionNode.accounts ?? []).map((account) => {
    const resolvedAccount = resolvedInputs.find(
      (input) => input.kind === "instructionAccountNode" && input.name === account.name
    );
    const hasDefaultValue = !!resolvedAccount.defaultValue && !isNode(resolvedAccount.defaultValue, ["identityValueNode", "payerValueNode"]) && (useAsync || !isAsyncDefaultValue(resolvedAccount.defaultValue, asyncResolvers));
    const docs = getDocblockFragment(account.docs ?? [], true);
    const optionalSign = hasDefaultValue || resolvedAccount.isOptional ? "?" : "";
    return fragment`${docs}${camelCase(account.name)}${optionalSign}: ${getAccountTypeFragment2(resolvedAccount)};`;
  });
  return mergeFragments(fragments, (c) => c.join("\n"));
}
function getAccountTypeFragment2(account) {
  const typeParam = `TAccount${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 = getLastNodeFromPath(instructionPath);
  const customData = scope.customInstructionData.get(instructionNode.name);
  if (customData) {
    return [
      void 0,
      pipe(
        fragment`${nameApi.dataArgsType(customData.importAs)}`,
        (f) => mergeFragmentImports(f, [scope.dataArgsManifest.looseType.imports]),
        (f) => 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 = 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) => 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`${camelCase(renamedName)}${optionalSign}: ${argsType}["${camelCase(arg.name)}"];`;
}
function getRemainingAccountsFragment(instructionNode) {
  const fragments = (instructionNode.remainingAccounts ?? []).flatMap((remainingAccountsNode) => {
    if (isNode(remainingAccountsNode.value, "resolverValueNode")) return [];
    const { name } = remainingAccountsNode.value;
    const allArguments = 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`${camelCase(name)}${optionalSign}: Array<${typeFragment}>;`;
  });
  if (fragments.length === 0) return;
  return mergeFragments(fragments, (c) => c.join("\n"));
}
function getInstructionRemainingAccountsFragment(scope) {
  const { remainingAccounts } = getLastNodeFromPath(scope.instructionPath);
  const fragments = (remainingAccounts ?? []).flatMap((a) => getRemainingAccountsFragment2(a, scope));
  if (fragments.length === 0) return;
  return 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 (isNode(remainingAccounts.value, "argumentValueNode")) {
      return getArgumentValueNodeFragment2(remainingAccounts, scope);
    }
    if (isNode(remainingAccounts.value, "resolverValueNode")) {
      return getResolverValueNodeFragment2(remainingAccounts, scope);
    }
    return null;
  })();
  if (remainingAccountsFragment === null) return [];
  return [remainingAccountsFragment];
}
function getArgumentValueNodeFragment2(remainingAccounts, scope) {
  const instructionNode = getLastNodeFromPath(scope.instructionPath);
  assertIsNode(remainingAccounts.value, "argumentValueNode");
  const argumentName = 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 = 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) {
  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 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 = getLastNodeFromPath(instructionPath);
  const programNode = 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(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 }) => 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 = 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 = 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("${camelCase(account.name)}", accounts.${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 = 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 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${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 = getLastNodeFromPath(instructionPath);
  const instructionTypeName = nameApi.instructionType(instructionNode.name);
  const accountTypeParamsFragments = (instructionNode.accounts ?? []).map((account) => {
    const typeParam = fragment`TAccount${pascalCase(account.name)}`;
    const camelName = camelCase(account.name);
    if (account.isSigner === "either") {
      const signerRole = use(
        account.isWritable ? "type WritableSignerAccount" : "type ReadonlySignerAccount",
        "solanaInstructions"
      );
      return pipe(
        fragment`typeof input["${camelName}"] extends TransactionSigner<${typeParam}> ? ${signerRole}<${typeParam}> & AccountSignerMeta<${typeParam}> : ${typeParam}`,
        (f) => addFragmentImports(f, "solanaSigners", ["type AccountSignerMeta", "type TransactionSigner"])
      );
    }
    return typeParam;
  });
  return pipe(
    mergeFragments([fragment`TProgramAddress`, ...accountTypeParamsFragments], (c) => c.join(", ")),
    (f) => mapFragmentContent(f, (c) => `${instructionTypeName}<${c}>`)
  );
}
function getInputTypeCallFragment(scope) {
  const { instructionPath, useAsync, nameApi } = scope;
  const instructionNode = 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${pascalCase(account.name)}`).join(", ");
  return fragment`${inputTypeName}<${accountTypeParams}>`;
}
function getInstructionParseFunctionFragment(scope) {
  const instructionNode = getLastNodeFromPath(scope.instructionPath);
  const programNode = 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 = 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 ? 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`${camelCase(account.name)}: getNextOptionalAccount()` : fragment`${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 = getLastNodeFromPath(instructionPath);
  const programNode = 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) => mapFragmentContent(getInstructionAccountMetaFragment(account), (c) => {
        const typeParam = `TAccount${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 = getLastNodeFromPath(scope.instructionPath);
  if (!findProgramNodeFromPath(scope.instructionPath)) {
    throw new Error("Instruction must be visited inside a program.");
  }
  const childScope = {
    ...scope,
    dataArgsManifest: visit(node, scope.typeManifestVisitor),
    extraArgsManifest: visit(
      definedTypeNode({
        name: scope.nameApi.instructionExtraType(node.name),
        type: 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();
  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) => [camelCase(name), camelCase(`${name}Arg`)]));
}
function getPdaFunctionFragment(scope) {
  const pdaNode = 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(isNodeFilter("variablePdaSeedNode"));
  if (variableSeeds.length === 0) return;
  const pdaNode = 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 = getLastNodeFromPath(scope.pdaPath);
  const programNode = 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(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 (isNode(seed, "variablePdaSeedNode")) {
      const name = camelCase(seed.name);
      const docs = getDocblockFragment(seed.docs ?? [], true);
      const { encoder: encoder2, looseType } = visit(seed.type, scope.typeManifestVisitor);
      return {
        ...seed,
        encodedValue: fragment`${encoder2}.encode(seeds.${name})`,
        inputAttribute: fragment`${docs}${name}: ${looseType};`
      };
    }
    if (isNode(seed.value, "programIdValueNode")) {
      const addressEncoder = use("getAddressEncoder", "solanaAddresses");
      return { ...seed, encodedValue: fragment`${addressEncoder}().encode(programAddress)` };
    }
    const { encoder } = visit(seed.type, scope.typeManifestVisitor);
    const { value } = visit(seed.value, scope.typeManifestVisitor);
    return { ...seed, encodedValue: fragment`${encoder}.encode(${value})` };
  });
}
function getPdaPageFragment(scope) {
  if (!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: 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 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 = resolveNestedTypeNode(event.data);
      return getDiscriminatorConditionFragment({
        ...scope,
        dataName: "data",
        discriminators: event.discriminators ?? [],
        ifTrue: `return ${programEventsEnum}.${variant};`,
        struct: isNode(resolvedData, "structTypeNode") ? resolvedData : 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 = 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: 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 = getResolvedInstructionInputsVisitor();
  const asyncInstructions = (scope.programNode.instructions ?? []).filter(
    (instruction) => hasAsyncFunction(instruction, 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 = 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 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 && isNode(a.defaultValue, "payerValueNode")),
    ...(instructionNode.arguments ?? []).filter((a) => 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 = isNode(typeNode, "enumTypeNode") && 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 (isNode(variant, "enumStructVariantTypeNode")) {
        return fragment`export function ${functionName}(kind: '${variantName}', data: ${getVariantContentType}<${looseName}, '${discriminatorName}', '${variantName}'>): ${getVariantType}<${looseName}, '${discriminatorName}', '${variantName}'>;`;
      }
      if (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 = visit(node, scope.typeManifestVisitor);
  return 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 NodeStack();
  let parentName = null;
  const getEventDiscriminatorConstantName = (constant) => {
    const eventNode = findLastNodeFromPath(stack.getPath(), "eventNode");
    if (!eventNode) return null;
    const constantDiscriminators = (eventNode.discriminators ?? []).filter(
      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 pipe(
    staticVisitor(() => typeManifest(), {
      keys: [
        ...REGISTERED_TYPE_NODE_KINDS,
        ...REGISTERED_VALUE_NODE_KINDS,
        "definedTypeLinkNode",
        "definedTypeNode",
        "accountNode",
        "eventNode",
        "instructionNode"
      ]
    }),
    (visitor) => 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 ? visit(link, self) : visit(account.data, self);
        parentName = null;
        return manifest;
      },
      visitAmountType(amountType, { self }) {
        return visit(amountType.number, self);
      },
      visitArrayType(arrayType, { self }) {
        const childManifest = 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) => visit(v, self)),
          { mergeValues: (renders) => `[${renders.join(", ")}]` }
        );
      },
      visitBooleanType(booleanType, { self }) {
        let sizeEncoder = fragment``;
        let sizeDecoder = fragment``;
        const resolvedSize = resolveNestedTypeNode(booleanType.size);
        if (resolvedSize.format !== "u8" || resolvedSize.endian !== "le") {
          const size = 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 (isNode(node.type, "bytesTypeNode") && isNode(node.value, "bytesValueNode")) {
          return visit(node.value, self);
        }
        return typeManifest({
          value: fragment`${visit(node.type, self).encoder}.encode(${visit(node.value, self).value})`
        });
      },
      visitDateTimeType(dateTimeType, { self }) {
        return visit(dateTimeType.number, self);
      },
      visitDefinedType(definedType, { self }) {
        parentName = {
          loose: nameApi.dataArgsType(definedType.name),
          strict: nameApi.dataType(definedType.name)
        };
        const manifest = 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(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(
          camelCase(currentParentName?.strict ?? "")
        );
        const name = nameApi.discriminatedUnionVariant(enumStructVariantType.name);
        const kindAttribute = `${discriminator}: "${name}"`;
        parentName = null;
        const structManifest = visit(enumStructVariantType.struct, self);
        parentName = currentParentName;
        return typeManifest({
          ...structManifest,
          decoder: fragment`['${name}', ${structManifest.decoder}]`,
          encoder: fragment`['${name}', ${structManifest.encoder}]`,
          looseType: pipe(
            structManifest.looseType,
            (f) => mapFragmentContent(f, (c) => `{ ${kindAttribute},${c.slice(1, -1)}}`)
          ),
          strictType: pipe(
            structManifest.strictType,
            (f) => mapFragmentContent(f, (c) => `{ ${kindAttribute},${c.slice(1, -1)}}`)
          )
        });
      },
      visitEnumTupleVariantType(enumTupleVariantType, { self }) {
        const currentParentName = parentName;
        const discriminator = nameApi.discriminatedUnionDiscriminator(
          camelCase(currentParentName?.strict ?? "")
        );
        const name = nameApi.discriminatedUnionVariant(enumTupleVariantType.name);
        const kindAttribute = `${discriminator}: "${name}"`;
        const struct = structTypeNode([
          structFieldTypeNode({
            name: "fields",
            type: enumTupleVariantType.tuple
          })
        ]);
        parentName = null;
        const structManifest = visit(struct, self);
        parentName = currentParentName;
        return typeManifest({
          ...structManifest,
          decoder: fragment`['${name}', ${structManifest.decoder}]`,
          encoder: fragment`['${name}', ${structManifest.encoder}]`,
          looseType: pipe(
            structManifest.looseType,
            (f) => mapFragmentContent(f, (c) => `{ ${kindAttribute},${c.slice(1, -1)}}`)
          ),
          strictType: pipe(
            structManifest.strictType,
            (f) => mapFragmentContent(f, (c) => `{ ${kindAttribute},${c.slice(1, -1)}}`)
          )
        });
      },
      visitEnumType(enumType, { self }) {
        const currentParentName = parentName;
        const encoderOptions = [];
        const decoderOptions = [];
        const enumSize = resolveNestedTypeNode(enumType.size);
        if (enumSize.format !== "u8" || enumSize.endian !== "le") {
          const sizeManifest = visit(enumType.size, self);
          encoderOptions.push(fragment`size: ${sizeManifest.encoder}`);
          decoderOptions.push(fragment`size: ${sizeManifest.decoder}`);
        }
        const discriminator = nameApi.discriminatedUnionDiscriminator(
          camelCase(currentParentName?.strict ?? "")
        );
        if (!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 (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) => 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 && isNode(enumNode, "enumTypeNode") ? isScalarEnum(enumNode) : !nonScalarEnums.includes(node.enum.name);
        if (!node.value && isScalar) {
          const variantName2 = nameApi.enumVariant(node.variant);
          return typeManifest({
            ...manifest,
            value: pipe(
              manifest.value,
              (f) => setFragmentContent(f, `${enumName}.${variantName2}`),
              (f) => addFragmentImports(f, importFrom, [enumName])
            )
          });
        }
        const variantName = nameApi.discriminatedUnionVariant(node.variant);
        if (!node.value) {
          return typeManifest({
            ...manifest,
            value: pipe(
              manifest.value,
              (f) => setFragmentContent(f, `${enumFunction}('${variantName}')`),
              (f) => addFragmentImports(f, importFrom, [enumFunction])
            )
          });
        }
        return typeManifest({
          ...manifest,
          value: pipe(
            visit(node.value, self).value,
            (f) => 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 = visit(event.data, self);
        parentName = null;
        return manifest;
      },
      visitFixedSizeType(node, { self }) {
        const manifest = 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 = visit(node.type, self);
        const prefixes = (node.prefix ?? []).map((c) => visit(c, self).value);
        const prefixEncoders = pipe(
          mergeFragments(prefixes, (cs) => cs.map((c) => `getConstantEncoder(${c})`).join(", ")),
          (f) => addFragmentImports(f, "solanaCodecsCore", ["getConstantEncoder"])
        );
        const prefixDecoders = 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 = visit(node.type, self);
        const suffixes = (node.suffix ?? []).map((c) => visit(c, self).value);
        const suffixEncoders = pipe(
          mergeFragments(suffixes, (cs) => cs.map((c) => `getConstantEncoder(${c})`).join(", ")),
          (f) => addFragmentImports(f, "solanaCodecsCore", ["getConstantEncoder"])
        );
        const suffixDecoders = 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 = structTypeNodeFromInstructionArgumentNodes(instruction.arguments ?? []);
        const manifest = link ? visit(link, self) : visit(struct, self);
        parentName = null;
        return manifest;
      },
      visitMapEntryValue(node, { self }) {
        return mergeTypeManifests([visit(node.key, self), visit(node.value, self)], {
          mergeValues: (renders) => `[${renders.join(", ")}]`
        });
      },
      visitMapType(mapType, { self }) {
        const key = visit(mapType.key, self);
        const value = 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) => 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 = visit(optionType.item, self);
        const encoderOptions = [];
        const decoderOptions = [];
        const optionPrefix = resolveNestedTypeNode(optionType.prefix);
        if (optionPrefix.format !== "u8" || optionPrefix.endian !== "le") {
          const prefixManifest = 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 = 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 = 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 = 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 = visit(node.type, self);
        const sentinel = 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 = 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) => visit(v, self)),
          { mergeValues: (renders) => `new Set([${renders.join(", ")}])` }
        );
      },
      visitSizePrefixType(node, { self }) {
        const manifest = visit(node.type, self);
        const prefix = 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 = 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 = 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 = camelCase(structFieldType.name);
        const originalChildManifest = 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 = 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 = pipe(
          mergeTypeManifests(
            (structType.fields ?? []).map((field) => 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 = findLastNodeFromPath(parentPath, "instructionNode");
        const accountNode = findLastNodeFromPath(parentPath, "accountNode");
        const eventNode = findLastNodeFromPath(parentPath, "eventNode");
        const discriminatorParent = instructionNode ?? accountNode ?? eventNode;
        const discriminatorPrefix = discriminatorParent && isNode(discriminatorParent, "eventNode") ? getEventDiscriminatorPrefix(nameApi, discriminatorParent.name) : discriminatorParent?.name;
        const discriminators = discriminatorParent?.discriminators ?? [];
        const fieldDiscriminators = discriminators.filter(isNodeFilter("fieldDiscriminatorNode"));
        const defaultValues = mergeFragments(
          optionalFields.map((f) => {
            const key = camelCase(f.name);
            if (fieldDiscriminators.some((d) => d.name === f.name)) {
              const constantName = nameApi.constant(camelCase(`${discriminatorPrefix}_${f.name}`));
              return f.defaultValueStrategy === "omitted" ? fragment`${key}: ${constantName}` : fragment`${key}: value.${key} ?? ${constantName}`;
            }
            const defaultValue = f.defaultValue;
            const value = 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) => visit(field, self)),
          { mergeValues: (renders) => `{ ${renders.join(", ")} }` }
        );
      },
      visitTupleType(tupleType, { self }) {
        const items = (tupleType.items ?? []).map((item) => 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) => visit(v, self)),
          { mergeValues: (renders) => `[${renders.join(", ")}]` }
        );
      },
      visitZeroableOptionType(node, { self }) {
        const childManifest = visit(node.item, self);
        const encoderOptions = [fragment`prefix: null`];
        const decoderOptions = [fragment`prefix: null`];
        if (node.zeroValue) {
          const zeroValueManifest = 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) => recordNodeStackVisitor(visitor, stack)
  );
}
function getArrayLikeSizeOption(count, visitor) {
  if (isNode(count, "fixedCountNode")) {
    return {
      decoder: fragment`size: ${count.value}`,
      encoder: fragment`size: ${count.value}`
    };
  }
  if (isNode(count, "remainderCountNode")) {
    return {
      decoder: fragment`size: 'remainder'`,
      encoder: fragment`size: 'remainder'`
    };
  }
  const prefix = resolveNestedTypeNode(count.prefix);
  if (prefix.format === "u32" && prefix.endian === "le") {
    return { decoder: void 0, encoder: void 0 };
  }
  const prefixManifest = visit(count.prefix, visitor);
  return {
    decoder: pipe(prefixManifest.decoder, (f) => mapFragmentContent(f, (c) => `size: ${c}`)),
    encoder: pipe(prefixManifest.encoder, (f) => mapFragmentContent(f, (c) => `size: ${c}`))
  };
}

// src/visitors/getRenderMapVisitor.ts
function getRenderMapVisitor(options = {}) {
  const linkables = new LinkableDictionary();
  const stack = new NodeStack();
  const customAccountData = parseCustomDataOptions(options.customAccountData ?? [], "AccountData");
  const customInstructionData = parseCustomDataOptions(options.customInstructionData ?? [], "InstructionData");
  const renderScopeWithTypeManifestVisitor = {
    asyncResolvers: (options.asyncResolvers ?? []).map(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(camelCase),
    renderParentInstructions: options.renderParentInstructions ?? false
  };
  const typeManifestVisitor = getTypeManifestVisitor({ ...renderScopeWithTypeManifestVisitor, stack });
  const renderScope = { ...renderScopeWithTypeManifestVisitor, typeManifestVisitor };
  const internalNodes = (options.internalNodes ?? []).map(camelCase);
  const isNotInternal = (node) => !internalNodes.includes(node.name);
  const resolvedInstructionInputVisitor = getResolvedInstructionInputsVisitor();
  const byteSizeVisitor = getByteSizeVisitor(linkables, { stack });
  const asPage = (fragment2, pageDependencyMap = {}) => {
    if (!fragment2) return void 0;
    return getPageFragment(fragment2, {
      ...renderScope,
      dependencyMap: { ...renderScope.dependencyMap, ...pageDependencyMap }
    });
  };
  return pipe(
    staticVisitor(() => createRenderMap(), {
      keys: [
        "rootNode",
        "programNode",
        "pdaNode",
        "accountNode",
        "definedTypeNode",
        "eventNode",
        "instructionNode"
      ]
    }),
    (v) => extendVisitor(v, {
      visitAccount(node) {
        return createRenderMap(
          `accounts/${camelCase(node.name)}.ts`,
          asPage(
            getAccountPageFragment({
              ...renderScope,
              accountPath: stack.getPath("accountNode"),
              size: visit(node, byteSizeVisitor)
            })
          )
        );
      },
      visitDefinedType(node) {
        return createRenderMap(
          `types/${camelCase(node.name)}.ts`,
          asPage(getTypePageFragment({ ...renderScope, node, size: visit(node, byteSizeVisitor) }), {
            generatedTypes: renderScope.getImportPath(".", "directory")
          })
        );
      },
      visitEvent(node) {
        return createRenderMap(
          `events/${camelCase(node.name)}.ts`,
          asPage(
            getEventPageFragment({
              ...renderScope,
              eventPath: stack.getPath("eventNode"),
              size: visit(node.data, byteSizeVisitor)
            })
          )
        );
      },
      visitInstruction(node) {
        return createRenderMap(
          `instructions/${camelCase(node.name)}.ts`,
          asPage(
            getInstructionPageFragment({
              ...renderScope,
              instructionPath: stack.getPath("instructionNode"),
              resolvedInputs: visit(node, resolvedInstructionInputVisitor),
              size: visit(node, byteSizeVisitor)
            })
          )
        );
      },
      visitPda(node) {
        return createRenderMap(
          `pdas/${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 mergeRenderMaps([
          createRenderMap({
            [`constants/${camelCase(node.name)}.ts`]: constantsToExport.length > 0 ? asPage(
              getConstantsPageFragment({
                ...renderScope,
                nodes: constantsToExport,
                programPath: stack.getPath("programNode")
              }),
              { generatedTypes: renderScope.getImportPath("../types", "directory") }
            ) : void 0,
            [`programs/${camelCase(node.name)}.ts`]: asPage(getProgramPageFragment(scope)),
            [`errors/${camelCase(node.name)}.ts`]: (node.errors ?? []).length > 0 ? asPage(getErrorPageFragment(scope)) : void 0
          }),
          ...(node.pdas ?? []).map((p) => visit(p, self)),
          ...(node.accounts ?? []).map((a) => visit(a, self)),
          ...(node.events ?? []).map((e) => visit(e, self)),
          ...(node.definedTypes ?? []).map((t) => visit(t, self)),
          ...customDataDefinedType.map((t) => visit(t, self)),
          ...getAllInstructionsWithSubs(node, { leavesOnly: !renderScope.renderParentInstructions }).map(
            (i) => visit(i, self)
          )
        ]);
      },
      visitRoot(node, { self }) {
        const programsToExport = getAllPrograms(node).filter(isNotInternal);
        const programsWithConstantsToExport = programsToExport.filter(
          (program) => (program.constants ?? []).some(isNotInternal)
        );
        const programsWithErrorsToExport = programsToExport.filter((p) => (p.errors ?? []).length > 0);
        const pdasToExport = getAllPdas(node);
        const accountsToExport = getAllAccounts(node).filter(isNotInternal);
        const eventsToExport = getAllEvents(node).filter(isNotInternal);
        const instructionsToExport = getAllInstructionsWithSubs(node, {
          leavesOnly: !renderScope.renderParentInstructions
        }).filter(isNotInternal);
        const definedTypesToExport = getAllDefinedTypes(node).filter(isNotInternal);
        const scope = {
          ...renderScope,
          accountsToExport,
          definedTypesToExport,
          eventsToExport,
          instructionsToExport,
          pdasToExport,
          programsToExport,
          programsWithConstantsToExport
        };
        return mergeRenderMaps([
          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))
          }),
          ...getAllPrograms(node).map((p) => visit(p, self))
        ]);
      }
    }),
    (v) => recordNodeStackVisitor(v, stack),
    (v) => recordLinkablesOnFirstVisitVisitor(v, linkables)
  );
}
function renderVisitor(packageFolder, options = {}) {
  return rootNodeVisitor(async (root) => {
    const generatedFolder = joinPath(packageFolder, options.generatedFolder ?? "src/generated");
    if (options.deleteFolderBeforeRendering ?? true) {
      deleteDirectory(generatedFolder);
    }
    let renderMap = visit(root, getRenderMapVisitor(options));
    const formatCode = await getCodeFormatter(packageFolder, options);
    renderMap = await mapRenderMapContentAsync(renderMap, formatCode);
    await syncPackageJson(renderMap, formatCode, packageFolder, options);
    writeRenderMap(renderMap, generatedFolder);
  });
}

export { DEFAULT_KIT_IMPORT_STRATEGY, DEFAULT_NAME_TRANSFORMERS, addToImportMap, createImportMap, renderVisitor as default, getExternalDependencies, getImportPathFactory, getNameApi, getRenderMapVisitor, getTypeManifestVisitor, importMapToString, mergeImportMaps, mergeTypeManifests, parseImportInput, removeFromImportMap, renderVisitor, typeManifest };
//# sourceMappingURL=index.node.mjs.map
//# sourceMappingURL=index.node.mjs.map