refactor: share normalized account lookups

This commit is contained in:
Peter Steinberger
2026-03-22 18:24:20 +00:00
parent 017d295edb
commit d06413e335
10 changed files with 132 additions and 41 deletions

View File

@@ -7,7 +7,7 @@ import { formatErrorMessage } from "../infra/errors.js";
import { resetDirectoryCache } from "../infra/outbound/target-resolver.js";
import type { createSubsystemLogger } from "../logging/subsystem.js";
import type { PluginRuntime } from "../plugins/runtime/types.js";
import { resolveAccountEntry } from "../routing/account-lookup.js";
import { resolveAccountEntry, resolveNormalizedAccountEntry } from "../routing/account-lookup.js";
import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
@@ -162,13 +162,15 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
if (!normalizedAccountId) {
return undefined;
}
const matchKey = Object.keys(channelConfig.accounts).find(
(key) => normalizeAccountId(key) === normalizedAccountId,
const match = resolveNormalizedAccountEntry(
channelConfig.accounts,
normalizedAccountId,
normalizeAccountId,
);
if (!matchKey) {
if (typeof match?.healthMonitor?.enabled !== "boolean") {
return undefined;
}
return channelConfig.accounts[matchKey]?.healthMonitor?.enabled;
return match.healthMonitor.enabled;
};
const isHealthMonitorEnabled = (channelId: ChannelId, accountId: string): boolean => {

View File

@@ -6,7 +6,7 @@ export {
mergeAccountConfig,
} from "../channels/plugins/account-helpers.js";
export { normalizeChatType } from "../channels/chat-type.js";
export { resolveAccountEntry } from "../routing/account-lookup.js";
export { resolveAccountEntry, resolveNormalizedAccountEntry } from "../routing/account-lookup.js";
export {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { resolveAccountEntry } from "./account-lookup.js";
import { resolveAccountEntry, resolveNormalizedAccountEntry } from "./account-lookup.js";
describe("resolveAccountEntry", () => {
it("resolves direct and case-insensitive account keys", () => {
@@ -17,3 +17,26 @@ describe("resolveAccountEntry", () => {
expect(resolveAccountEntry(accounts, "default")).toBeUndefined();
});
});
describe("resolveNormalizedAccountEntry", () => {
it("resolves normalized account keys with a custom normalizer", () => {
const accounts = {
"Ops Team": { id: "ops" },
};
expect(
resolveNormalizedAccountEntry(accounts, "ops-team", (accountId) =>
accountId.trim().toLowerCase().replaceAll(" ", "-"),
),
).toEqual({ id: "ops" });
});
it("ignores prototype-chain values", () => {
const inherited = { default: { id: "polluted" } };
const accounts = Object.create(inherited) as Record<string, { id: string }>;
expect(
resolveNormalizedAccountEntry(accounts, "default", (accountId) => accountId),
).toBeUndefined();
});
});

View File

@@ -12,3 +12,19 @@ export function resolveAccountEntry<T>(
const matchKey = Object.keys(accounts).find((key) => key.toLowerCase() === normalized);
return matchKey ? accounts[matchKey] : undefined;
}
export function resolveNormalizedAccountEntry<T>(
accounts: Record<string, T> | undefined,
accountId: string,
normalizeAccountId: (accountId: string) => string,
): T | undefined {
if (!accounts || typeof accounts !== "object") {
return undefined;
}
if (Object.hasOwn(accounts, accountId)) {
return accounts[accountId];
}
const normalized = normalizeAccountId(accountId);
const matchKey = Object.keys(accounts).find((key) => normalizeAccountId(key) === normalized);
return matchKey ? accounts[matchKey] : undefined;
}