```ts
export function topologicalSort(nodes: readonly string[], edges: readonly (readonly [string, string])[]): string[] {
  if (!nodes.length) return [];
  
  const graph = new Map<string, Set<string>>();
  const indegree = new Map<string, number>();

  // Build the graph and initialize indegree for each node
  nodes.forEach(node => {
    graph.set(node, new Set<string>());
    indegree.set(node, 0);
  });
  edges.forEach(([from, to]) => {
    if (!graph.has(from) || !graph.has(to)) {
      throw new Error("Edge names must be in nodes");
    }
    graph.get(from)!.add(to);
    indegree.set(to, (indegree.get(to) || 0) + 1);
  });

  // Find all sources with zero indegree
  const sources = Array.from(indegree.entries()).filter(([node, count]) => count === 0).map(([node]) => node);

  let result: string[] = [];
  while (sources.length > 0) {
    const source = sources.shift()!;
    result.push(source);
    
    // Decrease the indegree of all nodes connected to this source
    sources.push(...graph.get(source)!.values());
    graph.delete(source);
  }

  if (result.length !== nodes.length) {
    throw new Error("Cycle detected in edges");
  }
  
  return result;
}
```