/* * A TypeScript mirror of shared/provision's slug rules, used ONLY to preview the * host a rename would move an instance to while the customer types. * * It is a second implementation of Slugify, BaseSlug and ReservedSlugs, and it * must change in the same commit as the Go one — the same hazard as * web/lib/targets.ts. The preview is a courtesy; the server's 409 is the * boundary, and the two are allowed to disagree without anything breaking. */ /** Mirrors provision.MinSlugLength / MaxSlugLength. */ export const MIN_SLUG_LENGTH = 3; export const MAX_SLUG_LENGTH = 40; /** Mirrors provision.ReservedSlugs. */ const RESERVED = new Set([ "www", "api", "app", "admin", "auth", "install", "static", "_next", "default", ]); /* * The tenant subdomain namespace. Also hardcoded in InstanceRecord.tsx and the * customer instance page; those predate this file and are left alone rather than * refactored under a rename change. */ export const INSTANCE_DOMAIN = "vantage.hostxtra.co.uk"; /** Mirrors provision.Slugify. */ export function slugify(name: string): string { return name .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, ""); } /** Mirrors provision.BaseSlug's truncation. */ export function baseSlug(name: string): string { return slugify(name).slice(0, MAX_SLUG_LENGTH); } /** The reason a name cannot become a slug, or undefined when it can. */ export function slugError(name: string): string | undefined { const base = slugify(name); if (base.length < MIN_SLUG_LENGTH) { return `Needs at least ${MIN_SLUG_LENGTH} letters or digits.`; } if (RESERVED.has(base.slice(0, MAX_SLUG_LENGTH))) { return "That name is reserved."; } return undefined; } /** The host an instance on this slug is reached at. */ export function hostFor(slug: string): string { return `${slug}.${INSTANCE_DOMAIN}`; }