Channels/Config — Channel Capabilities

Focus Deep-Dive

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 KeyEntries MapMatch SourceResult
"telegram"{ telegram: {...}, "*": {...} }directtelegram entry
"line"{ chat: {...}, "*": {...} }parentchat entry (if line is child of chat)
"matrix"{ "*": {...} }wildcardwildcard entry
"custom"{}nonenull