Spaces:
Paused
Paused
| export type ChannelMatchSource = "direct" | "parent" | "wildcard"; | |
| export type ChannelEntryMatch<T> = { | |
| entry?: T; | |
| key?: string; | |
| wildcardEntry?: T; | |
| wildcardKey?: string; | |
| parentEntry?: T; | |
| parentKey?: string; | |
| matchKey?: string; | |
| matchSource?: ChannelMatchSource; | |
| }; | |
| export function applyChannelMatchMeta< | |
| TResult extends { matchKey?: string; matchSource?: ChannelMatchSource }, | |
| >(result: TResult, match: ChannelEntryMatch<unknown>): TResult { | |
| if (match.matchKey && match.matchSource) { | |
| result.matchKey = match.matchKey; | |
| result.matchSource = match.matchSource; | |
| } | |
| return result; | |
| } | |
| export function resolveChannelMatchConfig< | |
| TEntry, | |
| TResult extends { matchKey?: string; matchSource?: ChannelMatchSource }, | |
| >(match: ChannelEntryMatch<TEntry>, resolveEntry: (entry: TEntry) => TResult): TResult | null { | |
| if (!match.entry) { | |
| return null; | |
| } | |
| return applyChannelMatchMeta(resolveEntry(match.entry), match); | |
| } | |
| export function normalizeChannelSlug(value: string): string { | |
| return value | |
| .trim() | |
| .toLowerCase() | |
| .replace(/^#/, "") | |
| .replace(/[^a-z0-9]+/g, "-") | |
| .replace(/^-+|-+$/g, ""); | |
| } | |
| export function buildChannelKeyCandidates(...keys: Array<string | undefined | null>): string[] { | |
| const seen = new Set<string>(); | |
| const candidates: string[] = []; | |
| for (const key of keys) { | |
| if (typeof key !== "string") { | |
| continue; | |
| } | |
| const trimmed = key.trim(); | |
| if (!trimmed || seen.has(trimmed)) { | |
| continue; | |
| } | |
| seen.add(trimmed); | |
| candidates.push(trimmed); | |
| } | |
| return candidates; | |
| } | |
| export function resolveChannelEntryMatch<T>(params: { | |
| entries?: Record<string, T>; | |
| keys: string[]; | |
| wildcardKey?: string; | |
| }): ChannelEntryMatch<T> { | |
| const entries = params.entries ?? {}; | |
| const match: ChannelEntryMatch<T> = {}; | |
| for (const key of params.keys) { | |
| if (!Object.prototype.hasOwnProperty.call(entries, key)) { | |
| continue; | |
| } | |
| match.entry = entries[key]; | |
| match.key = key; | |
| break; | |
| } | |
| if (params.wildcardKey && Object.prototype.hasOwnProperty.call(entries, params.wildcardKey)) { | |
| match.wildcardEntry = entries[params.wildcardKey]; | |
| match.wildcardKey = params.wildcardKey; | |
| } | |
| return match; | |
| } | |
| export function resolveChannelEntryMatchWithFallback<T>(params: { | |
| entries?: Record<string, T>; | |
| keys: string[]; | |
| parentKeys?: string[]; | |
| wildcardKey?: string; | |
| normalizeKey?: (value: string) => string; | |
| }): ChannelEntryMatch<T> { | |
| const direct = resolveChannelEntryMatch({ | |
| entries: params.entries, | |
| keys: params.keys, | |
| wildcardKey: params.wildcardKey, | |
| }); | |
| if (direct.entry && direct.key) { | |
| return { ...direct, matchKey: direct.key, matchSource: "direct" }; | |
| } | |
| const normalizeKey = params.normalizeKey; | |
| if (normalizeKey) { | |
| const normalizedKeys = params.keys.map((key) => normalizeKey(key)).filter(Boolean); | |
| if (normalizedKeys.length > 0) { | |
| for (const [entryKey, entry] of Object.entries(params.entries ?? {})) { | |
| const normalizedEntry = normalizeKey(entryKey); | |
| if (normalizedEntry && normalizedKeys.includes(normalizedEntry)) { | |
| return { | |
| ...direct, | |
| entry, | |
| key: entryKey, | |
| matchKey: entryKey, | |
| matchSource: "direct", | |
| }; | |
| } | |
| } | |
| } | |
| } | |
| const parentKeys = params.parentKeys ?? []; | |
| if (parentKeys.length > 0) { | |
| const parent = resolveChannelEntryMatch({ entries: params.entries, keys: parentKeys }); | |
| if (parent.entry && parent.key) { | |
| return { | |
| ...direct, | |
| entry: parent.entry, | |
| key: parent.key, | |
| parentEntry: parent.entry, | |
| parentKey: parent.key, | |
| matchKey: parent.key, | |
| matchSource: "parent", | |
| }; | |
| } | |
| if (normalizeKey) { | |
| const normalizedParentKeys = parentKeys.map((key) => normalizeKey(key)).filter(Boolean); | |
| if (normalizedParentKeys.length > 0) { | |
| for (const [entryKey, entry] of Object.entries(params.entries ?? {})) { | |
| const normalizedEntry = normalizeKey(entryKey); | |
| if (normalizedEntry && normalizedParentKeys.includes(normalizedEntry)) { | |
| return { | |
| ...direct, | |
| entry, | |
| key: entryKey, | |
| parentEntry: entry, | |
| parentKey: entryKey, | |
| matchKey: entryKey, | |
| matchSource: "parent", | |
| }; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (direct.wildcardEntry && direct.wildcardKey) { | |
| return { | |
| ...direct, | |
| entry: direct.wildcardEntry, | |
| key: direct.wildcardKey, | |
| matchKey: direct.wildcardKey, | |
| matchSource: "wildcard", | |
| }; | |
| } | |
| return direct; | |
| } | |
| export function resolveNestedAllowlistDecision(params: { | |
| outerConfigured: boolean; | |
| outerMatched: boolean; | |
| innerConfigured: boolean; | |
| innerMatched: boolean; | |
| }): boolean { | |
| if (!params.outerConfigured) { | |
| return true; | |
| } | |
| if (!params.outerMatched) { | |
| return false; | |
| } | |
| if (!params.innerConfigured) { | |
| return true; | |
| } | |
| return params.innerMatched; | |
| } | |