```ts
export function openWindows(span: string, booked: string[]): string {
  if (!isValidSpan(span) || !areAllBookedValid(booked)) {
    throw new Error("Invalid span or appointments");
  }

  const [start, end] = parseTimeSpan(span);
  const sortedBookings = sortAppointments(booked);

  let freeStretches: { start: string; end: string }[] = [];

  for (const [startTime, endTime] of sortedBookings) {
    if (!freeStretches.length || freeStretches[freeStretches.length - 1].end < startTime) {
      freeStretches.push({ start: formatTime(start), end: formatTime(startTime) });
    }
    const overlapStart = Math.max(freeStretches[freeStretches.length - 1].end, startTime);
    if (overlapStart <= endTime) {
      freeStretches[freeStretches.length - 1].end = formatTime(endTime);
    } else {
      freeStretches.push({ start: formatTime(overlapStart), end: formatTime(end) });
    }
  }

  return freeStretches.length > 0 ? freeStretches.map(bs => bs.start + "-" + bs.end).join(", ") : "none";
}

function isValidSpan(span: string): boolean {
  const [start, end] = span.split("-");
  return start && end && isTimeFormat(start) && isTimeFormat(end);
}

function areAllBookedValid(booked: string[]): boolean {
  return booked.every(booking => isTimeFormat(booking));
}

function sortAppointments(bookings: string[]): [string, string][] {
  return bookings.map(timeSlot => timeSlot.split("-")).sort((a, b) => {
    if (a[0] !== b[0]) return a[0] < b[0] ? -1 : 1;
    return a[1] < b[1] ? -1 : 1;
  });
}

function formatTime(time: string): string {
  const [hours, minutes] = time.split(":").map(Number);
  return String(hours).padStart(2, "0") + ":" + String(minutes).padStart(2, "0");
}

function isTimeFormat(time: string): boolean {
  return /^([01]?[0-9]|2[0-3]):([0-5][0-9])$/.test(time);
}
```