Repository: apache/superset
File: superset-frontend/src/utils/urlUtils.ts (TypeScript)

Code before:
}: {
  dashboardId: string | number;
  /**
   * Current applied data masks (for native filters).
   */
  dataMask: JsonObject;
  /**
   * Current active tabs in the dashboard.
   */
  activeTabs: string[];
  /**
   * The "anchor" component for the permalink. It will be scrolled into view
   * and highlighted upon page load.
   */
  anchor?: string;
  /**
   * Chart-level state (column order, sorting, filtering, etc.)
   */
  chartStates?: JsonObject;
  /**
   * Whether to include chart state in the permalink (default: false)
   */
  includeChartState?: boolean;
}) {
  const payload: JsonObject = {
    urlParams: getDashboardUrlParams(['edit']),
    dataMask,
    activeTabs,
    anchor,
  };

  // Only include chart states when explicitly requested AND when they exist
  if (includeChartState && chartStates && Object.keys(chartStates).length > 0) {
    payload.chartStates = chartStates;
  }

  return getPermalink(`/api/v1/dashboard/${dashboardId}/permalink`, payload);
}

const externalUrlRegex =
  /^([^:/?#]+:)?(?:(\/\/)?([^/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/;

// group 1 matches protocol
// group 2 matches '//'
// group 3 matches hostname
export function isUrlExternal(url: string) {
  const match = url.match(externalUrlRegex) || [];
  return (
    (typeof match[1] === 'string' && match[1].length > 0) ||
    match[2] === '//' ||
    (typeof match[3] === 'string' && match[3].length > 0)

Review comment (suggestion):
I think we should include `edit` in `URL_PARAMS` and later `RESERVED_DASHBOARD_URL_PARAMS`:
- https://github.com/apache/superset/blob/ae63f647710f86bc120c2e7a25ca2ea87aa75448/superset-frontend/src/constants.ts#L25
- https://github.com/apache/superset/blob/ae63f647710f86bc120c2e7a25ca2ea87aa75448/superset-frontend/src/constants.ts#L115-L120

Code after:
}: {
  dashboardId: string | number;
  /**
   * Current applied data masks (for native filters).
   */
  dataMask: JsonObject;
  /**
   * Current active tabs in the dashboard.
   */
  activeTabs: string[];
  /**
   * The "anchor" component for the permalink. It will be scrolled into view
   * and highlighted upon page load.
   */
  anchor?: string;
  /**
   * Chart-level state (column order, sorting, filtering, etc.)
   */
  chartStates?: JsonObject;
  /**
   * Whether to include chart state in the permalink (default: false)
   */
  includeChartState?: boolean;
}) {
  const payload: JsonObject = {
    urlParams: getDashboardUrlParams(),
    dataMask,
    activeTabs,
    anchor,
  };

  // Only include chart states when explicitly requested AND when they exist
  if (includeChartState && chartStates && Object.keys(chartStates).length > 0) {
    payload.chartStates = chartStates;
  }

  return getPermalink(`/api/v1/dashboard/${dashboardId}/permalink`, payload);
}

const externalUrlRegex =
  /^([^:/?#]+:)?(?:(\/\/)?([^/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/;

// group 1 matches protocol
// group 2 matches '//'
// group 3 matches hostname
export function isUrlExternal(url: string) {
  const match = url.match(externalUrlRegex) || [];
  return (
    (typeof match[1] === 'string' && match[1].length > 0) ||
    match[2] === '//' ||
    (typeof match[3] === 'string' && match[3].length > 0)