/*
RFC-0361: Centralized naming policy regexes and policy descriptors for Sternsystem, Mission, Release, and Bordbuch ids. RFC-0902: add KNOWN_TLDS and hasTldSuffix() for TLD-suffix detection.
- Does not define filename policies — that is DNA-6 / RFC-0360.
- Does not define content-level naming — that is RFC-0047.
- RFC-0361: initial centralized naming policy regexes and descriptors.
- RFC-0902: add KNOWN_TLDS set and hasTldSuffix() function; update examples to TLD-free IDs.
*/
export const STERNSYSTEM_ID_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
export const MISSION_ID_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*-m\d{6}$/;
export const RELEASE_ID_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*-r\d{6}$/;
export const BORDBUCH_EVENT_ID_REGEX = /^event-\d{6}$/;
export const NON_ASCII_REGEX = /[^\x00-\x7F]/;
// RFC-0902: Known TLD suffixes that must not appear as the last segment of a Sternsystem ID.
// Conservative list — add TLDs as needed. Removing a TLD from this set is a code change, not an RFC.
export const KNOWN_TLDS: ReadonlySet = new Set([
"com",
"org",
"net",
"de",
"at",
"ch",
"it",
"fr",
"es",
"nl",
"be",
"pl",
"se",
"no",
"dk",
"fi",
"pt",
"cz",
"sk",
"hu",
"ro",
"bg",
"hr",
"si",
"lt",
"lv",
"ee",
"ie",
"lu",
"mt",
"cy",
"gr",
"uk",
"us",
"ca",
"au",
"nz",
"io",
"co",
"ai",
"dev",
"app",
"eu",
"info",
"biz",
"me",
"tv",
"gg",
"to",
"xyz",
// "site" intentionally absent — legitimate suffix in system IDs (forge-site), not a domain-derived TLD marker.
"online",
"store",
"shop",
"blog",
"cloud",
]);
// RFC-0902: Returns true if the last hyphen-separated segment of the id is a known TLD.
// Single-segment IDs (e.g. "com", "warpgogol") return false — the concern is suffix segments.
export function hasTldSuffix(id: string): boolean {
const segments = id.split("-");
if (segments.length < 2) return false;
return KNOWN_TLDS.has(segments[segments.length - 1]);
}
export const STERNSYSTEM_ID_POLICY = {
regex: STERNSYSTEM_ID_REGEX,
charset: "ASCII lowercase letters (a-z), digits (0-9), hyphens (-)",
description: "kebab-case, lowercase, latin-only",
examples: ["warpgogol", "nicaragua-projekt"],
counterExamples: [
"Warpgogol-Com",
"nicaragüa-projekt",
"warpgogol--com",
"-warpgogol",
"warpgogol-",
],
tldCounterExamples: ["warpgogol-com", "nicaragua-projekt-org", "example-de"],
} as const;
export const MISSION_ID_POLICY = {
regex: MISSION_ID_REGEX,
format: "-m",
description: "system id + literal -m + zero-padded six-digit sequence",
examples: ["warpgogol-m000001", "nicaragua-projekt-m000042"],
counterExamples: ["warpgogol-com-m1", "warpgogol-com-M000001", "warpgogol-com-m0000001"],
} as const;
export const RELEASE_ID_POLICY = {
regex: RELEASE_ID_REGEX,
format: "-r",
description: "system id + literal -r + zero-padded six-digit sequence",
examples: ["warpgogol-r000001", "nicaragua-projekt-r000042"],
counterExamples: ["warpgogol-com-r1", "warpgogol-com-R000001", "warpgogol-com-r0000001"],
} as const;
export const BORDBUCH_EVENT_ID_POLICY = {
regex: BORDBUCH_EVENT_ID_REGEX,
format: "event-",
description: "literal 'event-' + zero-padded six-digit sequence",
examples: ["event-000001", "event-000042"],
counterExamples: ["event-1", "event-0000001", "EVENT-000001"],
} as const;
export function isLatinOnly(value: string): boolean {
return !NON_ASCII_REGEX.test(value);
}