```typescript
/** Tape-measure spans: parse to inches, format canonically, and add. */

const INCHES: Record<string, number> = { yd: 36, ft: 12, in: 1 };
const ORDER = ["yd", "ft", "in"];

export function parseSpan(text: string): number {
  if (typeof text !== "string" || !text) {
    throw new Error("invalid span text");
  }

  const parts = text.split(" ").filter(Boolean);
  if (parts.length === 0) {
    throw new Error("span cannot be empty");
  }

  const parsedParts: { value: number, unit: string }[] = [];
  for (const part of parts) {
    const [valueStr, unit] = part.split("").reverse().join("").match(/^(\d+)(yd|ft|in)$/)?.slice(1, 3) || [0, "yd"];
    if (!valueStr || !unit) {
      throw new Error("invalid span text");
    }
    parsedParts.push({ value: parseInt(valueStr, 10), unit });
  }

  const sortedParts = parsedParts.sort((a, b) => ORDER.indexOf(a.unit) - ORDER.indexOf(b.unit));

  let totalInches = 0;
  for (const { value, unit } of sortedParts) {
    if (!ORDER.includes(unit)) {
      throw new Error("unknown unit");
    }
    totalInches += value * INCHES[unit];
  }

  return totalInches;
}

export function formatSpan(inches: number): string {
  if (!Number.isInteger(inches) || inches < 0) {
    throw new Error("inches must be a non-negative integer");
  }
  const parts: { unit: string, value: number }[] = [];
  let remainingInches = inches;

  for (const [unit] of ORDER.reverse()) {
    const value = Math.floor(remainingInches / INCHES[unit]);
    if (value > 0) {
      parts.push({ unit, value });
      remainingInches -= value * INCHES[unit];
    }
  }

  return parts.map(({ unit, value }) => `${value}${unit}`).join(" ");
}

export function addSpans(first: string, second: string): string {
  const total = parseSpan(first) + parseSpan(second);
  return formatSpan(total);
}
```