function getItemKey(item) {
  // Do most specific checks at the top. most general at bottom..
  //
  // For sourcemapped opportunities that identify a source location
  const source = item.source;
  if (typeof source === 'string') return source;
  if (source && source.url) return `${source.url}:${source.line}:${source.column}`;
  // For the pre-grouped audits like resource-summary
  if (typeof item.label === 'string') return item.label;
  // For the pre-grouped audits like mainthread-work-breakdown
  if (typeof item.groupLabel === 'string') return item.groupLabel;
  // For user-timings
  if (typeof item.name === 'string') return item.name;
  // For dom-size
  if (typeof item.statistic === 'string') return item.statistic;
  // For third-party-summary
  if (item.entity && typeof item.entity.text === 'string') return item.entity.text;
  // For node
  if (typeof item.node?.path === 'string') return item.node.path;
  // Tap-targets
  if (
    typeof item.tapTarget?.path === 'string' &&
    typeof item.overlappingTarget?.path === 'string'
  ) {
    return `${item.tapTarget.path} + ${item.overlappingTarget.path}`;
  }
  // For most opportunities, diagnostics, etc where 1 row === 1 resource
  if (typeof item.url === 'string' && item.url) return item.url;
  if (typeof item.origin === 'string' && item.origin) return item.origin;

  // For everything else, use the entire object, actually works OK on most nodes.
  return JSON.stringify(deepPruneItemForKeySerialization(item));
}
@param {Record<string, any>} item @return {string}