```typescript
/**
 * Merges overlapping or touching spans into a single span.
 * Spans are combined if one starts before or at the end of another.
 * The result is sorted by start time.
 */
export function spanMerge(spans: number[][]): number[] {
  if (spans.length === 0) {
    return [];
  }

  // Sort spans by their start time
  const sorted = [...spans].sort((a, b) => a[0] - b[0]);

  const merged: number[] = [];
  let currentStart = sorted[0][0];
  let currentEnd = sorted[0][1];

  for (let i = 1; i < sorted.length; i++) {
    const nextStart = sorted[i][0];
    const nextEnd = sorted[i][1];

    // If the next span starts before or at the current end, they overlap or touch
    if (nextStart <= currentEnd) {
      currentEnd = Math.max(currentEnd, nextEnd);
    } else {
      merged.push([currentStart, currentEnd]);
      currentStart = nextStart;
      currentEnd = nextEnd;
    }
  }

  merged.push([currentStart, currentEnd]);
  return merged;
}
```