Channels/Config — Channel Capabilities
Config Entry Matching with Cascading Fallback Resolution
The channel capabilities system resolves per-channel configuration using a three-tier fallback strategy: direct key match, parent key match, and wildcard. It normalizes channel slugs, generates candidate keys, and tracks which resolution path was used for debugging.
3-Tier Fallback Slug Normalization Key Candidates Match Tracking
📚 What is this & why does it matter?
With 25+ channels, each with different capabilities, the config system must resolve the right setting quickly and fall back gracefully. This focus page covers the exact matching algorithm, how candidate keys are generated, and how the resolution path is tracked for transparency.
💡 Real-world analogy
Think of a DNS resolver. When you type api.us.example.com, DNS first tries the exact hostname, then the parent domain (us.example.com), then the root (example.com). Similarly, resolveChannelEntryMatchWithFallback tries the exact channel key, then parent keys, then wildcards.
Contents
Core Types
export type ChannelMatchSource = "direct" | "parent" | "wildcard";
export type ChannelEntryMatch<T> = {
entry?: T; // Matched entry value
key?: string; // Matched key
wildcardEntry?: T; // Wildcard entry (always populated if exists)
wildcardKey?: string; // Wildcard key
parentEntry?: T; // Parent entry value
parentKey?: string; // Parent key
matchKey?: string; // Final resolved key
matchSource?: ChannelMatchSource; // How match was found
};
Resolution Pipeline
flowchart TD
INPUT["Channel key + entries map"]
INPUT --> BUILD["buildChannelKeyCandidates()"]
BUILD --> DIRECT["Try direct match
resolveChannelEntryMatch()"]
DIRECT --> FOUND{"Direct match?"}
FOUND -->|Yes| RESULT_D["matchSource: 'direct'"]
FOUND -->|No| NORM{"normalizeKey
provided?"}
NORM -->|Yes| NORM_MATCH["Try normalized keys"]
NORM -->|No| PARENT["Try parent keys"]
NORM_MATCH --> NORM_FOUND{"Match?"}
NORM_FOUND -->|Yes| RESULT_D
NORM_FOUND -->|No| PARENT
PARENT --> PFOUND{"Parent match?"}
PFOUND -->|Yes| RESULT_P["matchSource: 'parent'"]
PFOUND -->|No| WILD{"Wildcard entry?"}
WILD -->|Yes| RESULT_W["matchSource: 'wildcard'"]
WILD -->|No| EMPTY["No match"]
Key Candidate Generation
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;
}
Slug Normalization
export function normalizeChannelSlug(value: string): string {
return value.trim().toLowerCase()
.replace(/^#/, "")
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
}
// "My Channel #1" → "my-channel-1"
// "#general" → "general"
Direct Match
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;
}
Match Meta Application
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;
}
Resolution Examples
| Input Key | Entries Map | Match Source | Result |
|---|---|---|---|
"telegram" | { telegram: {...}, "*": {...} } | direct | telegram entry |
"line" | { chat: {...}, "*": {...} } | parent | chat entry (if line is child of chat) |
"matrix" | { "*": {...} } | wildcard | wildcard entry |
"custom" | {} | none | null |