MCPcopy
hub / github.com/nowork-studio/NotFair / slugify

Function slugify

notfair-cmo/src/lib/slug.ts:22–56  ·  view source on GitHub ↗
(input: string, maxLen = 40)

Source from the content-addressed store, hash-verified

20 | { ok: false; reason: string };
21
22export function slugify(input: string, maxLen = 40): SlugResult {
23 const trimmed = input.trim();
24 if (!trimmed) return { ok: false, reason: "input is empty" };
25
26 const ascii = trimmed
27 .toLowerCase()
28 .normalize("NFKD")
29 .replace(/[̀-ͯ]/g, "")
30 .replace(/[^a-z0-9\s-]/g, "")
31 .replace(/\s+/g, "-")
32 .replace(/-+/g, "-")
33 .replace(/^-|-$/g, "");
34
35 if (!ascii) return { ok: false, reason: "no valid characters" };
36
37 const capped = ascii.slice(0, maxLen).replace(/-+$/g, "");
38
39 if (!SLUG_PATTERN.test(capped)) {
40 return { ok: false, reason: "result does not match slug pattern" };
41 }
42
43 if (RESERVED_SLUGS.has(capped)) {
44 // Explicitly say "system word" — the previous wording ("'notfair'
45 // is reserved") read like a row collision and led users to
46 // delete-and-retry expecting the conflict to clear, when really
47 // the slug is on a static block-list. Suggest a workaround so the
48 // user isn't stuck guessing.
49 return {
50 ok: false,
51 reason: `"${capped}" is a reserved system name — try a variation like "${capped}-team" or "${capped}-1".`,
52 };
53 }
54
55 return { ok: true, slug: capped };
56}
57
58export function isValidSlug(slug: string): boolean {
59 return SLUG_PATTERN.test(slug) && !RESERVED_SLUGS.has(slug);

Callers 9

AgentRenameCardFunction · 0.90
ProjectRenameCardFunction · 0.90
slug.test.tsFile · 0.90
addUserMcpServerActionFunction · 0.90
createAgentActionFunction · 0.90
renameProjectFullActionFunction · 0.90
scheduleCronActionFunction · 0.90
cloneAgentFunction · 0.90
createProjectFunction · 0.90

Calls

no outgoing calls

Tested by

no test coverage detected