Skip to main content
Glama
slug.ts6.44 kB
import { randomStringAndNumberByLength } from 'diginext-utils/dist/string/random'; import isEmpty from 'lodash/isEmpty'; import { containsEmoji, filterSpecialChars } from './contain'; import { containsChinese } from './contain'; declare let XRegExp: any; export type MakeSlugOptions = { delimiter?: string; lowercase?: boolean; replacements?: { [key: string]: string }; transliterate?: boolean; limit?: number; }; const defaults: MakeSlugOptions = { delimiter: '-', lowercase: true, replacements: {}, transliterate: typeof XRegExp === 'undefined' ? true : false, }; const char_map: { [key: string]: any } = { // Latin À: 'A', Á: 'A', Â: 'A', Ã: 'A', Ä: 'A', Å: 'A', Æ: 'AE', Ç: 'C', È: 'E', É: 'E', Ê: 'E', Ë: 'E', Ì: 'I', Í: 'I', Î: 'I', Ï: 'I', Ð: 'D', Ñ: 'N', Ò: 'O', Ó: 'O', Ô: 'O', Õ: 'O', Ö: 'O', Ő: 'O', Ø: 'O', Ù: 'U', Ú: 'U', Û: 'U', Ü: 'U', Ű: 'U', Ý: 'Y', Þ: 'TH', ß: 'ss', à: 'a', á: 'a', â: 'a', ã: 'a', ä: 'a', å: 'a', æ: 'ae', ç: 'c', è: 'e', é: 'e', ê: 'e', ë: 'e', ì: 'i', í: 'i', î: 'i', ï: 'i', ð: 'd', ñ: 'n', ò: 'o', ó: 'o', ô: 'o', õ: 'o', ö: 'o', ő: 'o', ø: 'o', ù: 'u', ú: 'u', û: 'u', ü: 'u', ű: 'u', ý: 'y', þ: 'th', ÿ: 'y', // Latin symbols '©': '(c)', // Greek Α: 'A', Β: 'B', Γ: 'G', Δ: 'D', Ε: 'E', Ζ: 'Z', Η: 'H', Θ: '8', Ι: 'I', Κ: 'K', Λ: 'L', Μ: 'M', Ν: 'N', Ξ: '3', Ο: 'O', Π: 'P', Ρ: 'R', Σ: 'S', Τ: 'T', Υ: 'Y', Φ: 'F', Χ: 'X', Ψ: 'PS', Ω: 'W', Ά: 'A', Έ: 'E', Ί: 'I', Ό: 'O', Ύ: 'Y', Ή: 'H', Ώ: 'W', Ϊ: 'I', Ϋ: 'Y', α: 'a', β: 'b', γ: 'g', δ: 'd', ε: 'e', ζ: 'z', η: 'h', θ: '8', ι: 'i', κ: 'k', λ: 'l', μ: 'm', ν: 'n', ξ: '3', ο: 'o', π: 'p', ρ: 'r', σ: 's', τ: 't', υ: 'y', φ: 'f', χ: 'x', ψ: 'ps', ω: 'w', ά: 'a', έ: 'e', ί: 'i', ό: 'o', ύ: 'y', ή: 'h', ώ: 'w', ς: 's', ϊ: 'i', ΰ: 'y', ϋ: 'y', ΐ: 'i', // Turkish Ş: 'S', İ: 'I', Ğ: 'G', ş: 's', ı: 'i', ğ: 'g', // Russian А: 'A', Б: 'B', В: 'V', Г: 'G', Д: 'D', Е: 'E', Ё: 'Yo', Ж: 'Zh', З: 'Z', И: 'I', Й: 'J', К: 'K', Л: 'L', М: 'M', Н: 'N', О: 'O', П: 'P', Р: 'R', С: 'S', Т: 'T', У: 'U', Ф: 'F', Х: 'H', Ц: 'C', Ч: 'Ch', Ш: 'Sh', Щ: 'Sh', Ъ: '', Ы: 'Y', Ь: '', Э: 'E', Ю: 'Yu', Я: 'Ya', а: 'a', б: 'b', в: 'v', г: 'g', д: 'd', е: 'e', ё: 'yo', ж: 'zh', з: 'z', и: 'i', й: 'j', к: 'k', л: 'l', м: 'm', н: 'n', о: 'o', п: 'p', р: 'r', с: 's', т: 't', у: 'u', ф: 'f', х: 'h', ц: 'c', ч: 'ch', ш: 'sh', щ: 'sh', ъ: '', ы: 'y', ь: '', э: 'e', ю: 'yu', я: 'ya', // Ukrainian Є: 'Ye', І: 'I', Ї: 'Yi', Ґ: 'G', є: 'ye', і: 'i', ї: 'yi', ґ: 'g', // Czech Č: 'C', Ď: 'D', Ě: 'E', Ň: 'N', Ř: 'R', Š: 'S', Ť: 'T', Ů: 'U', Ž: 'Z', č: 'c', ď: 'd', ě: 'e', ň: 'n', ř: 'r', š: 's', ť: 't', ů: 'u', ž: 'z', // Polish Ą: 'A', Ć: 'C', Ę: 'e', Ł: 'L', Ń: 'N', Ś: 'S', Ź: 'Z', Ż: 'Z', ą: 'a', ć: 'c', ę: 'e', ł: 'l', ń: 'n', ś: 's', ź: 'z', ż: 'z', // Latvian Ā: 'A', Ē: 'E', Ģ: 'G', Ī: 'i', Ķ: 'k', Ļ: 'L', Ņ: 'N', Ū: 'u', ā: 'a', ē: 'e', ģ: 'g', ī: 'i', ķ: 'k', ļ: 'l', ņ: 'n', ū: 'u', }; export const makeSlug = (input: string, opt: MakeSlugOptions = {}) => { if (!input) throw new Error(`"input" argument is required.`); // convert any input type to string ;) let str = `${input}`; str = filterSpecialChars(str); if (isEmpty(str) || containsChinese(str) || containsEmoji(str)) str = 'item'; // Merge options for (const key in defaults) { if (!Object.prototype.hasOwnProperty.call(opt, key)) (opt as any)[key] = (defaults as any)[key]; } // Vietnamese str = str.replace(/á|à|ả|ạ|ã|ă|ắ|ằ|ẳ|ẵ|ặ|â|ấ|ầ|ẩ|ẫ|ậ/gi, 'a'); str = str.replace(/é|è|ẻ|ẽ|ẹ|ê|ế|ề|ể|ễ|ệ/gi, 'e'); str = str.replace(/i|í|ì|ỉ|ĩ|ị/gi, 'i'); str = str.replace(/ó|ò|ỏ|õ|ọ|ô|ố|ồ|ổ|ỗ|ộ|ơ|ớ|ờ|ở|ỡ|ợ/gi, 'o'); str = str.replace(/ú|ù|ủ|ũ|ụ|ư|ứ|ừ|ử|ữ|ự/gi, 'u'); str = str.replace(/ý|ỳ|ỷ|ỹ|ỵ/gi, 'y'); str = str.replace(/đ/gi, 'd'); str = str.replace(/-----/gi, '-'); str = str.replace(/----/gi, '-'); str = str.replace(/---/gi, '-'); str = str.replace(/--/g, '-'); str = '@' + str + '@'; str = str.replace(/@-|-@|@/g, ''); // Make custom replacements for (const k in opt.replacements) { if (opt.replacements[k]) { str = str.replace(RegExp(k, 'g'), opt.replacements[k] as string); } } // Transliterate characters to ASCII if (opt.transliterate) for (const m in char_map) str = str.replace(RegExp(m, 'g'), char_map[m]); // Replace non-alphanumeric characters with our delimiter const alnum = typeof XRegExp === 'undefined' ? RegExp('[^a-z0-9]+', 'ig') : XRegExp('[^\\p{L}\\p{N}]+', 'ig'); str = str.replace(alnum, opt.delimiter || ''); // Remove duplicate delimiters str = str.replace( RegExp('[' + opt.delimiter + ']{2,}', 'g'), opt.delimiter || '', ); // Truncate slug to max. characters if (typeof opt.limit !== 'undefined') str = str.substring(0, opt.limit); // Remove delimiter from ends str = str.replace( RegExp('(^' + opt.delimiter + '|' + opt.delimiter + '$)', 'g'), '', ); return opt.lowercase ? str.toLowerCase() : str; }; /** * Create a unique slug by input string * @param prismaSchema Prisma schema * @param input Input string * @param opt Make slug options * @returns {string} Unique slug */ export async function makeUniqueSlug( prismaSchema: any, input: string, opt: MakeSlugOptions & { field: string; atempt: number } = { field: 'slug', atempt: 0, }, ) { const slug = makeSlug(input, opt); const uniqueSlug = await prismaSchema.findUnique({ where: { [opt.field]: slug }, }); if (uniqueSlug) { opt.atempt++; return makeUniqueSlug(prismaSchema, `${input}-${opt.atempt}`, { ...opt, field: opt.field, }); } return slug; } export function makeSlugByName(name: string) { try { return `${makeSlug(name)}-${randomStringAndNumberByLength(8)}`; } catch (error) { throw new Error( `Make Slug By Name failed: ${ error instanceof Error ? error.message : 'Unknown error' }`, ); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mrgoonie/reviewwebsite-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server