Ctrl K
ComunidadGuías…

shared/utils/domains.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { trim } from "es-toolkit/compat";
import env from "../env";

type Domain = {
  teamSubdomain: string;
  host: string;
  port?: string;
  custom: boolean;
};

/**
 * Removes the top level domain from the argument and slugifies it
 *
 * @param domain Domain string to slugify
 * @returns String with only non top-level domains
 */
export function slugifyDomain(domain: string) {
  return domain.split(".").slice(0, -1).join("-");
}

// strips protocol, userinfo, port, path, query, fragment, and whitespace from input
// to extract a clean hostname
function normalizeUrl(url: string) {
  const stripped = trim(url.replace(/(https?:)?\/\//i, ""));
  // Extract authority, which ends at the same delimiters as in a browser
  const authority = stripped.split(/[/\\?#]/)[0];
  // Strip userinfo if present (e.g. "user:pass@host" → "host")
  const atIndex = authority.lastIndexOf("@");
  const hostWithPort =
    atIndex !== -1 ? authority.substring(atIndex + 1) : authority;
  const host = hostWithPort.split(":")[0];
  // Hostnames are case-insensitive and may carry a trailing root label
  return host.toLowerCase().replace(/\.$/, "");
}

// The base domain is where root cookies are set in hosted mode
// It's also appended to a team's hosted subdomain to form their app URL
export function getBaseDomain() {
  const normalEnvUrl = normalizeUrl(env.URL);
  const tokens = normalEnvUrl.split(".");

  // remove reserved subdomains like "app"
  // from the env URL to form the base domain
  return tokens.length > 1 && RESERVED_SUBDOMAINS.includes(tokens[0])
    ? tokens.slice(1).join(".")
    : normalEnvUrl;
}

// we originally used the parse-domain npm module however this includes
// a large list of possible TLD's which increase the size of the bundle
// unnecessarily for our usecase of trusted input.
export function parseDomain(url: string): Domain {
  if (!url) {
    throw new TypeError("a non-empty url is required");
  }

  let port;
  try {
    const parsedUrl = new URL(url);
    port = parsedUrl.port || undefined;
  } catch (_err) {
    // ignore
  }

  const host = normalizeUrl(url);
  const baseDomain = getBaseDomain();
  const suffix = `.${baseDomain}`;

  // the host must be the base domain, or end with it on a label boundary,
  // otherwise it is a custom domain
  if (host !== baseDomain && !host.endsWith(suffix)) {
    return { teamSubdomain: "", host, port: undefined, custom: true };
  }

  // we consider anything in front of the baseUrl to be the subdomain
  const subdomain =
    host === baseDomain ? "" : host.substring(0, host.length - suffix.length);
  const isReservedSubdomain = RESERVED_SUBDOMAINS.includes(subdomain);

  return {
    teamSubdomain: isReservedSubdomain ? "" : subdomain,
    host,
    port,
    custom: false,
  };
}

export function getCookieDomain(domain: string, isCloudHosted: boolean) {
  // always use the base URL for cookies when in hosted mode
  // and the domain is not custom
  if (isCloudHosted) {
    const parsed = parseDomain(domain);

    if (!parsed.custom) {
      return getBaseDomain();
    }
  }

  return domain;
}

export const RESERVED_SUBDOMAINS = [
  "about",
  "account",
  "admin",
  "advertising",
  "api",
  "app",
  "assets",
  "archive",
  "beta",
  "billing",
  "blog",
  "cache",
  "cdn",
  "code",
  "community",
  "dashboard",
  "developer",
  "developers",
  "forum",
  "help",
  "home",
  "http",
  "https",
  "imap",
  "localhost",
  "mail",
  "marketing",
  "mcp",
  "mobile",
  "multiplayer",
  "new",
  "news",
  "newsletter",
  "ns1",
  "ns2",
  "ns3",
  "ns4",
  "password",
  "profile",
  "realtime",
  "sandbox",
  "script",
  "scripts",
  "setup",
  "signin",
  "signup",
  "site",
  "smtp",
  "support",
  "status",
  "static",
  "stats",
  "test",
  "update",
  "updates",
  "ws",
  "wss",
  "web",
  "websockets",
  "www",
  "www1",
  "www2",
  "www3",
  "www4",
];