```typescript
/** Prune a file listing with ordered drop and keep rules. */

function segmentFits(pattern: string, text: string): boolean {
  let p = 0;
  let t = 0;
  let star = -1;
  let mark = 0;
  while (t < text.length) {
    if (p < pattern.length && pattern[p] === "*") {
      star = p;
      mark = t;
      p += 1;
    } else if (p < pattern.length && pattern[p] === text[t]) {
      p += 1;
      t += 1;
    } else if (star >= 0) {
      p = star + 1;
      mark += 1;
      t = mark;
    } else {
      return false;
    }
  }
  while (p < pattern.length && pattern[p] === "*") {
    p += 1;
  }
  return p === pattern.length;
}

function splitStrict(text: string, what: string): string[] {
  if (typeof text !== "string" || text === "") {
    throw new Error(`${what} must be a non-empty string`);
  }
  const segments = text.split("/");
  if (segments.some((segment) => segment === "")) {
    throw new Error(`${what} has an empty segment: ${text}`);
  }
  return segments;
}

export function pruneListing(listing: string[], rules: string[]): string[] {
  if (!Array.isArray(listing) || !listing.every(String) || listing.includes("")) {
    throw new Error("invalid listing");
  }
  if (!Array.isArray(rules) || !rules.every(String) || rules.some(String.startsWith("!") && rules.length === 1)) {
    throw new Error("invalid rules");
  }

  const normalize = (path: string): [string[], boolean] => {
    const segments = splitStrict(path, "path");
    return [segments.map((segment) => segment.replace(/^!+/, "")), segments[0].startsWith("!")];
  };

  const matchesRule = ([patternSegments, isKeep]: [string[], boolean], [pathSegments]: [string[]]): boolean => {
    if (isKeep && pathSegments.length === 0) return false;
    if (!isKeep && patternSegments.length > pathSegments.length) return false;

    let i = 0;
    for (const segment of patternSegments) {
      if (segmentFits(segment, pathSegments[i])) {
        i += 1;
      } else if (segment !== "*") {
        return false;
      }
    }

    return true;
  };

  return listing.filter((path) => matchesRule(normalize(path), normalize(rules[0])));
}
```