```ts
/**
 * Merges overlapping or touching intervals into non-overlapping intervals.
 * @param intervals - A list of [start, end] pairs representing intervals.
 * @returns A list of merged non-overlapping intervals sorted by start time.
 */
export function mergeIntervals(intervals: number[][]): number[][] {
    if (intervals.length === 0) return [];

    const sortedIntervals = intervals.slice().sort((a, b) => a[0] - b[0]);

    const merged: number[][] = [];
    for (const current of sortedIntervals) {
        if (!merged.length || merged[merged.length - 1][1] < current[0]) {
            // No overlap or gap, add new interval
            merged.push(current);
        } else {
            // Overlap, merge with the last interval
            merged[merged.length - 1][1] = Math.max(merged[merged.length - 1][1], current[1]);
        }
    }

    return merged;
}
```