refactor(channels): centralize match metadata

This commit is contained in:
Peter Steinberger
2026-01-21 18:21:19 +00:00
parent b52ab96e2c
commit 88d76d4be5
11 changed files with 73 additions and 38 deletions

View File

@@ -16,6 +16,8 @@ export type AllowlistMatch<TSource extends string = AllowlistMatchSource> = {
matchSource?: TSource;
};
export function formatAllowlistMatchMeta(match?: AllowlistMatch | null): string {
export function formatAllowlistMatchMeta(
match?: { matchKey?: string; matchSource?: string } | null,
): string {
return `matchKey=${match?.matchKey ?? "none"} matchSource=${match?.matchSource ?? "none"}`;
}

View File

@@ -6,6 +6,8 @@ import {
resolveChannelEntryMatch,
resolveChannelEntryMatchWithFallback,
resolveNestedAllowlistDecision,
applyChannelMatchMeta,
resolveChannelMatchConfig,
} from "./channel-config.js";
describe("buildChannelKeyCandidates", () => {
@@ -90,6 +92,33 @@ describe("resolveChannelEntryMatchWithFallback", () => {
});
});
describe("applyChannelMatchMeta", () => {
it("copies match metadata onto resolved configs", () => {
const resolved = applyChannelMatchMeta(
{ allowed: true },
{ matchKey: "general", matchSource: "direct" },
);
expect(resolved.matchKey).toBe("general");
expect(resolved.matchSource).toBe("direct");
});
});
describe("resolveChannelMatchConfig", () => {
it("returns null when no entry is matched", () => {
const resolved = resolveChannelMatchConfig({ matchKey: "x" }, () => ({ allowed: true }));
expect(resolved).toBeNull();
});
it("resolves entry and applies match metadata", () => {
const resolved = resolveChannelMatchConfig(
{ entry: { allow: true }, matchKey: "*", matchSource: "wildcard" },
() => ({ allowed: true }),
);
expect(resolved?.matchKey).toBe("*");
expect(resolved?.matchSource).toBe("wildcard");
});
});
describe("resolveNestedAllowlistDecision", () => {
it("allows when outer allowlist is disabled", () => {
expect(

View File

@@ -11,6 +11,24 @@ export type ChannelEntryMatch<T> = {
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()

View File

@@ -1,8 +1,10 @@
export type { ChannelEntryMatch, ChannelMatchSource } from "../channel-config.js";
export {
applyChannelMatchMeta,
buildChannelKeyCandidates,
normalizeChannelSlug,
resolveChannelEntryMatch,
resolveChannelEntryMatchWithFallback,
resolveChannelMatchConfig,
resolveNestedAllowlistDecision,
} from "../channel-config.js";

View File

@@ -60,10 +60,12 @@ export {
listWhatsAppDirectoryPeersFromConfig,
} from "./directory-config.js";
export {
applyChannelMatchMeta,
buildChannelKeyCandidates,
normalizeChannelSlug,
resolveChannelEntryMatch,
resolveChannelEntryMatchWithFallback,
resolveChannelMatchConfig,
resolveNestedAllowlistDecision,
type ChannelEntryMatch,
type ChannelMatchSource,