refactor: dedupe helper trim readers

This commit is contained in:
Peter Steinberger
2026-04-07 08:21:53 +01:00
parent a5ff85f01c
commit b3e6822ef8
13 changed files with 46 additions and 27 deletions

View File

@@ -37,7 +37,7 @@ type MatrixResolvedAuth = Awaited<ReturnType<typeof resolveMatrixAuth>>;
const MATRIX_DIRECTORY_TIMEOUT_MS = 10_000;
function normalizeQuery(value?: string | null): string {
return value?.trim() ?? "";
return normalizeOptionalString(value) ?? "";
}
function resolveMatrixDirectoryLimit(limit?: number | null): number {
@@ -158,7 +158,7 @@ async function resolveMatrixRoomAlias(
method: "GET",
endpoint: `/_matrix/client/v3/directory/room/${encodeURIComponent(alias)}`,
});
return res.room_id?.trim() || null;
return normalizeOptionalString(res.room_id) ?? null;
} catch {
return null;
}
@@ -173,7 +173,7 @@ async function fetchMatrixRoomName(
method: "GET",
endpoint: `/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/state/m.room.name`,
});
return res.name?.trim() || null;
return normalizeOptionalString(res.name) ?? null;
} catch {
return null;
}
@@ -214,7 +214,9 @@ export async function listMatrixDirectoryGroupsLive(
method: "GET",
endpoint: "/_matrix/client/v3/joined_rooms",
});
const rooms = (joined.joined_rooms ?? []).map((roomId) => roomId.trim()).filter(Boolean);
const rooms = (joined.joined_rooms ?? [])
.map((roomId) => normalizeOptionalString(roomId))
.filter((roomId): roomId is string => Boolean(roomId));
const results: ChannelDirectoryEntry[] = [];
for (const roomId of rooms) {

View File

@@ -12,6 +12,7 @@ import {
postJsonRequest,
resolveProviderHttpRequestConfig,
} from "openclaw/plugin-sdk/provider-http";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
const DEFAULT_MINIMAX_MUSIC_BASE_URL = "https://api.minimax.io";
const DEFAULT_MINIMAX_MUSIC_MODEL = "music-2.5+";
@@ -38,7 +39,7 @@ type MinimaxMusicCreateResponse = {
function resolveMinimaxMusicBaseUrl(
cfg: Parameters<typeof resolveApiKeyForProvider>[0]["cfg"],
): string {
const direct = cfg?.models?.providers?.minimax?.baseUrl?.trim();
const direct = normalizeOptionalString(cfg?.models?.providers?.minimax?.baseUrl);
if (!direct) {
return DEFAULT_MINIMAX_MUSIC_BASE_URL;
}
@@ -78,7 +79,7 @@ function decodePossibleText(data: string): string {
}
function isLikelyRemoteUrl(value: string | undefined): boolean {
const trimmed = value?.trim();
const trimmed = normalizeOptionalString(value);
return Boolean(trimmed && /^https?:\/\//iu.test(trimmed));
}
@@ -112,7 +113,7 @@ function buildPrompt(req: MusicGenerationRequest): string {
}
function resolveMinimaxMusicModel(model: string | undefined): string {
const trimmed = model?.trim();
const trimmed = normalizeOptionalString(model);
if (!trimmed) {
return DEFAULT_MINIMAX_MUSIC_MODEL;
}

View File

@@ -24,6 +24,7 @@ import {
type WebSearchProviderPlugin,
type WebSearchProviderToolDefinition,
} from "openclaw/plugin-sdk/provider-web-search";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
const MINIMAX_SEARCH_ENDPOINT_GLOBAL = "https://api.minimax.io/v1/coding_plan/search";
const MINIMAX_SEARCH_ENDPOINT_CN = "https://api.minimaxi.com/v1/coding_plan/search";
@@ -57,7 +58,7 @@ function resolveMiniMaxApiKey(searchConfig?: SearchConfigRecord): string | undef
}
function isMiniMaxCnHost(value: string | undefined): boolean {
const trimmed = value?.trim();
const trimmed = normalizeOptionalString(value);
if (!trimmed) {
return false;
}
@@ -79,8 +80,10 @@ function resolveMiniMaxRegion(
!Array.isArray(searchConfig.minimax)
? (searchConfig.minimax as Record<string, unknown>)
: undefined;
if (typeof minimax?.region === "string" && minimax.region.trim()) {
return minimax.region === "cn" ? "cn" : "global";
const configuredRegion =
typeof minimax?.region === "string" ? normalizeOptionalString(minimax.region) : undefined;
if (configuredRegion) {
return configuredRegion === "cn" ? "cn" : "global";
}
// 2. Infer from the shared MiniMax host override.

View File

@@ -18,7 +18,7 @@ import {
type WizardPrompter,
} from "openclaw/plugin-sdk/setup-runtime";
import { formatCliCommand, formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
import { normalizeE164 } from "openclaw/plugin-sdk/text-runtime";
import { normalizeE164, normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { resolveDefaultSignalAccountId, resolveSignalAccount } from "./accounts.js";
const channel = "signal" as const;
@@ -29,7 +29,7 @@ const INVALID_SIGNAL_ACCOUNT_ERROR =
"Invalid E.164 phone number (must start with + and country code, e.g. +15555550123)";
export function normalizeSignalAccountInput(value: string | null | undefined): string | null {
const trimmed = value?.trim();
const trimmed = normalizeOptionalString(value);
if (!trimmed) {
return null;
}

View File

@@ -12,6 +12,7 @@ import {
logWebhookError,
logWebhookProcessed,
logWebhookReceived,
normalizeOptionalString,
startDiagnosticHeartbeat,
stopDiagnosticHeartbeat,
} from "openclaw/plugin-sdk/text-runtime";
@@ -106,7 +107,7 @@ function hasValidTelegramWebhookSecret(
}
function parseIpLiteral(value: string | undefined): string | undefined {
const trimmed = value?.trim();
const trimmed = normalizeOptionalString(value);
if (!trimmed) {
return undefined;
}

View File

@@ -1,6 +1,7 @@
import type { OpenClawConfig } from "../config/config.js";
import { coerceSecretRef, resolveSecretInputRef } from "../config/types.secrets.js";
import { resolveProviderSyntheticAuthWithPlugin } from "../plugins/provider-runtime.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import { normalizeOptionalSecretInput } from "../utils/normalize-secret-input.js";
import { listProfilesForProvider } from "./auth-profiles/profiles.js";
import { ensureAuthProfileStore } from "./auth-profiles/store.js";
@@ -54,7 +55,7 @@ export function normalizeApiKeyConfig(value: string): string {
}
export function toDiscoveryApiKey(value: string | undefined): string | undefined {
const trimmed = value?.trim();
const trimmed = normalizeOptionalString(value);
if (!trimmed || isNonSecretApiKeyMarker(trimmed)) {
return undefined;
}

View File

@@ -7,6 +7,7 @@ import {
normalizeAgentId,
parseAgentSessionKey,
} from "../routing/session-key.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import type { BootstrapContextMode } from "./bootstrap-files.js";
import {
mapToolContextToSpawnedRunMetadata,
@@ -201,7 +202,7 @@ async function persistInitialChildSessionRuntimeModel(params: {
}
function sanitizeMountPathHint(value?: string): string | undefined {
const trimmed = value?.trim();
const trimmed = normalizeOptionalString(value);
if (!trimmed) {
return undefined;
}

View File

@@ -20,6 +20,7 @@ import { GATEWAY_CLIENT_IDS, GATEWAY_CLIENT_MODES } from "../../gateway/protocol
import { getToolResult, runMessageAction } from "../../infra/outbound/message-action-runner.js";
import { POLL_CREATION_PARAM_DEFS, SHARED_POLL_CREATION_PARAM_NAMES } from "../../poll-params.js";
import { normalizeAccountId } from "../../routing/session-key.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import { stripReasoningTagsFromText } from "../../shared/text/reasoning-tags.js";
import { normalizeMessageChannel } from "../../utils/message-channel.js";
import { resolveSessionAgentId } from "../agent-scope.js";
@@ -547,7 +548,7 @@ function buildMessageToolSchema(params: {
}
function resolveAgentAccountId(value?: string): string | undefined {
const trimmed = value?.trim();
const trimmed = normalizeOptionalString(value);
if (!trimmed) {
return undefined;
}

View File

@@ -31,6 +31,7 @@ import { resolveCommitHash } from "../infra/git-commit.js";
import type { MediaUnderstandingDecision } from "../media-understanding/types.js";
import { listPluginCommands } from "../plugins/commands.js";
import { resolveAgentIdFromSessionKey } from "../routing/session-key.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import { resolveStatusTtsSnapshot } from "../tts/status-config.js";
import {
estimateUsageCost,
@@ -97,7 +98,7 @@ type StatusArgs = {
type NormalizedAuthMode = "api-key" | "oauth" | "token" | "aws-sdk" | "mixed" | "unknown";
function normalizeAuthMode(value?: string): NormalizedAuthMode | undefined {
const normalized = value?.trim().toLowerCase();
const normalized = normalizeOptionalString(value)?.toLowerCase();
if (!normalized) {
return undefined;
}

View File

@@ -1,8 +1,9 @@
import os from "node:os";
import path from "node:path";
import { normalizeOptionalString } from "../shared/string-coerce.js";
function normalize(value: string | undefined): string | undefined {
const trimmed = value?.trim();
const trimmed = normalizeOptionalString(value);
if (!trimmed) {
return undefined;
}

View File

@@ -1,5 +1,6 @@
import { URL } from "node:url";
import type { GatewayConfig } from "../config/types.gateway.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import {
loadOrCreateDeviceIdentity,
signDevicePayload,
@@ -46,13 +47,17 @@ const GATEWAY_SIGNATURE_HEADER = "x-openclaw-gateway-signature";
const GATEWAY_SIGNED_AT_HEADER = "x-openclaw-gateway-signed-at-ms";
function normalizeNonEmptyString(value: string | undefined): string | null {
const trimmed = value?.trim() ?? "";
const trimmed = normalizeOptionalString(value) ?? "";
return trimmed.length > 0 ? trimmed : null;
}
function normalizeTimeoutMs(value: string | number | undefined): number {
const raw =
typeof value === "number" ? value : typeof value === "string" ? value.trim() : undefined;
typeof value === "number"
? value
: typeof value === "string"
? normalizeOptionalString(value)
: undefined;
if (raw === undefined || raw === "") {
return DEFAULT_APNS_RELAY_TIMEOUT_MS;
}
@@ -64,7 +69,7 @@ function normalizeTimeoutMs(value: string | number | undefined): number {
}
function readAllowHttp(value: string | undefined): boolean {
const normalized = value?.trim().toLowerCase();
const normalized = normalizeOptionalString(value)?.toLowerCase();
return normalized === "1" || normalized === "true" || normalized === "yes";
}
@@ -79,7 +84,7 @@ function isLoopbackRelayHostname(hostname: string): boolean {
}
function parseReason(value: unknown): string | undefined {
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
return typeof value === "string" ? normalizeOptionalString(value) : undefined;
}
function buildRelayGatewaySignaturePayload(params: {

View File

@@ -3,6 +3,7 @@ import fs from "node:fs/promises";
import http2 from "node:http2";
import path from "node:path";
import { resolveStateDir } from "../config/paths.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import type { DeviceIdentity } from "./device-identity.js";
import { formatErrorMessage } from "./errors.js";
import { createAsyncLock, readJsonFile, writeJsonAtomic } from "./json-files.js";
@@ -254,7 +255,7 @@ function normalizePrivateKey(value: string): string {
}
function normalizeNonEmptyString(value: string | undefined): string | null {
const trimmed = value?.trim() ?? "";
const trimmed = normalizeOptionalString(value) ?? "";
return trimmed.length > 0 ? trimmed : null;
}
@@ -262,7 +263,7 @@ function normalizeDistribution(value: unknown): "official" | null {
if (typeof value !== "string") {
return null;
}
const normalized = value.trim().toLowerCase();
const normalized = normalizeOptionalString(value)?.toLowerCase();
return normalized === "official" ? "official" : null;
}

View File

@@ -1,4 +1,5 @@
import { createRequire } from "node:module";
import { normalizeOptionalString } from "./shared/string-coerce.js";
declare const __OPENCLAW_VERSION__: string | undefined;
const CORE_PACKAGE_NAME = "openclaw";
@@ -26,7 +27,7 @@ function readVersionFromJsonCandidates(
for (const candidate of candidates) {
try {
const parsed = require(candidate) as { name?: string; version?: string };
const version = parsed.version?.trim();
const version = normalizeOptionalString(parsed.version);
if (!version) {
continue;
}
@@ -46,7 +47,7 @@ function readVersionFromJsonCandidates(
function firstNonEmpty(...values: Array<string | undefined>): string | undefined {
for (const value of values) {
const trimmed = value?.trim();
const trimmed = normalizeOptionalString(value);
if (trimmed) {
return trimmed;
}
@@ -94,7 +95,7 @@ export const RUNTIME_SERVICE_VERSION_FALLBACK = "unknown";
type RuntimeVersionPreference = "env-first" | "runtime-first";
export function resolveUsableRuntimeVersion(version: string | undefined): string | undefined {
const trimmed = version?.trim();
const trimmed = normalizeOptionalString(version);
// "0.0.0" is the resolver's hard fallback when module metadata cannot be read.
// Prefer explicit service/package markers in that edge case.
if (!trimmed || trimmed === "0.0.0") {