refactor: dedupe shared string readers

This commit is contained in:
Peter Steinberger
2026-04-07 00:38:56 +01:00
parent 899f490c9c
commit 61f7d53731
11 changed files with 31 additions and 54 deletions

View File

@@ -1,17 +1,10 @@
import { parseFiniteNumber } from "openclaw/plugin-sdk/infra-runtime";
import { asNullableRecord } from "openclaw/plugin-sdk/text-runtime";
import { asNullableRecord, readStringField } from "openclaw/plugin-sdk/text-runtime";
import { extractHandleFromChatGuid, normalizeBlueBubblesHandle } from "./targets.js";
import type { BlueBubblesAttachment } from "./types.js";
export const asRecord = asNullableRecord;
function readString(record: Record<string, unknown> | null, key: string): string | undefined {
if (!record) {
return undefined;
}
const value = record[key];
return typeof value === "string" ? value : undefined;
}
const readString = readStringField;
function readNumber(record: Record<string, unknown> | null, key: string): number | undefined {
if (!record) {

View File

@@ -8,6 +8,7 @@
*/
import type { IncomingMessage, ServerResponse } from "node:http";
import { readStringValue } from "openclaw/plugin-sdk/text-runtime";
import { z } from "openclaw/plugin-sdk/zod";
import {
createFixedWindowRateLimiter,
@@ -228,7 +229,7 @@ function firstHeaderValue(value: string | string[] | undefined): string | undefi
if (Array.isArray(value)) {
return value[0];
}
return typeof value === "string" ? value : undefined;
return readStringValue(value);
}
function normalizeIpCandidate(raw: string): string {

View File

@@ -1,11 +1,4 @@
import { asOptionalObjectRecord } from "openclaw/plugin-sdk/text-runtime";
import { asOptionalObjectRecord, readStringField } from "openclaw/plugin-sdk/text-runtime";
export const asRecord = asOptionalObjectRecord;
export function readString(
record: Record<string, unknown> | undefined,
key: string,
): string | undefined {
const value = record?.[key];
return typeof value === "string" ? value : undefined;
}
export const readString = readStringField;

View File

@@ -1,5 +1,5 @@
import { formatErrorMessage as sharedFormatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { asNullableObjectRecord } from "openclaw/plugin-sdk/text-runtime";
import { asNullableObjectRecord, readStringField } from "openclaw/plugin-sdk/text-runtime";
import { normalizeShip } from "../targets.js";
// Cite types for message references
@@ -185,14 +185,7 @@ export async function resolveAuthorizedMessageText(params: {
export const asRecord = asNullableObjectRecord;
export const formatErrorMessage = sharedFormatErrorMessage;
export function readString(
record: Record<string, unknown> | null,
key: string,
): string | undefined {
const value = record?.[key];
return typeof value === "string" ? value : undefined;
}
export const readString = readStringField;
// Helper to recursively extract text from inline content
function renderInlineItem(

View File

@@ -1,3 +1,4 @@
import { asOptionalRecord, readStringField } from "openclaw/plugin-sdk/text-runtime";
import type { VoiceCallConfig } from "./config.js";
import { VoiceCallConfigSchema } from "./config.js";
@@ -9,16 +10,8 @@ export type VoiceCallLegacyConfigIssue = {
message: string;
};
function asObject(value: unknown): Record<string, unknown> | undefined {
return typeof value === "object" && value !== null && !Array.isArray(value)
? (value as Record<string, unknown>)
: undefined;
}
function getString(obj: Record<string, unknown> | undefined, key: string): string | undefined {
const value = obj?.[key];
return typeof value === "string" ? value : undefined;
}
const asObject = asOptionalRecord;
const getString = readStringField;
function getNumber(obj: Record<string, unknown> | undefined, key: string): number | undefined {
const value = obj?.[key];

View File

@@ -1,3 +1,4 @@
import { readStringValue } from "../shared/string-coerce.js";
import { normalizeToolParameterSchema } from "./pi-tools.schema.js";
import { resolveProviderRequestCapabilities } from "./provider-attribution.js";
@@ -15,9 +16,7 @@ type ToolWithParameters = {
parameters: unknown;
};
function optionalString(value: unknown): string | undefined {
return typeof value === "string" ? value : undefined;
}
const optionalString = readStringValue;
export function normalizeStrictOpenAIJsonSchema(schema: unknown): unknown {
return normalizeStrictOpenAIJsonSchemaRecursive(normalizeToolParameterSchema(schema ?? {}));

View File

@@ -1,11 +1,10 @@
import { randomUUID } from "node:crypto";
import fs from "node:fs";
import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js";
import { readStringValue } from "../shared/string-coerce.js";
export { asRecord } from "../shared/record-coerce.js";
export function asString(value: unknown): string | undefined {
return typeof value === "string" ? value : undefined;
}
export const asString = readStringValue;
export function asNumber(value: unknown): number | undefined {
return typeof value === "number" && Number.isFinite(value) ? value : undefined;

View File

@@ -1,11 +1,6 @@
type UnknownRecord = Record<string, unknown>;
import { readStringValue as readString } from "../shared/string-coerce.js";
function readString(value: unknown): string | undefined {
if (typeof value !== "string") {
return undefined;
}
return value;
}
type UnknownRecord = Record<string, unknown>;
function normalizeChannel(value: string): string {
return value.trim().toLowerCase();

View File

@@ -2,6 +2,7 @@ import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { DEFAULT_AGENT_ID } from "../routing/session-key.js";
import { readStringValue } from "../shared/string-coerce.js";
import { resolveAllowAlwaysPatternEntries } from "./exec-approvals-allowlist.js";
import type { ExecCommandSegment } from "./exec-approvals-analysis.js";
import { expandHomePrefix } from "./home-dir.js";
@@ -31,9 +32,7 @@ export function normalizeExecTarget(value?: string | null): ExecTarget | null {
}
/** Coerce a raw JSON field to string, returning undefined for non-string types. */
function toStringOrUndefined(value: unknown): string | undefined {
return typeof value === "string" ? value : undefined;
}
const toStringOrUndefined = readStringValue;
export function normalizeExecSecurity(value?: string | null): ExecSecurity | null {
const normalized = value?.trim().toLowerCase();

View File

@@ -4,6 +4,14 @@ export function asRecord(value: unknown): Record<string, unknown> {
return typeof value === "object" && value !== null ? (value as Record<string, unknown>) : {};
}
export function readStringField(
record: Record<string, unknown> | null | undefined,
key: string,
): string | undefined {
const value = record?.[key];
return typeof value === "string" ? value : undefined;
}
export function asOptionalRecord(value: unknown): Record<string, unknown> | undefined {
return isRecord(value) ? value : undefined;
}

View File

@@ -1,3 +1,7 @@
export function readStringValue(value: unknown): string | undefined {
return typeof value === "string" ? value : undefined;
}
export function normalizeNullableString(value: unknown): string | null {
if (typeof value !== "string") {
return null;