31 lines
887 B
TypeScript
31 lines
887 B
TypeScript
export function slugify(input: string): string {
|
|
return input
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/['"]/g, "")
|
|
.replace(/[^a-z0-9]+/g, "-")
|
|
.replace(/^-+|-+$/g, "");
|
|
}
|
|
|
|
export async function generateUniqueSlug(opts: {
|
|
base: string;
|
|
isTaken: (slug: string) => Promise<boolean>;
|
|
maxAttempts?: number;
|
|
}): Promise<string> {
|
|
const maxAttempts = opts.maxAttempts ?? 50;
|
|
const normalizedBase = slugify(opts.base) || "item";
|
|
|
|
let candidate = normalizedBase;
|
|
for (let i = 0; i < maxAttempts; i++) {
|
|
// First try the base, then base-2, base-3, ...
|
|
candidate = i === 0 ? normalizedBase : `${normalizedBase}-${i + 1}`;
|
|
// eslint-disable-next-line no-await-in-loop
|
|
const taken = await opts.isTaken(candidate);
|
|
if (!taken) return candidate;
|
|
}
|
|
|
|
// Last resort: append timestamp to avoid collisions
|
|
return `${normalizedBase}-${Date.now()}`;
|
|
}
|
|
|