```ts
export function topologicalSort(nodes: readonly string[], edges: readonly [string, string][]) {
    /**
     * Returns an ordering satisfying every edge in the input graph.
     */
    const graph = Array.from({ length: nodes.length }, () => new Set());
    for (const [from, to] of edges) {
        if (!nodes.includes(from)) {
            throw new Error(`Edge ${[from, to].join(' -> ')} has a node not in the input list`);
        }
        if (!nodes.includes(to)) {
            throw new Error(`Edge ${[from, to].join(' -> ')} has a node not in the input list`);
        }
        graph[from].add(to);
    }

    const visited: Set<string> = new Set();
    const ordering: string[] = [];

    function dfs(node: string) {
        if (visited.has(node)) {
            return;
        }
        if (!graph[node]) {
            throw new Error(`Cycle detected in the input graph`);
        }
        for (const neighbor of graph[node]) {
            dfs(neighbor);
        }
        visited.add(node);
        ordering.push(node);
    }

    while (nodes.some((node) => !visited.has(node))) {
        const free = nodes.find((n) => !graph[n] || !visited.has(n));
        if (!free) {
            throw new Error('Input graph contains a cycle');
        }
        dfs(free as string);
    }

    return ordering;
}
```