import type { Instruction } from '@solana/instructions'; import { type InstructionPlan, isInstructionPlan, sequentialInstructionPlan, singleInstructionPlan, } from './instruction-plan'; import { isTransactionPlan, sequentialTransactionPlan, SingleTransactionPlan, singleTransactionPlan, TransactionPlan, } from './transaction-plan'; /** * A flexible input type that can be used to create an {@link InstructionPlan}. * * This type accepts: * - A single {@link Instruction}. * - An existing {@link InstructionPlan}. * - An array of instructions and/or instruction plans. * * Use the {@link parseInstructionPlanInput} function to convert this input * into a proper {@link InstructionPlan}. * * @example * Using a single instruction. * ```ts * const input: InstructionPlanInput = myInstruction; * ``` * * @example * Use as argument type in a function that will parse it into an InstructionPlan. * ```ts * function myFunction(input: InstructionPlanInput) { * const plan = parseInstructionPlanInput(input); * // Use the plan... * } * ``` * * @see {@link parseInstructionPlanInput} * @see {@link InstructionPlan} */ export type InstructionPlanInput = Instruction | InstructionPlan | readonly (Instruction | InstructionPlan)[]; /** * Parses an {@link InstructionPlanInput} and returns an {@link InstructionPlan}. * * This function handles the following input types: * - A single {@link Instruction} is wrapped in a {@link SingleInstructionPlan}. * - An existing {@link InstructionPlan} is returned as-is. * - An array with a single element is unwrapped and parsed recursively. * - An array with multiple elements is wrapped in a divisible {@link SequentialInstructionPlan}. * * @param input - The input to parse into an instruction plan. * @return The parsed instruction plan. * * @example * Parsing a single instruction. * ```ts * const plan = parseInstructionPlanInput(myInstruction); * // Equivalent to: singleInstructionPlan(myInstruction) * ``` * * @example * Parsing an array of instructions. * ```ts * const plan = parseInstructionPlanInput([instructionA, instructionB]); * // Equivalent to: sequentialInstructionPlan([instructionA, instructionB]) * ``` * * @example * Parsing a mixed array with nested plans. * ```ts * const plan = parseInstructionPlanInput([ * instructionA, * parallelInstructionPlan([instructionB, instructionC]), * ]); * // Returns a sequential plan containing: * // - A single instruction plan for instructionA. * // - The parallel plan for instructionB and instructionC. * ``` * * @example * Single-element arrays are unwrapped. * ```ts * const plan = parseInstructionPlanInput([myInstruction]); * // Equivalent to: singleInstructionPlan(myInstruction) * ``` * * @see {@link InstructionPlanInput} * @see {@link InstructionPlan} */ export function parseInstructionPlanInput(input: InstructionPlanInput): InstructionPlan { if (Array.isArray(input) && input.length === 1) { return parseInstructionPlanInput(input[0]); } if (Array.isArray(input)) { return sequentialInstructionPlan(input.map(parseInstructionPlanInput)); } return isInstructionPlan(input) ? input : singleInstructionPlan(input as Instruction); } /** * A flexible input type that can be used to create a {@link TransactionPlan}. * * This type accepts: * - A single {@link TransactionMessage} with a fee payer. * - An existing {@link TransactionPlan}. * - An array of transaction messages and/or transaction plans. * * Use the {@link parseTransactionPlanInput} function to convert this input * into a proper {@link TransactionPlan}. * * @example * Using a single transaction message. * ```ts * const input: TransactionPlanInput = myTransactionMessage; * ``` * * @example * Use as argument type in a function that will parse it into a TransactionPlan. * ```ts * function myFunction(input: TransactionPlanInput) { * const plan = parseTransactionPlanInput(input); * // Use the plan... * } * ``` * * @see {@link parseTransactionPlanInput} * @see {@link TransactionPlan} */ export type TransactionPlanInput = | SingleTransactionPlan['message'] | TransactionPlan | readonly (SingleTransactionPlan['message'] | TransactionPlan)[]; /** * Parses a {@link TransactionPlanInput} and returns a {@link TransactionPlan}. * * This function handles the following input types: * - A single {@link TransactionMessage} is wrapped in a {@link SingleTransactionPlan}. * - An existing {@link TransactionPlan} is returned as-is. * - An array with a single element is unwrapped and parsed recursively. * - An array with multiple elements is wrapped in a divisible {@link SequentialTransactionPlan}. * * @param input - The input to parse into a transaction plan. * @return The parsed transaction plan. * * @example * Parsing a single transaction message. * ```ts * const plan = parseTransactionPlanInput(myTransactionMessage); * // Equivalent to: singleTransactionPlan(myTransactionMessage) * ``` * * @example * Parsing an array of transaction messages. * ```ts * const plan = parseTransactionPlanInput([messageA, messageB]); * // Equivalent to: sequentialTransactionPlan([messageA, messageB]) * ``` * * @example * Parsing a mixed array with nested plans. * ```ts * const plan = parseTransactionPlanInput([ * messageA, * parallelTransactionPlan([messageB, messageC]), * ]); * // Returns a sequential plan containing: * // - A single transaction plan for messageA. * // - The parallel plan for messageB and messageC. * ``` * * @example * Single-element arrays are unwrapped. * ```ts * const plan = parseTransactionPlanInput([myTransactionMessage]); * // Equivalent to: singleTransactionPlan(myTransactionMessage) * ``` * * @see {@link TransactionPlanInput} * @see {@link TransactionPlan} */ export function parseTransactionPlanInput(input: TransactionPlanInput): TransactionPlan { if (Array.isArray(input) && input.length === 1) { return parseTransactionPlanInput(input[0]); } if (Array.isArray(input)) { return sequentialTransactionPlan(input.map(item => parseTransactionPlanInput(item))); } return isTransactionPlan(input) ? input : singleTransactionPlan(input as SingleTransactionPlan['message']); } /** * Parses an {@link InstructionPlanInput} or {@link TransactionPlanInput} and * returns the appropriate plan type. * * This function automatically detects whether the input represents instructions * or transactions and delegates to the appropriate parser: * - If the input is a transaction message or transaction plan, it delegates * to {@link parseTransactionPlanInput}. * - Otherwise, it delegates to {@link parseInstructionPlanInput}. * * @param input - The input to parse, which can be either an instruction-based * or transaction-based input. * @returns The parsed plan, either an {@link InstructionPlan} or a {@link TransactionPlan}. * * @example * Parsing an instruction input. * ```ts * const plan = parseInstructionOrTransactionPlanInput(myInstruction); * // Returns an InstructionPlan * ``` * * @example * Parsing a transaction message input. * ```ts * const plan = parseInstructionOrTransactionPlanInput(myTransactionMessage); * // Returns a TransactionPlan * ``` * * @see {@link parseInstructionPlanInput} * @see {@link parseTransactionPlanInput} * @see {@link InstructionPlanInput} * @see {@link TransactionPlanInput} */ export function parseInstructionOrTransactionPlanInput( input: InstructionPlanInput | TransactionPlanInput, ): InstructionPlan | TransactionPlan { if (Array.isArray(input) && input.length === 0) { return parseTransactionPlanInput(input); } if (Array.isArray(input) && isTransactionPlanInput(input[0])) { return parseTransactionPlanInput(input as TransactionPlanInput); } if (isTransactionPlanInput(input)) { return parseTransactionPlanInput(input as TransactionPlanInput); } return parseInstructionPlanInput(input as InstructionPlanInput); } function isTransactionPlanInput(value: unknown): value is SingleTransactionPlan['message'] | TransactionPlan { return isTransactionPlan(value) || isTransactionMessage(value); } function isTransactionMessage(value: unknown): value is SingleTransactionPlan['message'] { return ( typeof value === 'object' && value !== null && 'instructions' in value && Array.isArray(value.instructions) && 'version' in value ); }