import { compileAllowlist, resolveCompiledAllowlistMatch, type AllowlistMatch, } from "../../../../src/channels/allowlist-match.js"; import { normalizeHyphenSlug, normalizeStringEntries, normalizeStringEntriesLower, } from "../../../../src/shared/string-normalization.js"; const SLACK_SLUG_CACHE_MAX = 512; const slackSlugCache = new Map(); export function normalizeSlackSlug(raw?: string) { const key = raw ?? ""; const cached = slackSlugCache.get(key); if (cached !== undefined) { return cached; } const normalized = normalizeHyphenSlug(raw); slackSlugCache.set(key, normalized); if (slackSlugCache.size > SLACK_SLUG_CACHE_MAX) { const oldest = slackSlugCache.keys().next(); if (!oldest.done) { slackSlugCache.delete(oldest.value); } } return normalized; } export function normalizeAllowList(list?: Array) { return normalizeStringEntries(list); } export function normalizeAllowListLower(list?: Array) { return normalizeStringEntriesLower(list); } export function normalizeSlackAllowOwnerEntry(entry: string): string | undefined { const trimmed = entry.trim().toLowerCase(); if (!trimmed || trimmed === "*") { return undefined; } const withoutPrefix = trimmed.replace(/^(slack:|user:)/, ""); return /^u[a-z0-9]+$/.test(withoutPrefix) ? withoutPrefix : undefined; } export type SlackAllowListMatch = AllowlistMatch< "wildcard" | "id" | "prefixed-id" | "prefixed-user" | "name" | "prefixed-name" | "slug" >; type SlackAllowListSource = Exclude; export function resolveSlackAllowListMatch(params: { allowList: string[]; id?: string; name?: string; allowNameMatching?: boolean; }): SlackAllowListMatch { const compiledAllowList = compileAllowlist(params.allowList); const id = params.id?.toLowerCase(); const name = params.name?.toLowerCase(); const slug = normalizeSlackSlug(name); const candidates: Array<{ value?: string; source: SlackAllowListSource }> = [ { value: id, source: "id" }, { value: id ? `slack:${id}` : undefined, source: "prefixed-id" }, { value: id ? `user:${id}` : undefined, source: "prefixed-user" }, ...(params.allowNameMatching === true ? ([ { value: name, source: "name" as const }, { value: name ? `slack:${name}` : undefined, source: "prefixed-name" as const }, { value: slug, source: "slug" as const }, ] satisfies Array<{ value?: string; source: SlackAllowListSource }>) : []), ]; return resolveCompiledAllowlistMatch({ compiledAllowlist: compiledAllowList, candidates, }); } export function allowListMatches(params: { allowList: string[]; id?: string; name?: string; allowNameMatching?: boolean; }) { return resolveSlackAllowListMatch(params).allowed; } export function resolveSlackUserAllowed(params: { allowList?: Array; userId?: string; userName?: string; allowNameMatching?: boolean; }) { const allowList = normalizeAllowListLower(params.allowList); if (allowList.length === 0) { return true; } return allowListMatches({ allowList, id: params.userId, name: params.userName, allowNameMatching: params.allowNameMatching, }); }