From de4a60692faf8c28a55f00c2e329a823f3462c67 Mon Sep 17 00:00:00 2001 From: Arunendra21 <156455722+Arunendra21@users.noreply.github.com> Date: Mon, 3 Aug 2026 21:53:04 +0530 Subject: [PATCH] fix: keep org slugs free of trailing dashes after truncation slugify stripped leading and trailing dashes before truncating to 48 characters, so when the 48 character cut landed on a "-" separator the final slug still ended with a dash. For example a long organization name could produce a slug like "my-org-", which then became "my-org--suffix" once the suffix was appended in default-hosted-organization.ts. Truncate first and strip the dashes afterwards so the slug never keeps a leading or trailing dash. Normal inputs are unchanged. Adds unit tests for slugify and toHex. Co-authored-by: eeshsaxena --- src/server/auth/org-slug.test.ts | 34 ++++++++++++++++++++++++++++++++ src/server/auth/org-slug.ts | 6 ++++-- 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 src/server/auth/org-slug.test.ts diff --git a/src/server/auth/org-slug.test.ts b/src/server/auth/org-slug.test.ts new file mode 100644 index 000000000..1f1919bce --- /dev/null +++ b/src/server/auth/org-slug.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; + +import { slugify, toHex } from "@/server/auth/org-slug"; + +describe("slugify", () => { + it("slugifies normal values", () => { + expect(slugify("My Workspace")).toBe("my-workspace"); + expect(slugify(" Hello, World! ")).toBe("hello-world"); + }); + + it("strips leading and trailing separators", () => { + expect(slugify("---weird---")).toBe("weird"); + }); + + it("falls back to 'workspace' when nothing is left", () => { + expect(slugify("")).toBe("workspace"); + expect(slugify("!!!")).toBe("workspace"); + }); + + it("does not leave a trailing dash when truncation lands on a separator", () => { + const value = `${"a".repeat(47)} extra words`; + const slug = slugify(value); + + expect(slug.length).toBeLessThanOrEqual(48); + expect(slug.endsWith("-")).toBe(false); + expect(slug).toBe("a".repeat(47)); + }); +}); + +describe("toHex", () => { + it("encodes a string as lowercase hex", () => { + expect(toHex("abc")).toBe("616263"); + }); +}); diff --git a/src/server/auth/org-slug.ts b/src/server/auth/org-slug.ts index ede1ff5b2..5971361a2 100644 --- a/src/server/auth/org-slug.ts +++ b/src/server/auth/org-slug.ts @@ -1,10 +1,12 @@ export function slugify(value: string) { + // Truncate before trimming dashes so a cut that lands on a "-" separator + // does not leave a leading or trailing dash in the final slug. const slug = value .toLowerCase() .trim() .replace(/[^a-z0-9]+/g, "-") - .replace(/^-+|-+$/g, "") - .slice(0, 48); + .slice(0, 48) + .replace(/^-+|-+$/g, ""); return slug || "workspace"; }