Files
openclaw/src/plugins/host-hook-json.ts
EVA 1adaa28dc8 [plugin sdk] Add generic plugin host-hook contracts (#72287)
Merged via squash.

Prepared head SHA: 68e5f2ce19
Co-authored-by: 100yenadmin <239388517+100yenadmin@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-04-27 17:07:02 -07:00

80 lines
2.1 KiB
TypeScript

export type PluginJsonPrimitive = string | number | boolean | null;
export type PluginJsonValue =
| PluginJsonPrimitive
| PluginJsonValue[]
| { [key: string]: PluginJsonValue };
export type PluginJsonValueLimits = {
maxDepth: number;
maxNodes: number;
maxObjectKeys: number;
maxStringLength: number;
maxSerializedBytes: number;
};
export const PLUGIN_JSON_VALUE_LIMITS: PluginJsonValueLimits = {
maxDepth: 32,
maxNodes: 4096,
maxObjectKeys: 512,
maxStringLength: 64 * 1024,
maxSerializedBytes: 256 * 1024,
};
function isPluginJsonValueWithinLimits(
value: unknown,
limits: PluginJsonValueLimits,
state: { depth: number; nodes: number },
): value is PluginJsonValue {
state.nodes += 1;
if (state.nodes > limits.maxNodes || state.depth > limits.maxDepth) {
return false;
}
if (value === null || typeof value === "boolean") {
return true;
}
if (typeof value === "string") {
return value.length <= limits.maxStringLength;
}
if (typeof value === "number") {
return Number.isFinite(value);
}
if (Array.isArray(value)) {
state.depth += 1;
const ok = value.every((entry) => isPluginJsonValueWithinLimits(entry, limits, state));
state.depth -= 1;
return ok;
}
if (typeof value !== "object") {
return false;
}
const prototype = Object.getPrototypeOf(value);
if (prototype !== Object.prototype && prototype !== null) {
return false;
}
const entries = Object.entries(value as Record<string, unknown>);
if (entries.length > limits.maxObjectKeys) {
return false;
}
state.depth += 1;
const ok = entries.every(
([key, entry]) =>
key.length <= limits.maxStringLength && isPluginJsonValueWithinLimits(entry, limits, state),
);
state.depth -= 1;
return ok;
}
export function isPluginJsonValue(value: unknown): value is PluginJsonValue {
if (!isPluginJsonValueWithinLimits(value, PLUGIN_JSON_VALUE_LIMITS, { depth: 0, nodes: 0 })) {
return false;
}
try {
return (
Buffer.byteLength(JSON.stringify(value), "utf8") <=
PLUGIN_JSON_VALUE_LIMITS.maxSerializedBytes
);
} catch {
return false;
}
}