2.7 KiB
validate — tenant-label validation
(no submodules)
Purpose
Provides the default RFC-1035-style hostname-label validator
(defaultLabelValidator) plus two pure string helpers (normalizeHost,
stripBaseDomain) that the resolver uses to take an inbound Host
header all the way down to a candidate tenant slug. Splitting these
helpers out of the resolver body keeps the resolver readable and the
host-parsing edge cases unit-testable in isolation.
Tenant labels are persisted as tenantId and compared against by
downstream RBAC / row-level filters, so the canonical form is enforced
here: lower-case ASCII, 1..63 characters, [a-z0-9-], no leading or
trailing hyphen.
Features
defaultLabelValidator— RFC 1035 §2.3.1 LDH-style validator.MAX_LABEL_LENGTHconstant exported for downstream schemas.normalizeHost(raw)— strip:portsuffix, lower-case, refuse the empty / starts-with-:cases.stripBaseDomain(host, baseDomain)— tri-state return:string(labels),null(exact match — no tenant),undefined(mismatch — caller decides whether to raise).
Usage: Basic
import {
defaultLabelValidator,
normalizeHost,
stripBaseDomain,
} from "@suaveplan/api-tenant-resolver-subdomain/validate";
const host = normalizeHost("Acme.API.Example.com:443");
// → "acme.api.example.com"
const labels = stripBaseDomain(host!, "api.example.com");
// → "acme"
defaultLabelValidator(labels!); // → true
Usage: Advanced
import { defaultLabelValidator } from "@suaveplan/api-tenant-resolver-subdomain/validate";
const RESERVED = new Set(["www", "admin", "api"]);
const customValidator = (label: string): boolean => {
if (!defaultLabelValidator(label)) return false;
return !RESERVED.has(label);
};
// → pass `customValidator` as `validate` in SubdomainTenantResolverOptions
API Reference
LabelValidator—(label: string) => boolean.defaultLabelValidator: LabelValidator.MAX_LABEL_LENGTH: 63.normalizeHost(raw: string | null | undefined): string | null.stripBaseDomain(host, baseDomain): string | null | undefined.
Implementation Notes
The label validator is hand-coded against the character-code range
(0x30..0x39, 0x61..0x7a, 0x2d) rather than a regex so the
hot-path has no allocator overhead. The tri-state stripBaseDomain
return is the standard "missing vs absent vs malformed" pattern from
the rest of the codebase: callers MUST distinguish "host is exactly the
apex" (advance the chain — common for marketing pages) from "host has
the wrong apex" (strict-mode error). normalizeHost does not honour
IPv6 bracketed literals ([::1]) — tenant resolution by IPv6 host is
not a supported deployment shape.