Files
openclaw/src/shared/safe-record.ts
Peter Steinberger 2aa4ff0825 refactor: consolidate duplicate helper exports (#106016)
* refactor(normalization): consolidate string helpers

* refactor(normalization): consolidate agent ids

* refactor(paths): consolidate user path resolution

* refactor(gateway): preserve transcript helper facade

* fix(normalization): align helper build aliases

* fix(memory): keep helper import line neutral

* chore(plugin-sdk): align consolidated helper surface

* refactor(normalization): reuse lowercase string helper

* refactor(normalization): reuse record guard

* refactor(normalization): share surrogate boundary helper

* refactor(agents): share token estimate constant

* refactor(memory): distinguish standalone helper contracts

* refactor(normalization): share error cause formatter

* refactor(config): distinguish eligibility predicates

* refactor(plugins): share registry state symbol

* refactor(cli): reuse outbound dependency adapter

* refactor(cron): distinguish positive duration parser

* fix(gateway): keep transcript helper private

* fix(paths): preserve public helper status

* refactor(channels): reuse registry normalizer

* refactor(agents): reuse agent core runtime facade

* refactor(auth): reuse locked profile upsert

* refactor(records): distinguish trap-safe guard

* refactor(migrations): distinguish sync directory helper

* test(plugins): drop stale duration alias expectations

* fix(channels): remove stale registry import

* chore(plugin-sdk): refresh helper API baseline

* fix(memory): keep renamed helpers private

* fix(plugins): keep registry state key private

* fix(plugin-sdk): tighten wildcard surface budget

* chore(plugin-sdk): refresh rebased helper API baseline
2026-07-13 03:22:00 -07:00

77 lines
1.8 KiB
TypeScript

/** Defensive object guard for values that may have hostile traps. */
export function isRecordWithoutThrowing(value: unknown): value is Record<string, unknown> {
try {
return Boolean(value && typeof value === "object" && !Array.isArray(value));
} catch {
return false;
}
}
/** Read one property from a record-like value without letting traps escape. */
export function readRecordValue(value: unknown, key: string): unknown {
if (!isRecordWithoutThrowing(value)) {
return undefined;
}
try {
return value[key];
} catch {
return undefined;
}
}
/** Copy array entries defensively from values that may throw on length/index access. */
export function copyArrayEntries(value: unknown): unknown[] {
let isArray: boolean;
try {
isArray = Array.isArray(value);
} catch {
return [];
}
if (!isArray) {
return [];
}
const arrayValue = value as readonly unknown[];
let length: number;
try {
length = arrayValue.length;
} catch {
return [];
}
const entries: unknown[] = [];
for (let index = 0; index < length; index += 1) {
try {
entries.push(arrayValue[index]);
} catch {
continue;
}
}
return entries;
}
/** Copy record entries whose values are also record-shaped. */
export function copyRecordEntries<T>(value: unknown): Array<[string, T]> {
if (!isRecordWithoutThrowing(value)) {
return [];
}
let keys: string[];
try {
keys = Object.keys(value);
} catch {
return [];
}
const entries: Array<[string, T]> = [];
for (const key of keys) {
const entry = readRecordValue(value, key);
// Callers use this for nested config maps; non-object leaves are ignored so
// later code does not need repeated record guards.
if (isRecordWithoutThrowing(entry)) {
entries.push([key, entry as T]);
}
}
return entries;
}