```ts
export function leafPaths(rows: [string, string][]): string[] {
  if (rows.length === 0) throw new Error("Input cannot be empty");

  const idToParent = new Map<string, string>();
  const parentToChildren = new Map<string, Set<string>>();

  for (const [id, parentId] of rows) {
    if (idToParent.has(id)) throw new Error(`Duplicate id: ${id}`);
    idToParent.set(id, parentId);

    if (!parentToChildren.has(parentId)) {
      parentToChildren.set(parentId, new Set());
    }
    parentToChildren.get(parentId)!.add(id);
  }

  const rootRows = rows.filter(([_, parentId]) => parentId === "");
  if (rootRows.length !== 1) throw new Error("Input must have exactly one root row");

  const rootNode = rootRows[0][0];
  const allNodes = new Set(rows.map(([id, _]) => id).concat(rows.map(([_, parentId]) => parentId)));
  for (const node of allNodes) {
    if (!idToParent.has(node)) throw new Error(`Node ${node} cannot be reached from the root`);
  }

  function findLeafPaths(currentNode: string, path: string): string[] {
    if (!parentToChildren.has(currentNode)) return [path];
    const children = parentToChildren.get(currentNode)!;
    return Array.from(children).flatMap(child =>
      findLeafPaths(child, `${path}/${child}`)
    );
  }

  const leafPaths = findLeafPaths(rootNode, rootNode);
  return leafPaths.sort();
}
```