From 7087845f5829fdc2ebb5ec2c44c9f37548418d7e Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 7 Apr 2026 08:56:52 +0100 Subject: [PATCH] refactor: dedupe trim reader aliases --- extensions/comfy/workflow-runtime.ts | 18 +++--- extensions/mattermost/src/channel.ts | 16 ++--- src/auto-reply/reply/inbound-meta.ts | 83 ++++++++++++------------- src/channels/account-snapshot-fields.ts | 53 +++++++--------- src/cli/cron-cli/schedule-options.ts | 18 ++---- src/infra/os-summary.ts | 7 +-- src/interactive/payload.ts | 34 +++++----- src/media/channel-inbound-roots.ts | 6 +- 8 files changed, 98 insertions(+), 137 deletions(-) diff --git a/extensions/comfy/workflow-runtime.ts b/extensions/comfy/workflow-runtime.ts index e7e2c687d9b..45a3c1fba09 100644 --- a/extensions/comfy/workflow-runtime.ts +++ b/extensions/comfy/workflow-runtime.ts @@ -91,10 +91,6 @@ export function _setComfyFetchGuardForTesting(impl: typeof fetchWithSsrFGuard | comfyFetchGuard = impl ?? fetchWithSsrFGuard; } -function readConfigString(config: ComfyProviderConfig, key: string): string | undefined { - return normalizeOptionalString(config[key]); -} - function readConfigBoolean(config: ComfyProviderConfig, key: string): boolean | undefined { const value = config[key]; return typeof value === "boolean" ? value : undefined; @@ -554,9 +550,9 @@ export function isComfyCapabilityConfigured(params: { const capabilityConfig = getComfyCapabilityConfig(config, params.capability); const hasWorkflow = Boolean( resolveComfyWorkflowSource(capabilityConfig).workflow || - readConfigString(capabilityConfig, "workflowPath"), + normalizeOptionalString(capabilityConfig.workflowPath), ); - const hasPromptNode = Boolean(readConfigString(capabilityConfig, "promptNodeId")); + const hasPromptNode = Boolean(normalizeOptionalString(capabilityConfig.promptNodeId)); if (!hasWorkflow || !hasPromptNode) { return false; } @@ -586,11 +582,11 @@ export async function runComfyWorkflow(params: { const workflow = await loadComfyWorkflow(capabilityConfig); const promptNodeId = getRequiredConfigString(capabilityConfig, "promptNodeId"); const promptInputName = - readConfigString(capabilityConfig, "promptInputName") ?? DEFAULT_PROMPT_INPUT_NAME; - const inputImageNodeId = readConfigString(capabilityConfig, "inputImageNodeId"); + normalizeOptionalString(capabilityConfig.promptInputName) ?? DEFAULT_PROMPT_INPUT_NAME; + const inputImageNodeId = normalizeOptionalString(capabilityConfig.inputImageNodeId); const inputImageInputName = - readConfigString(capabilityConfig, "inputImageInputName") ?? DEFAULT_INPUT_IMAGE_INPUT_NAME; - const outputNodeId = readConfigString(capabilityConfig, "outputNodeId"); + normalizeOptionalString(capabilityConfig.inputImageInputName) ?? DEFAULT_INPUT_IMAGE_INPUT_NAME; + const outputNodeId = normalizeOptionalString(capabilityConfig.outputNodeId); const pollIntervalMs = readConfigInteger(capabilityConfig, "pollIntervalMs") ?? DEFAULT_POLL_INTERVAL_MS; const timeoutMs = @@ -619,7 +615,7 @@ export async function runComfyWorkflow(params: { const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } = resolveProviderHttpRequestConfig({ - baseUrl: readConfigString(capabilityConfig, "baseUrl"), + baseUrl: normalizeOptionalString(capabilityConfig.baseUrl), defaultBaseUrl: mode === "cloud" ? DEFAULT_COMFY_CLOUD_BASE_URL : DEFAULT_COMFY_LOCAL_BASE_URL, allowPrivateNetwork: diff --git a/extensions/mattermost/src/channel.ts b/extensions/mattermost/src/channel.ts index a53cfe84ced..41ae51fa55b 100644 --- a/extensions/mattermost/src/channel.ts +++ b/extensions/mattermost/src/channel.ts @@ -16,6 +16,7 @@ import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState, } from "openclaw/plugin-sdk/status-helpers"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { mattermostApprovalAuth } from "./approval-auth.js"; import { chunkTextForOutbound, @@ -242,25 +243,18 @@ const mattermostMessageActions: ChannelMessageActionAdapter = { }, }; -function readTrimmedString(value: unknown): string | undefined { - if (typeof value !== "string") { - return undefined; - } - const trimmed = value.trim(); - return trimmed || undefined; -} - function parseMattermostReactActionParams(params: Record): { postId: string; emojiName: string; remove: boolean; } { - const postId = readTrimmedString(params.messageId) ?? readTrimmedString(params.postId); + const postId = + normalizeOptionalString(params.messageId) ?? normalizeOptionalString(params.postId); if (!postId) { throw new Error("Mattermost react requires messageId (post id)"); } - const emojiName = readTrimmedString(params.emoji)?.replace(/^:+|:+$/g, ""); + const emojiName = normalizeOptionalString(params.emoji)?.replace(/^:+|:+$/g, ""); if (!emojiName) { throw new Error("Mattermost react requires emoji"); } @@ -273,7 +267,7 @@ function parseMattermostReactActionParams(params: Record): { } function readMattermostReplyToId(params: Record): string | undefined { - return readTrimmedString(params.replyToId) ?? readTrimmedString(params.replyTo); + return normalizeOptionalString(params.replyToId) ?? normalizeOptionalString(params.replyTo); } export const mattermostPlugin: ChannelPlugin = createChatChannelPlugin({ diff --git a/src/auto-reply/reply/inbound-meta.ts b/src/auto-reply/reply/inbound-meta.ts index 41995624964..cf2718deb1f 100644 --- a/src/auto-reply/reply/inbound-meta.ts +++ b/src/auto-reply/reply/inbound-meta.ts @@ -7,10 +7,6 @@ import type { EnvelopeFormatOptions } from "../envelope.js"; import { formatEnvelopeTimestamp } from "../envelope.js"; import type { TemplateContext } from "../templating.js"; -function safeTrim(value: unknown): string | undefined { - return normalizeOptionalString(value); -} - function formatConversationTimestamp( value: unknown, envelope?: EnvelopeFormatOptions, @@ -22,9 +18,10 @@ function formatConversationTimestamp( } function resolveInboundChannel(ctx: TemplateContext): string | undefined { - let channelValue = safeTrim(ctx.OriginatingChannel) ?? safeTrim(ctx.Surface); + let channelValue = + normalizeOptionalString(ctx.OriginatingChannel) ?? normalizeOptionalString(ctx.Surface); if (!channelValue) { - const provider = safeTrim(ctx.Provider); + const provider = normalizeOptionalString(ctx.Provider); if (provider !== "webchat" && ctx.Surface !== "webchat") { channelValue = provider; } @@ -47,7 +44,7 @@ function resolveInboundFormattingHints(ctx: TemplateContext): getLoadedChannelPlugin(normalizedChannel)?.agentPrompt ?? getBundledChannelPlugin(normalizedChannel)?.agentPrompt; return agentPrompt?.inboundFormattingHints?.({ - accountId: safeTrim(ctx.AccountId) ?? undefined, + accountId: normalizeOptionalString(ctx.AccountId) ?? undefined, }); } @@ -71,11 +68,11 @@ export function buildInboundMetaSystemPrompt( const payload = { schema: "openclaw.inbound_meta.v1", - chat_id: safeTrim(ctx.OriginatingTo), - account_id: safeTrim(ctx.AccountId), + chat_id: normalizeOptionalString(ctx.OriginatingTo), + account_id: normalizeOptionalString(ctx.AccountId), channel: channelValue, - provider: safeTrim(ctx.Provider), - surface: safeTrim(ctx.Surface), + provider: normalizeOptionalString(ctx.Provider), + surface: normalizeOptionalString(ctx.Surface), chat_type: chatType ?? (isDirect ? "direct" : undefined), response_format: options?.includeFormattingHints === false ? undefined : resolveInboundFormattingHints(ctx), @@ -108,34 +105,34 @@ export function buildInboundUserContextPrefix( ); const shouldIncludeConversationInfo = !isDirect || includeDirectConversationInfo; - const messageId = safeTrim(ctx.MessageSid); - const messageIdFull = safeTrim(ctx.MessageSidFull); + const messageId = normalizeOptionalString(ctx.MessageSid); + const messageIdFull = normalizeOptionalString(ctx.MessageSidFull); const resolvedMessageId = messageId ?? messageIdFull; const timestampStr = formatConversationTimestamp(ctx.Timestamp, envelope); const conversationInfo = { message_id: shouldIncludeConversationInfo ? resolvedMessageId : undefined, - reply_to_id: shouldIncludeConversationInfo ? safeTrim(ctx.ReplyToId) : undefined, - sender_id: shouldIncludeConversationInfo ? safeTrim(ctx.SenderId) : undefined, - conversation_label: isDirect ? undefined : safeTrim(ctx.ConversationLabel), + reply_to_id: shouldIncludeConversationInfo ? normalizeOptionalString(ctx.ReplyToId) : undefined, + sender_id: shouldIncludeConversationInfo ? normalizeOptionalString(ctx.SenderId) : undefined, + conversation_label: isDirect ? undefined : normalizeOptionalString(ctx.ConversationLabel), sender: shouldIncludeConversationInfo - ? (safeTrim(ctx.SenderName) ?? - safeTrim(ctx.SenderE164) ?? - safeTrim(ctx.SenderId) ?? - safeTrim(ctx.SenderUsername)) + ? (normalizeOptionalString(ctx.SenderName) ?? + normalizeOptionalString(ctx.SenderE164) ?? + normalizeOptionalString(ctx.SenderId) ?? + normalizeOptionalString(ctx.SenderUsername)) : undefined, timestamp: timestampStr, - group_subject: safeTrim(ctx.GroupSubject), - group_channel: safeTrim(ctx.GroupChannel), - group_space: safeTrim(ctx.GroupSpace), - thread_label: safeTrim(ctx.ThreadLabel), + group_subject: normalizeOptionalString(ctx.GroupSubject), + group_channel: normalizeOptionalString(ctx.GroupChannel), + group_space: normalizeOptionalString(ctx.GroupSpace), + thread_label: normalizeOptionalString(ctx.ThreadLabel), topic_id: ctx.MessageThreadId != null ? String(ctx.MessageThreadId) : undefined, is_forum: ctx.IsForum === true ? true : undefined, is_group_chat: !isDirect ? true : undefined, was_mentioned: ctx.WasMentioned === true ? true : undefined, has_reply_context: ctx.ReplyToBody ? true : undefined, has_forwarded_context: ctx.ForwardedFrom ? true : undefined, - has_thread_starter: safeTrim(ctx.ThreadStarterBody) ? true : undefined, + has_thread_starter: normalizeOptionalString(ctx.ThreadStarterBody) ? true : undefined, history_count: Array.isArray(ctx.InboundHistory) && ctx.InboundHistory.length > 0 ? ctx.InboundHistory.length @@ -154,17 +151,17 @@ export function buildInboundUserContextPrefix( const senderInfo = { label: resolveSenderLabel({ - name: safeTrim(ctx.SenderName), - username: safeTrim(ctx.SenderUsername), - tag: safeTrim(ctx.SenderTag), - e164: safeTrim(ctx.SenderE164), - id: safeTrim(ctx.SenderId), + name: normalizeOptionalString(ctx.SenderName), + username: normalizeOptionalString(ctx.SenderUsername), + tag: normalizeOptionalString(ctx.SenderTag), + e164: normalizeOptionalString(ctx.SenderE164), + id: normalizeOptionalString(ctx.SenderId), }), - id: safeTrim(ctx.SenderId), - name: safeTrim(ctx.SenderName), - username: safeTrim(ctx.SenderUsername), - tag: safeTrim(ctx.SenderTag), - e164: safeTrim(ctx.SenderE164), + id: normalizeOptionalString(ctx.SenderId), + name: normalizeOptionalString(ctx.SenderName), + username: normalizeOptionalString(ctx.SenderUsername), + tag: normalizeOptionalString(ctx.SenderTag), + e164: normalizeOptionalString(ctx.SenderE164), }; if (senderInfo?.label) { blocks.push( @@ -174,7 +171,7 @@ export function buildInboundUserContextPrefix( ); } - if (safeTrim(ctx.ThreadStarterBody)) { + if (normalizeOptionalString(ctx.ThreadStarterBody)) { blocks.push( [ "Thread starter (untrusted, for context):", @@ -192,7 +189,7 @@ export function buildInboundUserContextPrefix( "```json", JSON.stringify( { - sender_label: safeTrim(ctx.ReplyToSender), + sender_label: normalizeOptionalString(ctx.ReplyToSender), is_quote: ctx.ReplyToIsQuote === true ? true : undefined, body: ctx.ReplyToBody, }, @@ -211,12 +208,12 @@ export function buildInboundUserContextPrefix( "```json", JSON.stringify( { - from: safeTrim(ctx.ForwardedFrom), - type: safeTrim(ctx.ForwardedFromType), - username: safeTrim(ctx.ForwardedFromUsername), - title: safeTrim(ctx.ForwardedFromTitle), - signature: safeTrim(ctx.ForwardedFromSignature), - chat_type: safeTrim(ctx.ForwardedFromChatType), + from: normalizeOptionalString(ctx.ForwardedFrom), + type: normalizeOptionalString(ctx.ForwardedFromType), + username: normalizeOptionalString(ctx.ForwardedFromUsername), + title: normalizeOptionalString(ctx.ForwardedFromTitle), + signature: normalizeOptionalString(ctx.ForwardedFromSignature), + chat_type: normalizeOptionalString(ctx.ForwardedFromChatType), date_ms: typeof ctx.ForwardedDate === "number" ? ctx.ForwardedDate : undefined, }, null, diff --git a/src/channels/account-snapshot-fields.ts b/src/channels/account-snapshot-fields.ts index 27bd87c816d..0f4021613e8 100644 --- a/src/channels/account-snapshot-fields.ts +++ b/src/channels/account-snapshot-fields.ts @@ -17,10 +17,6 @@ const CREDENTIAL_STATUS_KEYS = [ type CredentialStatusKey = (typeof CREDENTIAL_STATUS_KEYS)[number]; -function readTrimmedString(record: Record, key: string): string | undefined { - return normalizeOptionalString(record[key]); -} - function readBoolean(record: Record, key: string): boolean | undefined { return typeof record[key] === "boolean" ? record[key] : undefined; } @@ -130,20 +126,16 @@ export function projectCredentialSnapshotFields( if (!record) { return {}; } + const tokenSource = normalizeOptionalString(record.tokenSource); + const botTokenSource = normalizeOptionalString(record.botTokenSource); + const appTokenSource = normalizeOptionalString(record.appTokenSource); + const signingSecretSource = normalizeOptionalString(record.signingSecretSource); return { - ...(readTrimmedString(record, "tokenSource") - ? { tokenSource: readTrimmedString(record, "tokenSource") } - : {}), - ...(readTrimmedString(record, "botTokenSource") - ? { botTokenSource: readTrimmedString(record, "botTokenSource") } - : {}), - ...(readTrimmedString(record, "appTokenSource") - ? { appTokenSource: readTrimmedString(record, "appTokenSource") } - : {}), - ...(readTrimmedString(record, "signingSecretSource") - ? { signingSecretSource: readTrimmedString(record, "signingSecretSource") } - : {}), + ...(tokenSource ? { tokenSource } : {}), + ...(botTokenSource ? { botTokenSource } : {}), + ...(appTokenSource ? { appTokenSource } : {}), + ...(signingSecretSource ? { signingSecretSource } : {}), ...(readCredentialStatus(record, "tokenStatus") ? { tokenStatus: readCredentialStatus(record, "tokenStatus") } : {}), @@ -169,9 +161,16 @@ export function projectSafeChannelAccountSnapshotFields( if (!record) { return {}; } + const name = normalizeOptionalString(record.name); + const healthState = normalizeOptionalString(record.healthState); + const mode = normalizeOptionalString(record.mode); + const dmPolicy = normalizeOptionalString(record.dmPolicy); + const baseUrl = normalizeOptionalString(record.baseUrl); + const cliPath = normalizeOptionalString(record.cliPath); + const dbPath = normalizeOptionalString(record.dbPath); return { - ...(readTrimmedString(record, "name") ? { name: readTrimmedString(record, "name") } : {}), + ...(name ? { name } : {}), ...(readBoolean(record, "linked") !== undefined ? { linked: readBoolean(record, "linked") } : {}), @@ -187,27 +186,19 @@ export function projectSafeChannelAccountSnapshotFields( ...(readNumber(record, "lastInboundAt") !== undefined ? { lastInboundAt: readNumber(record, "lastInboundAt") } : {}), - ...(readTrimmedString(record, "healthState") - ? { healthState: readTrimmedString(record, "healthState") } - : {}), - ...(readTrimmedString(record, "mode") ? { mode: readTrimmedString(record, "mode") } : {}), - ...(readTrimmedString(record, "dmPolicy") - ? { dmPolicy: readTrimmedString(record, "dmPolicy") } - : {}), + ...(healthState ? { healthState } : {}), + ...(mode ? { mode } : {}), + ...(dmPolicy ? { dmPolicy } : {}), ...(readStringArray(record, "allowFrom") ? { allowFrom: readStringArray(record, "allowFrom") } : {}), ...projectCredentialSnapshotFields(account), - ...(readTrimmedString(record, "baseUrl") - ? { baseUrl: stripUrlUserInfo(readTrimmedString(record, "baseUrl")!) } - : {}), + ...(baseUrl ? { baseUrl: stripUrlUserInfo(baseUrl) } : {}), ...(readBoolean(record, "allowUnmentionedGroups") !== undefined ? { allowUnmentionedGroups: readBoolean(record, "allowUnmentionedGroups") } : {}), - ...(readTrimmedString(record, "cliPath") - ? { cliPath: readTrimmedString(record, "cliPath") } - : {}), - ...(readTrimmedString(record, "dbPath") ? { dbPath: readTrimmedString(record, "dbPath") } : {}), + ...(cliPath ? { cliPath } : {}), + ...(dbPath ? { dbPath } : {}), ...(readNumber(record, "port") !== undefined ? { port: readNumber(record, "port") } : {}), }; } diff --git a/src/cli/cron-cli/schedule-options.ts b/src/cli/cron-cli/schedule-options.ts index e87cc90942b..d8c1f63da07 100644 --- a/src/cli/cron-cli/schedule-options.ts +++ b/src/cli/cron-cli/schedule-options.ts @@ -75,16 +75,16 @@ export function applyExistingCronSchedulePatch( } function normalizeScheduleOptions(options: ScheduleOptionInput): NormalizedScheduleOptions { - const staggerRaw = readTrimmedString(options.stagger); + const staggerRaw = normalizeOptionalString(options.stagger) ?? ""; const useExact = Boolean(options.exact); if (staggerRaw && useExact) { throw new Error("Choose either --stagger or --exact, not both"); } return { - at: readTrimmedString(options.at), - every: readTrimmedString(options.every), - cronExpr: readTrimmedString(options.cron), - tz: readOptionalString(options.tz), + at: normalizeOptionalString(options.at) ?? "", + every: normalizeOptionalString(options.every) ?? "", + cronExpr: normalizeOptionalString(options.cron) ?? "", + tz: normalizeOptionalString(options.tz), requestedStaggerMs: parseCronStaggerMs({ staggerRaw, useExact }), }; } @@ -125,11 +125,3 @@ function resolveDirectSchedule(options: NormalizedScheduleOptions): CronSchedule } return undefined; } - -function readOptionalString(value: unknown): string | undefined { - return normalizeOptionalString(value); -} - -function readTrimmedString(value: unknown): string { - return typeof value === "string" ? value.trim() : ""; -} diff --git a/src/infra/os-summary.ts b/src/infra/os-summary.ts index 0cd71c74a27..0c5fdad0d36 100644 --- a/src/infra/os-summary.ts +++ b/src/infra/os-summary.ts @@ -1,5 +1,6 @@ import { spawnSync } from "node:child_process"; import os from "node:os"; +import { normalizeOptionalString } from "../shared/string-coerce.js"; export type OsSummary = { platform: NodeJS.Platform; @@ -8,13 +9,9 @@ export type OsSummary = { label: string; }; -function safeTrim(value: unknown): string { - return typeof value === "string" ? value.trim() : ""; -} - function macosVersion(): string { const res = spawnSync("sw_vers", ["-productVersion"], { encoding: "utf-8" }); - const out = safeTrim(res.stdout); + const out = normalizeOptionalString(res.stdout) ?? ""; return out || os.release(); } diff --git a/src/interactive/payload.ts b/src/interactive/payload.ts index da5974e5edd..d0dc2b48959 100644 --- a/src/interactive/payload.ts +++ b/src/interactive/payload.ts @@ -38,12 +38,8 @@ export type InteractiveReply = { blocks: InteractiveReplyBlock[]; }; -function readTrimmedString(value: unknown): string | undefined { - return normalizeOptionalString(value); -} - function normalizeButtonStyle(value: unknown): InteractiveButtonStyle | undefined { - const style = readTrimmedString(value)?.toLowerCase(); + const style = normalizeOptionalString(value)?.toLowerCase(); return style === "primary" || style === "secondary" || style === "success" || style === "danger" ? style : undefined; @@ -54,11 +50,11 @@ function normalizeInteractiveButton(raw: unknown): InteractiveReplyButton | unde return undefined; } const record = raw as Record; - const label = readTrimmedString(record.label) ?? readTrimmedString(record.text); + const label = normalizeOptionalString(record.label) ?? normalizeOptionalString(record.text); const value = - readTrimmedString(record.value) ?? - readTrimmedString(record.callbackData) ?? - readTrimmedString(record.callback_data); + normalizeOptionalString(record.value) ?? + normalizeOptionalString(record.callbackData) ?? + normalizeOptionalString(record.callback_data); if (!label || !value) { return undefined; } @@ -74,8 +70,8 @@ function normalizeInteractiveOption(raw: unknown): InteractiveReplyOption | unde return undefined; } const record = raw as Record; - const label = readTrimmedString(record.label) ?? readTrimmedString(record.text); - const value = readTrimmedString(record.value); + const label = normalizeOptionalString(record.label) ?? normalizeOptionalString(record.text); + const value = normalizeOptionalString(record.value); if (!label || !value) { return undefined; } @@ -87,9 +83,9 @@ function normalizeInteractiveBlock(raw: unknown): InteractiveReplyBlock | undefi return undefined; } const record = raw as Record; - const type = readTrimmedString(record.type)?.toLowerCase(); + const type = normalizeOptionalString(record.type)?.toLowerCase(); if (type === "text") { - const text = readTrimmedString(record.text); + const text = normalizeOptionalString(record.text); return text ? { type: "text", text } : undefined; } if (type === "buttons") { @@ -109,7 +105,7 @@ function normalizeInteractiveBlock(raw: unknown): InteractiveReplyBlock | undefi return options.length > 0 ? { type: "select", - placeholder: readTrimmedString(record.placeholder), + placeholder: normalizeOptionalString(record.placeholder), options, } : undefined; @@ -148,10 +144,12 @@ export function hasReplyContent(params: { hasChannelData?: boolean; extraContent?: boolean; }): boolean { + const text = normalizeOptionalString(params.text); + const mediaUrl = normalizeOptionalString(params.mediaUrl); return Boolean( - params.text?.trim() || - params.mediaUrl?.trim() || - params.mediaUrls?.some((entry) => Boolean(entry?.trim())) || + text || + mediaUrl || + params.mediaUrls?.some((entry) => Boolean(normalizeOptionalString(entry))) || hasInteractiveReplyBlocks(params.interactive) || params.hasChannelData || params.extraContent, @@ -186,7 +184,7 @@ export function resolveInteractiveTextFallback(params: { text?: string; interactive?: InteractiveReply; }): string | undefined { - const text = readTrimmedString(params.text); + const text = normalizeOptionalString(params.text); if (text) { return params.text; } diff --git a/src/media/channel-inbound-roots.ts b/src/media/channel-inbound-roots.ts index 1737dbaa924..b6143b2a360 100644 --- a/src/media/channel-inbound-roots.ts +++ b/src/media/channel-inbound-roots.ts @@ -3,12 +3,8 @@ import { getBootstrapChannelPlugin } from "../channels/plugins/bootstrap-registr import type { OpenClawConfig } from "../config/config.js"; import { normalizeOptionalString } from "../shared/string-coerce.js"; -function normalizeChannelId(value?: string | null): string | undefined { - return normalizeOptionalString(value)?.toLowerCase(); -} - function findChannelMessagingAdapter(channelId?: string | null) { - const normalized = normalizeChannelId(channelId); + const normalized = normalizeOptionalString(channelId)?.toLowerCase(); if (!normalized) { return undefined; }