refactor: dedupe lowercase empty-string readers

This commit is contained in:
Peter Steinberger
2026-04-07 10:45:18 +01:00
parent 55f07e0381
commit 3139d2007e
8 changed files with 26 additions and 13 deletions

View File

@@ -1,9 +1,10 @@
import type { OpenClawConfig } from "../../config/types.js";
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
import type { DirectoryConfigParams } from "./directory-types.js";
import type { ChannelDirectoryEntry } from "./types.js";
function resolveDirectoryQuery(query?: string | null): string {
return query?.trim().toLowerCase() || "";
return normalizeLowercaseStringOrEmpty(query);
}
function resolveDirectoryLimit(limit?: number | null): number | undefined {

View File

@@ -11,6 +11,7 @@ import {
normalizeAccountId,
normalizeOptionalAccountId,
} from "../../../routing/session-key.js";
import { normalizeLowercaseStringOrEmpty } from "../../../shared/string-coerce.js";
import { asObjectRecord } from "./object.js";
type ChannelMissingDefaultAccountContext = {
@@ -24,7 +25,7 @@ function normalizeBindingChannelKey(raw?: string | null): string {
if (normalized) {
return normalized;
}
return (raw ?? "").trim().toLowerCase();
return normalizeLowercaseStringOrEmpty(raw);
}
function collectChannelsMissingDefaultAccount(

View File

@@ -9,7 +9,10 @@ import { withFileLock as withPathLock } from "../infra/file-lock.js";
import { resolveRequiredHomeDir } from "../infra/home-dir.js";
import { readJsonFileWithFallback, writeJsonFileAtomically } from "../plugin-sdk/json-store.js";
import { DEFAULT_ACCOUNT_ID } from "../routing/session-key.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
} from "../shared/string-coerce.js";
const PAIRING_CODE_LENGTH = 8;
const PAIRING_CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
@@ -62,7 +65,7 @@ function resolveCredentialsDir(env: NodeJS.ProcessEnv = process.env): string {
/** Sanitize channel ID for use in filenames (prevent path traversal). */
function safeChannelKey(channel: PairingChannel): string {
const raw = String(channel).trim().toLowerCase();
const raw = normalizeLowercaseStringOrEmpty(String(channel));
if (!raw) {
throw new Error("invalid pairing channel");
}
@@ -78,7 +81,7 @@ function resolvePairingPath(channel: PairingChannel, env: NodeJS.ProcessEnv = pr
}
function safeAccountKey(accountId: string): string {
const raw = String(accountId).trim().toLowerCase();
const raw = normalizeLowercaseStringOrEmpty(String(accountId));
if (!raw) {
throw new Error("invalid pairing account id");
}
@@ -255,7 +258,7 @@ function generateUniqueCode(existing: Set<string>): string {
}
function normalizePairingAccountId(accountId?: string): string {
return accountId?.trim().toLowerCase() || "";
return normalizeLowercaseStringOrEmpty(accountId);
}
function requestMatchesAccountId(entry: PairingRequest, normalizedAccountId: string): boolean {

View File

@@ -3,6 +3,7 @@ import { normalizeChatChannelId } from "../channels/ids.js";
import { listRouteBindings } from "../config/bindings.js";
import type { OpenClawConfig } from "../config/config.js";
import type { AgentRouteBinding } from "../config/types.agents.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import { normalizeAccountId, normalizeAgentId } from "./session-key.js";
function normalizeBindingChannelId(raw?: string | null): string | null {
@@ -10,7 +11,7 @@ function normalizeBindingChannelId(raw?: string | null): string | null {
if (normalized) {
return normalized;
}
const fallback = (raw ?? "").trim().toLowerCase();
const fallback = normalizeLowercaseStringOrEmpty(raw);
return fallback || null;
}

View File

@@ -4,6 +4,7 @@ import { normalizeChatType } from "../channels/chat-type.js";
import type { OpenClawConfig } from "../config/config.js";
import { shouldLogVerbose } from "../globals.js";
import { logDebug } from "../logger.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import { listBindings } from "./bindings.js";
import {
buildAgentMainSessionKey,
@@ -76,7 +77,7 @@ export function resolveInboundLastRouteSessionKey(params: {
}
function normalizeToken(value: string | undefined | null): string {
return (value ?? "").trim().toLowerCase();
return normalizeLowercaseStringOrEmpty(value);
}
function normalizeId(value: unknown): string {

View File

@@ -1,5 +1,6 @@
import type { ChatType } from "../channels/chat-type.js";
import { parseAgentSessionKey, type ParsedAgentSessionKey } from "../sessions/session-key-utils.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "./account-id.js";
export {
@@ -27,7 +28,7 @@ const LEADING_DASH_RE = /^-+/;
const TRAILING_DASH_RE = /-+$/;
function normalizeToken(value: string | undefined | null): string {
return (value ?? "").trim().toLowerCase();
return normalizeLowercaseStringOrEmpty(value);
}
export function scopedHeartbeatWakeOptions<T extends object>(
@@ -152,12 +153,12 @@ export function buildAgentPeerSessionKey(params: {
}
peerId = peerId.toLowerCase();
if (dmScope === "per-account-channel-peer" && peerId) {
const channel = (params.channel ?? "").trim().toLowerCase() || "unknown";
const channel = normalizeLowercaseStringOrEmpty(params.channel) || "unknown";
const accountId = normalizeAccountId(params.accountId);
return `agent:${normalizeAgentId(params.agentId)}:${channel}:${accountId}:direct:${peerId}`;
}
if (dmScope === "per-channel-peer" && peerId) {
const channel = (params.channel ?? "").trim().toLowerCase() || "unknown";
const channel = normalizeLowercaseStringOrEmpty(params.channel) || "unknown";
return `agent:${normalizeAgentId(params.agentId)}:${channel}:direct:${peerId}`;
}
if (dmScope === "per-peer" && peerId) {
@@ -168,7 +169,7 @@ export function buildAgentPeerSessionKey(params: {
mainKey: params.mainKey,
});
}
const channel = (params.channel ?? "").trim().toLowerCase() || "unknown";
const channel = normalizeLowercaseStringOrEmpty(params.channel) || "unknown";
const peerId = ((params.peerId ?? "").trim() || "unknown").toLowerCase();
return `agent:${normalizeAgentId(params.agentId)}:${channel}:${peerKind}:${peerId}`;
}

View File

@@ -1,3 +1,4 @@
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import { parseAgentSessionKey } from "./session-key-utils.js";
export type SessionKeyChatType = "direct" | "group" | "channel" | "unknown";
@@ -58,7 +59,7 @@ export function deriveSessionChatTypeFromKey(
(scopedSessionKey: string) => SessionKeyChatType | undefined
> = [],
): SessionKeyChatType {
const raw = (sessionKey ?? "").trim().toLowerCase();
const raw = normalizeLowercaseStringOrEmpty(sessionKey);
if (!raw) {
return "unknown";
}

View File

@@ -18,6 +18,10 @@ export function normalizeOptionalLowercaseString(value: unknown): string | undef
return normalizeOptionalString(value)?.toLowerCase();
}
export function normalizeLowercaseStringOrEmpty(value: unknown): string {
return normalizeOptionalLowercaseString(value) ?? "";
}
export function resolvePrimaryStringValue(value: unknown): string | undefined {
if (typeof value === "string") {
return normalizeOptionalString(value);