```typescript
export function topologicalSort(nodes: readonly string[], edges: readonly (readonly [string, string])[]): string[] {
    const graph: { [node: string]: string[] } = {};
    for (const edge of edges) {
        if (!graph[edge[0]]) graph[edge[0]] = [];
        graph[edge[0]].push(edge[1]);
    }

    function hasCycle(node: string, visited: Set<string>, dfsStack: Set<string>): boolean {
        if (dfsStack.has(node)) return true;
        if (visited.has(node)) return false;

        visited.add(node);
        dfsStack.add(node);

        for (const neighbor of graph[node] || []) {
            if (hasCycle(neighbor, visited, dfsStack)) return true;
        }

        dfsStack.delete(node);
        return false;
    }

    const cycleNode = nodes.find(node => hasCycle(node, new Set(), new Set()));
    if (cycleNode) throw new Error(`Cycle detected involving node: ${cycleNode}`);

    function topologicalSortUtil(node: string): string[] {
        const result: string[] = [];
        const visited = new Set<string>();

        const dfs = (node: string) => {
            visited.add(node);
            for (const neighbor of graph[node] || []) {
                if (!visited.has(neighbor)) dfs(neighbor);
            }
            result.push(node);
        };

        for (const node of nodes) {
            if (!visited.has(node)) dfs(node);
        }

        return result.reverse();
    }

    const sortedNodes = topologicalSortUtil(nodes[0]);
    let index = 0;

    while (index < nodes.length && sortedNodes[index] !== nodes[index]) {
        const nextIndex = nodes.findIndex((node, i) => node === sortedNodes[index + 1] && i > index);
        if (nextIndex === -1) throw new Error(`Invalid edge from ${sortedNodes[index]} to ${nodes[nextIndex]}`);
        [sortedNodes[index], sortedNodes[nextIndex]] = [sortedNodes[nextIndex], sortedNodes[index]];
        index++;
    }

    return sortedNodes;
}
```