```ts
export function topologicalSort(nodes: readonly string[], edges: readonly (readonly [string, string])[]): string[] {
  const inDegree = new Map<string, number>();
  nodes.forEach(node => inDegree.set(node, 0));

  for (const [from, to] of edges) {
    if (!nodes.includes(from)) throw new Error(`Node ${from} not found`);
    if (!nodes.includes(to)) throw new Error(`Node ${to} not found`);

    inDegree.set(to, inDegree.get(to)! + 1);
  }

  const freeNodes = nodes.filter(node => inDegree.get(node)! === 0);

  for (const node of freeNodes) {
    if (!nodes.includes(node)) throw new Error(`Node ${node} not found`);

    const dependentNodes = edges.filter(([from, to]) => from === node);
    const sortedOrder = [node];

    while (dependentNodes.length > 0) {
      const [nextDependent] = dependentNodes.shift()!;
      if (!nodes.includes(nextDependent)) throw new Error(`Node ${nextDependent} not found`);

      sortedOrder.push(nextDependent);

      for (const [from, to] of edges) {
        if (to === nextDependent) {
          inDegree.set(from, inDegree.get(from)! - 1);
          dependentNodes.push([from]);
        }
      }

      freeNodes = nodes.filter(node => inDegree.get(node)! === 0);
    }

    for (const node of sortedOrder) {
      if (!nodes.includes(node)) throw new Error(`Node ${node} not found`);
    }
  }

  const topoOrder = [...freeNodes];
  for (const [from, to] of edges) {
    if (topoOrder.indexOf(from) < topoOrder.indexOf(to)) {
      const sortedOrder = [...topoOrder].sort((a, b) => inDegree.get(a)! - inDegree.get(b));
      throw new Error(`Cycle detected: ${sortedOrder.join(' -> ')} -> ${to}`);
    }
  }

  return topoOrder;
}
```