mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
refactor: dedupe channel trimmed readers
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { normalizeStringEntries } from "../shared/string-normalization.js";
|
||||
|
||||
export function mergeDmAllowFromSources(params: {
|
||||
allowFrom?: Array<string | number>;
|
||||
storeAllowFrom?: Array<string | number>;
|
||||
dmPolicy?: string;
|
||||
}): string[] {
|
||||
const storeEntries = params.dmPolicy === "allowlist" ? [] : (params.storeAllowFrom ?? []);
|
||||
return [...(params.allowFrom ?? []), ...storeEntries]
|
||||
.map((value) => String(value).trim())
|
||||
.filter(Boolean);
|
||||
return normalizeStringEntries([...(params.allowFrom ?? []), ...storeEntries]);
|
||||
}
|
||||
|
||||
export function resolveGroupAllowFromSources(params: {
|
||||
@@ -23,7 +23,7 @@ export function resolveGroupAllowFromSources(params: {
|
||||
: params.fallbackToAllowFrom === false
|
||||
? []
|
||||
: (params.allowFrom ?? []);
|
||||
return scoped.map((value) => String(value).trim()).filter(Boolean);
|
||||
return normalizeStringEntries(scoped);
|
||||
}
|
||||
|
||||
export function firstDefined<T>(...values: Array<T | undefined>) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { mapAllowFromEntries } from "openclaw/plugin-sdk/channel-config-helpers";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "../../shared/string-coerce.js";
|
||||
import { summarizeStringEntries } from "../../shared/string-sample.js";
|
||||
|
||||
export type AllowlistUserResolutionLike = {
|
||||
@@ -62,7 +65,7 @@ export function resolveAllowlistIdAdditions<T extends AllowlistUserResolutionLik
|
||||
}): string[] {
|
||||
const additions: string[] = [];
|
||||
for (const entry of params.existing) {
|
||||
const trimmed = String(entry).trim();
|
||||
const trimmed = normalizeOptionalString(entry) ?? "";
|
||||
const resolved = params.resolvedMap.get(trimmed);
|
||||
if (resolved?.resolved && resolved.id) {
|
||||
additions.push(resolved.id);
|
||||
@@ -76,7 +79,7 @@ export function canonicalizeAllowlistWithResolvedIds<
|
||||
>(params: { existing?: Array<string | number>; resolvedMap: Map<string, T> }): string[] {
|
||||
const canonicalized: string[] = [];
|
||||
for (const entry of params.existing ?? []) {
|
||||
const trimmed = String(entry).trim();
|
||||
const trimmed = normalizeOptionalString(entry) ?? "";
|
||||
if (!trimmed) {
|
||||
continue;
|
||||
}
|
||||
@@ -137,7 +140,7 @@ export function addAllowlistUserEntriesFromConfigEntry(target: Set<string>, entr
|
||||
return;
|
||||
}
|
||||
for (const value of users) {
|
||||
const trimmed = String(value).trim();
|
||||
const trimmed = normalizeOptionalString(value) ?? "";
|
||||
if (trimmed && trimmed !== "*") {
|
||||
target.add(trimmed);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
normalizeAccountId,
|
||||
normalizeOptionalAccountId,
|
||||
} from "../../routing/session-key.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import type { ChannelAccountSnapshot } from "./types.core.js";
|
||||
|
||||
export function createAccountListHelpers(
|
||||
@@ -188,10 +189,7 @@ export function describeAccountSnapshot<
|
||||
}): ChannelAccountSnapshot {
|
||||
return {
|
||||
accountId: String(params.account.accountId ?? DEFAULT_ACCOUNT_ID),
|
||||
name:
|
||||
typeof params.account.name === "string" && params.account.name.trim()
|
||||
? params.account.name
|
||||
: undefined,
|
||||
name: normalizeOptionalString(params.account.name),
|
||||
enabled: params.account.enabled !== false,
|
||||
configured: params.configured,
|
||||
...params.extra,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { listBundledChannelPluginIds } from "./bundled-ids.js";
|
||||
import {
|
||||
getBundledChannelPlugin,
|
||||
@@ -93,7 +94,7 @@ export function listBootstrapChannelPlugins(): readonly ChannelPlugin[] {
|
||||
}
|
||||
|
||||
export function getBootstrapChannelPlugin(id: ChannelId): ChannelPlugin | undefined {
|
||||
const resolvedId = String(id).trim();
|
||||
const resolvedId = normalizeOptionalString(id) ?? "";
|
||||
if (!resolvedId) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -120,7 +121,7 @@ export function getBootstrapChannelPlugin(id: ChannelId): ChannelPlugin | undefi
|
||||
}
|
||||
|
||||
export function getBootstrapChannelSecrets(id: ChannelId): ChannelPlugin["secrets"] | undefined {
|
||||
const resolvedId = String(id).trim();
|
||||
const resolvedId = normalizeOptionalString(id) ?? "";
|
||||
if (!resolvedId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "../../shared/string-coerce.js";
|
||||
import { normalizeStringEntries } from "../../shared/string-normalization.js";
|
||||
|
||||
export type ServicePrefix<TService extends string> = { prefix: string; service: TService };
|
||||
|
||||
@@ -34,7 +38,7 @@ function isAllowedParsedChatSender<TParsed extends ParsedChatAllowTarget>(params
|
||||
normalizeSender: (sender: string) => string;
|
||||
parseAllowTarget: (entry: string) => TParsed;
|
||||
}): boolean {
|
||||
const allowFrom = params.allowFrom.map((entry) => String(entry).trim());
|
||||
const allowFrom = normalizeStringEntries(params.allowFrom);
|
||||
if (allowFrom.length === 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -44,8 +48,8 @@ function isAllowedParsedChatSender<TParsed extends ParsedChatAllowTarget>(params
|
||||
|
||||
const senderNormalized = params.normalizeSender(params.sender);
|
||||
const chatId = params.chatId ?? undefined;
|
||||
const chatGuid = params.chatGuid?.trim();
|
||||
const chatIdentifier = params.chatIdentifier?.trim();
|
||||
const chatGuid = normalizeOptionalString(params.chatGuid);
|
||||
const chatIdentifier = normalizeOptionalString(params.chatIdentifier);
|
||||
|
||||
for (const entry of allowFrom) {
|
||||
if (!entry) {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import type { OpenClawConfig } from "../../config/types.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "../../shared/string-coerce.js";
|
||||
import type { DirectoryConfigParams } from "./directory-types.js";
|
||||
import type { ChannelDirectoryEntry } from "./types.js";
|
||||
|
||||
@@ -30,11 +33,11 @@ function normalizeDirectoryIds(params: {
|
||||
normalizeId?: (entry: string) => string | null | undefined;
|
||||
}): string[] {
|
||||
return params.rawIds
|
||||
.map((entry) => entry.trim())
|
||||
.map((entry) => normalizeOptionalString(entry) ?? "")
|
||||
.filter((entry) => Boolean(entry) && entry !== "*")
|
||||
.map((entry) => {
|
||||
const normalized = params.normalizeId ? params.normalizeId(entry) : entry;
|
||||
return typeof normalized === "string" ? normalized.trim() : "";
|
||||
return normalizeOptionalString(normalized) ?? "";
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
@@ -70,12 +73,12 @@ export function collectNormalizedDirectoryIds(params: {
|
||||
const ids = new Set<string>();
|
||||
for (const source of params.sources) {
|
||||
for (const value of source) {
|
||||
const raw = String(value).trim();
|
||||
const raw = normalizeOptionalString(value) ?? "";
|
||||
if (!raw || raw === "*") {
|
||||
continue;
|
||||
}
|
||||
const normalized = params.normalizeId(raw);
|
||||
const trimmed = typeof normalized === "string" ? normalized.trim() : "";
|
||||
const trimmed = normalizeOptionalString(normalized) ?? "";
|
||||
if (trimmed) {
|
||||
ids.add(trimmed);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
listChannelCatalogEntries,
|
||||
type PluginChannelCatalogEntry,
|
||||
} from "../../plugins/channel-catalog-registry.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import {
|
||||
isJavaScriptModulePath,
|
||||
loadChannelPluginModule,
|
||||
@@ -40,8 +41,8 @@ function resolveChannelPackageStateMetadata(
|
||||
if (!metadata || typeof metadata !== "object") {
|
||||
return null;
|
||||
}
|
||||
const specifier = typeof metadata.specifier === "string" ? metadata.specifier.trim() : "";
|
||||
const exportName = typeof metadata.exportName === "string" ? metadata.exportName.trim() : "";
|
||||
const specifier = normalizeOptionalString(metadata.specifier) ?? "";
|
||||
const exportName = normalizeOptionalString(metadata.exportName) ?? "";
|
||||
if (!specifier || !exportName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
getActivePluginChannelRegistryVersion,
|
||||
requireActivePluginChannelRegistry,
|
||||
} from "../../plugins/runtime.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { CHAT_CHANNEL_ORDER, type ChatChannelId, normalizeAnyChannelId } from "../registry.js";
|
||||
import { getBundledChannelPlugin } from "./bundled.js";
|
||||
import type { ChannelId, ChannelPlugin } from "./types.js";
|
||||
@@ -10,7 +11,7 @@ function dedupeChannels(channels: ChannelPlugin[]): ChannelPlugin[] {
|
||||
const seen = new Set<string>();
|
||||
const resolved: ChannelPlugin[] = [];
|
||||
for (const plugin of channels) {
|
||||
const id = String(plugin.id).trim();
|
||||
const id = normalizeOptionalString(plugin.id) ?? "";
|
||||
if (!id || seen.has(id)) {
|
||||
continue;
|
||||
}
|
||||
@@ -83,7 +84,7 @@ export function listChannelPlugins(): ChannelPlugin[] {
|
||||
}
|
||||
|
||||
export function getLoadedChannelPlugin(id: ChannelId): ChannelPlugin | undefined {
|
||||
const resolvedId = String(id).trim();
|
||||
const resolvedId = normalizeOptionalString(id) ?? "";
|
||||
if (!resolvedId) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -91,7 +92,7 @@ export function getLoadedChannelPlugin(id: ChannelId): ChannelPlugin | undefined
|
||||
}
|
||||
|
||||
export function getChannelPlugin(id: ChannelId): ChannelPlugin | undefined {
|
||||
const resolvedId = String(id).trim();
|
||||
const resolvedId = normalizeOptionalString(id) ?? "";
|
||||
if (!resolvedId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z, type ZodType } from "zod";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { getBundledChannelPlugin } from "./bundled.js";
|
||||
import { getChannelPlugin } from "./registry.js";
|
||||
import type { ChannelSetupAdapter } from "./types.adapters.js";
|
||||
@@ -499,8 +500,9 @@ export function resolveSingleAccountPromotionTarget(params: {
|
||||
const resolved = surface?.resolveSingleAccountPromotionTarget?.({
|
||||
channel: params.channel,
|
||||
});
|
||||
if (typeof resolved === "string" && resolved.trim()) {
|
||||
return resolveExistingAccountId(resolved);
|
||||
const normalizedResolved = normalizeOptionalString(resolved);
|
||||
if (normalizedResolved) {
|
||||
return resolveExistingAccountId(normalizedResolved);
|
||||
}
|
||||
return resolveExistingAccountId(DEFAULT_ACCOUNT_ID);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
getActivePluginRegistryVersion,
|
||||
requireActivePluginRegistry,
|
||||
} from "../../plugins/runtime.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { CHAT_CHANNEL_ORDER, type ChatChannelId } from "../registry.js";
|
||||
import { listBundledChannelSetupPlugins } from "./bundled.js";
|
||||
import type { ChannelId, ChannelPlugin } from "./types.js";
|
||||
@@ -26,7 +27,7 @@ function dedupeSetupPlugins(plugins: readonly ChannelPlugin[]): ChannelPlugin[]
|
||||
const seen = new Set<string>();
|
||||
const resolved: ChannelPlugin[] = [];
|
||||
for (const plugin of plugins) {
|
||||
const id = String(plugin.id).trim();
|
||||
const id = normalizeOptionalString(plugin.id) ?? "";
|
||||
if (!id || seen.has(id)) {
|
||||
continue;
|
||||
}
|
||||
@@ -81,7 +82,7 @@ export function listChannelSetupPlugins(): ChannelPlugin[] {
|
||||
}
|
||||
|
||||
export function getChannelSetupPlugin(id: ChannelId): ChannelPlugin | undefined {
|
||||
const resolvedId = String(id).trim();
|
||||
const resolvedId = normalizeOptionalString(id) ?? "";
|
||||
if (!resolvedId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { SecretInput } from "../../config/types.secrets.js";
|
||||
import { resolveSecretInputModeForEnvSelection } from "../../plugins/provider-auth-mode.js";
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { normalizeStringEntries } from "../../shared/string-normalization.js";
|
||||
import type { WizardPrompter } from "../../wizard/prompts.js";
|
||||
import {
|
||||
moveSingleAccountChannelSectionToDefaultAccount,
|
||||
@@ -50,10 +51,10 @@ export const promptAccountId: PromptAccountId = async (params: PromptAccountIdPa
|
||||
|
||||
const entered = await params.prompter.text({
|
||||
message: `New ${params.label} account id`,
|
||||
validate: (value) => (value?.trim() ? undefined : "Required"),
|
||||
validate: (value) => (normalizeOptionalString(value) ? undefined : "Required"),
|
||||
});
|
||||
const normalized = normalizeAccountId(String(entered));
|
||||
if (String(entered).trim() !== normalized) {
|
||||
if ((normalizeOptionalString(entered) ?? "") !== normalized) {
|
||||
await params.prompter.note(
|
||||
`Normalized account id to "${normalized}".`,
|
||||
`${params.label} account`,
|
||||
@@ -63,7 +64,7 @@ export const promptAccountId: PromptAccountId = async (params: PromptAccountIdPa
|
||||
};
|
||||
|
||||
export function addWildcardAllowFrom(allowFrom?: ReadonlyArray<string | number> | null): string[] {
|
||||
const next = (allowFrom ?? []).map((v) => String(v).trim()).filter(Boolean);
|
||||
const next = normalizeStringEntries(allowFrom ?? []);
|
||||
if (!next.includes("*")) {
|
||||
next.push("*");
|
||||
}
|
||||
@@ -74,7 +75,7 @@ export function mergeAllowFromEntries(
|
||||
current: Array<string | number> | null | undefined,
|
||||
additions: Array<string | number>,
|
||||
): string[] {
|
||||
const merged = [...(current ?? []), ...additions].map((v) => String(v).trim()).filter(Boolean);
|
||||
const merged = normalizeStringEntries([...(current ?? []), ...additions]);
|
||||
return [...new Set(merged)];
|
||||
}
|
||||
|
||||
@@ -144,9 +145,7 @@ export function normalizeAllowFromEntries(
|
||||
entries: Array<string | number>,
|
||||
normalizeEntry?: (value: string) => string | null | undefined,
|
||||
): string[] {
|
||||
const normalized = entries
|
||||
.map((entry) => String(entry).trim())
|
||||
.filter(Boolean)
|
||||
const normalized = normalizeStringEntries(entries)
|
||||
.map((entry) => {
|
||||
if (entry === "*") {
|
||||
return "*";
|
||||
@@ -154,8 +153,7 @@ export function normalizeAllowFromEntries(
|
||||
if (!normalizeEntry) {
|
||||
return entry;
|
||||
}
|
||||
const value = normalizeEntry(entry);
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
return normalizeOptionalString(normalizeEntry(entry)) ?? "";
|
||||
})
|
||||
.filter(Boolean);
|
||||
return [...new Set(normalized)];
|
||||
@@ -1215,7 +1213,7 @@ export async function promptParsedAllowFromForAccount<TConfig extends OpenClawCo
|
||||
placeholder: params.placeholder,
|
||||
initialValue: existing[0] ? String(existing[0]) : undefined,
|
||||
validate: (value) => {
|
||||
const raw = String(value ?? "").trim();
|
||||
const raw = normalizeOptionalString(value) ?? "";
|
||||
if (!raw) {
|
||||
return "Required";
|
||||
}
|
||||
@@ -1517,7 +1515,7 @@ export async function promptResolvedAllowFrom(params: {
|
||||
message: params.message,
|
||||
placeholder: params.placeholder,
|
||||
initialValue: params.existing[0] ? String(params.existing[0]) : undefined,
|
||||
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
|
||||
validate: (value) => (normalizeOptionalString(value) ? undefined : "Required"),
|
||||
});
|
||||
const parts = params.parseInputs(String(entry));
|
||||
if (!params.token) {
|
||||
|
||||
@@ -693,7 +693,7 @@ export function buildChannelSetupWizardAdapterFromSetupWizard(params: {
|
||||
initialValue,
|
||||
placeholder: textInput.placeholder,
|
||||
validate: (value) => {
|
||||
const trimmed = String(value ?? "").trim();
|
||||
const trimmed = normalizeOptionalString(value) ?? "";
|
||||
if (!trimmed && textInput.required !== false) {
|
||||
return "Required";
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { projectSafeChannelAccountSnapshotFields } from "../account-snapshot-fields.js";
|
||||
import { inspectReadOnlyChannelAccount } from "../read-only-account-inspect.js";
|
||||
import type { ChannelAccountSnapshot, ChannelPlugin } from "./types.js";
|
||||
@@ -21,7 +22,7 @@ async function buildSnapshotFromAccount<ResolvedAccount>(params: {
|
||||
probe: params.probe,
|
||||
audit: params.audit,
|
||||
});
|
||||
return typeof snapshot.accountId === "string" && snapshot.accountId.trim().length > 0
|
||||
return normalizeOptionalString(snapshot.accountId)
|
||||
? snapshot
|
||||
: {
|
||||
...snapshot,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type NodeMatchCandidate, resolveNodeIdFromCandidates } from "./node-match.js";
|
||||
import { normalizeOptionalString } from "./string-coerce.js";
|
||||
|
||||
type ResolveNodeFromListOptions<TNode extends NodeMatchCandidate> = {
|
||||
allowDefault?: boolean;
|
||||
@@ -10,7 +11,7 @@ export function resolveNodeIdFromNodeList<TNode extends NodeMatchCandidate>(
|
||||
query?: string,
|
||||
options: ResolveNodeFromListOptions<TNode> = {},
|
||||
): string {
|
||||
const q = String(query ?? "").trim();
|
||||
const q = normalizeOptionalString(query) ?? "";
|
||||
if (!q) {
|
||||
if (options.allowDefault === true && options.pickDefaultNode) {
|
||||
const picked = options.pickDefaultNode(nodes);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { normalizeOptionalLowercaseString, normalizeOptionalString } from "./string-coerce.js";
|
||||
|
||||
export function normalizeStringEntries(list?: ReadonlyArray<unknown>) {
|
||||
return (list ?? []).map((entry) => String(entry).trim()).filter(Boolean);
|
||||
return (list ?? []).map((entry) => normalizeOptionalString(String(entry)) ?? "").filter(Boolean);
|
||||
}
|
||||
|
||||
export function normalizeStringEntriesLower(list?: ReadonlyArray<unknown>) {
|
||||
@@ -40,7 +40,7 @@ export function normalizeSingleOrTrimmedStringList(value: unknown): string[] {
|
||||
|
||||
export function normalizeCsvOrLooseStringList(value: unknown): string[] {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((entry) => String(entry).trim()).filter(Boolean);
|
||||
return normalizeStringEntries(value);
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
return value
|
||||
|
||||
Reference in New Issue
Block a user