```ts
/**
 * Parses a span text into total inches.
 */
export function parseSpan(text: string): number {
  if (typeof text !== 'string' || text.trim() === '') {
    throw new Error('Invalid input');
  }

  const parts = text.split(/\s+/);
  const units = { yd: 36 * 12, ft: 12, in: 1 };
  let totalInches = 0;

  for (let i = 0; i < parts.length; i++) {
    const part = parts[i];
    const match = part.match(/(\d+)\s*(yd|ft|in)?/);
    if (!match) {
      throw new Error('Invalid input');
    }

    const value = parseInt(match[1], 10);
    const unit = match[2] || 'in';

    if (isNaN(value) || !units[unit]) {
      throw new Error('Invalid input');
    }

    totalInches += value * units[unit];
  }

  return totalInches;
}

/**
 * Renders a non-negative integer of total inches canonically.
 */
export function formatSpan(inches: number): string {
  if (typeof inches !== 'number' || !Number.isInteger(inches) || inches < 0) {
    throw new Error('Invalid input');
  }

  const units = { yd: 36 * 12, ft: 12 };
  let result = '';

  for (const [unit, conversion] of Object.entries(units)) {
    if (inches >= conversion) {
      result += `${Math.floor(inches / conversion)}${unit} `;
      inches %= conversion;
    }
  }

  if (inches > 0) {
    result += `${inches}in`;
  } else if (result === '') {
    result = '0in';
  }

  return result.trim();
}

/**
 * Takes two span texts and returns their sum in that canonical form.
 */
export function addSpans(first: string, second: string): string {
  const totalInches = parseSpan(first) + parseSpan(second);
  return formatSpan(totalInches);
}
```