mirror of
https://github.com/rookiestar28/ComfyUI-OpenClaw.git
synced 2026-08-14 08:52:45 +00:00
199 lines
6.1 KiB
JavaScript
199 lines
6.1 KiB
JavaScript
export const PARAMETER_LAB_POLICY = Object.freeze({
|
|
version: "1.0",
|
|
maxRequestBytes: 5 * 1024 * 1024,
|
|
maxWorkflowUtf8Bytes: 4 * 1024 * 1024,
|
|
maxSweepDimensions: 8,
|
|
maxValuesPerDimension: 50,
|
|
maxNodeIdUtf8Bytes: 128,
|
|
maxWidgetNameUtf8Bytes: 256,
|
|
maxScalarStringUtf8Bytes: 16 * 1024,
|
|
maxPlanUtf8Bytes: 8 * 1024 * 1024,
|
|
maxSweepCombinations: 50,
|
|
maxCompareItems: 8,
|
|
});
|
|
|
|
const encoder = new TextEncoder();
|
|
const valid = () => ({ ok: true, reason: "" });
|
|
const invalid = (reason) => ({ ok: false, reason });
|
|
|
|
function utf8Size(value) {
|
|
return encoder.encode(value).byteLength;
|
|
}
|
|
|
|
function hasControlCharacters(value) {
|
|
return Array.from(value).some((character) => {
|
|
const code = character.charCodeAt(0);
|
|
return code < 32 || code === 127;
|
|
});
|
|
}
|
|
|
|
function validateNodeId(nodeId) {
|
|
if (nodeId === null || nodeId === undefined || nodeId === "") {
|
|
return invalid("node_id_required");
|
|
}
|
|
if (
|
|
typeof nodeId !== "string" &&
|
|
!(typeof nodeId === "number" && Number.isInteger(nodeId))
|
|
) {
|
|
return invalid("invalid_node_id");
|
|
}
|
|
const normalized = String(nodeId);
|
|
if (!normalized.trim()) {
|
|
return invalid("node_id_required");
|
|
}
|
|
// IMPORTANT: replay keys use the first "." as the node/widget separator.
|
|
if (normalized.includes(".") || hasControlCharacters(normalized)) {
|
|
return invalid("invalid_node_id");
|
|
}
|
|
if (utf8Size(normalized) > PARAMETER_LAB_POLICY.maxNodeIdUtf8Bytes) {
|
|
return invalid("node_id_too_large");
|
|
}
|
|
return valid();
|
|
}
|
|
|
|
function validateWidgetName(widgetName) {
|
|
if (typeof widgetName !== "string" || !widgetName.trim()) {
|
|
return invalid("widget_name_required");
|
|
}
|
|
if (hasControlCharacters(widgetName)) {
|
|
return invalid("invalid_widget_name");
|
|
}
|
|
if (utf8Size(widgetName) > PARAMETER_LAB_POLICY.maxWidgetNameUtf8Bytes) {
|
|
return invalid("widget_name_too_large");
|
|
}
|
|
return valid();
|
|
}
|
|
|
|
export function isParameterLabScalar(value) {
|
|
return (
|
|
typeof value === "string" ||
|
|
typeof value === "boolean" ||
|
|
(typeof value === "number" && Number.isFinite(value))
|
|
);
|
|
}
|
|
|
|
function validateScalar(value) {
|
|
if (!isParameterLabScalar(value)) {
|
|
return invalid("invalid_scalar_value");
|
|
}
|
|
if (
|
|
typeof value === "string" &&
|
|
utf8Size(value) > PARAMETER_LAB_POLICY.maxScalarStringUtf8Bytes
|
|
) {
|
|
return invalid("scalar_string_too_large");
|
|
}
|
|
return valid();
|
|
}
|
|
|
|
export function validateParameterLabScalar(value) {
|
|
return validateScalar(value);
|
|
}
|
|
|
|
export function filterParameterLabCandidates(candidates) {
|
|
if (!Array.isArray(candidates)) {
|
|
return [];
|
|
}
|
|
const filtered = [];
|
|
const presentations = new Set();
|
|
for (const candidate of candidates) {
|
|
if (!validateScalar(candidate).ok) {
|
|
continue;
|
|
}
|
|
const presentation = String(candidate);
|
|
if (presentations.has(presentation)) {
|
|
continue;
|
|
}
|
|
presentations.add(presentation);
|
|
filtered.push(candidate);
|
|
}
|
|
return filtered;
|
|
}
|
|
|
|
export function validateParameterLabDimensions(dimensions) {
|
|
if (!Array.isArray(dimensions) || dimensions.length === 0) {
|
|
return invalid("dimensions_required");
|
|
}
|
|
if (dimensions.length > PARAMETER_LAB_POLICY.maxSweepDimensions) {
|
|
return invalid("too_many_dimensions");
|
|
}
|
|
|
|
const dimensionKeys = new Set();
|
|
let combinations = 1;
|
|
for (const dimension of dimensions) {
|
|
if (!dimension || typeof dimension !== "object" || Array.isArray(dimension)) {
|
|
return invalid("invalid_dimension");
|
|
}
|
|
const nodeResult = validateNodeId(dimension.node_id);
|
|
if (!nodeResult.ok) {
|
|
return nodeResult;
|
|
}
|
|
const widgetResult = validateWidgetName(dimension.widget_name);
|
|
if (!widgetResult.ok) {
|
|
return widgetResult;
|
|
}
|
|
const dimensionKey = `${String(dimension.node_id)}\u0000${dimension.widget_name}`;
|
|
if (dimensionKeys.has(dimensionKey)) {
|
|
return invalid("duplicate_dimension");
|
|
}
|
|
dimensionKeys.add(dimensionKey);
|
|
|
|
if (!Array.isArray(dimension.values) || dimension.values.length === 0) {
|
|
return invalid("values_required");
|
|
}
|
|
if (dimension.values.length > PARAMETER_LAB_POLICY.maxValuesPerDimension) {
|
|
return invalid("too_many_values");
|
|
}
|
|
const presentations = new Set();
|
|
for (const value of dimension.values) {
|
|
const scalarResult = validateScalar(value);
|
|
if (!scalarResult.ok) {
|
|
return scalarResult;
|
|
}
|
|
const presentation = String(value);
|
|
if (presentations.has(presentation)) {
|
|
return invalid("duplicate_ambiguous_value");
|
|
}
|
|
presentations.add(presentation);
|
|
}
|
|
|
|
const strategy = dimension.strategy || "grid";
|
|
if (strategy !== "grid" && strategy !== "compare") {
|
|
return invalid("invalid_strategy");
|
|
}
|
|
combinations *= dimension.values.length;
|
|
if (combinations > PARAMETER_LAB_POLICY.maxSweepCombinations) {
|
|
return invalid("sweep_too_large");
|
|
}
|
|
}
|
|
return valid();
|
|
}
|
|
|
|
export function validateParameterLabWorkflow(workflowJson) {
|
|
if (typeof workflowJson !== "string" || !workflowJson.trim()) {
|
|
return invalid("workflow_required");
|
|
}
|
|
if (utf8Size(workflowJson) > PARAMETER_LAB_POLICY.maxWorkflowUtf8Bytes) {
|
|
return invalid("workflow_too_large");
|
|
}
|
|
return valid();
|
|
}
|
|
|
|
export function validateParameterLabRequestBody(payload) {
|
|
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
return invalid("invalid_payload");
|
|
}
|
|
let serialized;
|
|
try {
|
|
serialized = JSON.stringify(payload);
|
|
} catch {
|
|
return invalid("invalid_payload");
|
|
}
|
|
if (typeof serialized !== "string") {
|
|
return invalid("invalid_payload");
|
|
}
|
|
if (utf8Size(serialized) > PARAMETER_LAB_POLICY.maxRequestBytes) {
|
|
return invalid("payload_too_large");
|
|
}
|
|
return valid();
|
|
}
|